0ed7460c8d517f140158e3977103a4361b087b3f
[WebKit.git] / Source / WebCore / platform / graphics / mac / ComplexTextController.cpp
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
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
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
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
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 = 0;
190     const UChar* curr = cp;
191     const UChar* end = cp + m_end;
192
193     GlyphData glyphData;
194     GlyphData nextGlyphData;
195
196     bool isSurrogate = U16_IS_SURROGATE(*curr);
197     if (isSurrogate) {
198         if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
199             return;
200         nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
201     } else
202         nextGlyphData = m_font.glyphDataForCharacter(*curr, false);
203
204     UChar newC = 0;
205
206     bool isSmallCaps;
207     bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr;
208
209     if (nextIsSmallCaps)
210         m_smallCapsBuffer[curr - cp] = newC;
211
212     while (true) {
213         curr = curr + (isSurrogate ? 2 : 1);
214         if (curr == end)
215             break;
216
217         glyphData = nextGlyphData;
218         isSmallCaps = nextIsSmallCaps;
219         int index = curr - cp;
220         isSurrogate = U16_IS_SURROGATE(*curr);
221         UChar c = *curr;
222         bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK);
223         if (isSurrogate) {
224             if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
225                 return;
226             nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
227         } else
228             nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps ? SmallCapsVariant : AutoVariant);
229
230         if (!isSurrogate && m_font.isSmallCaps()) {
231             nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c;
232             if (nextIsSmallCaps)
233                 m_smallCapsBuffer[index] = forceSmallCaps ? c : newC;
234         }
235
236         if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) {
237             int itemStart = static_cast<int>(indexOfFontTransition);
238             int itemLength = index - indexOfFontTransition;
239             collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0);
240             indexOfFontTransition = index;
241         }
242     }
243
244     int itemLength = m_end - indexOfFontTransition;
245     if (itemLength) {
246         int itemStart = indexOfFontTransition;
247         collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0);
248     }
249
250     if (!m_run.ltr())
251         m_complexTextRuns.reverse();
252 }
253
254 #if USE(CORE_TEXT) && USE(ATSUI)
255 static inline bool shouldUseATSUIAPI()
256 {
257     enum TypeRenderingAPIToUse { UnInitialized, UseATSUI, UseCoreText };
258     static TypeRenderingAPIToUse apiToUse = UnInitialized;
259
260     if (UNLIKELY(apiToUse == UnInitialized)) {
261         if (&CTGetCoreTextVersion != 0 && CTGetCoreTextVersion() >= kCTVersionNumber10_6)
262             apiToUse = UseCoreText;
263         else
264             apiToUse = UseATSUI;
265     }
266
267     return apiToUse == UseATSUI;
268 }
269 #endif
270
271 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
272 {
273 #if USE(CORE_TEXT) && USE(ATSUI)
274     return shouldUseATSUIAPI() ? m_atsuiIndices[i] : m_coreTextIndices[i];
275 #elif USE(ATSUI)
276     return m_atsuiIndices[i];
277 #elif USE(CORE_TEXT)
278     return m_coreTextIndices[i];
279 #endif
280 }
281
282 void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData)
283 {
284 #if USE(CORE_TEXT) && USE(ATSUI)
285     if (shouldUseATSUIAPI())
286         return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
287     return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
288 #elif USE(ATSUI)
289     return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
290 #elif USE(CORE_TEXT)
291     return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
292 #endif
293 }
294
295 ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr)
296     : m_fontData(fontData)
297     , m_characters(characters)
298     , m_stringLocation(stringLocation)
299     , m_stringLength(stringLength)
300     , m_indexEnd(stringLength)
301     , m_isMonotonic(true)
302 {
303 #if USE(CORE_TEXT) && USE(ATSUI)
304     shouldUseATSUIAPI() ? createTextRunFromFontDataATSUI(ltr) : createTextRunFromFontDataCoreText(ltr);
305 #elif USE(ATSUI)
306     createTextRunFromFontDataATSUI(ltr);
307 #elif USE(CORE_TEXT)
308     createTextRunFromFontDataCoreText(ltr);
309 #endif
310 }
311
312 void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
313 {
314     ASSERT(m_isMonotonic);
315     m_isMonotonic = false;
316
317     Vector<bool, 64> mappedIndices(m_stringLength);
318     for (size_t i = 0; i < m_glyphCount; ++i) {
319         ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
320         mappedIndices[indexAt(i)] = true;
321     }
322
323     m_glyphEndOffsets.grow(m_glyphCount);
324     for (size_t i = 0; i < m_glyphCount; ++i) {
325         CFIndex nextMappedIndex = m_indexEnd;
326         for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
327             if (mappedIndices[j]) {
328                 nextMappedIndex = j;
329                 break;
330             }
331         }
332         m_glyphEndOffsets[i] = nextMappedIndex;
333     }
334 }
335
336 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer)
337 {
338     if (static_cast<int>(offset) > m_end)
339         offset = m_end;
340
341     if (offset <= m_currentCharacter)
342         return;
343
344     m_currentCharacter = offset;
345
346     size_t runCount = m_complexTextRuns.size();
347
348     bool ltr = m_run.ltr();
349
350     unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar;
351     while (m_currentRun < runCount) {
352         const ComplexTextRun& complexTextRun = *m_complexTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun];
353         size_t glyphCount = complexTextRun.glyphCount();
354         unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
355         while (m_glyphInCurrentRun < glyphCount) {
356             unsigned glyphStartOffset = complexTextRun.indexAt(g);
357             unsigned glyphEndOffset;
358             if (complexTextRun.isMonotonic()) {
359                 if (ltr)
360                     glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd()));
361                 else
362                     glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd()));
363             } else
364                 glyphEndOffset = complexTextRun.endOffsetAt(g);
365
366             CGSize adjustedAdvance = m_adjustedAdvances[k];
367
368             if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
369                 return;
370
371             if (glyphBuffer && !m_characterInCurrentGlyph)
372                 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance);
373
374             unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
375             m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
376             // FIXME: Instead of dividing the glyph's advance equally between the characters, this
377             // could use the glyph's "ligature carets". However, there is no Core Text API to get the
378             // ligature carets.
379             if (glyphStartOffset == glyphEndOffset) {
380                 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
381                 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
382                 m_runWidthSoFar += adjustedAdvance.width;
383             } else
384                 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
385
386             if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
387                 return;
388
389             m_numGlyphsSoFar++;
390             m_glyphInCurrentRun++;
391             m_characterInCurrentGlyph = 0;
392             if (ltr) {
393                 g++;
394                 k++;
395             } else {
396                 g--;
397                 k--;
398             }
399         }
400         m_currentRun++;
401         m_glyphInCurrentRun = 0;
402     }
403 }
404
405 void ComplexTextController::adjustGlyphsAndAdvances()
406 {
407     CGFloat widthSinceLastCommit = 0;
408     size_t runCount = m_complexTextRuns.size();
409     bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
410     for (size_t r = 0; r < runCount; ++r) {
411         ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
412         unsigned glyphCount = complexTextRun.glyphCount();
413         const SimpleFontData* fontData = complexTextRun.fontData();
414
415         const CGGlyph* glyphs = complexTextRun.glyphs();
416         const CGSize* advances = complexTextRun.advances();
417
418         bool lastRun = r + 1 == runCount;
419         bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
420         const UChar* cp = complexTextRun.characters();
421         CGPoint glyphOrigin = CGPointZero;
422         CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max();
423         bool isMonotonic = true;
424
425         for (unsigned i = 0; i < glyphCount; i++) {
426             CFIndex characterIndex = complexTextRun.indexAt(i);
427             if (m_run.ltr()) {
428                 if (characterIndex < lastCharacterIndex)
429                     isMonotonic = false;
430             } else {
431                 if (characterIndex > lastCharacterIndex)
432                     isMonotonic = false;
433             }
434             UChar ch = *(cp + characterIndex);
435             bool lastGlyph = lastRun && i + 1 == glyphCount;
436             UChar nextCh;
437             if (lastGlyph)
438                 nextCh = ' ';
439             else if (i + 1 < glyphCount)
440                 nextCh = *(cp + complexTextRun.indexAt(i + 1));
441             else
442                 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
443
444             bool treatAsSpace = Font::treatAsSpace(ch);
445             CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
446             CGSize advance = treatAsSpace ? CGSizeMake(fontData->spaceWidth(), advances[i].height) : advances[i];
447
448             if (ch == '\t' && m_run.allowTabs()) {
449                 float tabWidth = m_font.tabWidth(*fontData);
450                 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth + widthSinceLastCommit, tabWidth);
451             } else if (ch == zeroWidthSpace || (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace)) {
452                 advance.width = 0;
453                 glyph = fontData->spaceGlyph();
454             }
455
456             float roundedAdvanceWidth = roundf(advance.width);
457             if (roundsAdvances)
458                 advance.width = roundedAdvanceWidth;
459
460             advance.width += fontData->syntheticBoldOffset();
461
462             if (hasExtraSpacing) {
463                 // If we're a glyph with an advance, go ahead and add in letter-spacing.
464                 // That way we weed out zero width lurkers.  This behavior matches the fast text code path.
465                 if (advance.width && m_font.letterSpacing())
466                     advance.width += m_font.letterSpacing();
467
468                 // Handle justification and word-spacing.
469                 if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
470                     // Distribute the run's total expansion evenly over all expansion opportunities in the run.
471                     if (m_expansion) {
472                         if (!treatAsSpace && !m_afterExpansion) {
473                             // Take the expansion opportunity before this ideograph.
474                             m_expansion -= m_expansionPerOpportunity;
475                             m_totalWidth += m_expansionPerOpportunity;
476                             if (m_adjustedAdvances.isEmpty())
477                                 m_leadingExpansion = m_expansionPerOpportunity;
478                             else
479                                 m_adjustedAdvances.last().width += m_expansionPerOpportunity;
480                         }
481                         if (!lastGlyph || m_run.allowsTrailingExpansion()) {
482                             m_expansion -= m_expansionPerOpportunity;
483                             advance.width += m_expansionPerOpportunity;
484                             m_afterExpansion = true;
485                         }
486                     } else
487                         m_afterExpansion = false;
488
489                     // Account for word-spacing.
490                     if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
491                         advance.width += m_font.wordSpacing();
492                 } else
493                     m_afterExpansion = false;
494             }
495
496             widthSinceLastCommit += advance.width;
497
498             // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
499             if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
500                 glyph = 0;
501
502             advance.height *= -1;
503             m_adjustedAdvances.append(advance);
504             m_adjustedGlyphs.append(glyph);
505             
506             FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
507             glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
508             m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x());
509             m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
510             m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y());
511             m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
512             glyphOrigin.x += advance.width;
513             glyphOrigin.y += advance.height;
514             
515             lastCharacterIndex = characterIndex;
516         }
517         if (!isMonotonic)
518             complexTextRun.setIsNonMonotonic();
519     }
520     m_totalWidth += widthSinceLastCommit;
521 }
522
523 } // namespace WebCore