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 "RenderBlock.h"
31 #include "RenderText.h"
32 #include "TextBreakIterator.h"
35 #include <ApplicationServices/ApplicationServices.h>
37 #include <CoreText/CoreText.h>
39 #include <wtf/StdLibExtras.h>
40 #include <wtf/unicode/CharacterNames.h>
46 static bool isNeeded(RenderText* text, const Font& font)
48 TextRun run = RenderBlock::constructTextRun(text, font, text, text->style());
49 return font.codePath(run) == Font::Complex;
52 TextLayout(RenderText* text, const Font& font, float xPos)
54 , m_run(constructTextRun(text, font, xPos))
55 , m_controller(adoptPtr(new ComplexTextController(&m_font, m_run, true)))
59 float width(unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts)
61 m_controller->advance(from, 0, ByWholeGlyphs, fallbackFonts);
62 float beforeWidth = m_controller->runWidthSoFar();
63 if (m_font.wordSpacing() && from && Font::treatAsSpace(m_run[from]))
64 beforeWidth += m_font.wordSpacing();
65 m_controller->advance(from + len, 0, ByWholeGlyphs, fallbackFonts);
66 float afterWidth = m_controller->runWidthSoFar();
67 return afterWidth - beforeWidth;
71 static TextRun constructTextRun(RenderText* text, const Font& font, float xPos)
73 TextRun run = RenderBlock::constructTextRun(text, font, text, text->style());
74 run.setCharactersLength(text->textLength());
75 ASSERT(run.charactersLength() >= run.length());
81 // ComplexTextController has only references to its Font and TextRun so they must be kept alive here.
84 OwnPtr<ComplexTextController> m_controller;
87 PassOwnPtr<TextLayout> Font::createLayout(RenderText* text, float xPos, bool collapseWhiteSpace) const
89 if (!collapseWhiteSpace || !TextLayout::isNeeded(text, *this))
91 return adoptPtr(new TextLayout(text, *this, xPos));
94 void Font::deleteLayout(TextLayout* layout)
99 float Font::width(TextLayout& layout, unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts)
101 return layout.width(from, len, fallbackFonts);
104 static inline CGFloat roundCGFloat(CGFloat f)
106 if (sizeof(CGFloat) == sizeof(float))
107 return roundf(static_cast<float>(f));
108 return static_cast<CGFloat>(round(f));
111 static inline CGFloat ceilCGFloat(CGFloat f)
113 if (sizeof(CGFloat) == sizeof(float))
114 return ceilf(static_cast<float>(f));
115 return static_cast<CGFloat>(ceil(f));
118 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis)
122 , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
123 , m_forTextEmphasis(forTextEmphasis)
124 , m_currentCharacter(0)
125 , m_end(run.length())
128 , m_numGlyphsSoFar(0)
130 , m_glyphInCurrentRun(0)
131 , m_characterInCurrentGlyph(0)
132 , m_finalRoundingWidth(0)
133 , m_expansion(run.expansion())
134 , m_leadingExpansion(0)
135 , m_afterExpansion(!run.allowsLeadingExpansion())
136 , m_fallbackFonts(fallbackFonts)
137 , m_minGlyphBoundingBoxX(std::numeric_limits<float>::max())
138 , m_maxGlyphBoundingBoxX(std::numeric_limits<float>::min())
139 , m_minGlyphBoundingBoxY(std::numeric_limits<float>::max())
140 , m_maxGlyphBoundingBoxY(std::numeric_limits<float>::min())
141 , m_lastRoundingGlyph(0)
144 m_expansionPerOpportunity = 0;
146 bool isAfterExpansion = m_afterExpansion;
147 unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, isAfterExpansion);
148 if (isAfterExpansion && !m_run.allowsTrailingExpansion())
149 expansionOpportunityCount--;
151 if (!expansionOpportunityCount)
152 m_expansionPerOpportunity = 0;
154 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
157 collectComplexTextRuns();
158 adjustGlyphsAndAdvances();
161 m_runIndices.reserveInitialCapacity(m_complexTextRuns.size());
163 m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size());
164 unsigned glyphCountSoFar = 0;
165 for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
166 m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar);
167 glyphCountSoFar += m_complexTextRuns[i]->glyphCount();
171 m_runWidthSoFar = m_leadingExpansion;
174 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
176 if (h >= m_totalWidth)
177 return m_run.ltr() ? m_end : 0;
179 h -= m_leadingExpansion;
181 return m_run.ltr() ? 0 : m_end;
185 size_t runCount = m_complexTextRuns.size();
186 size_t offsetIntoAdjustedGlyphs = 0;
188 for (size_t r = 0; r < runCount; ++r) {
189 const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
190 for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
191 size_t index = offsetIntoAdjustedGlyphs + j;
192 CGFloat adjustedAdvance = m_adjustedAdvances[index].width;
194 adjustedAdvance += complexTextRun.initialAdvance().width;
195 if (x < adjustedAdvance) {
196 CFIndex hitGlyphStart = complexTextRun.indexAt(j);
199 hitGlyphEnd = std::max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
201 hitGlyphEnd = std::max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
203 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
204 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
206 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
207 int stringLength = complexTextRun.stringLength();
208 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(StringView(complexTextRun.characters(), stringLength));
210 if (isTextBreak(cursorPositionIterator, hitIndex))
211 clusterStart = hitIndex;
213 clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex);
214 if (clusterStart == TextBreakDone)
218 if (!includePartialGlyphs)
219 return complexTextRun.stringLocation() + clusterStart;
221 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex);
222 if (clusterEnd == TextBreakDone)
223 clusterEnd = stringLength;
225 CGFloat clusterWidth;
226 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
227 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
228 // reordering and no font fallback should occur within a CTLine.
229 if (clusterEnd - clusterStart > 1) {
230 clusterWidth = adjustedAdvance;
231 int firstGlyphBeforeCluster = j - 1;
232 while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
233 CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
234 clusterWidth += width;
236 firstGlyphBeforeCluster--;
238 unsigned firstGlyphAfterCluster = j + 1;
239 while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
240 clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
241 firstGlyphAfterCluster++;
244 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
245 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
247 if (x <= clusterWidth / 2)
248 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
250 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
252 x -= adjustedAdvance;
254 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
257 ASSERT_NOT_REACHED();
261 static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
263 ASSERT(iterator < end);
268 unsigned remainingCharacters = end - iterator;
269 U16_NEXT(iterator, i, remainingCharacters, baseCharacter);
270 iterator = iterator + i;
272 if (U_IS_SURROGATE(baseCharacter))
276 while (iterator < end) {
277 UChar32 nextCharacter;
279 U16_NEXT(iterator, markLength, end - iterator, nextCharacter);
280 if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
282 markCount += markLength;
283 iterator += markLength;
289 void ComplexTextController::collectComplexTextRuns()
294 // We break up glyph run generation for the string by FontData.
297 if (m_run.is8Bit()) {
298 String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length());
299 cp = stringFor8BitRun.characters16();
300 m_stringsFor8BitRuns.append(stringFor8BitRun);
302 cp = m_run.characters16();
304 if (m_font.isSmallCaps())
305 m_smallCapsBuffer.resize(m_end);
307 unsigned indexOfFontTransition = 0;
308 const UChar* curr = cp;
309 const UChar* end = cp + m_end;
311 const SimpleFontData* fontData;
313 const SimpleFontData* nextFontData;
314 bool nextIsMissingGlyph;
317 const UChar* sequenceStart = curr;
318 UChar32 baseCharacter;
319 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
322 UChar uppercaseCharacter = 0;
325 bool nextIsSmallCaps = m_font.isSmallCaps() && !(U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK) && (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
326 ASSERT(uppercaseCharacter == 0 || u_toupper(baseCharacter) <= 0xFFFF);
328 if (nextIsSmallCaps) {
329 m_smallCapsBuffer[sequenceStart - cp] = uppercaseCharacter;
330 for (unsigned i = 0; i < markCount; ++i)
331 m_smallCapsBuffer[sequenceStart - cp + i + 1] = sequenceStart[i + 1];
334 nextIsMissingGlyph = false;
335 nextFontData = m_font.fontDataForCombiningCharacterSequence(sequenceStart, curr - sequenceStart, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
337 nextIsMissingGlyph = true;
340 fontData = nextFontData;
341 isMissingGlyph = nextIsMissingGlyph;
342 isSmallCaps = nextIsSmallCaps;
343 int index = curr - cp;
345 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
348 if (m_font.isSmallCaps()) {
349 ASSERT(u_toupper(baseCharacter) <= 0xFFFF);
350 uppercaseCharacter = u_toupper(baseCharacter);
351 nextIsSmallCaps = uppercaseCharacter != baseCharacter;
352 if (nextIsSmallCaps) {
353 m_smallCapsBuffer[index] = uppercaseCharacter;
354 for (unsigned i = 0; i < markCount; ++i)
355 m_smallCapsBuffer[index + i + 1] = cp[index + i + 1];
359 nextIsMissingGlyph = false;
360 if (baseCharacter == zeroWidthJoiner)
361 nextFontData = fontData;
363 nextFontData = m_font.fontDataForCombiningCharacterSequence(cp + index, curr - cp - index, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
365 nextIsMissingGlyph = true;
368 if (nextFontData != fontData || nextIsMissingGlyph != isMissingGlyph) {
369 int itemStart = static_cast<int>(indexOfFontTransition);
370 int itemLength = index - indexOfFontTransition;
371 collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !isMissingGlyph ? fontData : 0);
372 indexOfFontTransition = index;
376 int itemLength = m_end - indexOfFontTransition;
378 int itemStart = indexOfFontTransition;
379 collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !nextIsMissingGlyph ? nextFontData : 0);
383 m_complexTextRuns.reverse();
386 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
388 ASSERT(i < m_glyphCount);
390 return m_coreTextIndices[i];
393 void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
395 ASSERT(m_isMonotonic);
396 m_isMonotonic = false;
398 Vector<bool, 64> mappedIndices(m_stringLength);
399 for (size_t i = 0; i < m_glyphCount; ++i) {
400 ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
401 mappedIndices[indexAt(i)] = true;
404 m_glyphEndOffsets.grow(m_glyphCount);
405 for (size_t i = 0; i < m_glyphCount; ++i) {
406 CFIndex nextMappedIndex = m_indexEnd;
407 for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
408 if (mappedIndices[j]) {
413 m_glyphEndOffsets[i] = nextMappedIndex;
417 unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph)
421 size_t runCount = m_complexTextRuns.size();
422 if (m_currentRun >= runCount)
426 for (unsigned i = 0; i < m_currentRun; ++i)
427 leftmostGlyph += m_complexTextRuns[i]->glyphCount();
431 if (m_runIndices.isEmpty()) {
432 unsigned firstRun = 0;
433 unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]);
434 for (unsigned i = 1; i < runCount; ++i) {
435 unsigned offset = stringBegin(*m_complexTextRuns[i]);
436 if (offset < firstRunOffset) {
438 firstRunOffset = offset;
441 m_runIndices.uncheckedAppend(firstRun);
444 while (m_runIndices.size() <= m_currentRun) {
445 unsigned offset = stringEnd(*m_complexTextRuns[m_runIndices.last()]);
447 for (unsigned i = 0; i < runCount; ++i) {
448 if (offset == stringBegin(*m_complexTextRuns[i])) {
449 m_runIndices.uncheckedAppend(i);
455 unsigned currentRunIndex = m_runIndices[m_currentRun];
456 leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex];
457 return currentRunIndex;
460 unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph)
463 leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount();
469 return indexOfCurrentRun(leftmostGlyph);
472 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const SimpleFontData*>* fallbackFonts)
474 if (static_cast<int>(offset) > m_end)
477 if (offset <= m_currentCharacter) {
478 m_runWidthSoFar = m_leadingExpansion;
479 m_numGlyphsSoFar = 0;
481 m_glyphInCurrentRun = 0;
482 m_characterInCurrentGlyph = 0;
485 m_currentCharacter = offset;
487 size_t runCount = m_complexTextRuns.size();
489 unsigned leftmostGlyph = 0;
490 unsigned currentRunIndex = indexOfCurrentRun(leftmostGlyph);
491 while (m_currentRun < runCount) {
492 const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex];
493 bool ltr = complexTextRun.isLTR();
494 size_t glyphCount = complexTextRun.glyphCount();
495 unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
496 unsigned k = leftmostGlyph + g;
497 if (fallbackFonts && complexTextRun.fontData() != m_font.primaryFont())
498 fallbackFonts->add(complexTextRun.fontData());
500 // We must store the initial advance for the first glyph we are going to draw.
501 // When leftmostGlyph is 0, it represents the first glyph to draw, taking into
502 // account the text direction.
503 if (glyphBuffer && !leftmostGlyph)
504 glyphBuffer->setInitialAdvance(complexTextRun.initialAdvance());
506 while (m_glyphInCurrentRun < glyphCount) {
507 unsigned glyphStartOffset = complexTextRun.indexAt(g);
508 unsigned glyphEndOffset;
509 if (complexTextRun.isMonotonic()) {
511 glyphEndOffset = std::max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd()));
513 glyphEndOffset = std::max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd()));
515 glyphEndOffset = complexTextRun.endOffsetAt(g);
517 CGSize adjustedAdvance = m_adjustedAdvances[k];
519 if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
522 if (glyphBuffer && !m_characterInCurrentGlyph)
523 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance, complexTextRun.indexAt(m_glyphInCurrentRun));
525 unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
526 m_characterInCurrentGlyph = std::min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
527 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
528 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
530 if (glyphStartOffset == glyphEndOffset) {
531 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
532 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
533 m_runWidthSoFar += adjustedAdvance.width;
534 } else if (iterationStyle == ByWholeGlyphs) {
535 if (!oldCharacterInCurrentGlyph)
536 m_runWidthSoFar += adjustedAdvance.width;
538 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
540 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
544 m_glyphInCurrentRun++;
545 m_characterInCurrentGlyph = 0;
554 currentRunIndex = incrementCurrentRun(leftmostGlyph);
555 m_glyphInCurrentRun = 0;
557 if (!m_run.ltr() && m_numGlyphsSoFar == m_adjustedAdvances.size())
558 m_runWidthSoFar += m_finalRoundingWidth;
561 void ComplexTextController::adjustGlyphsAndAdvances()
563 CGFloat widthSinceLastCommit = 0;
564 size_t runCount = m_complexTextRuns.size();
565 bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
566 for (size_t r = 0; r < runCount; ++r) {
567 ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
568 unsigned glyphCount = complexTextRun.glyphCount();
569 const SimpleFontData* fontData = complexTextRun.fontData();
571 bool isEmoji = fontData->platformData().m_isEmoji;
574 // Represent the initial advance for a text run by adjusting the advance
575 // of the last glyph of the previous text run in the glyph buffer.
576 if (r && m_adjustedAdvances.size()) {
577 CGSize previousAdvance = m_adjustedAdvances.last();
578 previousAdvance.width += complexTextRun.initialAdvance().width;
579 previousAdvance.height -= complexTextRun.initialAdvance().height;
580 m_adjustedAdvances[m_adjustedAdvances.size() - 1] = previousAdvance;
582 widthSinceLastCommit += complexTextRun.initialAdvance().width;
584 if (!complexTextRun.isLTR())
587 const CGGlyph* glyphs = complexTextRun.glyphs();
588 const CGSize* advances = complexTextRun.advances();
590 bool lastRun = r + 1 == runCount;
591 bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
592 float spaceWidth = fontData->spaceWidth() - fontData->syntheticBoldOffset();
593 CGFloat roundedSpaceWidth = roundCGFloat(spaceWidth);
594 const UChar* cp = complexTextRun.characters();
595 CGPoint glyphOrigin = CGPointZero;
596 CFIndex lastCharacterIndex = m_run.ltr() ? std::numeric_limits<CFIndex>::min() : std::numeric_limits<CFIndex>::max();
597 bool isMonotonic = true;
599 for (unsigned i = 0; i < glyphCount; i++) {
600 CFIndex characterIndex = complexTextRun.indexAt(i);
602 if (characterIndex < lastCharacterIndex)
605 if (characterIndex > lastCharacterIndex)
608 UChar ch = *(cp + characterIndex);
609 bool lastGlyph = lastRun && i + 1 == glyphCount;
613 else if (i + 1 < glyphCount)
614 nextCh = *(cp + complexTextRun.indexAt(i + 1));
616 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
618 bool treatAsSpace = Font::treatAsSpace(ch);
619 CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
620 CGSize advance = treatAsSpace ? CGSizeMake(spaceWidth, advances[i].height) : advances[i];
622 if (isEmoji && advance.width)
623 advance.width = fontData->widthForGlyph(glyph);
626 if (ch == '\t' && m_run.allowTabs())
627 advance.width = m_font.tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_totalWidth + widthSinceLastCommit);
628 else if (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
630 glyph = fontData->spaceGlyph();
633 float roundedAdvanceWidth = roundf(advance.width);
635 advance.width = roundedAdvanceWidth;
637 advance.width += fontData->syntheticBoldOffset();
640 // We special case spaces in two ways when applying word rounding.
641 // First, we round spaces to an adjusted width in all fonts.
642 // Second, in fixed-pitch fonts we ensure that all glyphs that
643 // match the width of the space glyph have the same width as the space glyph.
644 if (m_run.applyWordRounding() && roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
645 advance.width = fontData->adjustedSpaceWidth();
647 if (hasExtraSpacing) {
648 // If we're a glyph with an advance, go ahead and add in letter-spacing.
649 // That way we weed out zero width lurkers. This behavior matches the fast text code path.
650 if (advance.width && m_font.letterSpacing())
651 advance.width += m_font.letterSpacing();
653 // Handle justification and word-spacing.
654 if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
655 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
657 float previousExpansion = m_expansion;
658 if (!treatAsSpace && !m_afterExpansion) {
659 // Take the expansion opportunity before this ideograph.
660 m_expansion -= m_expansionPerOpportunity;
661 float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
662 m_totalWidth += expansionAtThisOpportunity;
663 if (m_adjustedAdvances.isEmpty())
664 m_leadingExpansion = expansionAtThisOpportunity;
666 m_adjustedAdvances.last().width += expansionAtThisOpportunity;
667 previousExpansion = m_expansion;
669 if (!lastGlyph || m_run.allowsTrailingExpansion()) {
670 m_expansion -= m_expansionPerOpportunity;
671 advance.width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
672 m_afterExpansion = true;
675 m_afterExpansion = false;
677 // Account for word-spacing.
678 if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || r > 0) && m_font.wordSpacing())
679 advance.width += m_font.wordSpacing();
681 m_afterExpansion = false;
684 // Apply rounding hacks if needed.
685 // We adjust the width of the last character of a "word" to ensure an integer width.
686 // Force characters that are used to determine word boundaries for the rounding hack
687 // to be integer width, so the following words will start on an integer boundary.
688 if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch))
689 advance.width = ceilCGFloat(advance.width);
691 // Check to see if the next character is a "rounding hack character", if so, adjust the
692 // width so that the total run width will be on an integer boundary.
693 if ((m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh)) || (m_run.applyRunRounding() && lastGlyph)) {
694 CGFloat totalWidth = widthSinceLastCommit + advance.width;
695 widthSinceLastCommit = ceilCGFloat(totalWidth);
696 CGFloat extraWidth = widthSinceLastCommit - totalWidth;
698 advance.width += extraWidth;
700 if (m_lastRoundingGlyph)
701 m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth;
703 m_finalRoundingWidth = extraWidth;
704 m_lastRoundingGlyph = m_adjustedAdvances.size() + 1;
706 m_totalWidth += widthSinceLastCommit;
707 widthSinceLastCommit = 0;
709 widthSinceLastCommit += advance.width;
711 // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
712 if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
715 advance.height *= -1;
716 m_adjustedAdvances.append(advance);
717 m_adjustedGlyphs.append(glyph);
719 FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
720 glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
721 m_minGlyphBoundingBoxX = std::min(m_minGlyphBoundingBoxX, glyphBounds.x());
722 m_maxGlyphBoundingBoxX = std::max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
723 m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, glyphBounds.y());
724 m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
725 glyphOrigin.x += advance.width;
726 glyphOrigin.y += advance.height;
728 lastCharacterIndex = characterIndex;
731 complexTextRun.setIsNonMonotonic();
733 m_totalWidth += widthSinceLastCommit;
736 } // namespace WebCore