• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007, 2008 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 #include "CoreTextController.h"
28 
29 #if USE(CORE_TEXT)
30 
31 #include "CharacterNames.h"
32 #include "Font.h"
33 #include "FontCache.h"
34 #include "SimpleFontData.h"
35 #include "TextBreakIterator.h"
36 #include <wtf/MathExtras.h>
37 
38 using namespace std;
39 
40 namespace WebCore {
41 
roundCGFloat(CGFloat f)42 static inline CGFloat roundCGFloat(CGFloat f)
43 {
44     if (sizeof(CGFloat) == sizeof(float))
45         return roundf(static_cast<float>(f));
46     return static_cast<CGFloat>(round(f));
47 }
48 
ceilCGFloat(CGFloat f)49 static inline CGFloat ceilCGFloat(CGFloat f)
50 {
51     if (sizeof(CGFloat) == sizeof(float))
52         return ceilf(static_cast<float>(f));
53     return static_cast<CGFloat>(ceil(f));
54 }
55 
CoreTextRun(CTRunRef ctRun,const SimpleFontData * fontData,const UChar * characters,unsigned stringLocation,size_t stringLength)56 CoreTextController::CoreTextRun::CoreTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength)
57     : m_CTRun(ctRun)
58     , m_fontData(fontData)
59     , m_characters(characters)
60     , m_stringLocation(stringLocation)
61     , m_stringLength(stringLength)
62 {
63     m_glyphCount = CTRunGetGlyphCount(ctRun);
64     m_indices = CTRunGetStringIndicesPtr(ctRun);
65     if (!m_indices) {
66         m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex)));
67         CFDataIncreaseLength(m_indicesData.get(), m_glyphCount * sizeof(CFIndex));
68         m_indices = reinterpret_cast<const CFIndex*>(CFDataGetMutableBytePtr(m_indicesData.get()));
69         CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), const_cast<CFIndex*>(m_indices));
70     }
71 }
72 
73 // Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on
74 // glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path.
CoreTextRun(const SimpleFontData * fontData,const UChar * characters,unsigned stringLocation,size_t stringLength,bool ltr)75 CoreTextController::CoreTextRun::CoreTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr)
76     : m_fontData(fontData)
77     , m_characters(characters)
78     , m_stringLocation(stringLocation)
79     , m_stringLength(stringLength)
80 {
81     Vector<CFIndex, 16> indices;
82     unsigned r = 0;
83     while (r < stringLength) {
84         indices.append(r);
85         if (U_IS_SURROGATE(characters[r])) {
86             ASSERT(r + 1 < stringLength);
87             ASSERT(U_IS_SURROGATE_LEAD(characters[r]));
88             ASSERT(U_IS_TRAIL(characters[r + 1]));
89             r += 2;
90         } else
91             r++;
92     }
93     m_glyphCount = indices.size();
94     if (!ltr) {
95         for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end)
96             std::swap(indices[r], indices[end]);
97     }
98     m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex)));
99     CFDataAppendBytes(m_indicesData.get(), reinterpret_cast<const UInt8*>(indices.data()), m_glyphCount * sizeof(CFIndex));
100     m_indices = reinterpret_cast<const CFIndex*>(CFDataGetBytePtr(m_indicesData.get()));
101 }
102 
CoreTextController(const Font * font,const TextRun & run,bool mayUseNaturalWritingDirection)103 CoreTextController::CoreTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection)
104     : m_font(*font)
105     , m_run(run)
106     , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
107     , m_currentCharacter(0)
108     , m_end(run.length())
109     , m_totalWidth(0)
110     , m_runWidthSoFar(0)
111     , m_numGlyphsSoFar(0)
112     , m_currentRun(0)
113     , m_glyphInCurrentRun(0)
114     , m_finalRoundingWidth(0)
115     , m_lastRoundingGlyph(0)
116 {
117     m_padding = m_run.padding();
118     if (!m_padding)
119         m_padPerSpace = 0;
120     else {
121         float numSpaces = 0;
122         for (int s = 0; s < m_run.length(); s++)
123             if (Font::treatAsSpace(m_run[s]))
124                 numSpaces++;
125 
126         if (numSpaces == 0)
127             m_padPerSpace = 0;
128         else
129             m_padPerSpace = ceilf(m_run.padding() / numSpaces);
130     }
131 
132     collectCoreTextRuns();
133     adjustGlyphsAndAdvances();
134 }
135 
offsetForPosition(int h,bool includePartialGlyphs)136 int CoreTextController::offsetForPosition(int h, bool includePartialGlyphs)
137 {
138     // FIXME: For positions occurring within a ligature, we should return the closest "ligature caret" or
139     // approximate it by dividing the width of the ligature by the number of characters it encompasses.
140     // However, Core Text does not expose a low-level API for directly finding
141     // out how many characters a ligature encompasses (the "attachment count").
142     if (h >= m_totalWidth)
143         return m_run.ltr() ? m_end : 0;
144     if (h < 0)
145         return m_run.ltr() ? 0 : m_end;
146 
147     CGFloat x = h;
148 
149     size_t runCount = m_coreTextRuns.size();
150     size_t offsetIntoAdjustedGlyphs = 0;
151 
152     for (size_t r = 0; r < runCount; ++r) {
153         const CoreTextRun& coreTextRun = m_coreTextRuns[r];
154         for (unsigned j = 0; j < coreTextRun.glyphCount(); ++j) {
155             CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
156             if (x <= adjustedAdvance) {
157                 CFIndex hitIndex = coreTextRun.indexAt(j);
158                 int stringLength = coreTextRun.stringLength();
159                 TextBreakIterator* characterIterator = characterBreakIterator(coreTextRun.characters(), stringLength);
160                 int clusterStart;
161                 if (isTextBreak(characterIterator, hitIndex))
162                     clusterStart = hitIndex;
163                 else {
164                     clusterStart = textBreakPreceding(characterIterator, hitIndex);
165                     if (clusterStart == TextBreakDone)
166                         clusterStart = 0;
167                 }
168 
169                 if (!includePartialGlyphs)
170                     return coreTextRun.stringLocation() + clusterStart;
171 
172                 int clusterEnd = textBreakFollowing(characterIterator, hitIndex);
173                 if (clusterEnd == TextBreakDone)
174                     clusterEnd = stringLength;
175 
176                 CGFloat clusterWidth = adjustedAdvance;
177                 // FIXME: The search stops at the boundaries of coreTextRun. In theory, it should go on into neighboring CoreTextRuns
178                 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
179                 // reordering and on font fallback should occur within a CTLine.
180                 if (clusterEnd - clusterStart > 1) {
181                     int firstGlyphBeforeCluster = j - 1;
182                     while (firstGlyphBeforeCluster && coreTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
183                         CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
184                         clusterWidth += width;
185                         x += width;
186                         firstGlyphBeforeCluster--;
187                     }
188                     unsigned firstGlyphAfterCluster = j + 1;
189                     while (firstGlyphAfterCluster < coreTextRun.glyphCount() && coreTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
190                         clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
191                         firstGlyphAfterCluster++;
192                     }
193                 }
194                 if (x <= clusterWidth / 2)
195                     return coreTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
196                 else
197                     return coreTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
198             }
199             x -= adjustedAdvance;
200         }
201         offsetIntoAdjustedGlyphs += coreTextRun.glyphCount();
202     }
203 
204     ASSERT_NOT_REACHED();
205     return 0;
206 }
207 
collectCoreTextRuns()208 void CoreTextController::collectCoreTextRuns()
209 {
210     if (!m_end)
211         return;
212 
213     // We break up glyph run generation for the string by FontData and (if needed) the use of small caps.
214     const UChar* cp = m_run.characters();
215     bool hasTrailingSoftHyphen = m_run[m_end - 1] == softHyphen;
216 
217     if (m_font.isSmallCaps() || hasTrailingSoftHyphen)
218         m_smallCapsBuffer.resize(m_end);
219 
220     unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0;
221     const UChar* curr = m_run.rtl() ? cp + m_end  - 1 : cp;
222     const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end;
223 
224     // FIXME: Using HYPHEN-MINUS rather than HYPHEN because Times has a HYPHEN-MINUS glyph that looks like its
225     // SOFT-HYPHEN glyph, and has no HYPHEN glyph.
226     static const UChar hyphen = '-';
227 
228     if (hasTrailingSoftHyphen && m_run.rtl()) {
229         collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData);
230         indexOfFontTransition--;
231         curr--;
232     }
233 
234     GlyphData glyphData;
235     GlyphData nextGlyphData;
236 
237     bool isSurrogate = U16_IS_SURROGATE(*curr);
238     if (isSurrogate) {
239         if (m_run.ltr()) {
240             if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
241                 return;
242             nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
243         } else {
244             if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
245                 return;
246             nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
247         }
248     } else
249         nextGlyphData = m_font.glyphDataForCharacter(*curr, false);
250 
251     UChar newC = 0;
252 
253     bool isSmallCaps;
254     bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr;
255 
256     if (nextIsSmallCaps)
257         m_smallCapsBuffer[curr - cp] = newC;
258 
259     while (true) {
260         curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1);
261         if (curr == end)
262             break;
263 
264         glyphData = nextGlyphData;
265         isSmallCaps = nextIsSmallCaps;
266         int index = curr - cp;
267         isSurrogate = U16_IS_SURROGATE(*curr);
268         UChar c = *curr;
269         bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK);
270         if (isSurrogate) {
271             if (m_run.ltr()) {
272                 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
273                     return;
274                 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
275             } else {
276                 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
277                     return;
278                 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
279             }
280         } else
281             nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps);
282 
283         if (!isSurrogate && m_font.isSmallCaps()) {
284             nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c;
285             if (nextIsSmallCaps)
286                 m_smallCapsBuffer[index] = forceSmallCaps ? c : newC;
287         }
288 
289         if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) {
290             int itemStart = m_run.rtl() ? index + 1 : indexOfFontTransition;
291             int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition;
292             collectCoreTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0);
293             indexOfFontTransition = index;
294         }
295     }
296 
297     int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition - (hasTrailingSoftHyphen ? 1 : 0);
298     if (itemLength) {
299         int itemStart = m_run.rtl() ? 0 : indexOfFontTransition;
300         collectCoreTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0);
301     }
302 
303     if (hasTrailingSoftHyphen && m_run.ltr())
304         collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData);
305 }
306 
advance(unsigned offset,GlyphBuffer * glyphBuffer)307 void CoreTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer)
308 {
309     // FIXME: For offsets falling inside a ligature, we should advance only as far as the appropriate "ligature caret"
310     // or divide the width of the ligature by the number of offsets it encompasses and make an advance proportional
311     // to the offsets into the ligature. However, Core Text does not expose a low-level API for
312     // directly finding out how many characters a ligature encompasses (the "attachment count").
313     if (static_cast<int>(offset) > m_end)
314         offset = m_end;
315 
316     if (offset <= m_currentCharacter)
317         return;
318 
319     m_currentCharacter = offset;
320 
321     size_t runCount = m_coreTextRuns.size();
322 
323     bool ltr = m_run.ltr();
324 
325     unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar;
326     while (m_currentRun < runCount) {
327         const CoreTextRun& coreTextRun = m_coreTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun];
328         size_t glyphCount = coreTextRun.glyphCount();
329         unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
330         while (m_glyphInCurrentRun < glyphCount) {
331             if (coreTextRun.indexAt(g) + coreTextRun.stringLocation() >= m_currentCharacter)
332                 return;
333             CGSize adjustedAdvance = m_adjustedAdvances[k];
334             if (glyphBuffer)
335                 glyphBuffer->add(m_adjustedGlyphs[k], coreTextRun.fontData(), adjustedAdvance);
336             m_runWidthSoFar += adjustedAdvance.width;
337             m_numGlyphsSoFar++;
338             m_glyphInCurrentRun++;
339             if (ltr) {
340                 g++;
341                 k++;
342             } else {
343                 g--;
344                 k--;
345             }
346         }
347         m_currentRun++;
348         m_glyphInCurrentRun = 0;
349     }
350     if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size())
351         m_runWidthSoFar += m_finalRoundingWidth;
352 }
353 
collectCoreTextRunsForCharacters(const UChar * cp,unsigned length,unsigned stringLocation,const SimpleFontData * fontData)354 void CoreTextController::collectCoreTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData)
355 {
356     if (!fontData) {
357         // Create a run of missing glyphs from the primary font.
358         m_coreTextRuns.append(CoreTextRun(m_font.primaryFont(), cp, stringLocation, length, m_run.ltr()));
359         return;
360     }
361 
362     RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(NULL, cp, length, kCFAllocatorNull));
363 
364     RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(NULL, string.get(), fontData->getCFStringAttributes()));
365 
366     RetainPtr<CTTypesetterRef> typesetter;
367 
368     if (!m_mayUseNaturalWritingDirection || m_run.directionalOverride()) {
369         static const void* optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel };
370         static const void* ltrOptionValues[] = { kCFBooleanFalse };
371         static const void* rtlOptionValues[] = { kCFBooleanTrue };
372         static CFDictionaryRef ltrTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, ltrOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
373         static CFDictionaryRef rtlTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
374         typesetter.adoptCF(CTTypesetterCreateWithAttributedStringAndOptions(attributedString.get(), m_run.ltr() ? ltrTypesetterOptions : rtlTypesetterOptions));
375     } else
376         typesetter.adoptCF(CTTypesetterCreateWithAttributedString(attributedString.get()));
377 
378     RetainPtr<CTLineRef> line(AdoptCF, CTTypesetterCreateLine(typesetter.get(), CFRangeMake(0, 0)));
379 
380     CFArrayRef runArray = CTLineGetGlyphRuns(line.get());
381 
382     CFIndex runCount = CFArrayGetCount(runArray);
383 
384     for (CFIndex r = 0; r < runCount; r++) {
385         CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r));
386         ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID());
387         m_coreTextRuns.append(CoreTextRun(ctRun, fontData, cp, stringLocation, length));
388     }
389 }
390 
adjustGlyphsAndAdvances()391 void CoreTextController::adjustGlyphsAndAdvances()
392 {
393     size_t runCount = m_coreTextRuns.size();
394     for (size_t r = 0; r < runCount; ++r) {
395         const CoreTextRun& coreTextRun = m_coreTextRuns[r];
396         unsigned glyphCount = coreTextRun.glyphCount();
397         const SimpleFontData* fontData = coreTextRun.fontData();
398 
399         Vector<CGGlyph, 256> glyphsVector;
400         const CGGlyph* glyphs;
401 
402         Vector<CGSize, 256> advancesVector;
403         const CGSize* advances;
404 
405         if (coreTextRun.ctRun()) {
406             glyphs = CTRunGetGlyphsPtr(coreTextRun.ctRun());
407             if (!glyphs) {
408                 glyphsVector.grow(glyphCount);
409                 CTRunGetGlyphs(coreTextRun.ctRun(), CFRangeMake(0, 0), glyphsVector.data());
410                 glyphs = glyphsVector.data();
411             }
412 
413             advances = CTRunGetAdvancesPtr(coreTextRun.ctRun());
414             if (!advances) {
415                 advancesVector.grow(glyphCount);
416                 CTRunGetAdvances(coreTextRun.ctRun(), CFRangeMake(0, 0), advancesVector.data());
417                 advances = advancesVector.data();
418             }
419         } else {
420             // Synthesize a run of missing glyphs.
421             glyphsVector.fill(0, glyphCount);
422             glyphs = glyphsVector.data();
423             advancesVector.fill(CGSizeMake(fontData->widthForGlyph(0), 0), glyphCount);
424             advances = advancesVector.data();
425         }
426 
427         bool lastRun = r + 1 == runCount;
428         const UChar* cp = coreTextRun.characters();
429         CGFloat roundedSpaceWidth = roundCGFloat(fontData->m_spaceWidth);
430         bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
431         bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_padding) && !m_run.spacingDisabled();
432 
433 
434         for (unsigned i = 0; i < glyphCount; i++) {
435             CFIndex characterIndex = coreTextRun.indexAt(i);
436             UChar ch = *(cp + characterIndex);
437             bool lastGlyph = lastRun && i + 1 == glyphCount;
438             UChar nextCh;
439             if (lastGlyph)
440                 nextCh = ' ';
441             else if (i + 1 < glyphCount)
442                 nextCh = *(cp + coreTextRun.indexAt(i + 1));
443             else
444                 nextCh = *(m_coreTextRuns[r + 1].characters() + m_coreTextRuns[r + 1].indexAt(0));
445 
446             bool treatAsSpace = Font::treatAsSpace(ch);
447             CGGlyph glyph = treatAsSpace ? fontData->m_spaceGlyph : glyphs[i];
448             CGSize advance = treatAsSpace ? CGSizeMake(fontData->m_spaceWidth, advances[i].height) : advances[i];
449 
450             if (ch == '\t' && m_run.allowTabs()) {
451                 float tabWidth = m_font.tabWidth();
452                 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth, tabWidth);
453             } else if (ch == zeroWidthSpace || Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
454                 advance.width = 0;
455                 glyph = fontData->m_spaceGlyph;
456             }
457 
458             float roundedAdvanceWidth = roundf(advance.width);
459             if (roundsAdvances)
460                 advance.width = roundedAdvanceWidth;
461 
462             advance.width += fontData->m_syntheticBoldOffset;
463 
464             // We special case spaces in two ways when applying word rounding.
465             // First, we round spaces to an adjusted width in all fonts.
466             // Second, in fixed-pitch fonts we ensure that all glyphs that
467             // match the width of the space glyph have the same width as the space glyph.
468             if (roundedAdvanceWidth == roundedSpaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding())
469                 advance.width = fontData->m_adjustedSpaceWidth;
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 (glyph == fontData->m_spaceGlyph) {
479                     // Account for padding. WebCore uses space padding to justify text.
480                     // We distribute the specified padding over the available spaces in the run.
481                     if (m_padding) {
482                         // Use leftover padding if not evenly divisible by number of spaces.
483                         if (m_padding < m_padPerSpace) {
484                             advance.width += m_padding;
485                             m_padding = 0;
486                         } else {
487                             advance.width += m_padPerSpace;
488                             m_padding -= m_padPerSpace;
489                         }
490                     }
491 
492                     // Account for word-spacing.
493                     if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
494                         advance.width += m_font.wordSpacing();
495                 }
496             }
497 
498             // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters
499             // followed by a character defined by isRoundingHackCharacter()) are always an integer width.
500             // We adjust the width of the last character of a "word" to ensure an integer width.
501             // Force characters that are used to determine word boundaries for the rounding hack
502             // to be integer width, so the following words will start on an integer boundary.
503             if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch))
504                 advance.width = ceilCGFloat(advance.width);
505 
506             // Check to see if the next character is a "rounding hack character", if so, adjust the
507             // width so that the total run width will be on an integer boundary.
508             if (m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh) || m_run.applyRunRounding() && lastGlyph) {
509                 CGFloat totalWidth = m_totalWidth + advance.width;
510                 CGFloat extraWidth = ceilCGFloat(totalWidth) - totalWidth;
511                 if (m_run.ltr())
512                     advance.width += extraWidth;
513                 else {
514                     m_totalWidth += extraWidth;
515                     if (m_lastRoundingGlyph)
516                         m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth;
517                     else
518                         m_finalRoundingWidth = extraWidth;
519                     m_lastRoundingGlyph = m_adjustedAdvances.size() + 1;
520                 }
521             }
522 
523             m_totalWidth += advance.width;
524             advance.height *= -1;
525             m_adjustedAdvances.append(advance);
526             m_adjustedGlyphs.append(glyph);
527         }
528     }
529 }
530 
531 } // namespace WebCore
532 
533 #endif // USE(CORE_TEXT)
534