2 * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #include "ComplexTextController.h"
28 #include "FloatSize.h"
30 #include "TextBreakIterator.h"
32 #include <ApplicationServices/ApplicationServices.h>
33 #include <wtf/StdLibExtras.h>
34 #include <wtf/unicode/CharacterNames.h>
36 #if defined(BUILDING_ON_LEOPARD)
37 // Undefined when compiling agains the 10.5 SDK.
38 #define kCTVersionNumber10_6 0x00030000
45 static inline CGFloat roundCGFloat(CGFloat f)
47 if (sizeof(CGFloat) == sizeof(float))
48 return roundf(static_cast<float>(f));
49 return static_cast<CGFloat>(round(f));
52 static inline CGFloat ceilCGFloat(CGFloat f)
54 if (sizeof(CGFloat) == sizeof(float))
55 return ceilf(static_cast<float>(f));
56 return static_cast<CGFloat>(ceil(f));
59 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis)
62 , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
63 , m_forTextEmphasis(forTextEmphasis)
64 , m_currentCharacter(0)
70 , m_glyphInCurrentRun(0)
71 , m_characterInCurrentGlyph(0)
72 , m_finalRoundingWidth(0)
73 , m_expansion(run.expansion())
74 , m_leadingExpansion(0)
75 , m_afterExpansion(!run.allowsLeadingExpansion())
76 , m_fallbackFonts(fallbackFonts)
77 , m_minGlyphBoundingBoxX(numeric_limits<float>::max())
78 , m_maxGlyphBoundingBoxX(numeric_limits<float>::min())
79 , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
80 , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
81 , m_lastRoundingGlyph(0)
84 m_expansionPerOpportunity = 0;
86 bool isAfterExpansion = m_afterExpansion;
87 unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
88 if (isAfterExpansion && !m_run.allowsTrailingExpansion())
89 expansionOpportunityCount--;
91 if (!expansionOpportunityCount)
92 m_expansionPerOpportunity = 0;
94 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
97 collectComplexTextRuns();
98 adjustGlyphsAndAdvances();
100 m_runWidthSoFar = m_leadingExpansion;
103 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
105 if (h >= m_totalWidth)
106 return m_run.ltr() ? m_end : 0;
108 h -= m_leadingExpansion;
110 return m_run.ltr() ? 0 : m_end;
114 size_t runCount = m_complexTextRuns.size();
115 size_t offsetIntoAdjustedGlyphs = 0;
117 for (size_t r = 0; r < runCount; ++r) {
118 const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
119 for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
120 CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
121 if (x < adjustedAdvance) {
122 CFIndex hitGlyphStart = complexTextRun.indexAt(j);
125 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
127 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
129 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
130 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
132 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
133 int stringLength = complexTextRun.stringLength();
134 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength);
136 if (isTextBreak(cursorPositionIterator, hitIndex))
137 clusterStart = hitIndex;
139 clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex);
140 if (clusterStart == TextBreakDone)
144 if (!includePartialGlyphs)
145 return complexTextRun.stringLocation() + clusterStart;
147 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex);
148 if (clusterEnd == TextBreakDone)
149 clusterEnd = stringLength;
151 CGFloat clusterWidth;
152 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
153 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
154 // reordering and no font fallback should occur within a CTLine.
155 if (clusterEnd - clusterStart > 1) {
156 clusterWidth = adjustedAdvance;
157 int firstGlyphBeforeCluster = j - 1;
158 while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
159 CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
160 clusterWidth += width;
162 firstGlyphBeforeCluster--;
164 unsigned firstGlyphAfterCluster = j + 1;
165 while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
166 clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
167 firstGlyphAfterCluster++;
170 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
171 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
173 if (x <= clusterWidth / 2)
174 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
176 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
178 x -= adjustedAdvance;
180 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
183 ASSERT_NOT_REACHED();
187 static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
189 ASSERT(iterator < end);
193 baseCharacter = *iterator++;
195 if (U16_IS_SURROGATE(baseCharacter)) {
196 if (!U16_IS_LEAD(baseCharacter))
200 UChar trail = *iterator++;
201 if (!U16_IS_TRAIL(trail))
203 baseCharacter = U16_GET_SUPPLEMENTARY(baseCharacter, trail);
207 while (iterator < end && ((U_GET_GC_MASK(*iterator) & U_GC_M_MASK) || *iterator == zeroWidthJoiner || *iterator == zeroWidthNonJoiner)) {
215 void ComplexTextController::collectComplexTextRuns()
220 // We break up glyph run generation for the string by FontData.
221 const UChar* cp = m_run.characters();
223 if (m_font.isSmallCaps())
224 m_smallCapsBuffer.resize(m_end);
226 unsigned indexOfFontTransition = 0;
227 const UChar* curr = cp;
228 const UChar* end = cp + m_end;
230 const SimpleFontData* fontData;
232 const SimpleFontData* nextFontData;
233 bool nextIsMissingGlyph;
236 const UChar* sequenceStart = curr;
237 UChar32 baseCharacter;
238 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
241 UChar uppercaseCharacter = 0;
244 bool nextIsSmallCaps = m_font.isSmallCaps() && !(U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK) && (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
246 if (nextIsSmallCaps) {
247 m_smallCapsBuffer[sequenceStart - cp] = uppercaseCharacter;
248 for (unsigned i = 0; i < markCount; ++i)
249 m_smallCapsBuffer[sequenceStart - cp + i + 1] = sequenceStart[i + 1];
252 nextIsMissingGlyph = false;
253 nextFontData = m_font.fontDataForCombiningCharacterSequence(sequenceStart, curr - sequenceStart, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
256 nextFontData = systemFallbackFontData();
258 nextIsMissingGlyph = true;
262 fontData = nextFontData;
263 isMissingGlyph = nextIsMissingGlyph;
264 isSmallCaps = nextIsSmallCaps;
265 int index = curr - cp;
267 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
270 if (m_font.isSmallCaps()) {
271 nextIsSmallCaps = (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
272 if (nextIsSmallCaps) {
273 m_smallCapsBuffer[index] = uppercaseCharacter;
274 for (unsigned i = 0; i < markCount; ++i)
275 m_smallCapsBuffer[index + i + 1] = cp[index + i + 1];
279 nextIsMissingGlyph = false;
280 nextFontData = m_font.fontDataForCombiningCharacterSequence(cp + index, curr - cp - index, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
283 nextFontData = systemFallbackFontData();
285 nextIsMissingGlyph = true;
288 if (nextFontData != fontData || nextIsMissingGlyph != isMissingGlyph) {
289 int itemStart = static_cast<int>(indexOfFontTransition);
290 int itemLength = index - indexOfFontTransition;
291 collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !isMissingGlyph ? fontData : 0);
292 indexOfFontTransition = index;
296 int itemLength = m_end - indexOfFontTransition;
298 int itemStart = indexOfFontTransition;
299 collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !nextIsMissingGlyph ? nextFontData : 0);
303 m_complexTextRuns.reverse();
306 #if USE(CORE_TEXT) && USE(ATSUI)
307 static inline bool shouldUseATSUIAPI()
309 enum TypeRenderingAPIToUse { UnInitialized, UseATSUI, UseCoreText };
310 static TypeRenderingAPIToUse apiToUse = UnInitialized;
312 if (UNLIKELY(apiToUse == UnInitialized)) {
313 if (&CTGetCoreTextVersion != 0 && CTGetCoreTextVersion() >= kCTVersionNumber10_6)
314 apiToUse = UseCoreText;
319 return apiToUse == UseATSUI;
323 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
325 #if USE(CORE_TEXT) && USE(ATSUI)
326 return shouldUseATSUIAPI() ? m_atsuiIndices[i] : m_coreTextIndices[i];
328 return m_atsuiIndices[i];
330 return m_coreTextIndices[i];
334 void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData)
337 // Create a run of missing glyphs from the primary font.
338 m_complexTextRuns.append(ComplexTextRun::create(m_font.primaryFont(), cp, stringLocation, length, m_run.ltr()));
342 #if USE(CORE_TEXT) && USE(ATSUI)
343 if (shouldUseATSUIAPI())
344 return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
345 return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
347 return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
349 return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
353 ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr)
354 : m_fontData(fontData)
355 , m_characters(characters)
356 , m_stringLocation(stringLocation)
357 , m_stringLength(stringLength)
358 , m_indexEnd(stringLength)
359 , m_isMonotonic(true)
361 #if USE(CORE_TEXT) && USE(ATSUI)
362 shouldUseATSUIAPI() ? createTextRunFromFontDataATSUI(ltr) : createTextRunFromFontDataCoreText(ltr);
364 createTextRunFromFontDataATSUI(ltr);
366 createTextRunFromFontDataCoreText(ltr);
370 void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
372 ASSERT(m_isMonotonic);
373 m_isMonotonic = false;
375 Vector<bool, 64> mappedIndices(m_stringLength);
376 for (size_t i = 0; i < m_glyphCount; ++i) {
377 ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
378 mappedIndices[indexAt(i)] = true;
381 m_glyphEndOffsets.grow(m_glyphCount);
382 for (size_t i = 0; i < m_glyphCount; ++i) {
383 CFIndex nextMappedIndex = m_indexEnd;
384 for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
385 if (mappedIndices[j]) {
390 m_glyphEndOffsets[i] = nextMappedIndex;
394 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer)
396 if (static_cast<int>(offset) > m_end)
399 if (offset <= m_currentCharacter)
402 m_currentCharacter = offset;
404 size_t runCount = m_complexTextRuns.size();
406 bool ltr = m_run.ltr();
408 unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar;
409 while (m_currentRun < runCount) {
410 const ComplexTextRun& complexTextRun = *m_complexTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun];
411 size_t glyphCount = complexTextRun.glyphCount();
412 unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
413 while (m_glyphInCurrentRun < glyphCount) {
414 unsigned glyphStartOffset = complexTextRun.indexAt(g);
415 unsigned glyphEndOffset;
416 if (complexTextRun.isMonotonic()) {
418 glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd()));
420 glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd()));
422 glyphEndOffset = complexTextRun.endOffsetAt(g);
424 CGSize adjustedAdvance = m_adjustedAdvances[k];
426 if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
429 if (glyphBuffer && !m_characterInCurrentGlyph)
430 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance);
432 unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
433 m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
434 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
435 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
437 if (glyphStartOffset == glyphEndOffset) {
438 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
439 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
440 m_runWidthSoFar += adjustedAdvance.width;
442 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
444 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
448 m_glyphInCurrentRun++;
449 m_characterInCurrentGlyph = 0;
459 m_glyphInCurrentRun = 0;
461 if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size())
462 m_runWidthSoFar += m_finalRoundingWidth;
465 void ComplexTextController::adjustGlyphsAndAdvances()
467 CGFloat widthSinceLastCommit = 0;
468 size_t runCount = m_complexTextRuns.size();
469 bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
470 for (size_t r = 0; r < runCount; ++r) {
471 ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
472 unsigned glyphCount = complexTextRun.glyphCount();
473 const SimpleFontData* fontData = complexTextRun.fontData();
475 const CGGlyph* glyphs = complexTextRun.glyphs();
476 const CGSize* advances = complexTextRun.advances();
478 bool lastRun = r + 1 == runCount;
479 bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
480 float spaceWidth = fontData->spaceWidth() - fontData->syntheticBoldOffset();
481 CGFloat roundedSpaceWidth = roundCGFloat(spaceWidth);
482 const UChar* cp = complexTextRun.characters();
483 CGPoint glyphOrigin = CGPointZero;
484 CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max();
485 bool isMonotonic = true;
487 for (unsigned i = 0; i < glyphCount; i++) {
488 CFIndex characterIndex = complexTextRun.indexAt(i);
490 if (characterIndex < lastCharacterIndex)
493 if (characterIndex > lastCharacterIndex)
496 UChar ch = *(cp + characterIndex);
497 bool lastGlyph = lastRun && i + 1 == glyphCount;
501 else if (i + 1 < glyphCount)
502 nextCh = *(cp + complexTextRun.indexAt(i + 1));
504 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
506 bool treatAsSpace = Font::treatAsSpace(ch);
507 CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
508 CGSize advance = treatAsSpace ? CGSizeMake(spaceWidth, advances[i].height) : advances[i];
510 if (ch == '\t' && m_run.allowTabs()) {
511 float tabWidth = m_font.tabWidth(*fontData);
512 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth + widthSinceLastCommit, tabWidth);
513 } else if (ch == zeroWidthSpace || (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace)) {
515 glyph = fontData->spaceGlyph();
518 float roundedAdvanceWidth = roundf(advance.width);
520 advance.width = roundedAdvanceWidth;
522 advance.width += fontData->syntheticBoldOffset();
525 // We special case spaces in two ways when applying word rounding.
526 // First, we round spaces to an adjusted width in all fonts.
527 // Second, in fixed-pitch fonts we ensure that all glyphs that
528 // match the width of the space glyph have the same width as the space glyph.
529 if (m_run.applyWordRounding() && roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
530 advance.width = fontData->adjustedSpaceWidth();
532 if (hasExtraSpacing) {
533 // If we're a glyph with an advance, go ahead and add in letter-spacing.
534 // That way we weed out zero width lurkers. This behavior matches the fast text code path.
535 if (advance.width && m_font.letterSpacing())
536 advance.width += m_font.letterSpacing();
538 // Handle justification and word-spacing.
539 if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
540 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
542 float previousExpansion = m_expansion;
543 if (!treatAsSpace && !m_afterExpansion) {
544 // Take the expansion opportunity before this ideograph.
545 m_expansion -= m_expansionPerOpportunity;
546 float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
547 m_totalWidth += expansionAtThisOpportunity;
548 if (m_adjustedAdvances.isEmpty())
549 m_leadingExpansion = expansionAtThisOpportunity;
551 m_adjustedAdvances.last().width += expansionAtThisOpportunity;
552 previousExpansion = m_expansion;
554 if (!lastGlyph || m_run.allowsTrailingExpansion()) {
555 m_expansion -= m_expansionPerOpportunity;
556 advance.width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
557 m_afterExpansion = true;
560 m_afterExpansion = false;
562 // Account for word-spacing.
563 if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
564 advance.width += m_font.wordSpacing();
566 m_afterExpansion = false;
569 // Apply rounding hacks if needed.
570 // We adjust the width of the last character of a "word" to ensure an integer width.
571 // Force characters that are used to determine word boundaries for the rounding hack
572 // to be integer width, so the following words will start on an integer boundary.
573 if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch))
574 advance.width = ceilCGFloat(advance.width);
576 // Check to see if the next character is a "rounding hack character", if so, adjust the
577 // width so that the total run width will be on an integer boundary.
578 if ((m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh)) || (m_run.applyRunRounding() && lastGlyph)) {
579 CGFloat totalWidth = widthSinceLastCommit + advance.width;
580 widthSinceLastCommit = ceilCGFloat(totalWidth);
581 CGFloat extraWidth = widthSinceLastCommit - totalWidth;
583 advance.width += extraWidth;
585 if (m_lastRoundingGlyph)
586 m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth;
588 m_finalRoundingWidth = extraWidth;
589 m_lastRoundingGlyph = m_adjustedAdvances.size() + 1;
591 m_totalWidth += widthSinceLastCommit;
592 widthSinceLastCommit = 0;
594 widthSinceLastCommit += advance.width;
596 // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
597 if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
600 advance.height *= -1;
601 m_adjustedAdvances.append(advance);
602 m_adjustedGlyphs.append(glyph);
604 FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
605 glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
606 m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x());
607 m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
608 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y());
609 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
610 glyphOrigin.x += advance.width;
611 glyphOrigin.y += advance.height;
613 lastCharacterIndex = characterIndex;
616 complexTextRun.setIsNonMonotonic();
618 m_totalWidth += widthSinceLastCommit;
621 } // namespace WebCore