1 /* 2 * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include "config.h" 26 #include "ComplexTextController.h" 27 28 #include "FloatSize.h" 29 #include "Font.h" 30 #include "TextBreakIterator.h" 31 #include "TextRun.h" 32 #include <ApplicationServices/ApplicationServices.h> 33 #include <wtf/StdLibExtras.h> 34 #include <wtf/unicode/CharacterNames.h> 35 36 #if defined(BUILDING_ON_LEOPARD) 37 // Undefined when compiling agains the 10.5 SDK. 38 #define kCTVersionNumber10_6 0x00030000 39 #endif 40 41 using namespace std; 42 43 namespace WebCore { 44 45 static inline CGFloat roundCGFloat(CGFloat f) 46 { 47 if (sizeof(CGFloat) == sizeof(float)) 48 return roundf(static_cast<float>(f)); 49 return static_cast<CGFloat>(round(f)); 50 } 51 52 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis) 53 : m_font(*font) 54 , m_run(run) 55 , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection) 56 , m_forTextEmphasis(forTextEmphasis) 57 , m_currentCharacter(0) 58 , m_end(run.length()) 59 , m_totalWidth(0) 60 , m_runWidthSoFar(0) 61 , m_numGlyphsSoFar(0) 62 , m_currentRun(0) 63 , m_glyphInCurrentRun(0) 64 , m_characterInCurrentGlyph(0) 65 , m_expansion(run.expansion()) 66 , m_leadingExpansion(0) 67 , m_afterExpansion(!run.allowsLeadingExpansion()) 68 , m_fallbackFonts(fallbackFonts) 69 , m_minGlyphBoundingBoxX(numeric_limits<float>::max()) 70 , m_maxGlyphBoundingBoxX(numeric_limits<float>::min()) 71 , m_minGlyphBoundingBoxY(numeric_limits<float>::max()) 72 , m_maxGlyphBoundingBoxY(numeric_limits<float>::min()) 73 { 74 if (!m_expansion) 75 m_expansionPerOpportunity = 0; 76 else { 77 bool isAfterExpansion = m_afterExpansion; 78 unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion); 79 if (isAfterExpansion && !m_run.allowsTrailingExpansion()) 80 expansionOpportunityCount--; 81 82 if (!expansionOpportunityCount) 83 m_expansionPerOpportunity = 0; 84 else 85 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; 86 } 87 88 collectComplexTextRuns(); 89 adjustGlyphsAndAdvances(); 90 91 m_runWidthSoFar = m_leadingExpansion; 92 } 93 94 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs) 95 { 96 if (h >= m_totalWidth) 97 return m_run.ltr() ? m_end : 0; 98 99 h -= m_leadingExpansion; 100 if (h < 0) 101 return m_run.ltr() ? 0 : m_end; 102 103 CGFloat x = h; 104 105 size_t runCount = m_complexTextRuns.size(); 106 size_t offsetIntoAdjustedGlyphs = 0; 107 108 for (size_t r = 0; r < runCount; ++r) { 109 const ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; 110 for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) { 111 CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width; 112 if (x < adjustedAdvance) { 113 CFIndex hitGlyphStart = complexTextRun.indexAt(j); 114 CFIndex hitGlyphEnd; 115 if (m_run.ltr()) 116 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd())); 117 else 118 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd())); 119 120 // FIXME: Instead of dividing the glyph's advance equally between the characters, this 121 // could use the glyph's "ligature carets". However, there is no Core Text API to get the 122 // ligature carets. 123 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance); 124 int stringLength = complexTextRun.stringLength(); 125 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength); 126 int clusterStart; 127 if (isTextBreak(cursorPositionIterator, hitIndex)) 128 clusterStart = hitIndex; 129 else { 130 clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex); 131 if (clusterStart == TextBreakDone) 132 clusterStart = 0; 133 } 134 135 if (!includePartialGlyphs) 136 return complexTextRun.stringLocation() + clusterStart; 137 138 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex); 139 if (clusterEnd == TextBreakDone) 140 clusterEnd = stringLength; 141 142 CGFloat clusterWidth; 143 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns 144 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no 145 // reordering and on font fallback should occur within a CTLine. 146 if (clusterEnd - clusterStart > 1) { 147 clusterWidth = adjustedAdvance; 148 int firstGlyphBeforeCluster = j - 1; 149 while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) { 150 CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width; 151 clusterWidth += width; 152 x += width; 153 firstGlyphBeforeCluster--; 154 } 155 unsigned firstGlyphAfterCluster = j + 1; 156 while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) { 157 clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width; 158 firstGlyphAfterCluster++; 159 } 160 } else { 161 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart); 162 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1); 163 } 164 if (x <= clusterWidth / 2) 165 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd); 166 else 167 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart); 168 } 169 x -= adjustedAdvance; 170 } 171 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount(); 172 } 173 174 ASSERT_NOT_REACHED(); 175 return 0; 176 } 177 178 void ComplexTextController::collectComplexTextRuns() 179 { 180 if (!m_end) 181 return; 182 183 // We break up glyph run generation for the string by FontData and (if needed) the use of small caps. 184 const UChar* cp = m_run.characters(); 185 186 if (m_font.isSmallCaps()) 187 m_smallCapsBuffer.resize(m_end); 188 189 unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0; 190 const UChar* curr = m_run.rtl() ? cp + m_end - 1 : cp; 191 const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end; 192 193 GlyphData glyphData; 194 GlyphData nextGlyphData; 195 196 bool isSurrogate = U16_IS_SURROGATE(*curr); 197 if (isSurrogate) { 198 if (m_run.ltr()) { 199 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) 200 return; 201 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); 202 } else { 203 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) 204 return; 205 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); 206 } 207 } else 208 nextGlyphData = m_font.glyphDataForCharacter(*curr, false); 209 210 UChar newC = 0; 211 212 bool isSmallCaps; 213 bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr; 214 215 if (nextIsSmallCaps) 216 m_smallCapsBuffer[curr - cp] = newC; 217 218 while (true) { 219 curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1); 220 if (curr == end) 221 break; 222 223 glyphData = nextGlyphData; 224 isSmallCaps = nextIsSmallCaps; 225 int index = curr - cp; 226 isSurrogate = U16_IS_SURROGATE(*curr); 227 UChar c = *curr; 228 bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK); 229 if (isSurrogate) { 230 if (m_run.ltr()) { 231 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) 232 return; 233 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); 234 } else { 235 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) 236 return; 237 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); 238 } 239 } else 240 nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps ? SmallCapsVariant : AutoVariant); 241 242 if (!isSurrogate && m_font.isSmallCaps()) { 243 nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c; 244 if (nextIsSmallCaps) 245 m_smallCapsBuffer[index] = forceSmallCaps ? c : newC; 246 } 247 248 if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) { 249 int itemStart = m_run.rtl() ? index + 1 : static_cast<int>(indexOfFontTransition); 250 int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition; 251 collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0); 252 indexOfFontTransition = index; 253 } 254 } 255 256 int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition; 257 if (itemLength) { 258 int itemStart = m_run.rtl() ? 0 : indexOfFontTransition; 259 collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0); 260 } 261 } 262 263 #if USE(CORE_TEXT) && USE(ATSUI) 264 static inline bool shouldUseATSUIAPI() 265 { 266 enum TypeRenderingAPIToUse { UnInitialized, UseATSUI, UseCoreText }; 267 static TypeRenderingAPIToUse apiToUse = UnInitialized; 268 269 if (UNLIKELY(apiToUse == UnInitialized)) { 270 if (&CTGetCoreTextVersion != 0 && CTGetCoreTextVersion() >= kCTVersionNumber10_6) 271 apiToUse = UseCoreText; 272 else 273 apiToUse = UseATSUI; 274 } 275 276 return apiToUse == UseATSUI; 277 } 278 #endif 279 280 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const 281 { 282 #if USE(CORE_TEXT) && USE(ATSUI) 283 return shouldUseATSUIAPI() ? m_atsuiIndices[i] : m_coreTextIndices[i]; 284 #elif USE(ATSUI) 285 return m_atsuiIndices[i]; 286 #elif USE(CORE_TEXT) 287 return m_coreTextIndices[i]; 288 #endif 289 } 290 291 void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) 292 { 293 #if USE(CORE_TEXT) && USE(ATSUI) 294 if (shouldUseATSUIAPI()) 295 return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData); 296 return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData); 297 #elif USE(ATSUI) 298 return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData); 299 #elif USE(CORE_TEXT) 300 return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData); 301 #endif 302 } 303 304 ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) 305 : m_fontData(fontData) 306 , m_characters(characters) 307 , m_stringLocation(stringLocation) 308 , m_stringLength(stringLength) 309 , m_indexEnd(stringLength) 310 , m_isMonotonic(true) 311 { 312 #if USE(CORE_TEXT) && USE(ATSUI) 313 shouldUseATSUIAPI() ? createTextRunFromFontDataATSUI(ltr) : createTextRunFromFontDataCoreText(ltr); 314 #elif USE(ATSUI) 315 createTextRunFromFontDataATSUI(ltr); 316 #elif USE(CORE_TEXT) 317 createTextRunFromFontDataCoreText(ltr); 318 #endif 319 } 320 321 void ComplexTextController::ComplexTextRun::setIsNonMonotonic() 322 { 323 ASSERT(m_isMonotonic); 324 m_isMonotonic = false; 325 326 Vector<bool, 64> mappedIndices(m_stringLength); 327 for (size_t i = 0; i < m_glyphCount; ++i) { 328 ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength)); 329 mappedIndices[indexAt(i)] = true; 330 } 331 332 m_glyphEndOffsets.grow(m_glyphCount); 333 for (size_t i = 0; i < m_glyphCount; ++i) { 334 CFIndex nextMappedIndex = m_indexEnd; 335 for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) { 336 if (mappedIndices[j]) { 337 nextMappedIndex = j; 338 break; 339 } 340 } 341 m_glyphEndOffsets[i] = nextMappedIndex; 342 } 343 } 344 345 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) 346 { 347 if (static_cast<int>(offset) > m_end) 348 offset = m_end; 349 350 if (offset <= m_currentCharacter) 351 return; 352 353 m_currentCharacter = offset; 354 355 size_t runCount = m_complexTextRuns.size(); 356 357 bool ltr = m_run.ltr(); 358 359 unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar; 360 while (m_currentRun < runCount) { 361 const ComplexTextRun& complexTextRun = *m_complexTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun]; 362 size_t glyphCount = complexTextRun.glyphCount(); 363 unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun; 364 while (m_glyphInCurrentRun < glyphCount) { 365 unsigned glyphStartOffset = complexTextRun.indexAt(g); 366 unsigned glyphEndOffset; 367 if (complexTextRun.isMonotonic()) { 368 if (ltr) 369 glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd())); 370 else 371 glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd())); 372 } else 373 glyphEndOffset = complexTextRun.endOffsetAt(g); 374 375 CGSize adjustedAdvance = m_adjustedAdvances[k]; 376 377 if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter) 378 return; 379 380 if (glyphBuffer && !m_characterInCurrentGlyph) 381 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance); 382 383 unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph; 384 m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset; 385 // FIXME: Instead of dividing the glyph's advance equally between the characters, this 386 // could use the glyph's "ligature carets". However, there is no Core Text API to get the 387 // ligature carets. 388 if (glyphStartOffset == glyphEndOffset) { 389 // When there are multiple glyphs per character we need to advance by the full width of the glyph. 390 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph); 391 m_runWidthSoFar += adjustedAdvance.width; 392 } else 393 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset); 394 395 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter) 396 return; 397 398 m_numGlyphsSoFar++; 399 m_glyphInCurrentRun++; 400 m_characterInCurrentGlyph = 0; 401 if (ltr) { 402 g++; 403 k++; 404 } else { 405 g--; 406 k--; 407 } 408 } 409 m_currentRun++; 410 m_glyphInCurrentRun = 0; 411 } 412 } 413 414 void ComplexTextController::adjustGlyphsAndAdvances() 415 { 416 CGFloat widthSinceLastCommit = 0; 417 size_t runCount = m_complexTextRuns.size(); 418 bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled(); 419 for (size_t r = 0; r < runCount; ++r) { 420 ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; 421 unsigned glyphCount = complexTextRun.glyphCount(); 422 const SimpleFontData* fontData = complexTextRun.fontData(); 423 424 const CGGlyph* glyphs = complexTextRun.glyphs(); 425 const CGSize* advances = complexTextRun.advances(); 426 427 bool lastRun = r + 1 == runCount; 428 bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances(); 429 const UChar* cp = complexTextRun.characters(); 430 CGPoint glyphOrigin = CGPointZero; 431 CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max(); 432 bool isMonotonic = true; 433 434 for (unsigned i = 0; i < glyphCount; i++) { 435 CFIndex characterIndex = complexTextRun.indexAt(i); 436 if (m_run.ltr()) { 437 if (characterIndex < lastCharacterIndex) 438 isMonotonic = false; 439 } else { 440 if (characterIndex > lastCharacterIndex) 441 isMonotonic = false; 442 } 443 UChar ch = *(cp + characterIndex); 444 bool lastGlyph = lastRun && i + 1 == glyphCount; 445 UChar nextCh; 446 if (lastGlyph) 447 nextCh = ' '; 448 else if (i + 1 < glyphCount) 449 nextCh = *(cp + complexTextRun.indexAt(i + 1)); 450 else 451 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0)); 452 453 bool treatAsSpace = Font::treatAsSpace(ch); 454 CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i]; 455 CGSize advance = treatAsSpace ? CGSizeMake(fontData->spaceWidth(), advances[i].height) : advances[i]; 456 457 if (ch == '\t' && m_run.allowTabs()) { 458 float tabWidth = m_font.tabWidth(*fontData); 459 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth + widthSinceLastCommit, tabWidth); 460 } else if (ch == zeroWidthSpace || (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace)) { 461 advance.width = 0; 462 glyph = fontData->spaceGlyph(); 463 } 464 465 float roundedAdvanceWidth = roundf(advance.width); 466 if (roundsAdvances) 467 advance.width = roundedAdvanceWidth; 468 469 advance.width += fontData->syntheticBoldOffset(); 470 471 if (hasExtraSpacing) { 472 // If we're a glyph with an advance, go ahead and add in letter-spacing. 473 // That way we weed out zero width lurkers. This behavior matches the fast text code path. 474 if (advance.width && m_font.letterSpacing()) 475 advance.width += m_font.letterSpacing(); 476 477 // Handle justification and word-spacing. 478 if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) { 479 // Distribute the run's total expansion evenly over all expansion opportunities in the run. 480 if (m_expansion) { 481 if (!treatAsSpace && !m_afterExpansion) { 482 // Take the expansion opportunity before this ideograph. 483 m_expansion -= m_expansionPerOpportunity; 484 m_totalWidth += m_expansionPerOpportunity; 485 if (m_adjustedAdvances.isEmpty()) 486 m_leadingExpansion = m_expansionPerOpportunity; 487 else 488 m_adjustedAdvances.last().width += m_expansionPerOpportunity; 489 } 490 if (!lastGlyph || m_run.allowsTrailingExpansion()) { 491 m_expansion -= m_expansionPerOpportunity; 492 advance.width += m_expansionPerOpportunity; 493 m_afterExpansion = true; 494 } 495 } else 496 m_afterExpansion = false; 497 498 // Account for word-spacing. 499 if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) 500 advance.width += m_font.wordSpacing(); 501 } else 502 m_afterExpansion = false; 503 } 504 505 widthSinceLastCommit += advance.width; 506 507 // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space. 508 if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK))) 509 glyph = 0; 510 511 advance.height *= -1; 512 m_adjustedAdvances.append(advance); 513 m_adjustedGlyphs.append(glyph); 514 515 FloatRect glyphBounds = fontData->boundsForGlyph(glyph); 516 glyphBounds.move(glyphOrigin.x, glyphOrigin.y); 517 m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x()); 518 m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX()); 519 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y()); 520 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY()); 521 glyphOrigin.x += advance.width; 522 glyphOrigin.y += advance.height; 523 524 lastCharacterIndex = characterIndex; 525 } 526 if (!isMonotonic) 527 complexTextRun.setIsNonMonotonic(); 528 } 529 m_totalWidth += widthSinceLastCommit; 530 } 531 532 } // namespace WebCore 533