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;
149 expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters8(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
151 expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters16(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
152 if (isAfterExpansion && !m_run.allowsTrailingExpansion())
153 expansionOpportunityCount--;
155 if (!expansionOpportunityCount)
156 m_expansionPerOpportunity = 0;
158 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
161 collectComplexTextRuns();
162 adjustGlyphsAndAdvances();
165 m_runIndices.reserveInitialCapacity(m_complexTextRuns.size());
167 m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size());
168 unsigned glyphCountSoFar = 0;
169 for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
170 m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar);
171 glyphCountSoFar += m_complexTextRuns[i]->glyphCount();
175 m_runWidthSoFar = m_leadingExpansion;
178 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
180 if (h >= m_totalWidth)
181 return m_run.ltr() ? m_end : 0;
183 h -= m_leadingExpansion;
185 return m_run.ltr() ? 0 : m_end;
189 size_t runCount = m_complexTextRuns.size();
190 size_t offsetIntoAdjustedGlyphs = 0;
192 for (size_t r = 0; r < runCount; ++r) {
193 const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
194 for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
195 size_t index = offsetIntoAdjustedGlyphs + j;
196 CGFloat adjustedAdvance = m_adjustedAdvances[index].width;
198 adjustedAdvance += complexTextRun.initialAdvance().width;
199 if (x < adjustedAdvance) {
200 CFIndex hitGlyphStart = complexTextRun.indexAt(j);
203 hitGlyphEnd = std::max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
205 hitGlyphEnd = std::max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
207 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
208 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
210 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
211 int stringLength = complexTextRun.stringLength();
212 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(StringView(complexTextRun.characters(), stringLength));
214 if (isTextBreak(cursorPositionIterator, hitIndex))
215 clusterStart = hitIndex;
217 clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex);
218 if (clusterStart == TextBreakDone)
222 if (!includePartialGlyphs)
223 return complexTextRun.stringLocation() + clusterStart;
225 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex);
226 if (clusterEnd == TextBreakDone)
227 clusterEnd = stringLength;
229 CGFloat clusterWidth;
230 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
231 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
232 // reordering and no font fallback should occur within a CTLine.
233 if (clusterEnd - clusterStart > 1) {
234 clusterWidth = adjustedAdvance;
235 int firstGlyphBeforeCluster = j - 1;
236 while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
237 CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
238 clusterWidth += width;
240 firstGlyphBeforeCluster--;
242 unsigned firstGlyphAfterCluster = j + 1;
243 while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
244 clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
245 firstGlyphAfterCluster++;
248 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
249 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
251 if (x <= clusterWidth / 2)
252 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
254 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
256 x -= adjustedAdvance;
258 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
261 ASSERT_NOT_REACHED();
265 static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
267 ASSERT(iterator < end);
272 unsigned remainingCharacters = end - iterator;
273 U16_NEXT(iterator, i, remainingCharacters, baseCharacter);
274 iterator = iterator + i;
276 if (U_IS_SURROGATE(baseCharacter))
280 while (iterator < end) {
281 UChar32 nextCharacter;
283 U16_NEXT(iterator, markLength, end - iterator, nextCharacter);
284 if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
286 markCount += markLength;
287 iterator += markLength;
293 void ComplexTextController::collectComplexTextRuns()
298 // We break up glyph run generation for the string by FontData.
301 if (m_run.is8Bit()) {
302 String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length());
303 cp = stringFor8BitRun.characters16();
304 m_stringsFor8BitRuns.append(stringFor8BitRun);
306 cp = m_run.characters16();
308 if (m_font.isSmallCaps())
309 m_smallCapsBuffer.resize(m_end);
311 unsigned indexOfFontTransition = 0;
312 const UChar* curr = cp;
313 const UChar* end = cp + m_end;
315 const SimpleFontData* fontData;
317 const SimpleFontData* nextFontData;
318 bool nextIsMissingGlyph;
321 const UChar* sequenceStart = curr;
322 UChar32 baseCharacter;
323 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
326 UChar uppercaseCharacter = 0;
329 bool nextIsSmallCaps = m_font.isSmallCaps() && !(U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK) && (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
330 ASSERT(uppercaseCharacter == 0 || u_toupper(baseCharacter) <= 0xFFFF);
332 if (nextIsSmallCaps) {
333 m_smallCapsBuffer[sequenceStart - cp] = uppercaseCharacter;
334 for (unsigned i = 0; i < markCount; ++i)
335 m_smallCapsBuffer[sequenceStart - cp + i + 1] = sequenceStart[i + 1];
338 nextIsMissingGlyph = false;
339 nextFontData = m_font.fontDataForCombiningCharacterSequence(sequenceStart, curr - sequenceStart, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
341 nextIsMissingGlyph = true;
344 fontData = nextFontData;
345 isMissingGlyph = nextIsMissingGlyph;
346 isSmallCaps = nextIsSmallCaps;
347 int index = curr - cp;
349 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
352 if (m_font.isSmallCaps()) {
353 ASSERT(u_toupper(baseCharacter) <= 0xFFFF);
354 uppercaseCharacter = u_toupper(baseCharacter);
355 nextIsSmallCaps = uppercaseCharacter != baseCharacter;
356 if (nextIsSmallCaps) {
357 m_smallCapsBuffer[index] = uppercaseCharacter;
358 for (unsigned i = 0; i < markCount; ++i)
359 m_smallCapsBuffer[index + i + 1] = cp[index + i + 1];
363 nextIsMissingGlyph = false;
364 if (baseCharacter == zeroWidthJoiner)
365 nextFontData = fontData;
367 nextFontData = m_font.fontDataForCombiningCharacterSequence(cp + index, curr - cp - index, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
369 nextIsMissingGlyph = true;
372 if (nextFontData != fontData || nextIsMissingGlyph != isMissingGlyph) {
373 int itemStart = static_cast<int>(indexOfFontTransition);
374 int itemLength = index - indexOfFontTransition;
375 collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !isMissingGlyph ? fontData : 0);
376 indexOfFontTransition = index;
380 int itemLength = m_end - indexOfFontTransition;
382 int itemStart = indexOfFontTransition;
383 collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !nextIsMissingGlyph ? nextFontData : 0);
387 m_complexTextRuns.reverse();
390 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
392 ASSERT(i < m_glyphCount);
394 return m_coreTextIndices[i];
397 void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
399 ASSERT(m_isMonotonic);
400 m_isMonotonic = false;
402 Vector<bool, 64> mappedIndices(m_stringLength);
403 for (size_t i = 0; i < m_glyphCount; ++i) {
404 ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
405 mappedIndices[indexAt(i)] = true;
408 m_glyphEndOffsets.grow(m_glyphCount);
409 for (size_t i = 0; i < m_glyphCount; ++i) {
410 CFIndex nextMappedIndex = m_indexEnd;
411 for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
412 if (mappedIndices[j]) {
417 m_glyphEndOffsets[i] = nextMappedIndex;
421 unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph)
425 size_t runCount = m_complexTextRuns.size();
426 if (m_currentRun >= runCount)
430 for (unsigned i = 0; i < m_currentRun; ++i)
431 leftmostGlyph += m_complexTextRuns[i]->glyphCount();
435 if (m_runIndices.isEmpty()) {
436 unsigned firstRun = 0;
437 unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]);
438 for (unsigned i = 1; i < runCount; ++i) {
439 unsigned offset = stringBegin(*m_complexTextRuns[i]);
440 if (offset < firstRunOffset) {
442 firstRunOffset = offset;
445 m_runIndices.uncheckedAppend(firstRun);
448 while (m_runIndices.size() <= m_currentRun) {
449 unsigned offset = stringEnd(*m_complexTextRuns[m_runIndices.last()]);
451 for (unsigned i = 0; i < runCount; ++i) {
452 if (offset == stringBegin(*m_complexTextRuns[i])) {
453 m_runIndices.uncheckedAppend(i);
459 unsigned currentRunIndex = m_runIndices[m_currentRun];
460 leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex];
461 return currentRunIndex;
464 unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph)
467 leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount();
473 return indexOfCurrentRun(leftmostGlyph);
476 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const SimpleFontData*>* fallbackFonts)
478 if (static_cast<int>(offset) > m_end)
481 if (offset <= m_currentCharacter) {
482 m_runWidthSoFar = m_leadingExpansion;
483 m_numGlyphsSoFar = 0;
485 m_glyphInCurrentRun = 0;
486 m_characterInCurrentGlyph = 0;
489 m_currentCharacter = offset;
491 size_t runCount = m_complexTextRuns.size();
493 unsigned leftmostGlyph = 0;
494 unsigned currentRunIndex = indexOfCurrentRun(leftmostGlyph);
495 while (m_currentRun < runCount) {
496 const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex];
497 bool ltr = complexTextRun.isLTR();
498 size_t glyphCount = complexTextRun.glyphCount();
499 unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
500 unsigned k = leftmostGlyph + g;
501 if (fallbackFonts && complexTextRun.fontData() != m_font.primaryFont())
502 fallbackFonts->add(complexTextRun.fontData());
504 // We must store the initial advance for the first glyph we are going to draw.
505 // When leftmostGlyph is 0, it represents the first glyph to draw, taking into
506 // account the text direction.
507 if (glyphBuffer && !leftmostGlyph)
508 glyphBuffer->setInitialAdvance(complexTextRun.initialAdvance());
510 while (m_glyphInCurrentRun < glyphCount) {
511 unsigned glyphStartOffset = complexTextRun.indexAt(g);
512 unsigned glyphEndOffset;
513 if (complexTextRun.isMonotonic()) {
515 glyphEndOffset = std::max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd()));
517 glyphEndOffset = std::max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd()));
519 glyphEndOffset = complexTextRun.endOffsetAt(g);
521 CGSize adjustedAdvance = m_adjustedAdvances[k];
523 if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
526 if (glyphBuffer && !m_characterInCurrentGlyph)
527 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance, complexTextRun.indexAt(m_glyphInCurrentRun));
529 unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
530 m_characterInCurrentGlyph = std::min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
531 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
532 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
534 if (glyphStartOffset == glyphEndOffset) {
535 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
536 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
537 m_runWidthSoFar += adjustedAdvance.width;
538 } else if (iterationStyle == ByWholeGlyphs) {
539 if (!oldCharacterInCurrentGlyph)
540 m_runWidthSoFar += adjustedAdvance.width;
542 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
544 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
548 m_glyphInCurrentRun++;
549 m_characterInCurrentGlyph = 0;
558 currentRunIndex = incrementCurrentRun(leftmostGlyph);
559 m_glyphInCurrentRun = 0;
561 if (!m_run.ltr() && m_numGlyphsSoFar == m_adjustedAdvances.size())
562 m_runWidthSoFar += m_finalRoundingWidth;
565 void ComplexTextController::adjustGlyphsAndAdvances()
567 CGFloat widthSinceLastCommit = 0;
568 size_t runCount = m_complexTextRuns.size();
569 bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
570 for (size_t r = 0; r < runCount; ++r) {
571 ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
572 unsigned glyphCount = complexTextRun.glyphCount();
573 const SimpleFontData* fontData = complexTextRun.fontData();
575 bool isEmoji = fontData->platformData().m_isEmoji;
578 // Represent the initial advance for a text run by adjusting the advance
579 // of the last glyph of the previous text run in the glyph buffer.
580 if (r && m_adjustedAdvances.size()) {
581 CGSize previousAdvance = m_adjustedAdvances.last();
582 previousAdvance.width += complexTextRun.initialAdvance().width;
583 previousAdvance.height -= complexTextRun.initialAdvance().height;
584 m_adjustedAdvances[m_adjustedAdvances.size() - 1] = previousAdvance;
586 widthSinceLastCommit += complexTextRun.initialAdvance().width;
588 if (!complexTextRun.isLTR())
591 const CGGlyph* glyphs = complexTextRun.glyphs();
592 const CGSize* advances = complexTextRun.advances();
594 bool lastRun = r + 1 == runCount;
595 bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
596 float spaceWidth = fontData->spaceWidth() - fontData->syntheticBoldOffset();
597 CGFloat roundedSpaceWidth = roundCGFloat(spaceWidth);
598 const UChar* cp = complexTextRun.characters();
599 CGPoint glyphOrigin = CGPointZero;
600 CFIndex lastCharacterIndex = m_run.ltr() ? std::numeric_limits<CFIndex>::min() : std::numeric_limits<CFIndex>::max();
601 bool isMonotonic = true;
603 for (unsigned i = 0; i < glyphCount; i++) {
604 CFIndex characterIndex = complexTextRun.indexAt(i);
606 if (characterIndex < lastCharacterIndex)
609 if (characterIndex > lastCharacterIndex)
612 UChar ch = *(cp + characterIndex);
613 bool lastGlyph = lastRun && i + 1 == glyphCount;
617 else if (i + 1 < glyphCount)
618 nextCh = *(cp + complexTextRun.indexAt(i + 1));
620 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
622 bool treatAsSpace = Font::treatAsSpace(ch);
623 CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
624 CGSize advance = treatAsSpace ? CGSizeMake(spaceWidth, advances[i].height) : advances[i];
626 if (isEmoji && advance.width)
627 advance.width = fontData->widthForGlyph(glyph);
630 if (ch == '\t' && m_run.allowTabs())
631 advance.width = m_font.tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_totalWidth + widthSinceLastCommit);
632 else if (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
634 glyph = fontData->spaceGlyph();
637 float roundedAdvanceWidth = roundf(advance.width);
639 advance.width = roundedAdvanceWidth;
641 advance.width += fontData->syntheticBoldOffset();
644 // We special case spaces in two ways when applying word rounding.
645 // First, we round spaces to an adjusted width in all fonts.
646 // Second, in fixed-pitch fonts we ensure that all glyphs that
647 // match the width of the space glyph have the same width as the space glyph.
648 if (m_run.applyWordRounding() && roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
649 advance.width = fontData->adjustedSpaceWidth();
651 if (hasExtraSpacing) {
652 // If we're a glyph with an advance, go ahead and add in letter-spacing.
653 // That way we weed out zero width lurkers. This behavior matches the fast text code path.
654 if (advance.width && m_font.letterSpacing())
655 advance.width += m_font.letterSpacing();
657 // Handle justification and word-spacing.
658 if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
659 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
661 float previousExpansion = m_expansion;
662 if (!treatAsSpace && !m_afterExpansion) {
663 // Take the expansion opportunity before this ideograph.
664 m_expansion -= m_expansionPerOpportunity;
665 float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
666 m_totalWidth += expansionAtThisOpportunity;
667 if (m_adjustedAdvances.isEmpty())
668 m_leadingExpansion = expansionAtThisOpportunity;
670 m_adjustedAdvances.last().width += expansionAtThisOpportunity;
671 previousExpansion = m_expansion;
673 if (!lastGlyph || m_run.allowsTrailingExpansion()) {
674 m_expansion -= m_expansionPerOpportunity;
675 advance.width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
676 m_afterExpansion = true;
679 m_afterExpansion = false;
681 // Account for word-spacing.
682 if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || r > 0) && m_font.wordSpacing())
683 advance.width += m_font.wordSpacing();
685 m_afterExpansion = false;
688 // Apply rounding hacks if needed.
689 // We adjust the width of the last character of a "word" to ensure an integer width.
690 // Force characters that are used to determine word boundaries for the rounding hack
691 // to be integer width, so the following words will start on an integer boundary.
692 if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch))
693 advance.width = ceilCGFloat(advance.width);
695 // Check to see if the next character is a "rounding hack character", if so, adjust the
696 // width so that the total run width will be on an integer boundary.
697 if ((m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh)) || (m_run.applyRunRounding() && lastGlyph)) {
698 CGFloat totalWidth = widthSinceLastCommit + advance.width;
699 widthSinceLastCommit = ceilCGFloat(totalWidth);
700 CGFloat extraWidth = widthSinceLastCommit - totalWidth;
702 advance.width += extraWidth;
704 if (m_lastRoundingGlyph)
705 m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth;
707 m_finalRoundingWidth = extraWidth;
708 m_lastRoundingGlyph = m_adjustedAdvances.size() + 1;
710 m_totalWidth += widthSinceLastCommit;
711 widthSinceLastCommit = 0;
713 widthSinceLastCommit += advance.width;
715 // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
716 if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
719 advance.height *= -1;
720 m_adjustedAdvances.append(advance);
721 m_adjustedGlyphs.append(glyph);
723 FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
724 glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
725 m_minGlyphBoundingBoxX = std::min(m_minGlyphBoundingBoxX, glyphBounds.x());
726 m_maxGlyphBoundingBoxX = std::max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
727 m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, glyphBounds.y());
728 m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
729 glyphOrigin.x += advance.width;
730 glyphOrigin.y += advance.height;
732 lastCharacterIndex = characterIndex;
735 complexTextRun.setIsNonMonotonic();
737 m_totalWidth += widthSinceLastCommit;
740 } // namespace WebCore