1 /*
2 * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Holger Hans Peter Freyther
4 * Copyright (C) 2014 Google Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "config.h"
24 #include "platform/fonts/WidthIterator.h"
25
26 #include "platform/fonts/Character.h"
27 #include "platform/fonts/Font.h"
28 #include "platform/fonts/FontPlatformFeatures.h"
29 #include "platform/fonts/GlyphBuffer.h"
30 #include "platform/fonts/Latin1TextIterator.h"
31 #include "platform/fonts/SimpleFontData.h"
32 #include "platform/text/SurrogatePairAwareTextIterator.h"
33 #include "wtf/MathExtras.h"
34
35 using namespace WTF;
36 using namespace Unicode;
37
38 namespace blink {
39
WidthIterator(const Font * font,const TextRun & run,HashSet<const SimpleFontData * > * fallbackFonts,bool accountForGlyphBounds,bool forTextEmphasis)40 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
41 : m_font(font)
42 , m_run(run)
43 , m_currentCharacter(0)
44 , m_runWidthSoFar(0)
45 , m_isAfterExpansion(!run.allowsLeadingExpansion())
46 , m_fallbackFonts(fallbackFonts)
47 , m_maxGlyphBoundingBoxY(std::numeric_limits<float>::min())
48 , m_minGlyphBoundingBoxY(std::numeric_limits<float>::max())
49 , m_firstGlyphOverflow(0)
50 , m_lastGlyphOverflow(0)
51 , m_accountForGlyphBounds(accountForGlyphBounds)
52 , m_forTextEmphasis(forTextEmphasis)
53 {
54 // If the padding is non-zero, count the number of spaces in the run
55 // and divide that by the padding for per space addition.
56 m_expansion = m_run.expansion();
57 if (!m_expansion)
58 m_expansionPerOpportunity = 0;
59 else {
60 bool isAfterExpansion = m_isAfterExpansion;
61 unsigned expansionOpportunityCount = m_run.is8Bit() ? Character::expansionOpportunityCount(m_run.characters8(), m_run.length(), m_run.direction(), isAfterExpansion) : Character::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.direction(), isAfterExpansion);
62 if (isAfterExpansion && !m_run.allowsTrailingExpansion())
63 expansionOpportunityCount--;
64
65 if (!expansionOpportunityCount)
66 m_expansionPerOpportunity = 0;
67 else
68 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
69 }
70 }
71
glyphDataForCharacter(CharacterData & charData,bool normalizeSpace)72 GlyphData WidthIterator::glyphDataForCharacter(CharacterData& charData, bool normalizeSpace)
73 {
74 ASSERT(m_font);
75
76 #if ENABLE(SVG_FONTS)
77 if (TextRun::RenderingContext* renderingContext = m_run.renderingContext()) {
78 return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, charData.character,
79 m_run.rtl(), charData.characterOffset, charData.clusterLength);
80 }
81 #endif
82
83 return m_font->glyphDataForCharacter(charData.character, m_run.rtl(), normalizeSpace);
84 }
85
characterWidth(UChar32 character,const GlyphData & glyphData) const86 float WidthIterator::characterWidth(UChar32 character, const GlyphData& glyphData) const
87 {
88 const SimpleFontData* fontData = glyphData.fontData;
89 ASSERT(fontData);
90
91 if (UNLIKELY(character == '\t' && m_run.allowTabs()))
92 return m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar);
93
94 float width = fontData->widthForGlyph(glyphData.glyph);
95
96 // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
97 if (UNLIKELY(m_run.horizontalGlyphStretch() != 1))
98 width *= m_run.horizontalGlyphStretch();
99
100 return width;
101 }
102
cacheFallbackFont(const SimpleFontData * fontData,const SimpleFontData * primaryFont)103 void WidthIterator::cacheFallbackFont(const SimpleFontData* fontData,
104 const SimpleFontData* primaryFont)
105 {
106 if (fontData == primaryFont)
107 return;
108
109 m_fallbackFonts->add(fontData);
110 }
111
adjustSpacing(float width,const CharacterData & charData,const SimpleFontData & fontData,GlyphBuffer * glyphBuffer)112 float WidthIterator::adjustSpacing(float width, const CharacterData& charData,
113 const SimpleFontData& fontData, GlyphBuffer* glyphBuffer)
114 {
115 // Account for letter-spacing.
116 if (width)
117 width += m_font->fontDescription().letterSpacing();
118
119 static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText();
120 bool treatAsSpace = Character::treatAsSpace(charData.character);
121 if (treatAsSpace || (expandAroundIdeographs && Character::isCJKIdeographOrSymbol(charData.character))) {
122 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
123 if (m_expansion) {
124 if (!treatAsSpace && !m_isAfterExpansion) {
125 // Take the expansion opportunity before this ideograph.
126 m_expansion -= m_expansionPerOpportunity;
127 float expansionAtThisOpportunity = m_expansionPerOpportunity;
128 m_runWidthSoFar += expansionAtThisOpportunity;
129 if (glyphBuffer) {
130 if (glyphBuffer->isEmpty()) {
131 if (m_forTextEmphasis)
132 glyphBuffer->add(fontData.zeroWidthSpaceGlyph(), &fontData, m_expansionPerOpportunity);
133 else
134 glyphBuffer->add(fontData.spaceGlyph(), &fontData, expansionAtThisOpportunity);
135 } else {
136 glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
137 }
138 }
139 }
140 if (m_run.allowsTrailingExpansion()
141 || (m_run.ltr() && charData.characterOffset + charData.clusterLength < static_cast<size_t>(m_run.length()))
142 || (m_run.rtl() && charData.characterOffset)) {
143 m_expansion -= m_expansionPerOpportunity;
144 width += m_expansionPerOpportunity;
145 m_isAfterExpansion = true;
146 }
147 } else {
148 m_isAfterExpansion = false;
149 }
150
151 // Account for word spacing.
152 // We apply additional space between "words" by adding width to the space character.
153 if (treatAsSpace && (charData.character != '\t' || !m_run.allowTabs())
154 && (charData.characterOffset || charData.character == noBreakSpace)
155 && m_font->fontDescription().wordSpacing()) {
156 width += m_font->fontDescription().wordSpacing();
157 }
158 } else {
159 m_isAfterExpansion = false;
160 }
161
162 return width;
163 }
164
updateGlyphBounds(const GlyphData & glyphData,float width,bool firstCharacter)165 void WidthIterator::updateGlyphBounds(const GlyphData& glyphData, float width, bool firstCharacter)
166 {
167 ASSERT(glyphData.fontData);
168 FloatRect bounds = glyphData.fontData->boundsForGlyph(glyphData.glyph);
169
170 if (firstCharacter)
171 m_firstGlyphOverflow = std::max<float>(0, -bounds.x());
172 m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width);
173 m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY());
174 m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y());
175 }
176
177 template <typename TextIterator>
advanceInternal(TextIterator & textIterator,GlyphBuffer * glyphBuffer)178 unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
179 {
180 bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion)
181 && !m_run.spacingDisabled();
182
183 const SimpleFontData* primaryFont = m_font->primaryFont();
184 const SimpleFontData* lastFontData = primaryFont;
185 bool normalizeSpace = m_run.normalizeSpace();
186
187 CharacterData charData;
188 while (textIterator.consume(charData.character, charData.clusterLength)) {
189 charData.characterOffset = textIterator.currentCharacter();
190
191 GlyphData glyphData = glyphDataForCharacter(charData, normalizeSpace);
192
193 // Some fonts do not have a glyph for zero-width-space,
194 // in that case use the space character and override the width.
195 float width;
196 if (!glyphData.glyph && Character::treatAsZeroWidthSpaceInComplexScript(charData.character)) {
197 charData.character = space;
198 glyphData = glyphDataForCharacter(charData);
199 width = 0;
200 } else {
201 width = characterWidth(charData.character, glyphData);
202 }
203
204 Glyph glyph = glyphData.glyph;
205 const SimpleFontData* fontData = glyphData.fontData;
206 ASSERT(fontData);
207
208 if (m_fallbackFonts && lastFontData != fontData && width) {
209 lastFontData = fontData;
210 cacheFallbackFont(fontData, primaryFont);
211 }
212
213 if (hasExtraSpacing)
214 width = adjustSpacing(width, charData, *fontData, glyphBuffer);
215
216 if (m_accountForGlyphBounds)
217 updateGlyphBounds(glyphData, width, !charData.characterOffset);
218
219 if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(charData.character))
220 glyph = 0;
221
222 // Advance past the character we just dealt with.
223 textIterator.advance(charData.clusterLength);
224 m_runWidthSoFar += width;
225
226 if (glyphBuffer)
227 glyphBuffer->add(glyph, fontData, width);
228 }
229
230 unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
231 m_currentCharacter = textIterator.currentCharacter();
232
233 return consumedCharacters;
234 }
235
advance(int offset,GlyphBuffer * glyphBuffer)236 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
237 {
238 int length = m_run.length();
239
240 if (offset > length)
241 offset = length;
242
243 if (m_currentCharacter >= static_cast<unsigned>(offset))
244 return 0;
245
246 if (m_run.is8Bit()) {
247 Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
248 return advanceInternal(textIterator, glyphBuffer);
249 }
250
251 SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
252 return advanceInternal(textIterator, glyphBuffer);
253 }
254
advanceOneCharacter(float & width)255 bool WidthIterator::advanceOneCharacter(float& width)
256 {
257 float initialWidth = m_runWidthSoFar;
258
259 if (!advance(m_currentCharacter + 1))
260 return false;
261
262 width = m_runWidthSoFar - initialWidth;
263 return true;
264 }
265
266 } // namespace blink
267