Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
     20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     23  */
     24 
     25 #include "config.h"
     26 #include "ComplexTextController.h"
     27 
     28 #include "FloatSize.h"
     29 #include "Font.h"
     30 #include "TextBreakIterator.h"
     31 #include "TextRun.h"
     32 #include <ApplicationServices/ApplicationServices.h>
     33 #include <wtf/StdLibExtras.h>
     34 #include <wtf/unicode/CharacterNames.h>
     35 
     36 #if defined(BUILDING_ON_LEOPARD)
     37 // Undefined when compiling agains the 10.5 SDK.
     38 #define kCTVersionNumber10_6 0x00030000
     39 #endif
     40 
     41 using namespace std;
     42 
     43 namespace WebCore {
     44 
     45 static inline CGFloat roundCGFloat(CGFloat f)
     46 {
     47     if (sizeof(CGFloat) == sizeof(float))
     48         return roundf(static_cast<float>(f));
     49     return static_cast<CGFloat>(round(f));
     50 }
     51 
     52 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis)
     53     : m_font(*font)
     54     , m_run(run)
     55     , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
     56     , m_forTextEmphasis(forTextEmphasis)
     57     , m_currentCharacter(0)
     58     , m_end(run.length())
     59     , m_totalWidth(0)
     60     , m_runWidthSoFar(0)
     61     , m_numGlyphsSoFar(0)
     62     , m_currentRun(0)
     63     , m_glyphInCurrentRun(0)
     64     , m_characterInCurrentGlyph(0)
     65     , m_expansion(run.expansion())
     66     , m_leadingExpansion(0)
     67     , m_afterExpansion(!run.allowsLeadingExpansion())
     68     , m_fallbackFonts(fallbackFonts)
     69     , m_minGlyphBoundingBoxX(numeric_limits<float>::max())
     70     , m_maxGlyphBoundingBoxX(numeric_limits<float>::min())
     71     , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
     72     , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
     73 {
     74     if (!m_expansion)
     75         m_expansionPerOpportunity = 0;
     76     else {
     77         bool isAfterExpansion = m_afterExpansion;
     78         unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
     79         if (isAfterExpansion && !m_run.allowsTrailingExpansion())
     80             expansionOpportunityCount--;
     81 
     82         if (!expansionOpportunityCount)
     83             m_expansionPerOpportunity = 0;
     84         else
     85             m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
     86     }
     87 
     88     collectComplexTextRuns();
     89     adjustGlyphsAndAdvances();
     90 
     91     m_runWidthSoFar = m_leadingExpansion;
     92 }
     93 
     94 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
     95 {
     96     if (h >= m_totalWidth)
     97         return m_run.ltr() ? m_end : 0;
     98 
     99     h -= m_leadingExpansion;
    100     if (h < 0)
    101         return m_run.ltr() ? 0 : m_end;
    102 
    103     CGFloat x = h;
    104 
    105     size_t runCount = m_complexTextRuns.size();
    106     size_t offsetIntoAdjustedGlyphs = 0;
    107 
    108     for (size_t r = 0; r < runCount; ++r) {
    109         const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
    110         for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
    111             CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
    112             if (x < adjustedAdvance) {
    113                 CFIndex hitGlyphStart = complexTextRun.indexAt(j);
    114                 CFIndex hitGlyphEnd;
    115                 if (m_run.ltr())
    116                     hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
    117                 else
    118                     hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
    119 
    120                 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
    121                 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
    122                 // ligature carets.
    123                 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
    124                 int stringLength = complexTextRun.stringLength();
    125                 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength);
    126                 int clusterStart;
    127                 if (isTextBreak(cursorPositionIterator, hitIndex))
    128                     clusterStart = hitIndex;
    129                 else {
    130                     clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex);
    131                     if (clusterStart == TextBreakDone)
    132                         clusterStart = 0;
    133                 }
    134 
    135                 if (!includePartialGlyphs)
    136                     return complexTextRun.stringLocation() + clusterStart;
    137 
    138                 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex);
    139                 if (clusterEnd == TextBreakDone)
    140                     clusterEnd = stringLength;
    141 
    142                 CGFloat clusterWidth;
    143                 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
    144                 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
    145                 // reordering and on font fallback should occur within a CTLine.
    146                 if (clusterEnd - clusterStart > 1) {
    147                     clusterWidth = adjustedAdvance;
    148                     int firstGlyphBeforeCluster = j - 1;
    149                     while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
    150                         CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
    151                         clusterWidth += width;
    152                         x += width;
    153                         firstGlyphBeforeCluster--;
    154                     }
    155                     unsigned firstGlyphAfterCluster = j + 1;
    156                     while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
    157                         clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
    158                         firstGlyphAfterCluster++;
    159                     }
    160                 } else {
    161                     clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
    162                     x -=  clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
    163                 }
    164                 if (x <= clusterWidth / 2)
    165                     return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
    166                 else
    167                     return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
    168             }
    169             x -= adjustedAdvance;
    170         }
    171         offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
    172     }
    173 
    174     ASSERT_NOT_REACHED();
    175     return 0;
    176 }
    177 
    178 void ComplexTextController::collectComplexTextRuns()
    179 {
    180     if (!m_end)
    181         return;
    182 
    183     // We break up glyph run generation for the string by FontData and (if needed) the use of small caps.
    184     const UChar* cp = m_run.characters();
    185 
    186     if (m_font.isSmallCaps())
    187         m_smallCapsBuffer.resize(m_end);
    188 
    189     unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0;
    190     const UChar* curr = m_run.rtl() ? cp + m_end  - 1 : cp;
    191     const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end;
    192 
    193     GlyphData glyphData;
    194     GlyphData nextGlyphData;
    195 
    196     bool isSurrogate = U16_IS_SURROGATE(*curr);
    197     if (isSurrogate) {
    198         if (m_run.ltr()) {
    199             if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
    200                 return;
    201             nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
    202         } else {
    203             if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
    204                 return;
    205             nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
    206         }
    207     } else
    208         nextGlyphData = m_font.glyphDataForCharacter(*curr, false);
    209 
    210     UChar newC = 0;
    211 
    212     bool isSmallCaps;
    213     bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr;
    214 
    215     if (nextIsSmallCaps)
    216         m_smallCapsBuffer[curr - cp] = newC;
    217 
    218     while (true) {
    219         curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1);
    220         if (curr == end)
    221             break;
    222 
    223         glyphData = nextGlyphData;
    224         isSmallCaps = nextIsSmallCaps;
    225         int index = curr - cp;
    226         isSurrogate = U16_IS_SURROGATE(*curr);
    227         UChar c = *curr;
    228         bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK);
    229         if (isSurrogate) {
    230             if (m_run.ltr()) {
    231                 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
    232                     return;
    233                 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
    234             } else {
    235                 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
    236                     return;
    237                 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
    238             }
    239         } else
    240             nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps ? SmallCapsVariant : AutoVariant);
    241 
    242         if (!isSurrogate && m_font.isSmallCaps()) {
    243             nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c;
    244             if (nextIsSmallCaps)
    245                 m_smallCapsBuffer[index] = forceSmallCaps ? c : newC;
    246         }
    247 
    248         if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) {
    249             int itemStart = m_run.rtl() ? index + 1 : static_cast<int>(indexOfFontTransition);
    250             int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition;
    251             collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0);
    252             indexOfFontTransition = index;
    253         }
    254     }
    255 
    256     int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition;
    257     if (itemLength) {
    258         int itemStart = m_run.rtl() ? 0 : indexOfFontTransition;
    259         collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0);
    260     }
    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 }
    413 
    414 void ComplexTextController::adjustGlyphsAndAdvances()
    415 {
    416     CGFloat widthSinceLastCommit = 0;
    417     size_t runCount = m_complexTextRuns.size();
    418     bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
    419     for (size_t r = 0; r < runCount; ++r) {
    420         ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
    421         unsigned glyphCount = complexTextRun.glyphCount();
    422         const SimpleFontData* fontData = complexTextRun.fontData();
    423 
    424         const CGGlyph* glyphs = complexTextRun.glyphs();
    425         const CGSize* advances = complexTextRun.advances();
    426 
    427         bool lastRun = r + 1 == runCount;
    428         bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
    429         const UChar* cp = complexTextRun.characters();
    430         CGPoint glyphOrigin = CGPointZero;
    431         CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max();
    432         bool isMonotonic = true;
    433 
    434         for (unsigned i = 0; i < glyphCount; i++) {
    435             CFIndex characterIndex = complexTextRun.indexAt(i);
    436             if (m_run.ltr()) {
    437                 if (characterIndex < lastCharacterIndex)
    438                     isMonotonic = false;
    439             } else {
    440                 if (characterIndex > lastCharacterIndex)
    441                     isMonotonic = false;
    442             }
    443             UChar ch = *(cp + characterIndex);
    444             bool lastGlyph = lastRun && i + 1 == glyphCount;
    445             UChar nextCh;
    446             if (lastGlyph)
    447                 nextCh = ' ';
    448             else if (i + 1 < glyphCount)
    449                 nextCh = *(cp + complexTextRun.indexAt(i + 1));
    450             else
    451                 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
    452 
    453             bool treatAsSpace = Font::treatAsSpace(ch);
    454             CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
    455             CGSize advance = treatAsSpace ? CGSizeMake(fontData->spaceWidth(), advances[i].height) : advances[i];
    456 
    457             if (ch == '\t' && m_run.allowTabs()) {
    458                 float tabWidth = m_font.tabWidth(*fontData);
    459                 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth + widthSinceLastCommit, tabWidth);
    460             } else if (ch == zeroWidthSpace || (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace)) {
    461                 advance.width = 0;
    462                 glyph = fontData->spaceGlyph();
    463             }
    464 
    465             float roundedAdvanceWidth = roundf(advance.width);
    466             if (roundsAdvances)
    467                 advance.width = roundedAdvanceWidth;
    468 
    469             advance.width += fontData->syntheticBoldOffset();
    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 (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
    479                     // Distribute the run's total expansion evenly over all expansion opportunities in the run.
    480                     if (m_expansion) {
    481                         if (!treatAsSpace && !m_afterExpansion) {
    482                             // Take the expansion opportunity before this ideograph.
    483                             m_expansion -= m_expansionPerOpportunity;
    484                             m_totalWidth += m_expansionPerOpportunity;
    485                             if (m_adjustedAdvances.isEmpty())
    486                                 m_leadingExpansion = m_expansionPerOpportunity;
    487                             else
    488                                 m_adjustedAdvances.last().width += m_expansionPerOpportunity;
    489                         }
    490                         if (!lastGlyph || m_run.allowsTrailingExpansion()) {
    491                             m_expansion -= m_expansionPerOpportunity;
    492                             advance.width += m_expansionPerOpportunity;
    493                             m_afterExpansion = true;
    494                         }
    495                     } else
    496                         m_afterExpansion = false;
    497 
    498                     // Account for word-spacing.
    499                     if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
    500                         advance.width += m_font.wordSpacing();
    501                 } else
    502                     m_afterExpansion = false;
    503             }
    504 
    505             widthSinceLastCommit += advance.width;
    506 
    507             // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
    508             if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
    509                 glyph = 0;
    510 
    511             advance.height *= -1;
    512             m_adjustedAdvances.append(advance);
    513             m_adjustedGlyphs.append(glyph);
    514 
    515             FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
    516             glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
    517             m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x());
    518             m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
    519             m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y());
    520             m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
    521             glyphOrigin.x += advance.width;
    522             glyphOrigin.y += advance.height;
    523 
    524             lastCharacterIndex = characterIndex;
    525         }
    526         if (!isMonotonic)
    527             complexTextRun.setIsNonMonotonic();
    528     }
    529     m_totalWidth += widthSinceLastCommit;
    530 }
    531 
    532 } // namespace WebCore
    533