Complex spaces with synthetic bold are too wide
[WebKit-https.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 no 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 static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
188 {
189     ASSERT(iterator < end);
190
191     markCount = 0;
192
193     baseCharacter = *iterator++;
194
195     if (U16_IS_SURROGATE(baseCharacter)) {
196         if (!U16_IS_LEAD(baseCharacter))
197             return false;
198         if (iterator == end)
199             return false;
200         UChar trail = *iterator++;
201         if (!U16_IS_TRAIL(trail))
202             return false;
203         baseCharacter = U16_GET_SUPPLEMENTARY(baseCharacter, trail);
204     }
205
206     // Consume marks.
207     while (iterator < end && ((U_GET_GC_MASK(*iterator) & U_GC_M_MASK) || *iterator == zeroWidthJoiner || *iterator == zeroWidthNonJoiner)) {
208         iterator++;
209         markCount++;
210     }
211
212     return true;
213 }
214
215 void ComplexTextController::collectComplexTextRuns()
216 {
217     if (!m_end)
218         return;
219
220     // We break up glyph run generation for the string by FontData.
221     const UChar* cp = m_run.characters();
222
223     if (m_font.isSmallCaps())
224         m_smallCapsBuffer.resize(m_end);
225
226     unsigned indexOfFontTransition = 0;
227     const UChar* curr = cp;
228     const UChar* end = cp + m_end;
229
230     const SimpleFontData* fontData;
231     bool isMissingGlyph;
232     const SimpleFontData* nextFontData;
233     bool nextIsMissingGlyph;
234
235     unsigned markCount;
236     const UChar* sequenceStart = curr;
237     UChar32 baseCharacter;
238     if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
239         return;
240
241     UChar uppercaseCharacter = 0;
242
243     bool isSmallCaps;
244     bool nextIsSmallCaps = m_font.isSmallCaps() && !(U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK) && (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
245
246     if (nextIsSmallCaps) {
247         m_smallCapsBuffer[sequenceStart - cp] = uppercaseCharacter;
248         for (unsigned i = 0; i < markCount; ++i)
249             m_smallCapsBuffer[sequenceStart - cp + i + 1] = sequenceStart[i + 1];
250     }
251
252     nextIsMissingGlyph = false;
253     nextFontData = m_font.fontDataForCombiningCharacterSequence(sequenceStart, curr - sequenceStart, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
254     if (!nextFontData) {
255         if (markCount)
256             nextFontData = systemFallbackFontData();
257         else
258             nextIsMissingGlyph = true;
259     }
260
261     while (curr < end) {
262         fontData = nextFontData;
263         isMissingGlyph = nextIsMissingGlyph;
264         isSmallCaps = nextIsSmallCaps;
265         int index = curr - cp;
266
267         if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
268             return;
269
270         if (m_font.isSmallCaps()) {
271             nextIsSmallCaps = (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
272             if (nextIsSmallCaps) {
273                 m_smallCapsBuffer[index] = uppercaseCharacter;
274                 for (unsigned i = 0; i < markCount; ++i)
275                     m_smallCapsBuffer[index + i + 1] = cp[index + i + 1];
276             }
277         }
278
279         nextIsMissingGlyph = false;
280         nextFontData = m_font.fontDataForCombiningCharacterSequence(cp + index, curr - cp - index, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
281         if (!nextFontData) {
282             if (markCount)
283                 nextFontData = systemFallbackFontData();
284             else
285                 nextIsMissingGlyph = true;
286         }
287
288         if (nextFontData != fontData || nextIsMissingGlyph != isMissingGlyph) {
289             int itemStart = static_cast<int>(indexOfFontTransition);
290             int itemLength = index - indexOfFontTransition;
291             collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !isMissingGlyph ? fontData : 0);
292             indexOfFontTransition = index;
293         }
294     }
295
296     int itemLength = m_end - indexOfFontTransition;
297     if (itemLength) {
298         int itemStart = indexOfFontTransition;
299         collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !nextIsMissingGlyph ? nextFontData : 0);
300     }
301
302     if (!m_run.ltr())
303         m_complexTextRuns.reverse();
304 }
305
306 #if USE(CORE_TEXT) && USE(ATSUI)
307 static inline bool shouldUseATSUIAPI()
308 {
309     enum TypeRenderingAPIToUse { UnInitialized, UseATSUI, UseCoreText };
310     static TypeRenderingAPIToUse apiToUse = UnInitialized;
311
312     if (UNLIKELY(apiToUse == UnInitialized)) {
313         if (&CTGetCoreTextVersion != 0 && CTGetCoreTextVersion() >= kCTVersionNumber10_6)
314             apiToUse = UseCoreText;
315         else
316             apiToUse = UseATSUI;
317     }
318
319     return apiToUse == UseATSUI;
320 }
321 #endif
322
323 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
324 {
325 #if USE(CORE_TEXT) && USE(ATSUI)
326     return shouldUseATSUIAPI() ? m_atsuiIndices[i] : m_coreTextIndices[i];
327 #elif USE(ATSUI)
328     return m_atsuiIndices[i];
329 #elif USE(CORE_TEXT)
330     return m_coreTextIndices[i];
331 #endif
332 }
333
334 void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData)
335 {
336     if (!fontData) {
337         // Create a run of missing glyphs from the primary font.
338         m_complexTextRuns.append(ComplexTextRun::create(m_font.primaryFont(), cp, stringLocation, length, m_run.ltr()));
339         return;
340     }
341
342 #if USE(CORE_TEXT) && USE(ATSUI)
343     if (shouldUseATSUIAPI())
344         return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
345     return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
346 #elif USE(ATSUI)
347     return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
348 #elif USE(CORE_TEXT)
349     return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
350 #endif
351 }
352
353 ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr)
354     : m_fontData(fontData)
355     , m_characters(characters)
356     , m_stringLocation(stringLocation)
357     , m_stringLength(stringLength)
358     , m_indexEnd(stringLength)
359     , m_isMonotonic(true)
360 {
361 #if USE(CORE_TEXT) && USE(ATSUI)
362     shouldUseATSUIAPI() ? createTextRunFromFontDataATSUI(ltr) : createTextRunFromFontDataCoreText(ltr);
363 #elif USE(ATSUI)
364     createTextRunFromFontDataATSUI(ltr);
365 #elif USE(CORE_TEXT)
366     createTextRunFromFontDataCoreText(ltr);
367 #endif
368 }
369
370 void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
371 {
372     ASSERT(m_isMonotonic);
373     m_isMonotonic = false;
374
375     Vector<bool, 64> mappedIndices(m_stringLength);
376     for (size_t i = 0; i < m_glyphCount; ++i) {
377         ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
378         mappedIndices[indexAt(i)] = true;
379     }
380
381     m_glyphEndOffsets.grow(m_glyphCount);
382     for (size_t i = 0; i < m_glyphCount; ++i) {
383         CFIndex nextMappedIndex = m_indexEnd;
384         for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
385             if (mappedIndices[j]) {
386                 nextMappedIndex = j;
387                 break;
388             }
389         }
390         m_glyphEndOffsets[i] = nextMappedIndex;
391     }
392 }
393
394 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer)
395 {
396     if (static_cast<int>(offset) > m_end)
397         offset = m_end;
398
399     if (offset <= m_currentCharacter)
400         return;
401
402     m_currentCharacter = offset;
403
404     size_t runCount = m_complexTextRuns.size();
405
406     bool ltr = m_run.ltr();
407
408     unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar;
409     while (m_currentRun < runCount) {
410         const ComplexTextRun& complexTextRun = *m_complexTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun];
411         size_t glyphCount = complexTextRun.glyphCount();
412         unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
413         while (m_glyphInCurrentRun < glyphCount) {
414             unsigned glyphStartOffset = complexTextRun.indexAt(g);
415             unsigned glyphEndOffset;
416             if (complexTextRun.isMonotonic()) {
417                 if (ltr)
418                     glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd()));
419                 else
420                     glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd()));
421             } else
422                 glyphEndOffset = complexTextRun.endOffsetAt(g);
423
424             CGSize adjustedAdvance = m_adjustedAdvances[k];
425
426             if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
427                 return;
428
429             if (glyphBuffer && !m_characterInCurrentGlyph)
430                 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance);
431
432             unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
433             m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
434             // FIXME: Instead of dividing the glyph's advance equally between the characters, this
435             // could use the glyph's "ligature carets". However, there is no Core Text API to get the
436             // ligature carets.
437             if (glyphStartOffset == glyphEndOffset) {
438                 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
439                 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
440                 m_runWidthSoFar += adjustedAdvance.width;
441             } else
442                 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
443
444             if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
445                 return;
446
447             m_numGlyphsSoFar++;
448             m_glyphInCurrentRun++;
449             m_characterInCurrentGlyph = 0;
450             if (ltr) {
451                 g++;
452                 k++;
453             } else {
454                 g--;
455                 k--;
456             }
457         }
458         m_currentRun++;
459         m_glyphInCurrentRun = 0;
460     }
461     if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size())
462         m_runWidthSoFar += m_finalRoundingWidth;
463 }
464
465 void ComplexTextController::adjustGlyphsAndAdvances()
466 {
467     CGFloat widthSinceLastCommit = 0;
468     size_t runCount = m_complexTextRuns.size();
469     bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
470     for (size_t r = 0; r < runCount; ++r) {
471         ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
472         unsigned glyphCount = complexTextRun.glyphCount();
473         const SimpleFontData* fontData = complexTextRun.fontData();
474
475         const CGGlyph* glyphs = complexTextRun.glyphs();
476         const CGSize* advances = complexTextRun.advances();
477
478         bool lastRun = r + 1 == runCount;
479         bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
480         float spaceWidth = fontData->spaceWidth() - fontData->syntheticBoldOffset();
481         CGFloat roundedSpaceWidth = roundCGFloat(spaceWidth);
482         const UChar* cp = complexTextRun.characters();
483         CGPoint glyphOrigin = CGPointZero;
484         CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max();
485         bool isMonotonic = true;
486
487         for (unsigned i = 0; i < glyphCount; i++) {
488             CFIndex characterIndex = complexTextRun.indexAt(i);
489             if (m_run.ltr()) {
490                 if (characterIndex < lastCharacterIndex)
491                     isMonotonic = false;
492             } else {
493                 if (characterIndex > lastCharacterIndex)
494                     isMonotonic = false;
495             }
496             UChar ch = *(cp + characterIndex);
497             bool lastGlyph = lastRun && i + 1 == glyphCount;
498             UChar nextCh;
499             if (lastGlyph)
500                 nextCh = ' ';
501             else if (i + 1 < glyphCount)
502                 nextCh = *(cp + complexTextRun.indexAt(i + 1));
503             else
504                 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
505
506             bool treatAsSpace = Font::treatAsSpace(ch);
507             CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
508             CGSize advance = treatAsSpace ? CGSizeMake(spaceWidth, advances[i].height) : advances[i];
509
510             if (ch == '\t' && m_run.allowTabs()) {
511                 float tabWidth = m_font.tabWidth(*fontData);
512                 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth + widthSinceLastCommit, tabWidth);
513             } else if (ch == zeroWidthSpace || (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace)) {
514                 advance.width = 0;
515                 glyph = fontData->spaceGlyph();
516             }
517
518             float roundedAdvanceWidth = roundf(advance.width);
519             if (roundsAdvances)
520                 advance.width = roundedAdvanceWidth;
521
522             advance.width += fontData->syntheticBoldOffset();
523
524  
525             // We special case spaces in two ways when applying word rounding. 
526             // First, we round spaces to an adjusted width in all fonts. 
527             // Second, in fixed-pitch fonts we ensure that all glyphs that 
528             // match the width of the space glyph have the same width as the space glyph. 
529             if (m_run.applyWordRounding() && roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
530                 advance.width = fontData->adjustedSpaceWidth();
531
532             if (hasExtraSpacing) {
533                 // If we're a glyph with an advance, go ahead and add in letter-spacing.
534                 // That way we weed out zero width lurkers.  This behavior matches the fast text code path.
535                 if (advance.width && m_font.letterSpacing())
536                     advance.width += m_font.letterSpacing();
537
538                 // Handle justification and word-spacing.
539                 if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
540                     // Distribute the run's total expansion evenly over all expansion opportunities in the run.
541                     if (m_expansion) {
542                         float previousExpansion = m_expansion;
543                         if (!treatAsSpace && !m_afterExpansion) {
544                             // Take the expansion opportunity before this ideograph.
545                             m_expansion -= m_expansionPerOpportunity;
546                             float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
547                             m_totalWidth += expansionAtThisOpportunity;
548                             if (m_adjustedAdvances.isEmpty())
549                                 m_leadingExpansion = expansionAtThisOpportunity;
550                             else
551                                 m_adjustedAdvances.last().width += expansionAtThisOpportunity;
552                             previousExpansion = m_expansion;
553                         }
554                         if (!lastGlyph || m_run.allowsTrailingExpansion()) {
555                             m_expansion -= m_expansionPerOpportunity;
556                             advance.width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
557                             m_afterExpansion = true;
558                         }
559                     } else
560                         m_afterExpansion = false;
561
562                     // Account for word-spacing.
563                     if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
564                         advance.width += m_font.wordSpacing();
565                 } else
566                     m_afterExpansion = false;
567             }
568
569             // Apply rounding hacks if needed.
570             // We adjust the width of the last character of a "word" to ensure an integer width. 
571             // Force characters that are used to determine word boundaries for the rounding hack 
572             // to be integer width, so the following words will start on an integer boundary. 
573             if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch)) 
574                 advance.width = ceilCGFloat(advance.width); 
575
576             // Check to see if the next character is a "rounding hack character", if so, adjust the 
577             // width so that the total run width will be on an integer boundary. 
578             if ((m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh)) || (m_run.applyRunRounding() && lastGlyph)) { 
579                 CGFloat totalWidth = widthSinceLastCommit + advance.width; 
580                 widthSinceLastCommit = ceilCGFloat(totalWidth); 
581                 CGFloat extraWidth = widthSinceLastCommit - totalWidth; 
582                 if (m_run.ltr()) 
583                     advance.width += extraWidth; 
584                 else { 
585                     if (m_lastRoundingGlyph) 
586                         m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth; 
587                     else 
588                         m_finalRoundingWidth = extraWidth; 
589                     m_lastRoundingGlyph = m_adjustedAdvances.size() + 1; 
590                 } 
591                 m_totalWidth += widthSinceLastCommit; 
592                 widthSinceLastCommit = 0; 
593             } else 
594                 widthSinceLastCommit += advance.width; 
595
596             // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
597             if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
598                 glyph = 0;
599
600             advance.height *= -1;
601             m_adjustedAdvances.append(advance);
602             m_adjustedGlyphs.append(glyph);
603             
604             FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
605             glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
606             m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x());
607             m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
608             m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y());
609             m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
610             glyphOrigin.x += advance.width;
611             glyphOrigin.y += advance.height;
612             
613             lastCharacterIndex = characterIndex;
614         }
615         if (!isMonotonic)
616             complexTextRun.setIsNonMonotonic();
617     }
618     m_totalWidth += widthSinceLastCommit;
619 }
620
621 } // namespace WebCore