1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 
25 #include "config.h"
26 #include "ComplexTextController.h"
27 
28 #include "FloatSize.h"
29 #include "Font.h"
30 #include "TextBreakIterator.h"
31 #include "TextRun.h"
32 #include <ApplicationServices/ApplicationServices.h>
33 #include <wtf/StdLibExtras.h>
34 #include <wtf/unicode/CharacterNames.h>
35 
36 #if defined(BUILDING_ON_LEOPARD)
37 // Undefined when compiling agains the 10.5 SDK.
38 #define kCTVersionNumber10_6 0x00030000
39 #endif
40 
41 using namespace std;
42 
43 namespace WebCore {
44 
roundCGFloat(CGFloat f)45 static inline CGFloat roundCGFloat(CGFloat f)
46 {
47     if (sizeof(CGFloat) == sizeof(float))
48         return roundf(static_cast<float>(f));
49     return static_cast<CGFloat>(round(f));
50 }
51 
ComplexTextController(const Font * font,const TextRun & run,bool mayUseNaturalWritingDirection,HashSet<const SimpleFontData * > * fallbackFonts,bool forTextEmphasis)52 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis)
53     : m_font(*font)
54     , m_run(run)
55     , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
56     , m_forTextEmphasis(forTextEmphasis)
57     , m_currentCharacter(0)
58     , m_end(run.length())
59     , m_totalWidth(0)
60     , m_runWidthSoFar(0)
61     , m_numGlyphsSoFar(0)
62     , m_currentRun(0)
63     , m_glyphInCurrentRun(0)
64     , m_characterInCurrentGlyph(0)
65     , m_expansion(run.expansion())
66     , m_leadingExpansion(0)
67     , m_afterExpansion(!run.allowsLeadingExpansion())
68     , m_fallbackFonts(fallbackFonts)
69     , m_minGlyphBoundingBoxX(numeric_limits<float>::max())
70     , m_maxGlyphBoundingBoxX(numeric_limits<float>::min())
71     , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
72     , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
73 {
74     if (!m_expansion)
75         m_expansionPerOpportunity = 0;
76     else {
77         bool isAfterExpansion = m_afterExpansion;
78         unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
79         if (isAfterExpansion && !m_run.allowsTrailingExpansion())
80             expansionOpportunityCount--;
81 
82         if (!expansionOpportunityCount)
83             m_expansionPerOpportunity = 0;
84         else
85             m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
86     }
87 
88     collectComplexTextRuns();
89     adjustGlyphsAndAdvances();
90 
91     m_runWidthSoFar = m_leadingExpansion;
92 }
93 
offsetForPosition(float h,bool includePartialGlyphs)94 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
95 {
96     if (h >= m_totalWidth)
97         return m_run.ltr() ? m_end : 0;
98 
99     h -= m_leadingExpansion;
100     if (h < 0)
101         return m_run.ltr() ? 0 : m_end;
102 
103     CGFloat x = h;
104 
105     size_t runCount = m_complexTextRuns.size();
106     size_t offsetIntoAdjustedGlyphs = 0;
107 
108     for (size_t r = 0; r < runCount; ++r) {
109         const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
110         for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
111             CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
112             if (x < adjustedAdvance) {
113                 CFIndex hitGlyphStart = complexTextRun.indexAt(j);
114                 CFIndex hitGlyphEnd;
115                 if (m_run.ltr())
116                     hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
117                 else
118                     hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
119 
120                 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
121                 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
122                 // ligature carets.
123                 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
124                 int stringLength = complexTextRun.stringLength();
125                 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength);
126                 int clusterStart;
127                 if (isTextBreak(cursorPositionIterator, hitIndex))
128                     clusterStart = hitIndex;
129                 else {
130                     clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex);
131                     if (clusterStart == TextBreakDone)
132                         clusterStart = 0;
133                 }
134 
135                 if (!includePartialGlyphs)
136                     return complexTextRun.stringLocation() + clusterStart;
137 
138                 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex);
139                 if (clusterEnd == TextBreakDone)
140                     clusterEnd = stringLength;
141 
142                 CGFloat clusterWidth;
143                 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
144                 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
145                 // reordering and on font fallback should occur within a CTLine.
146                 if (clusterEnd - clusterStart > 1) {
147                     clusterWidth = adjustedAdvance;
148                     int firstGlyphBeforeCluster = j - 1;
149                     while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
150                         CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
151                         clusterWidth += width;
152                         x += width;
153                         firstGlyphBeforeCluster--;
154                     }
155                     unsigned firstGlyphAfterCluster = j + 1;
156                     while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
157                         clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
158                         firstGlyphAfterCluster++;
159                     }
160                 } else {
161                     clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
162                     x -=  clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
163                 }
164                 if (x <= clusterWidth / 2)
165                     return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
166                 else
167                     return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
168             }
169             x -= adjustedAdvance;
170         }
171         offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
172     }
173 
174     ASSERT_NOT_REACHED();
175     return 0;
176 }
177 
collectComplexTextRuns()178 void ComplexTextController::collectComplexTextRuns()
179 {
180     if (!m_end)
181         return;
182 
183     // We break up glyph run generation for the string by FontData and (if needed) the use of small caps.
184     const UChar* cp = m_run.characters();
185 
186     if (m_font.isSmallCaps())
187         m_smallCapsBuffer.resize(m_end);
188 
189     unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0;
190     const UChar* curr = m_run.rtl() ? cp + m_end  - 1 : cp;
191     const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end;
192 
193     GlyphData glyphData;
194     GlyphData nextGlyphData;
195 
196     bool isSurrogate = U16_IS_SURROGATE(*curr);
197     if (isSurrogate) {
198         if (m_run.ltr()) {
199             if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
200                 return;
201             nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
202         } else {
203             if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
204                 return;
205             nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
206         }
207     } else
208         nextGlyphData = m_font.glyphDataForCharacter(*curr, false);
209 
210     UChar newC = 0;
211 
212     bool isSmallCaps;
213     bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr;
214 
215     if (nextIsSmallCaps)
216         m_smallCapsBuffer[curr - cp] = newC;
217 
218     while (true) {
219         curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1);
220         if (curr == end)
221             break;
222 
223         glyphData = nextGlyphData;
224         isSmallCaps = nextIsSmallCaps;
225         int index = curr - cp;
226         isSurrogate = U16_IS_SURROGATE(*curr);
227         UChar c = *curr;
228         bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK);
229         if (isSurrogate) {
230             if (m_run.ltr()) {
231                 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
232                     return;
233                 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
234             } else {
235                 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
236                     return;
237                 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
238             }
239         } else
240             nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps ? SmallCapsVariant : AutoVariant);
241 
242         if (!isSurrogate && m_font.isSmallCaps()) {
243             nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c;
244             if (nextIsSmallCaps)
245                 m_smallCapsBuffer[index] = forceSmallCaps ? c : newC;
246         }
247 
248         if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) {
249             int itemStart = m_run.rtl() ? index + 1 : static_cast<int>(indexOfFontTransition);
250             int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition;
251             collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0);
252             indexOfFontTransition = index;
253         }
254     }
255 
256     int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition;
257     if (itemLength) {
258         int itemStart = m_run.rtl() ? 0 : indexOfFontTransition;
259         collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0);
260     }
261 }
262 
263 #if USE(CORE_TEXT) && USE(ATSUI)
shouldUseATSUIAPI()264 static inline bool shouldUseATSUIAPI()
265 {
266     enum TypeRenderingAPIToUse { UnInitialized, UseATSUI, UseCoreText };
267     static TypeRenderingAPIToUse apiToUse = UnInitialized;
268 
269     if (UNLIKELY(apiToUse == UnInitialized)) {
270         if (&CTGetCoreTextVersion != 0 && CTGetCoreTextVersion() >= kCTVersionNumber10_6)
271             apiToUse = UseCoreText;
272         else
273             apiToUse = UseATSUI;
274     }
275 
276     return apiToUse == UseATSUI;
277 }
278 #endif
279 
indexAt(size_t i) const280 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
281 {
282 #if USE(CORE_TEXT) && USE(ATSUI)
283     return shouldUseATSUIAPI() ? m_atsuiIndices[i] : m_coreTextIndices[i];
284 #elif USE(ATSUI)
285     return m_atsuiIndices[i];
286 #elif USE(CORE_TEXT)
287     return m_coreTextIndices[i];
288 #endif
289 }
290 
collectComplexTextRunsForCharacters(const UChar * cp,unsigned length,unsigned stringLocation,const SimpleFontData * fontData)291 void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData)
292 {
293 #if USE(CORE_TEXT) && USE(ATSUI)
294     if (shouldUseATSUIAPI())
295         return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
296     return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
297 #elif USE(ATSUI)
298     return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
299 #elif USE(CORE_TEXT)
300     return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
301 #endif
302 }
303 
ComplexTextRun(const SimpleFontData * fontData,const UChar * characters,unsigned stringLocation,size_t stringLength,bool ltr)304 ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr)
305     : m_fontData(fontData)
306     , m_characters(characters)
307     , m_stringLocation(stringLocation)
308     , m_stringLength(stringLength)
309     , m_indexEnd(stringLength)
310     , m_isMonotonic(true)
311 {
312 #if USE(CORE_TEXT) && USE(ATSUI)
313     shouldUseATSUIAPI() ? createTextRunFromFontDataATSUI(ltr) : createTextRunFromFontDataCoreText(ltr);
314 #elif USE(ATSUI)
315     createTextRunFromFontDataATSUI(ltr);
316 #elif USE(CORE_TEXT)
317     createTextRunFromFontDataCoreText(ltr);
318 #endif
319 }
320 
setIsNonMonotonic()321 void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
322 {
323     ASSERT(m_isMonotonic);
324     m_isMonotonic = false;
325 
326     Vector<bool, 64> mappedIndices(m_stringLength);
327     for (size_t i = 0; i < m_glyphCount; ++i) {
328         ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
329         mappedIndices[indexAt(i)] = true;
330     }
331 
332     m_glyphEndOffsets.grow(m_glyphCount);
333     for (size_t i = 0; i < m_glyphCount; ++i) {
334         CFIndex nextMappedIndex = m_indexEnd;
335         for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
336             if (mappedIndices[j]) {
337                 nextMappedIndex = j;
338                 break;
339             }
340         }
341         m_glyphEndOffsets[i] = nextMappedIndex;
342     }
343 }
344 
advance(unsigned offset,GlyphBuffer * glyphBuffer)345 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer)
346 {
347     if (static_cast<int>(offset) > m_end)
348         offset = m_end;
349 
350     if (offset <= m_currentCharacter)
351         return;
352 
353     m_currentCharacter = offset;
354 
355     size_t runCount = m_complexTextRuns.size();
356 
357     bool ltr = m_run.ltr();
358 
359     unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar;
360     while (m_currentRun < runCount) {
361         const ComplexTextRun& complexTextRun = *m_complexTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun];
362         size_t glyphCount = complexTextRun.glyphCount();
363         unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
364         while (m_glyphInCurrentRun < glyphCount) {
365             unsigned glyphStartOffset = complexTextRun.indexAt(g);
366             unsigned glyphEndOffset;
367             if (complexTextRun.isMonotonic()) {
368                 if (ltr)
369                     glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd()));
370                 else
371                     glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd()));
372             } else
373                 glyphEndOffset = complexTextRun.endOffsetAt(g);
374 
375             CGSize adjustedAdvance = m_adjustedAdvances[k];
376 
377             if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
378                 return;
379 
380             if (glyphBuffer && !m_characterInCurrentGlyph)
381                 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance);
382 
383             unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
384             m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
385             // FIXME: Instead of dividing the glyph's advance equally between the characters, this
386             // could use the glyph's "ligature carets". However, there is no Core Text API to get the
387             // ligature carets.
388             if (glyphStartOffset == glyphEndOffset) {
389                 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
390                 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
391                 m_runWidthSoFar += adjustedAdvance.width;
392             } else
393                 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
394 
395             if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
396                 return;
397 
398             m_numGlyphsSoFar++;
399             m_glyphInCurrentRun++;
400             m_characterInCurrentGlyph = 0;
401             if (ltr) {
402                 g++;
403                 k++;
404             } else {
405                 g--;
406                 k--;
407             }
408         }
409         m_currentRun++;
410         m_glyphInCurrentRun = 0;
411     }
412 }
413 
adjustGlyphsAndAdvances()414 void ComplexTextController::adjustGlyphsAndAdvances()
415 {
416     CGFloat widthSinceLastCommit = 0;
417     size_t runCount = m_complexTextRuns.size();
418     bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
419     for (size_t r = 0; r < runCount; ++r) {
420         ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
421         unsigned glyphCount = complexTextRun.glyphCount();
422         const SimpleFontData* fontData = complexTextRun.fontData();
423 
424         const CGGlyph* glyphs = complexTextRun.glyphs();
425         const CGSize* advances = complexTextRun.advances();
426 
427         bool lastRun = r + 1 == runCount;
428         bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
429         const UChar* cp = complexTextRun.characters();
430         CGPoint glyphOrigin = CGPointZero;
431         CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max();
432         bool isMonotonic = true;
433 
434         for (unsigned i = 0; i < glyphCount; i++) {
435             CFIndex characterIndex = complexTextRun.indexAt(i);
436             if (m_run.ltr()) {
437                 if (characterIndex < lastCharacterIndex)
438                     isMonotonic = false;
439             } else {
440                 if (characterIndex > lastCharacterIndex)
441                     isMonotonic = false;
442             }
443             UChar ch = *(cp + characterIndex);
444             bool lastGlyph = lastRun && i + 1 == glyphCount;
445             UChar nextCh;
446             if (lastGlyph)
447                 nextCh = ' ';
448             else if (i + 1 < glyphCount)
449                 nextCh = *(cp + complexTextRun.indexAt(i + 1));
450             else
451                 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
452 
453             bool treatAsSpace = Font::treatAsSpace(ch);
454             CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
455             CGSize advance = treatAsSpace ? CGSizeMake(fontData->spaceWidth(), advances[i].height) : advances[i];
456 
457             if (ch == '\t' && m_run.allowTabs()) {
458                 float tabWidth = m_font.tabWidth(*fontData);
459                 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth + widthSinceLastCommit, tabWidth);
460             } else if (ch == zeroWidthSpace || (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace)) {
461                 advance.width = 0;
462                 glyph = fontData->spaceGlyph();
463             }
464 
465             float roundedAdvanceWidth = roundf(advance.width);
466             if (roundsAdvances)
467                 advance.width = roundedAdvanceWidth;
468 
469             advance.width += fontData->syntheticBoldOffset();
470 
471             if (hasExtraSpacing) {
472                 // If we're a glyph with an advance, go ahead and add in letter-spacing.
473                 // That way we weed out zero width lurkers.  This behavior matches the fast text code path.
474                 if (advance.width && m_font.letterSpacing())
475                     advance.width += m_font.letterSpacing();
476 
477                 // Handle justification and word-spacing.
478                 if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
479                     // Distribute the run's total expansion evenly over all expansion opportunities in the run.
480                     if (m_expansion) {
481                         if (!treatAsSpace && !m_afterExpansion) {
482                             // Take the expansion opportunity before this ideograph.
483                             m_expansion -= m_expansionPerOpportunity;
484                             m_totalWidth += m_expansionPerOpportunity;
485                             if (m_adjustedAdvances.isEmpty())
486                                 m_leadingExpansion = m_expansionPerOpportunity;
487                             else
488                                 m_adjustedAdvances.last().width += m_expansionPerOpportunity;
489                         }
490                         if (!lastGlyph || m_run.allowsTrailingExpansion()) {
491                             m_expansion -= m_expansionPerOpportunity;
492                             advance.width += m_expansionPerOpportunity;
493                             m_afterExpansion = true;
494                         }
495                     } else
496                         m_afterExpansion = false;
497 
498                     // Account for word-spacing.
499                     if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
500                         advance.width += m_font.wordSpacing();
501                 } else
502                     m_afterExpansion = false;
503             }
504 
505             widthSinceLastCommit += advance.width;
506 
507             // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
508             if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
509                 glyph = 0;
510 
511             advance.height *= -1;
512             m_adjustedAdvances.append(advance);
513             m_adjustedGlyphs.append(glyph);
514 
515             FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
516             glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
517             m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x());
518             m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
519             m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y());
520             m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
521             glyphOrigin.x += advance.width;
522             glyphOrigin.y += advance.height;
523 
524             lastCharacterIndex = characterIndex;
525         }
526         if (!isMonotonic)
527             complexTextRun.setIsNonMonotonic();
528     }
529     m_totalWidth += widthSinceLastCommit;
530 }
531 
532 } // namespace WebCore
533