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 "platform/fonts/mac/ComplexTextController.h"
28 #include "platform/fonts/Character.h"
29 #include "platform/fonts/Font.h"
30 #include "platform/fonts/GlyphBuffer.h"
31 #include "platform/geometry/FloatSize.h"
32 #include "platform/text/TextBreakIterator.h"
33 #include "platform/text/TextRun.h"
34 #include "wtf/StdLibExtras.h"
35 #include "wtf/unicode/CharacterNames.h"
36 #include <ApplicationServices/ApplicationServices.h>
42 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis)
46 , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
47 , m_forTextEmphasis(forTextEmphasis)
48 , m_currentCharacter(0)
54 , m_glyphInCurrentRun(0)
55 , m_characterInCurrentGlyph(0)
56 , m_expansion(run.expansion())
57 , m_leadingExpansion(0)
58 , m_afterExpansion(!run.allowsLeadingExpansion())
59 , m_fallbackFonts(fallbackFonts)
60 , m_minGlyphBoundingBoxX(numeric_limits<float>::max())
61 , m_maxGlyphBoundingBoxX(numeric_limits<float>::min())
62 , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
63 , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
66 m_expansionPerOpportunity = 0;
68 bool isAfterExpansion = m_afterExpansion;
69 unsigned expansionOpportunityCount;
71 expansionOpportunityCount = Character::expansionOpportunityCount(m_run.characters8(), m_end, m_run.direction(), isAfterExpansion);
73 expansionOpportunityCount = Character::expansionOpportunityCount(m_run.characters16(), m_end, m_run.direction(), isAfterExpansion);
74 if (isAfterExpansion && !m_run.allowsTrailingExpansion())
75 expansionOpportunityCount--;
77 if (!expansionOpportunityCount)
78 m_expansionPerOpportunity = 0;
80 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
83 collectComplexTextRuns();
84 adjustGlyphsAndAdvances();
87 m_runIndices.reserveInitialCapacity(m_complexTextRuns.size());
89 m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size());
90 unsigned glyphCountSoFar = 0;
91 for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
92 m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar);
93 glyphCountSoFar += m_complexTextRuns[i]->glyphCount();
97 m_runWidthSoFar = m_leadingExpansion;
100 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
102 if (h >= m_totalWidth)
103 return m_run.ltr() ? m_end : 0;
105 h -= m_leadingExpansion;
107 return m_run.ltr() ? 0 : m_end;
111 size_t runCount = m_complexTextRuns.size();
112 size_t offsetIntoAdjustedGlyphs = 0;
114 for (size_t r = 0; r < runCount; ++r) {
115 const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
116 for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
117 CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
118 if (x < adjustedAdvance) {
119 CFIndex hitGlyphStart = complexTextRun.indexAt(j);
122 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
124 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
126 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
127 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
129 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
130 int stringLength = complexTextRun.stringLength();
131 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength);
133 if (cursorPositionIterator->isBoundary(hitIndex))
134 clusterStart = hitIndex;
136 clusterStart = cursorPositionIterator->preceding(hitIndex);
137 if (clusterStart == TextBreakDone)
141 if (!includePartialGlyphs)
142 return complexTextRun.stringLocation() + clusterStart;
144 int clusterEnd = cursorPositionIterator->following(hitIndex);
145 if (clusterEnd == TextBreakDone)
146 clusterEnd = stringLength;
148 CGFloat clusterWidth;
149 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
150 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
151 // reordering and no font fallback should occur within a CTLine.
152 if (clusterEnd - clusterStart > 1) {
153 clusterWidth = adjustedAdvance;
154 int firstGlyphBeforeCluster = j - 1;
155 while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
156 CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
157 clusterWidth += width;
159 firstGlyphBeforeCluster--;
161 unsigned firstGlyphAfterCluster = j + 1;
162 while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
163 clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
164 firstGlyphAfterCluster++;
167 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
168 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
170 if (x <= clusterWidth / 2)
171 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
173 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
175 x -= adjustedAdvance;
177 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
180 ASSERT_NOT_REACHED();
184 static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
186 ASSERT(iterator < end);
190 baseCharacter = *iterator++;
192 if (U16_IS_SURROGATE(baseCharacter)) {
193 if (!U16_IS_LEAD(baseCharacter))
197 UChar trail = *iterator++;
198 if (!U16_IS_TRAIL(trail))
200 baseCharacter = U16_GET_SUPPLEMENTARY(baseCharacter, trail);
204 while (iterator < end) {
205 UChar32 nextCharacter;
207 U16_NEXT(iterator, markLength, end - iterator, nextCharacter);
208 if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
210 markCount += markLength;
211 iterator += markLength;
217 void ComplexTextController::collectComplexTextRuns()
222 // We break up glyph run generation for the string by FontData.
225 if (m_run.is8Bit()) {
226 String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length());
227 cp = stringFor8BitRun.characters16();
228 m_stringsFor8BitRuns.append(stringFor8BitRun);
230 cp = m_run.characters16();
232 if (m_font.fontDescription().variant() == FontVariantSmallCaps)
233 m_smallCapsBuffer.resize(m_end);
235 unsigned indexOfFontTransition = 0;
236 const UChar* curr = cp;
237 const UChar* end = cp + m_end;
239 const SimpleFontData* fontData;
241 const SimpleFontData* nextFontData;
242 bool nextIsMissingGlyph;
245 const UChar* sequenceStart = curr;
246 UChar32 baseCharacter;
247 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
250 UChar uppercaseCharacter = 0;
253 bool nextIsSmallCaps = m_font.fontDescription().variant() == FontVariantSmallCaps && !(U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK) && (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
255 if (nextIsSmallCaps) {
256 m_smallCapsBuffer[sequenceStart - cp] = uppercaseCharacter;
257 for (unsigned i = 0; i < markCount; ++i)
258 m_smallCapsBuffer[sequenceStart - cp + i + 1] = sequenceStart[i + 1];
261 nextIsMissingGlyph = false;
262 nextFontData = m_font.fontDataForCombiningCharacterSequence(sequenceStart, curr - sequenceStart, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
264 nextIsMissingGlyph = true;
267 fontData = nextFontData;
268 isMissingGlyph = nextIsMissingGlyph;
269 isSmallCaps = nextIsSmallCaps;
270 int index = curr - cp;
272 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
275 if (m_font.fontDescription().variant()) {
276 nextIsSmallCaps = (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
277 if (nextIsSmallCaps) {
278 m_smallCapsBuffer[index] = uppercaseCharacter;
279 for (unsigned i = 0; i < markCount; ++i)
280 m_smallCapsBuffer[index + i + 1] = cp[index + i + 1];
284 nextIsMissingGlyph = false;
285 if (baseCharacter == zeroWidthJoiner)
286 nextFontData = fontData;
288 nextFontData = m_font.fontDataForCombiningCharacterSequence(cp + index, curr - cp - index, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
290 nextIsMissingGlyph = true;
293 if (nextFontData != fontData || nextIsMissingGlyph != isMissingGlyph) {
294 int itemStart = static_cast<int>(indexOfFontTransition);
295 int itemLength = index - indexOfFontTransition;
296 collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !isMissingGlyph ? fontData : 0);
297 indexOfFontTransition = index;
301 int itemLength = m_end - indexOfFontTransition;
303 int itemStart = indexOfFontTransition;
304 collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !nextIsMissingGlyph ? nextFontData : 0);
308 m_complexTextRuns.reverse();
311 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
313 return m_coreTextIndices[i];
316 void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
318 ASSERT(m_isMonotonic);
319 m_isMonotonic = false;
321 Vector<bool, 64> mappedIndices(m_stringLength);
322 for (size_t i = 0; i < m_glyphCount; ++i) {
323 ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
324 mappedIndices[indexAt(i)] = true;
327 m_glyphEndOffsets.grow(m_glyphCount);
328 for (size_t i = 0; i < m_glyphCount; ++i) {
329 CFIndex nextMappedIndex = m_indexEnd;
330 for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
331 if (mappedIndices[j]) {
336 m_glyphEndOffsets[i] = nextMappedIndex;
340 unsigned ComplexTextController::findNextRunIndex(unsigned runIndex) const
342 const unsigned runOffset = stringEnd(*m_complexTextRuns[runIndex]);
344 // Finds the run with the lowest stringBegin() offset that starts at or
345 // after |runOffset|.
347 // Note that this can't just find a run whose stringBegin() equals the
348 // stringEnd() of the previous run because CoreText on Mac OS X 10.6 does
349 // not return runs covering BiDi control chars, so this has to handle the
352 unsigned lowestOffset = UINT_MAX;
353 for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
354 unsigned offset = stringBegin(*m_complexTextRuns[i]);
355 if (i != runIndex && offset >= runOffset && offset < lowestOffset) {
356 lowestOffset = offset;
361 ASSERT(lowestOffset != UINT_MAX);
365 unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph)
369 size_t runCount = m_complexTextRuns.size();
370 if (m_currentRun >= runCount)
374 for (unsigned i = 0; i < m_currentRun; ++i)
375 leftmostGlyph += m_complexTextRuns[i]->glyphCount();
379 if (m_runIndices.isEmpty()) {
380 unsigned firstRun = 0;
381 unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]);
382 for (unsigned i = 1; i < runCount; ++i) {
383 unsigned offset = stringBegin(*m_complexTextRuns[i]);
384 if (offset < firstRunOffset) {
386 firstRunOffset = offset;
389 m_runIndices.uncheckedAppend(firstRun);
392 while (m_runIndices.size() <= m_currentRun) {
393 m_runIndices.uncheckedAppend(findNextRunIndex(m_runIndices.last()));
396 unsigned currentRunIndex = m_runIndices[m_currentRun];
397 leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex];
398 return currentRunIndex;
401 unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph)
404 leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount();
410 return indexOfCurrentRun(leftmostGlyph);
413 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const SimpleFontData*>* fallbackFonts)
415 if (static_cast<int>(offset) > m_end)
418 if (offset <= m_currentCharacter) {
419 m_runWidthSoFar = m_leadingExpansion;
420 m_numGlyphsSoFar = 0;
422 m_glyphInCurrentRun = 0;
423 m_characterInCurrentGlyph = 0;
426 m_currentCharacter = offset;
428 size_t runCount = m_complexTextRuns.size();
430 unsigned leftmostGlyph = 0;
431 unsigned currentRunIndex = indexOfCurrentRun(leftmostGlyph);
432 while (m_currentRun < runCount) {
433 const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex];
434 bool ltr = complexTextRun.isLTR();
435 size_t glyphCount = complexTextRun.glyphCount();
436 unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
437 unsigned k = leftmostGlyph + g;
438 if (fallbackFonts && complexTextRun.fontData() != m_font.primaryFont())
439 fallbackFonts->add(complexTextRun.fontData());
441 while (m_glyphInCurrentRun < glyphCount) {
442 unsigned glyphStartOffset = complexTextRun.indexAt(g);
443 unsigned glyphEndOffset;
444 if (complexTextRun.isMonotonic()) {
446 glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd()));
448 glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd()));
450 glyphEndOffset = complexTextRun.endOffsetAt(g);
452 CGSize adjustedAdvance = m_adjustedAdvances[k];
454 if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
457 if (glyphBuffer && !m_characterInCurrentGlyph)
458 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), FloatSize(adjustedAdvance));
460 unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
461 m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
462 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
463 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
465 if (glyphStartOffset == glyphEndOffset) {
466 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
467 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
468 m_runWidthSoFar += adjustedAdvance.width;
469 } else if (iterationStyle == ByWholeGlyphs) {
470 if (!oldCharacterInCurrentGlyph)
471 m_runWidthSoFar += adjustedAdvance.width;
473 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
475 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
479 m_glyphInCurrentRun++;
480 m_characterInCurrentGlyph = 0;
489 currentRunIndex = incrementCurrentRun(leftmostGlyph);
490 m_glyphInCurrentRun = 0;
494 void ComplexTextController::adjustGlyphsAndAdvances()
496 CGFloat widthSinceLastCommit = 0;
497 size_t runCount = m_complexTextRuns.size();
498 bool hasExtraSpacing = (m_font.fontDescription().letterSpacing() || m_font.fontDescription().wordSpacing() || m_expansion) && !m_run.spacingDisabled();
499 for (size_t r = 0; r < runCount; ++r) {
500 ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
501 unsigned glyphCount = complexTextRun.glyphCount();
502 const SimpleFontData* fontData = complexTextRun.fontData();
504 if (!complexTextRun.isLTR())
507 const CGGlyph* glyphs = complexTextRun.glyphs();
508 const CGSize* advances = complexTextRun.advances();
510 bool lastRun = r + 1 == runCount;
511 bool roundsAdvances = fontData->platformData().roundsGlyphAdvances();
512 float spaceWidth = fontData->spaceWidth() - fontData->syntheticBoldOffset();
513 const UChar* cp = complexTextRun.characters();
514 CGPoint glyphOrigin = CGPointZero;
515 CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max();
516 bool isMonotonic = true;
518 for (unsigned i = 0; i < glyphCount; i++) {
519 CFIndex characterIndex = complexTextRun.indexAt(i);
521 if (characterIndex < lastCharacterIndex)
524 if (characterIndex > lastCharacterIndex)
527 UChar ch = *(cp + characterIndex);
528 bool lastGlyph = lastRun && i + 1 == glyphCount;
532 else if (i + 1 < glyphCount)
533 nextCh = *(cp + complexTextRun.indexAt(i + 1));
535 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
537 bool treatAsSpace = Character::treatAsSpace(ch);
538 CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
539 CGSize advance = treatAsSpace ? CGSizeMake(spaceWidth, advances[i].height) : advances[i];
541 if (ch == '\t' && m_run.allowTabs()) {
542 advance.width = m_font.tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_totalWidth + widthSinceLastCommit);
543 } else if (Character::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
545 glyph = fontData->spaceGlyph();
548 float roundedAdvanceWidth = roundf(advance.width);
550 advance.width = roundedAdvanceWidth;
552 advance.width += fontData->syntheticBoldOffset();
554 if (hasExtraSpacing) {
555 // If we're a glyph with an advance, go ahead and add in letter-spacing.
556 // That way we weed out zero width lurkers. This behavior matches the fast text code path.
557 if (advance.width && m_font.fontDescription().letterSpacing())
558 advance.width += m_font.fontDescription().letterSpacing();
560 // Handle justification and word-spacing.
561 if (treatAsSpace || Character::isCJKIdeographOrSymbol(ch)) {
562 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
564 if (!treatAsSpace && !m_afterExpansion) {
565 // Take the expansion opportunity before this ideograph.
566 m_expansion -= m_expansionPerOpportunity;
567 float expansionAtThisOpportunity = m_expansionPerOpportunity;
568 m_totalWidth += expansionAtThisOpportunity;
569 if (m_adjustedAdvances.isEmpty())
570 m_leadingExpansion = expansionAtThisOpportunity;
572 m_adjustedAdvances.last().width += expansionAtThisOpportunity;
574 if (!lastGlyph || m_run.allowsTrailingExpansion()) {
575 m_expansion -= m_expansionPerOpportunity;
576 advance.width += m_expansionPerOpportunity;
577 m_afterExpansion = true;
580 m_afterExpansion = false;
582 // Account for word-spacing.
583 if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || r > 0) && m_font.fontDescription().wordSpacing())
584 advance.width += m_font.fontDescription().wordSpacing();
586 m_afterExpansion = false;
589 widthSinceLastCommit += advance.width;
591 // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
592 if (m_forTextEmphasis && (!Character::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
595 advance.height *= -1;
596 m_adjustedAdvances.append(advance);
597 m_adjustedGlyphs.append(glyph);
599 FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
600 glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
601 m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x());
602 m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
603 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y());
604 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
605 glyphOrigin.x += advance.width;
606 glyphOrigin.y += advance.height;
608 lastCharacterIndex = characterIndex;
611 complexTextRun.setIsNonMonotonic();
613 m_totalWidth += widthSinceLastCommit;