Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2007, 2008, 2009 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 "CharacterNames.h"
     29 #include "Font.h"
     30 #include "TextBreakIterator.h"
     31 
     32 #include <wtf/StdLibExtras.h>
     33 
     34 #if defined(BUILDING_ON_LEOPARD)
     35 // Undefined when compiling agains the 10.5 SDK.
     36 #define kCTVersionNumber10_6 0x00030000
     37 #endif
     38 
     39 using namespace std;
     40 
     41 namespace WebCore {
     42 
     43 static inline CGFloat roundCGFloat(CGFloat f)
     44 {
     45     if (sizeof(CGFloat) == sizeof(float))
     46         return roundf(static_cast<float>(f));
     47     return static_cast<CGFloat>(round(f));
     48 }
     49 
     50 static inline CGFloat ceilCGFloat(CGFloat f)
     51 {
     52     if (sizeof(CGFloat) == sizeof(float))
     53         return ceilf(static_cast<float>(f));
     54     return static_cast<CGFloat>(ceil(f));
     55 }
     56 
     57 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts)
     58     : m_font(*font)
     59     , m_run(run)
     60     , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
     61     , m_currentCharacter(0)
     62     , m_end(run.length())
     63     , m_totalWidth(0)
     64     , m_runWidthSoFar(0)
     65     , m_numGlyphsSoFar(0)
     66     , m_currentRun(0)
     67     , m_glyphInCurrentRun(0)
     68     , m_characterInCurrentGlyph(0)
     69     , m_finalRoundingWidth(0)
     70     , m_fallbackFonts(fallbackFonts)
     71     , m_lastRoundingGlyph(0)
     72 {
     73     m_padding = m_run.padding();
     74     if (!m_padding)
     75         m_padPerSpace = 0;
     76     else {
     77         float numSpaces = 0;
     78         for (int s = 0; s < m_run.length(); s++)
     79             if (Font::treatAsSpace(m_run[s]))
     80                 numSpaces++;
     81 
     82         if (numSpaces == 0)
     83             m_padPerSpace = 0;
     84         else
     85             m_padPerSpace = ceilf(m_run.padding() / numSpaces);
     86     }
     87 
     88     collectComplexTextRuns();
     89     adjustGlyphsAndAdvances();
     90 }
     91 
     92 int ComplexTextController::offsetForPosition(int h, bool includePartialGlyphs)
     93 {
     94     if (h >= m_totalWidth)
     95         return m_run.ltr() ? m_end : 0;
     96     if (h < 0)
     97         return m_run.ltr() ? 0 : m_end;
     98 
     99     CGFloat x = h;
    100 
    101     size_t runCount = m_complexTextRuns.size();
    102     size_t offsetIntoAdjustedGlyphs = 0;
    103 
    104     for (size_t r = 0; r < runCount; ++r) {
    105         const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
    106         for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
    107             CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
    108             if (x < adjustedAdvance) {
    109                 CFIndex hitGlyphStart = complexTextRun.indexAt(j);
    110                 CFIndex hitGlyphEnd;
    111                 if (m_run.ltr())
    112                     hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : complexTextRun.stringLength());
    113                 else
    114                     hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : complexTextRun.stringLength());
    115 
    116                 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
    117                 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
    118                 // ligature carets.
    119                 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
    120                 int stringLength = complexTextRun.stringLength();
    121                 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength);
    122                 int clusterStart;
    123                 if (isTextBreak(cursorPositionIterator, hitIndex))
    124                     clusterStart = hitIndex;
    125                 else {
    126                     clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex);
    127                     if (clusterStart == TextBreakDone)
    128                         clusterStart = 0;
    129                 }
    130 
    131                 if (!includePartialGlyphs)
    132                     return complexTextRun.stringLocation() + clusterStart;
    133 
    134                 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex);
    135                 if (clusterEnd == TextBreakDone)
    136                     clusterEnd = stringLength;
    137 
    138                 CGFloat clusterWidth;
    139                 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
    140                 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
    141                 // reordering and on font fallback should occur within a CTLine.
    142                 if (clusterEnd - clusterStart > 1) {
    143                     clusterWidth = adjustedAdvance;
    144                     int firstGlyphBeforeCluster = j - 1;
    145                     while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
    146                         CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
    147                         clusterWidth += width;
    148                         x += width;
    149                         firstGlyphBeforeCluster--;
    150                     }
    151                     unsigned firstGlyphAfterCluster = j + 1;
    152                     while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
    153                         clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
    154                         firstGlyphAfterCluster++;
    155                     }
    156                 } else {
    157                     clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
    158                     x -=  clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
    159                 }
    160                 if (x <= clusterWidth / 2)
    161                     return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
    162                 else
    163                     return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
    164             }
    165             x -= adjustedAdvance;
    166         }
    167         offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
    168     }
    169 
    170     ASSERT_NOT_REACHED();
    171     return 0;
    172 }
    173 
    174 void ComplexTextController::collectComplexTextRuns()
    175 {
    176     if (!m_end)
    177         return;
    178 
    179     // We break up glyph run generation for the string by FontData and (if needed) the use of small caps.
    180     const UChar* cp = m_run.characters();
    181     bool hasTrailingSoftHyphen = m_run[m_end - 1] == softHyphen;
    182 
    183     if (m_font.isSmallCaps() || hasTrailingSoftHyphen)
    184         m_smallCapsBuffer.resize(m_end);
    185 
    186     unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0;
    187     const UChar* curr = m_run.rtl() ? cp + m_end  - 1 : cp;
    188     const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end;
    189 
    190     // FIXME: Using HYPHEN-MINUS rather than HYPHEN because Times has a HYPHEN-MINUS glyph that looks like its
    191     // SOFT-HYPHEN glyph, and has no HYPHEN glyph.
    192     static const UChar hyphen = '-';
    193 
    194     if (hasTrailingSoftHyphen && m_run.rtl()) {
    195         collectComplexTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData);
    196         indexOfFontTransition--;
    197         curr--;
    198     }
    199 
    200     GlyphData glyphData;
    201     GlyphData nextGlyphData;
    202 
    203     bool isSurrogate = U16_IS_SURROGATE(*curr);
    204     if (isSurrogate) {
    205         if (m_run.ltr()) {
    206             if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
    207                 return;
    208             nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
    209         } else {
    210             if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
    211                 return;
    212             nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
    213         }
    214     } else
    215         nextGlyphData = m_font.glyphDataForCharacter(*curr, false);
    216 
    217     UChar newC = 0;
    218 
    219     bool isSmallCaps;
    220     bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr;
    221 
    222     if (nextIsSmallCaps)
    223         m_smallCapsBuffer[curr - cp] = newC;
    224 
    225     while (true) {
    226         curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1);
    227         if (curr == end)
    228             break;
    229 
    230         glyphData = nextGlyphData;
    231         isSmallCaps = nextIsSmallCaps;
    232         int index = curr - cp;
    233         isSurrogate = U16_IS_SURROGATE(*curr);
    234         UChar c = *curr;
    235         bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK);
    236         if (isSurrogate) {
    237             if (m_run.ltr()) {
    238                 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
    239                     return;
    240                 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
    241             } else {
    242                 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
    243                     return;
    244                 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
    245             }
    246         } else
    247             nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps);
    248 
    249         if (!isSurrogate && m_font.isSmallCaps()) {
    250             nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c;
    251             if (nextIsSmallCaps)
    252                 m_smallCapsBuffer[index] = forceSmallCaps ? c : newC;
    253         }
    254 
    255         if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) {
    256             int itemStart = m_run.rtl() ? index + 1 : indexOfFontTransition;
    257             int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition;
    258             collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0);
    259             indexOfFontTransition = index;
    260         }
    261     }
    262 
    263     int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition - (hasTrailingSoftHyphen ? 1 : 0);
    264     if (itemLength) {
    265         int itemStart = m_run.rtl() ? 0 : indexOfFontTransition;
    266         collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0);
    267     }
    268 
    269     if (hasTrailingSoftHyphen && m_run.ltr())
    270         collectComplexTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData);
    271 }
    272 
    273 #if USE(CORE_TEXT) && USE(ATSUI)
    274 static inline bool shouldUseATSUIAPI()
    275 {
    276     enum TypeRenderingAPIToUse { UnInitialized, UseATSUI, UseCoreText };
    277     DEFINE_STATIC_LOCAL(TypeRenderingAPIToUse, apiToUse, (UnInitialized));
    278 
    279     if (UNLIKELY(apiToUse == UnInitialized)) {
    280         if (&CTGetCoreTextVersion != 0 && CTGetCoreTextVersion() >= kCTVersionNumber10_6)
    281             apiToUse = UseCoreText;
    282         else
    283             apiToUse = UseATSUI;
    284     }
    285 
    286     return apiToUse == UseATSUI;
    287 }
    288 #endif
    289 
    290 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
    291 {
    292 #if USE(CORE_TEXT) && USE(ATSUI)
    293     return shouldUseATSUIAPI() ? m_atsuiIndices[i] : m_coreTextIndices[i];
    294 #elif USE(ATSUI)
    295     return m_atsuiIndices[i];
    296 #elif USE(CORE_TEXT)
    297     return m_coreTextIndices[i];
    298 #endif
    299 }
    300 
    301 void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData)
    302 {
    303 #if USE(CORE_TEXT) && USE(ATSUI)
    304     if (shouldUseATSUIAPI())
    305         return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
    306     return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
    307 #elif USE(ATSUI)
    308     return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
    309 #elif USE(CORE_TEXT)
    310     return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
    311 #endif
    312 }
    313 
    314 ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr)
    315     : m_fontData(fontData)
    316     , m_characters(characters)
    317     , m_stringLocation(stringLocation)
    318     , m_stringLength(stringLength)
    319     , m_isMonotonic(true)
    320 {
    321 #if USE(CORE_TEXT) && USE(ATSUI)
    322     shouldUseATSUIAPI() ? createTextRunFromFontDataATSUI(ltr) : createTextRunFromFontDataCoreText(ltr);
    323 #elif USE(ATSUI)
    324     createTextRunFromFontDataATSUI(ltr);
    325 #elif USE(CORE_TEXT)
    326     createTextRunFromFontDataCoreText(ltr);
    327 #endif
    328 }
    329 
    330 void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
    331 {
    332     ASSERT(m_isMonotonic);
    333     m_isMonotonic = false;
    334 
    335     Vector<bool, 64> mappedIndices(m_stringLength);
    336     for (size_t i = 0; i < m_glyphCount; ++i) {
    337         ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
    338         mappedIndices[indexAt(i)] = true;
    339     }
    340 
    341     m_glyphEndOffsets.grow(m_glyphCount);
    342     for (size_t i = 0; i < m_glyphCount; ++i) {
    343         CFIndex nextMappedIndex = m_stringLength;
    344         for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
    345             if (mappedIndices[j]) {
    346                 nextMappedIndex = j;
    347                 break;
    348             }
    349         }
    350         m_glyphEndOffsets[i] = nextMappedIndex;
    351     }
    352 }
    353 
    354 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer)
    355 {
    356     if (static_cast<int>(offset) > m_end)
    357         offset = m_end;
    358 
    359     if (offset <= m_currentCharacter)
    360         return;
    361 
    362     m_currentCharacter = offset;
    363 
    364     size_t runCount = m_complexTextRuns.size();
    365 
    366     bool ltr = m_run.ltr();
    367 
    368     unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar;
    369     while (m_currentRun < runCount) {
    370         const ComplexTextRun& complexTextRun = *m_complexTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun];
    371         size_t glyphCount = complexTextRun.glyphCount();
    372         unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
    373         while (m_glyphInCurrentRun < glyphCount) {
    374             unsigned glyphStartOffset = complexTextRun.indexAt(g);
    375             unsigned glyphEndOffset;
    376             if (complexTextRun.isMonotonic()) {
    377                 if (ltr)
    378                     glyphEndOffset = max<unsigned>(glyphStartOffset, g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.stringLength());
    379                 else
    380                     glyphEndOffset = max<unsigned>(glyphStartOffset, g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.stringLength());
    381             } else
    382                 glyphEndOffset = complexTextRun.endOffsetAt(g);
    383 
    384             CGSize adjustedAdvance = m_adjustedAdvances[k];
    385 
    386             if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
    387                 return;
    388 
    389             if (glyphBuffer && !m_characterInCurrentGlyph)
    390                 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance);
    391 
    392             unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
    393             m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
    394             // FIXME: Instead of dividing the glyph's advance equially between the characters, this
    395             // could use the glyph's "ligature carets". However, there is no Core Text API to get the
    396             // ligature carets.
    397             m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
    398 
    399             if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
    400                 return;
    401 
    402             m_numGlyphsSoFar++;
    403             m_glyphInCurrentRun++;
    404             m_characterInCurrentGlyph = 0;
    405             if (ltr) {
    406                 g++;
    407                 k++;
    408             } else {
    409                 g--;
    410                 k--;
    411             }
    412         }
    413         m_currentRun++;
    414         m_glyphInCurrentRun = 0;
    415     }
    416     if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size())
    417         m_runWidthSoFar += m_finalRoundingWidth;
    418 }
    419 
    420 void ComplexTextController::adjustGlyphsAndAdvances()
    421 {
    422     size_t runCount = m_complexTextRuns.size();
    423     for (size_t r = 0; r < runCount; ++r) {
    424         ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
    425         unsigned glyphCount = complexTextRun.glyphCount();
    426         const SimpleFontData* fontData = complexTextRun.fontData();
    427 
    428         const CGGlyph* glyphs = complexTextRun.glyphs();
    429         const CGSize* advances = complexTextRun.advances();
    430 
    431         bool lastRun = r + 1 == runCount;
    432         const UChar* cp = complexTextRun.characters();
    433         CGFloat roundedSpaceWidth = roundCGFloat(fontData->spaceWidth());
    434         bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
    435         bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_padding) && !m_run.spacingDisabled();
    436         CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max();
    437         bool isMonotonic = true;
    438 
    439         for (unsigned i = 0; i < glyphCount; i++) {
    440             CFIndex characterIndex = complexTextRun.indexAt(i);
    441             if (m_run.ltr()) {
    442                 if (characterIndex < lastCharacterIndex)
    443                     isMonotonic = false;
    444             } else {
    445                 if (characterIndex > lastCharacterIndex)
    446                     isMonotonic = false;
    447             }
    448             UChar ch = *(cp + characterIndex);
    449             bool lastGlyph = lastRun && i + 1 == glyphCount;
    450             UChar nextCh;
    451             if (lastGlyph)
    452                 nextCh = ' ';
    453             else if (i + 1 < glyphCount)
    454                 nextCh = *(cp + complexTextRun.indexAt(i + 1));
    455             else
    456                 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
    457 
    458             bool treatAsSpace = Font::treatAsSpace(ch);
    459             CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
    460             CGSize advance = treatAsSpace ? CGSizeMake(fontData->spaceWidth(), advances[i].height) : advances[i];
    461 
    462             if (ch == '\t' && m_run.allowTabs()) {
    463                 float tabWidth = m_font.tabWidth();
    464                 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth, tabWidth);
    465             } else if (ch == zeroWidthSpace || Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
    466                 advance.width = 0;
    467                 glyph = fontData->spaceGlyph();
    468             }
    469 
    470             float roundedAdvanceWidth = roundf(advance.width);
    471             if (roundsAdvances)
    472                 advance.width = roundedAdvanceWidth;
    473 
    474             advance.width += fontData->syntheticBoldOffset();
    475 
    476             // We special case spaces in two ways when applying word rounding.
    477             // First, we round spaces to an adjusted width in all fonts.
    478             // Second, in fixed-pitch fonts we ensure that all glyphs that
    479             // match the width of the space glyph have the same width as the space glyph.
    480             if (roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()) && m_run.applyWordRounding())
    481                 advance.width = fontData->adjustedSpaceWidth();
    482 
    483             if (hasExtraSpacing) {
    484                 // If we're a glyph with an advance, go ahead and add in letter-spacing.
    485                 // That way we weed out zero width lurkers.  This behavior matches the fast text code path.
    486                 if (advance.width && m_font.letterSpacing())
    487                     advance.width += m_font.letterSpacing();
    488 
    489                 // Handle justification and word-spacing.
    490                 if (glyph == fontData->spaceGlyph()) {
    491                     // Account for padding. WebCore uses space padding to justify text.
    492                     // We distribute the specified padding over the available spaces in the run.
    493                     if (m_padding) {
    494                         // Use leftover padding if not evenly divisible by number of spaces.
    495                         if (m_padding < m_padPerSpace) {
    496                             advance.width += m_padding;
    497                             m_padding = 0;
    498                         } else {
    499                             advance.width += m_padPerSpace;
    500                             m_padding -= m_padPerSpace;
    501                         }
    502                     }
    503 
    504                     // Account for word-spacing.
    505                     if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
    506                         advance.width += m_font.wordSpacing();
    507                 }
    508             }
    509 
    510             // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters
    511             // followed by a character defined by isRoundingHackCharacter()) are always an integer width.
    512             // We adjust the width of the last character of a "word" to ensure an integer width.
    513             // Force characters that are used to determine word boundaries for the rounding hack
    514             // to be integer width, so the following words will start on an integer boundary.
    515             if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch))
    516                 advance.width = ceilCGFloat(advance.width);
    517 
    518             // Check to see if the next character is a "rounding hack character", if so, adjust the
    519             // width so that the total run width will be on an integer boundary.
    520             if (m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh) || m_run.applyRunRounding() && lastGlyph) {
    521                 CGFloat totalWidth = m_totalWidth + advance.width;
    522                 CGFloat extraWidth = ceilCGFloat(totalWidth) - totalWidth;
    523                 if (m_run.ltr())
    524                     advance.width += extraWidth;
    525                 else {
    526                     m_totalWidth += extraWidth;
    527                     if (m_lastRoundingGlyph)
    528                         m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth;
    529                     else
    530                         m_finalRoundingWidth = extraWidth;
    531                     m_lastRoundingGlyph = m_adjustedAdvances.size() + 1;
    532                 }
    533             }
    534 
    535             m_totalWidth += advance.width;
    536             advance.height *= -1;
    537             m_adjustedAdvances.append(advance);
    538             m_adjustedGlyphs.append(glyph);
    539             lastCharacterIndex = characterIndex;
    540         }
    541         if (!isMonotonic)
    542             complexTextRun.setIsNonMonotonic();
    543     }
    544 }
    545 
    546 } // namespace WebCore
    547