Add an option to enable legacy rounding hacks
[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 static inline CGFloat ceilCGFloat(CGFloat f)
53 {
54     if (sizeof(CGFloat) == sizeof(float))
55         return ceilf(static_cast<float>(f));
56     return static_cast<CGFloat>(ceil(f));
57 }
58
59 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis)
60     : m_font(*font)
61     , m_run(run)
62     , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
63     , m_forTextEmphasis(forTextEmphasis)
64     , m_currentCharacter(0)
65     , m_end(run.length())
66     , m_totalWidth(0)
67     , m_runWidthSoFar(0)
68     , m_numGlyphsSoFar(0)
69     , m_currentRun(0)
70     , m_glyphInCurrentRun(0)
71     , m_characterInCurrentGlyph(0)
72     , m_finalRoundingWidth(0)
73     , m_expansion(run.expansion())
74     , m_leadingExpansion(0)
75     , m_afterExpansion(!run.allowsLeadingExpansion())
76     , m_fallbackFonts(fallbackFonts)
77     , m_minGlyphBoundingBoxX(numeric_limits<float>::max())
78     , m_maxGlyphBoundingBoxX(numeric_limits<float>::min())
79     , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
80     , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
81     , m_lastRoundingGlyph(0)
82 {
83     if (!m_expansion)
84         m_expansionPerOpportunity = 0;
85     else {
86         bool isAfterExpansion = m_afterExpansion;
87         unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
88         if (isAfterExpansion && !m_run.allowsTrailingExpansion())
89             expansionOpportunityCount--;
90
91         if (!expansionOpportunityCount)
92             m_expansionPerOpportunity = 0;
93         else
94             m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
95     }
96
97     collectComplexTextRuns();
98     adjustGlyphsAndAdvances();
99
100     m_runWidthSoFar = m_leadingExpansion;
101 }
102
103 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
104 {
105     if (h >= m_totalWidth)
106         return m_run.ltr() ? m_end : 0;
107
108     h -= m_leadingExpansion;
109     if (h < 0)
110         return m_run.ltr() ? 0 : m_end;
111
112     CGFloat x = h;
113
114     size_t runCount = m_complexTextRuns.size();
115     size_t offsetIntoAdjustedGlyphs = 0;
116
117     for (size_t r = 0; r < runCount; ++r) {
118         const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
119         for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
120             CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
121             if (x < adjustedAdvance) {
122                 CFIndex hitGlyphStart = complexTextRun.indexAt(j);
123                 CFIndex hitGlyphEnd;
124                 if (m_run.ltr())
125                     hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
126                 else
127                     hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
128
129                 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
130                 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
131                 // ligature carets.
132                 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
133                 int stringLength = complexTextRun.stringLength();
134                 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength);
135                 int clusterStart;
136                 if (isTextBreak(cursorPositionIterator, hitIndex))
137                     clusterStart = hitIndex;
138                 else {
139                     clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex);
140                     if (clusterStart == TextBreakDone)
141                         clusterStart = 0;
142                 }
143
144                 if (!includePartialGlyphs)
145                     return complexTextRun.stringLocation() + clusterStart;
146
147                 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex);
148                 if (clusterEnd == TextBreakDone)
149                     clusterEnd = stringLength;
150
151                 CGFloat clusterWidth;
152                 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
153                 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
154                 // reordering and on font fallback should occur within a CTLine.
155                 if (clusterEnd - clusterStart > 1) {
156                     clusterWidth = adjustedAdvance;
157                     int firstGlyphBeforeCluster = j - 1;
158                     while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
159                         CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
160                         clusterWidth += width;
161                         x += width;
162                         firstGlyphBeforeCluster--;
163                     }
164                     unsigned firstGlyphAfterCluster = j + 1;
165                     while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
166                         clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
167                         firstGlyphAfterCluster++;
168                     }
169                 } else {
170                     clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
171                     x -=  clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
172                 }
173                 if (x <= clusterWidth / 2)
174                     return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
175                 else
176                     return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
177             }
178             x -= adjustedAdvance;
179         }
180         offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
181     }
182
183     ASSERT_NOT_REACHED();
184     return 0;
185 }
186
187 void ComplexTextController::collectComplexTextRuns()
188 {
189     if (!m_end)
190         return;
191
192     // We break up glyph run generation for the string by FontData and (if needed) the use of small caps.
193     const UChar* cp = m_run.characters();
194
195     if (m_font.isSmallCaps())
196         m_smallCapsBuffer.resize(m_end);
197
198     unsigned indexOfFontTransition = 0;
199     const UChar* curr = cp;
200     const UChar* end = cp + m_end;
201
202     GlyphData glyphData;
203     GlyphData nextGlyphData;
204
205     bool isSurrogate = U16_IS_SURROGATE(*curr);
206     if (isSurrogate) {
207         if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
208             return;
209         nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
210     } else
211         nextGlyphData = m_font.glyphDataForCharacter(*curr, false);
212
213     UChar newC = 0;
214
215     bool isSmallCaps;
216     bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr;
217
218     if (nextIsSmallCaps)
219         m_smallCapsBuffer[curr - cp] = newC;
220
221     while (true) {
222         curr = curr + (isSurrogate ? 2 : 1);
223         if (curr == end)
224             break;
225
226         glyphData = nextGlyphData;
227         isSmallCaps = nextIsSmallCaps;
228         int index = curr - cp;
229         isSurrogate = U16_IS_SURROGATE(*curr);
230         UChar c = *curr;
231         bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK);
232         if (isSurrogate) {
233             if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
234                 return;
235             nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
236         } else
237             nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps ? SmallCapsVariant : AutoVariant);
238
239         if (!isSurrogate && m_font.isSmallCaps()) {
240             nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c;
241             if (nextIsSmallCaps)
242                 m_smallCapsBuffer[index] = forceSmallCaps ? c : newC;
243         }
244
245         if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) {
246             int itemStart = static_cast<int>(indexOfFontTransition);
247             int itemLength = index - indexOfFontTransition;
248             collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0);
249             indexOfFontTransition = index;
250         }
251     }
252
253     int itemLength = m_end - indexOfFontTransition;
254     if (itemLength) {
255         int itemStart = indexOfFontTransition;
256         collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0);
257     }
258
259     if (!m_run.ltr())
260         m_complexTextRuns.reverse();
261 }
262
263 #if USE(CORE_TEXT) && USE(ATSUI)
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
280 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
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
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
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
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     if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size())
413         m_runWidthSoFar += m_finalRoundingWidth;
414 }
415
416 void ComplexTextController::adjustGlyphsAndAdvances()
417 {
418     CGFloat widthSinceLastCommit = 0;
419     size_t runCount = m_complexTextRuns.size();
420     bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
421     for (size_t r = 0; r < runCount; ++r) {
422         ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
423         unsigned glyphCount = complexTextRun.glyphCount();
424         const SimpleFontData* fontData = complexTextRun.fontData();
425
426         const CGGlyph* glyphs = complexTextRun.glyphs();
427         const CGSize* advances = complexTextRun.advances();
428
429         bool lastRun = r + 1 == runCount;
430         bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
431         CGFloat roundedSpaceWidth = roundCGFloat(fontData->spaceWidth());
432         const UChar* cp = complexTextRun.characters();
433         CGPoint glyphOrigin = CGPointZero;
434         CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max();
435         bool isMonotonic = true;
436
437         for (unsigned i = 0; i < glyphCount; i++) {
438             CFIndex characterIndex = complexTextRun.indexAt(i);
439             if (m_run.ltr()) {
440                 if (characterIndex < lastCharacterIndex)
441                     isMonotonic = false;
442             } else {
443                 if (characterIndex > lastCharacterIndex)
444                     isMonotonic = false;
445             }
446             UChar ch = *(cp + characterIndex);
447             bool lastGlyph = lastRun && i + 1 == glyphCount;
448             UChar nextCh;
449             if (lastGlyph)
450                 nextCh = ' ';
451             else if (i + 1 < glyphCount)
452                 nextCh = *(cp + complexTextRun.indexAt(i + 1));
453             else
454                 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
455
456             bool treatAsSpace = Font::treatAsSpace(ch);
457             CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
458             CGSize advance = treatAsSpace ? CGSizeMake(fontData->spaceWidth(), advances[i].height) : advances[i];
459
460             if (ch == '\t' && m_run.allowTabs()) {
461                 float tabWidth = m_font.tabWidth(*fontData);
462                 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth + widthSinceLastCommit, tabWidth);
463             } else if (ch == zeroWidthSpace || (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace)) {
464                 advance.width = 0;
465                 glyph = fontData->spaceGlyph();
466             }
467
468             float roundedAdvanceWidth = roundf(advance.width);
469             if (roundsAdvances)
470                 advance.width = roundedAdvanceWidth;
471
472             advance.width += fontData->syntheticBoldOffset();
473
474  
475             // We special case spaces in two ways when applying word rounding. 
476             // First, we round spaces to an adjusted width in all fonts. 
477             // Second, in fixed-pitch fonts we ensure that all glyphs that 
478             // match the width of the space glyph have the same width as the space glyph. 
479             if (m_run.applyWordRounding() && roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
480                 advance.width = fontData->adjustedSpaceWidth();
481
482             if (hasExtraSpacing) {
483                 // If we're a glyph with an advance, go ahead and add in letter-spacing.
484                 // That way we weed out zero width lurkers.  This behavior matches the fast text code path.
485                 if (advance.width && m_font.letterSpacing())
486                     advance.width += m_font.letterSpacing();
487
488                 // Handle justification and word-spacing.
489                 if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
490                     // Distribute the run's total expansion evenly over all expansion opportunities in the run.
491                     if (m_expansion) {
492                         if (!treatAsSpace && !m_afterExpansion) {
493                             // Take the expansion opportunity before this ideograph.
494                             m_expansion -= m_expansionPerOpportunity;
495                             m_totalWidth += m_expansionPerOpportunity;
496                             if (m_adjustedAdvances.isEmpty())
497                                 m_leadingExpansion = m_expansionPerOpportunity;
498                             else
499                                 m_adjustedAdvances.last().width += m_expansionPerOpportunity;
500                         }
501                         if (!lastGlyph || m_run.allowsTrailingExpansion()) {
502                             m_expansion -= m_expansionPerOpportunity;
503                             advance.width += m_expansionPerOpportunity;
504                             m_afterExpansion = true;
505                         }
506                     } else
507                         m_afterExpansion = false;
508
509                     // Account for word-spacing.
510                     if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
511                         advance.width += m_font.wordSpacing();
512                 } else
513                     m_afterExpansion = false;
514             }
515
516             // Apply rounding hacks if needed.
517             // We adjust the width of the last character of a "word" to ensure an integer width. 
518             // Force characters that are used to determine word boundaries for the rounding hack 
519             // to be integer width, so the following words will start on an integer boundary. 
520             if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch)) 
521                 advance.width = ceilCGFloat(advance.width); 
522
523             // Check to see if the next character is a "rounding hack character", if so, adjust the 
524             // width so that the total run width will be on an integer boundary. 
525             if ((m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh)) || (m_run.applyRunRounding() && lastGlyph)) { 
526                 CGFloat totalWidth = widthSinceLastCommit + advance.width; 
527                 widthSinceLastCommit = ceilCGFloat(totalWidth); 
528                 CGFloat extraWidth = widthSinceLastCommit - totalWidth; 
529                 if (m_run.ltr()) 
530                     advance.width += extraWidth; 
531                 else { 
532                     if (m_lastRoundingGlyph) 
533                         m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth; 
534                     else 
535                         m_finalRoundingWidth = extraWidth; 
536                     m_lastRoundingGlyph = m_adjustedAdvances.size() + 1; 
537                 } 
538                 m_totalWidth += widthSinceLastCommit; 
539                 widthSinceLastCommit = 0; 
540             } else 
541                 widthSinceLastCommit += advance.width; 
542
543             // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
544             if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
545                 glyph = 0;
546
547             advance.height *= -1;
548             m_adjustedAdvances.append(advance);
549             m_adjustedGlyphs.append(glyph);
550             
551             FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
552             glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
553             m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x());
554             m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
555             m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y());
556             m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
557             glyphOrigin.x += advance.width;
558             glyphOrigin.y += advance.height;
559             
560             lastCharacterIndex = characterIndex;
561         }
562         if (!isMonotonic)
563             complexTextRun.setIsNonMonotonic();
564     }
565     m_totalWidth += widthSinceLastCommit;
566 }
567
568 } // namespace WebCore