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
41using namespace std;
42
43namespace WebCore {
44
45static 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
52ComplexTextController::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
94int 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
178void 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)
264static 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
280CFIndex 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
291void 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
304ComplexTextController::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
321void 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
345void 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
414void 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