OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserv
ed. | |
3 * Copyright (C) 2008 Holger Hans Peter Freyther | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Library General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Library General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Library General Public License | |
16 * along with this library; see the file COPYING.LIB. If not, write to | |
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
18 * Boston, MA 02110-1301, USA. | |
19 * | |
20 */ | |
21 | |
22 #include "config.h" | |
23 #include "core/platform/graphics/WidthIterator.h" | |
24 | |
25 #include "core/platform/graphics/SimpleFontData.h" | |
26 #include "platform/fonts/GlyphBuffer.h" | |
27 #include "platform/fonts/Latin1TextIterator.h" | |
28 #include "platform/text/SurrogatePairAwareTextIterator.h" | |
29 #include "wtf/MathExtras.h" | |
30 | |
31 using namespace WTF; | |
32 using namespace Unicode; | |
33 using namespace std; | |
34 | |
35 namespace WebCore { | |
36 | |
37 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const
SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphas
is) | |
38 : m_font(font) | |
39 , m_run(run) | |
40 , m_currentCharacter(0) | |
41 , m_runWidthSoFar(0) | |
42 , m_isAfterExpansion(!run.allowsLeadingExpansion()) | |
43 , m_finalRoundingWidth(0) | |
44 , m_typesettingFeatures(font->typesettingFeatures()) | |
45 , m_fallbackFonts(fallbackFonts) | |
46 , m_accountForGlyphBounds(accountForGlyphBounds) | |
47 , m_maxGlyphBoundingBoxY(numeric_limits<float>::min()) | |
48 , m_minGlyphBoundingBoxY(numeric_limits<float>::max()) | |
49 , m_firstGlyphOverflow(0) | |
50 , m_lastGlyphOverflow(0) | |
51 , m_forTextEmphasis(forTextEmphasis) | |
52 { | |
53 // If the padding is non-zero, count the number of spaces in the run | |
54 // and divide that by the padding for per space addition. | |
55 m_expansion = m_run.expansion(); | |
56 if (!m_expansion) | |
57 m_expansionPerOpportunity = 0; | |
58 else { | |
59 bool isAfterExpansion = m_isAfterExpansion; | |
60 unsigned expansionOpportunityCount = m_run.is8Bit() ? Font::expansionOpp
ortunityCount(m_run.characters8(), m_run.length(), m_run.ltr() ? LTR : RTL, isAf
terExpansion) : Font::expansionOpportunityCount(m_run.characters16(), m_run.leng
th(), m_run.ltr() ? LTR : RTL, isAfterExpansion); | |
61 if (isAfterExpansion && !m_run.allowsTrailingExpansion()) | |
62 expansionOpportunityCount--; | |
63 | |
64 if (!expansionOpportunityCount) | |
65 m_expansionPerOpportunity = 0; | |
66 else | |
67 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; | |
68 } | |
69 } | |
70 | |
71 GlyphData WidthIterator::glyphDataForCharacter(UChar32 character, bool mirror, i
nt currentCharacter, unsigned& advanceLength) | |
72 { | |
73 ASSERT(m_font); | |
74 | |
75 #if ENABLE(SVG_FONTS) | |
76 if (TextRun::RenderingContext* renderingContext = m_run.renderingContext()) | |
77 return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, ch
aracter, mirror, currentCharacter, advanceLength); | |
78 #else | |
79 UNUSED_PARAM(currentCharacter); | |
80 UNUSED_PARAM(advanceLength); | |
81 #endif | |
82 | |
83 return m_font->glyphDataForCharacter(character, mirror); | |
84 } | |
85 | |
86 struct OriginalAdvancesForCharacterTreatedAsSpace { | |
87 public: | |
88 OriginalAdvancesForCharacterTreatedAsSpace(bool isSpace, float advanceBefore
, float advanceAt) | |
89 : characterIsSpace(isSpace) | |
90 , advanceBeforeCharacter(advanceBefore) | |
91 , advanceAtCharacter(advanceAt) | |
92 { | |
93 } | |
94 | |
95 bool characterIsSpace; | |
96 float advanceBeforeCharacter; | |
97 float advanceAtCharacter; | |
98 }; | |
99 | |
100 typedef Vector<pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> Charac
tersTreatedAsSpace; | |
101 | |
102 static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, unsi
gned& lastGlyphCount, const SimpleFontData* fontData, TypesettingFeatures typese
ttingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace) | |
103 { | |
104 ASSERT(typesettingFeatures & (Kerning | Ligatures)); | |
105 | |
106 if (!glyphBuffer) | |
107 return 0; | |
108 | |
109 unsigned glyphBufferSize = glyphBuffer->size(); | |
110 if (glyphBuffer->size() <= lastGlyphCount + 1) | |
111 return 0; | |
112 | |
113 GlyphBufferAdvance* advances = glyphBuffer->advances(0); | |
114 float widthDifference = 0; | |
115 for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i) | |
116 widthDifference -= advances[i].width(); | |
117 | |
118 if (!ltr) | |
119 glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount); | |
120 | |
121 fontData->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + la
stGlyphCount, glyphBufferSize - lastGlyphCount, typesettingFeatures); | |
122 | |
123 if (!ltr) | |
124 glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount); | |
125 | |
126 for (size_t i = 0; i < charactersTreatedAsSpace.size(); ++i) { | |
127 int spaceOffset = charactersTreatedAsSpace[i].first; | |
128 const OriginalAdvancesForCharacterTreatedAsSpace& originalAdvances = cha
ractersTreatedAsSpace[i].second; | |
129 if (spaceOffset && !originalAdvances.characterIsSpace) | |
130 glyphBuffer->advances(spaceOffset - 1)->setWidth(originalAdvances.ad
vanceBeforeCharacter); | |
131 glyphBuffer->advances(spaceOffset)->setWidth(originalAdvances.advanceAtC
haracter); | |
132 } | |
133 charactersTreatedAsSpace.clear(); | |
134 | |
135 for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i) | |
136 widthDifference += advances[i].width(); | |
137 | |
138 lastGlyphCount = glyphBufferSize; | |
139 return widthDifference; | |
140 } | |
141 | |
142 template <typename TextIterator> | |
143 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, Glyph
Buffer* glyphBuffer) | |
144 { | |
145 bool rtl = m_run.rtl(); | |
146 bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() ||
m_expansion) && !m_run.spacingDisabled(); | |
147 | |
148 float widthSinceLastRounding = m_runWidthSoFar; | |
149 m_runWidthSoFar = floorf(m_runWidthSoFar); | |
150 widthSinceLastRounding -= m_runWidthSoFar; | |
151 | |
152 float lastRoundingWidth = m_finalRoundingWidth; | |
153 FloatRect bounds; | |
154 | |
155 const SimpleFontData* primaryFont = m_font->primaryFont(); | |
156 const SimpleFontData* lastFontData = primaryFont; | |
157 unsigned lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0; | |
158 | |
159 UChar32 character = 0; | |
160 unsigned clusterLength = 0; | |
161 CharactersTreatedAsSpace charactersTreatedAsSpace; | |
162 while (textIterator.consume(character, clusterLength)) { | |
163 unsigned advanceLength = clusterLength; | |
164 const GlyphData& glyphData = glyphDataForCharacter(character, rtl, textI
terator.currentCharacter(), advanceLength); | |
165 Glyph glyph = glyphData.glyph; | |
166 const SimpleFontData* fontData = glyphData.fontData; | |
167 | |
168 ASSERT(fontData); | |
169 | |
170 // Now that we have a glyph and font data, get its width. | |
171 float width; | |
172 if (character == '\t' && m_run.allowTabs()) | |
173 width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() +
m_runWidthSoFar + widthSinceLastRounding); | |
174 else { | |
175 width = fontData->widthForGlyph(glyph); | |
176 | |
177 // SVG uses horizontalGlyphStretch(), when textLength is used to str
etch/squeeze text. | |
178 width *= m_run.horizontalGlyphStretch(); | |
179 | |
180 // We special case spaces in two ways when applying word rounding. | |
181 // First, we round spaces to an adjusted width in all fonts. | |
182 // Second, in fixed-pitch fonts we ensure that all characters that | |
183 // match the width of the space character have the same width as the
space character. | |
184 if (m_run.applyWordRounding() && width == fontData->spaceWidth() &&
(fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph())) | |
185 width = fontData->adjustedSpaceWidth(); | |
186 } | |
187 | |
188 if (fontData != lastFontData && width) { | |
189 if (shouldApplyFontTransforms()) | |
190 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(),
lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace); | |
191 | |
192 lastFontData = fontData; | |
193 if (m_fallbackFonts && fontData != primaryFont) { | |
194 // FIXME: This does a little extra work that could be avoided if | |
195 // glyphDataForCharacter() returned whether it chose to use a sm
all caps font. | |
196 if (!m_font->isSmallCaps() || character == toUpper(character)) | |
197 m_fallbackFonts->add(fontData); | |
198 else { | |
199 const GlyphData& uppercaseGlyphData = m_font->glyphDataForCh
aracter(toUpper(character), rtl); | |
200 if (uppercaseGlyphData.fontData != primaryFont) | |
201 m_fallbackFonts->add(uppercaseGlyphData.fontData); | |
202 } | |
203 } | |
204 } | |
205 | |
206 if (hasExtraSpacing) { | |
207 // Account for letter-spacing. | |
208 if (width && m_font->letterSpacing()) | |
209 width += m_font->letterSpacing(); | |
210 | |
211 static bool expandAroundIdeographs = Font::canExpandAroundIdeographs
InComplexText(); | |
212 bool treatAsSpace = Font::treatAsSpace(character); | |
213 if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographO
rSymbol(character))) { | |
214 // Distribute the run's total expansion evenly over all expansio
n opportunities in the run. | |
215 if (m_expansion) { | |
216 float previousExpansion = m_expansion; | |
217 if (!treatAsSpace && !m_isAfterExpansion) { | |
218 // Take the expansion opportunity before this ideograph. | |
219 m_expansion -= m_expansionPerOpportunity; | |
220 float expansionAtThisOpportunity = !m_run.applyWordRound
ing() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansi
on); | |
221 m_runWidthSoFar += expansionAtThisOpportunity; | |
222 if (glyphBuffer) { | |
223 if (glyphBuffer->isEmpty()) { | |
224 if (m_forTextEmphasis) | |
225 glyphBuffer->add(fontData->zeroWidthSpaceGly
ph(), fontData, m_expansionPerOpportunity); | |
226 else | |
227 glyphBuffer->add(fontData->spaceGlyph(), fon
tData, expansionAtThisOpportunity); | |
228 } else | |
229 glyphBuffer->expandLastAdvance(expansionAtThisOp
portunity); | |
230 } | |
231 previousExpansion = m_expansion; | |
232 } | |
233 if (m_run.allowsTrailingExpansion() || (m_run.ltr() && textI
terator.currentCharacter() + advanceLength < static_cast<size_t>(m_run.length())
) | |
234 || (m_run.rtl() && textIterator.currentCharacter())) { | |
235 m_expansion -= m_expansionPerOpportunity; | |
236 width += !m_run.applyWordRounding() ? m_expansionPerOppo
rtunity : roundf(previousExpansion) - roundf(m_expansion); | |
237 m_isAfterExpansion = true; | |
238 } | |
239 } else | |
240 m_isAfterExpansion = false; | |
241 | |
242 // Account for word spacing. | |
243 // We apply additional space between "words" by adding width to
the space character. | |
244 if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) &&
(textIterator.currentCharacter() || character == noBreakSpace) && m_font->wordS
pacing()) | |
245 width += m_font->wordSpacing(); | |
246 } else | |
247 m_isAfterExpansion = false; | |
248 } | |
249 | |
250 if (shouldApplyFontTransforms() && glyphBuffer && Font::treatAsSpace(cha
racter)) | |
251 charactersTreatedAsSpace.append(make_pair(glyphBuffer->size(), | |
252 OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', gly
phBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1) : 0, width)))
; | |
253 | |
254 if (m_accountForGlyphBounds) { | |
255 bounds = fontData->boundsForGlyph(glyph); | |
256 if (!textIterator.currentCharacter()) | |
257 m_firstGlyphOverflow = max<float>(0, -bounds.x()); | |
258 } | |
259 | |
260 if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(character)) | |
261 glyph = 0; | |
262 | |
263 // Advance past the character we just dealt with. | |
264 textIterator.advance(advanceLength); | |
265 | |
266 float oldWidth = width; | |
267 | |
268 // Force characters that are used to determine word boundaries for the r
ounding hack | |
269 // to be integer width, so following words will start on an integer boun
dary. | |
270 if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(character
)) { | |
271 width = ceilf(width); | |
272 | |
273 // Since widthSinceLastRounding can lose precision if we include mea
surements for | |
274 // preceding whitespace, we bypass it here. | |
275 m_runWidthSoFar += width; | |
276 | |
277 // Since this is a rounding hack character, we should have reset thi
s sum on the previous | |
278 // iteration. | |
279 ASSERT(!widthSinceLastRounding); | |
280 } else { | |
281 // Check to see if the next character is a "rounding hack character"
, if so, adjust | |
282 // width so that the total run width will be on an integer boundary. | |
283 if ((m_run.applyWordRounding() && textIterator.currentCharacter() <
m_run.length() && Font::isRoundingHackCharacter(*(textIterator.characters()))) | |
284 || (m_run.applyRunRounding() && textIterator.currentCharacter()
>= m_run.length())) { | |
285 float totalWidth = widthSinceLastRounding + width; | |
286 widthSinceLastRounding = ceilf(totalWidth); | |
287 width += widthSinceLastRounding - totalWidth; | |
288 m_runWidthSoFar += widthSinceLastRounding; | |
289 widthSinceLastRounding = 0; | |
290 } else | |
291 widthSinceLastRounding += width; | |
292 } | |
293 | |
294 if (glyphBuffer) | |
295 glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidt
h : width)); | |
296 | |
297 lastRoundingWidth = width - oldWidth; | |
298 | |
299 if (m_accountForGlyphBounds) { | |
300 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY()); | |
301 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y()); | |
302 m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width); | |
303 } | |
304 } | |
305 | |
306 if (shouldApplyFontTransforms()) | |
307 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGly
phCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace); | |
308 | |
309 unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCha
racter; | |
310 m_currentCharacter = textIterator.currentCharacter(); | |
311 m_runWidthSoFar += widthSinceLastRounding; | |
312 m_finalRoundingWidth = lastRoundingWidth; | |
313 return consumedCharacters; | |
314 } | |
315 | |
316 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) | |
317 { | |
318 int length = m_run.length(); | |
319 | |
320 if (offset > length) | |
321 offset = length; | |
322 | |
323 if (m_currentCharacter >= static_cast<unsigned>(offset)) | |
324 return 0; | |
325 | |
326 if (m_run.is8Bit()) { | |
327 Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_curre
ntCharacter, offset, length); | |
328 return advanceInternal(textIterator, glyphBuffer); | |
329 } | |
330 | |
331 SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter)
, m_currentCharacter, offset, length); | |
332 return advanceInternal(textIterator, glyphBuffer); | |
333 } | |
334 | |
335 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer) | |
336 { | |
337 unsigned oldSize = glyphBuffer.size(); | |
338 advance(m_currentCharacter + 1, &glyphBuffer); | |
339 float w = 0; | |
340 for (unsigned i = oldSize; i < glyphBuffer.size(); ++i) | |
341 w += glyphBuffer.advanceAt(i); | |
342 width = w; | |
343 return glyphBuffer.size() > oldSize; | |
344 } | |
345 | |
346 } | |
OLD | NEW |