1 /* 2 * Copyright (C) 2007, 2008, 2009 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 "CharacterNames.h" 29 #include "Font.h" 30 #include "TextBreakIterator.h" 31 32 #include <wtf/StdLibExtras.h> 33 34 #if defined(BUILDING_ON_LEOPARD) 35 // Undefined when compiling agains the 10.5 SDK. 36 #define kCTVersionNumber10_6 0x00030000 37 #endif 38 39 using namespace std; 40 41 namespace WebCore { 42 43 static inline CGFloat roundCGFloat(CGFloat f) 44 { 45 if (sizeof(CGFloat) == sizeof(float)) 46 return roundf(static_cast<float>(f)); 47 return static_cast<CGFloat>(round(f)); 48 } 49 50 static inline CGFloat ceilCGFloat(CGFloat f) 51 { 52 if (sizeof(CGFloat) == sizeof(float)) 53 return ceilf(static_cast<float>(f)); 54 return static_cast<CGFloat>(ceil(f)); 55 } 56 57 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts) 58 : m_font(*font) 59 , m_run(run) 60 , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection) 61 , m_currentCharacter(0) 62 , m_end(run.length()) 63 , m_totalWidth(0) 64 , m_runWidthSoFar(0) 65 , m_numGlyphsSoFar(0) 66 , m_currentRun(0) 67 , m_glyphInCurrentRun(0) 68 , m_characterInCurrentGlyph(0) 69 , m_finalRoundingWidth(0) 70 , m_fallbackFonts(fallbackFonts) 71 , m_lastRoundingGlyph(0) 72 { 73 m_padding = m_run.padding(); 74 if (!m_padding) 75 m_padPerSpace = 0; 76 else { 77 float numSpaces = 0; 78 for (int s = 0; s < m_run.length(); s++) 79 if (Font::treatAsSpace(m_run[s])) 80 numSpaces++; 81 82 if (numSpaces == 0) 83 m_padPerSpace = 0; 84 else 85 m_padPerSpace = ceilf(m_run.padding() / numSpaces); 86 } 87 88 collectComplexTextRuns(); 89 adjustGlyphsAndAdvances(); 90 } 91 92 int ComplexTextController::offsetForPosition(int h, bool includePartialGlyphs) 93 { 94 if (h >= m_totalWidth) 95 return m_run.ltr() ? m_end : 0; 96 if (h < 0) 97 return m_run.ltr() ? 0 : m_end; 98 99 CGFloat x = h; 100 101 size_t runCount = m_complexTextRuns.size(); 102 size_t offsetIntoAdjustedGlyphs = 0; 103 104 for (size_t r = 0; r < runCount; ++r) { 105 const ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; 106 for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) { 107 CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width; 108 if (x < adjustedAdvance) { 109 CFIndex hitGlyphStart = complexTextRun.indexAt(j); 110 CFIndex hitGlyphEnd; 111 if (m_run.ltr()) 112 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : complexTextRun.stringLength()); 113 else 114 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : complexTextRun.stringLength()); 115 116 // FIXME: Instead of dividing the glyph's advance equally between the characters, this 117 // could use the glyph's "ligature carets". However, there is no Core Text API to get the 118 // ligature carets. 119 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance); 120 int stringLength = complexTextRun.stringLength(); 121 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength); 122 int clusterStart; 123 if (isTextBreak(cursorPositionIterator, hitIndex)) 124 clusterStart = hitIndex; 125 else { 126 clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex); 127 if (clusterStart == TextBreakDone) 128 clusterStart = 0; 129 } 130 131 if (!includePartialGlyphs) 132 return complexTextRun.stringLocation() + clusterStart; 133 134 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex); 135 if (clusterEnd == TextBreakDone) 136 clusterEnd = stringLength; 137 138 CGFloat clusterWidth; 139 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns 140 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no 141 // reordering and on font fallback should occur within a CTLine. 142 if (clusterEnd - clusterStart > 1) { 143 clusterWidth = adjustedAdvance; 144 int firstGlyphBeforeCluster = j - 1; 145 while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) { 146 CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width; 147 clusterWidth += width; 148 x += width; 149 firstGlyphBeforeCluster--; 150 } 151 unsigned firstGlyphAfterCluster = j + 1; 152 while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) { 153 clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width; 154 firstGlyphAfterCluster++; 155 } 156 } else { 157 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart); 158 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1); 159 } 160 if (x <= clusterWidth / 2) 161 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd); 162 else 163 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart); 164 } 165 x -= adjustedAdvance; 166 } 167 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount(); 168 } 169 170 ASSERT_NOT_REACHED(); 171 return 0; 172 } 173 174 void ComplexTextController::collectComplexTextRuns() 175 { 176 if (!m_end) 177 return; 178 179 // We break up glyph run generation for the string by FontData and (if needed) the use of small caps. 180 const UChar* cp = m_run.characters(); 181 bool hasTrailingSoftHyphen = m_run[m_end - 1] == softHyphen; 182 183 if (m_font.isSmallCaps() || hasTrailingSoftHyphen) 184 m_smallCapsBuffer.resize(m_end); 185 186 unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0; 187 const UChar* curr = m_run.rtl() ? cp + m_end - 1 : cp; 188 const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end; 189 190 // FIXME: Using HYPHEN-MINUS rather than HYPHEN because Times has a HYPHEN-MINUS glyph that looks like its 191 // SOFT-HYPHEN glyph, and has no HYPHEN glyph. 192 static const UChar hyphen = '-'; 193 194 if (hasTrailingSoftHyphen && m_run.rtl()) { 195 collectComplexTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); 196 indexOfFontTransition--; 197 curr--; 198 } 199 200 GlyphData glyphData; 201 GlyphData nextGlyphData; 202 203 bool isSurrogate = U16_IS_SURROGATE(*curr); 204 if (isSurrogate) { 205 if (m_run.ltr()) { 206 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) 207 return; 208 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); 209 } else { 210 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) 211 return; 212 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); 213 } 214 } else 215 nextGlyphData = m_font.glyphDataForCharacter(*curr, false); 216 217 UChar newC = 0; 218 219 bool isSmallCaps; 220 bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr; 221 222 if (nextIsSmallCaps) 223 m_smallCapsBuffer[curr - cp] = newC; 224 225 while (true) { 226 curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1); 227 if (curr == end) 228 break; 229 230 glyphData = nextGlyphData; 231 isSmallCaps = nextIsSmallCaps; 232 int index = curr - cp; 233 isSurrogate = U16_IS_SURROGATE(*curr); 234 UChar c = *curr; 235 bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK); 236 if (isSurrogate) { 237 if (m_run.ltr()) { 238 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) 239 return; 240 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); 241 } else { 242 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) 243 return; 244 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); 245 } 246 } else 247 nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps); 248 249 if (!isSurrogate && m_font.isSmallCaps()) { 250 nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c; 251 if (nextIsSmallCaps) 252 m_smallCapsBuffer[index] = forceSmallCaps ? c : newC; 253 } 254 255 if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) { 256 int itemStart = m_run.rtl() ? index + 1 : indexOfFontTransition; 257 int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition; 258 collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0); 259 indexOfFontTransition = index; 260 } 261 } 262 263 int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition - (hasTrailingSoftHyphen ? 1 : 0); 264 if (itemLength) { 265 int itemStart = m_run.rtl() ? 0 : indexOfFontTransition; 266 collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0); 267 } 268 269 if (hasTrailingSoftHyphen && m_run.ltr()) 270 collectComplexTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); 271 } 272 273 #if USE(CORE_TEXT) && USE(ATSUI) 274 static inline bool shouldUseATSUIAPI() 275 { 276 enum TypeRenderingAPIToUse { UnInitialized, UseATSUI, UseCoreText }; 277 DEFINE_STATIC_LOCAL(TypeRenderingAPIToUse, apiToUse, (UnInitialized)); 278 279 if (UNLIKELY(apiToUse == UnInitialized)) { 280 if (&CTGetCoreTextVersion != 0 && CTGetCoreTextVersion() >= kCTVersionNumber10_6) 281 apiToUse = UseCoreText; 282 else 283 apiToUse = UseATSUI; 284 } 285 286 return apiToUse == UseATSUI; 287 } 288 #endif 289 290 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const 291 { 292 #if USE(CORE_TEXT) && USE(ATSUI) 293 return shouldUseATSUIAPI() ? m_atsuiIndices[i] : m_coreTextIndices[i]; 294 #elif USE(ATSUI) 295 return m_atsuiIndices[i]; 296 #elif USE(CORE_TEXT) 297 return m_coreTextIndices[i]; 298 #endif 299 } 300 301 void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) 302 { 303 #if USE(CORE_TEXT) && USE(ATSUI) 304 if (shouldUseATSUIAPI()) 305 return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData); 306 return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData); 307 #elif USE(ATSUI) 308 return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData); 309 #elif USE(CORE_TEXT) 310 return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData); 311 #endif 312 } 313 314 ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) 315 : m_fontData(fontData) 316 , m_characters(characters) 317 , m_stringLocation(stringLocation) 318 , m_stringLength(stringLength) 319 , m_isMonotonic(true) 320 { 321 #if USE(CORE_TEXT) && USE(ATSUI) 322 shouldUseATSUIAPI() ? createTextRunFromFontDataATSUI(ltr) : createTextRunFromFontDataCoreText(ltr); 323 #elif USE(ATSUI) 324 createTextRunFromFontDataATSUI(ltr); 325 #elif USE(CORE_TEXT) 326 createTextRunFromFontDataCoreText(ltr); 327 #endif 328 } 329 330 void ComplexTextController::ComplexTextRun::setIsNonMonotonic() 331 { 332 ASSERT(m_isMonotonic); 333 m_isMonotonic = false; 334 335 Vector<bool, 64> mappedIndices(m_stringLength); 336 for (size_t i = 0; i < m_glyphCount; ++i) { 337 ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength)); 338 mappedIndices[indexAt(i)] = true; 339 } 340 341 m_glyphEndOffsets.grow(m_glyphCount); 342 for (size_t i = 0; i < m_glyphCount; ++i) { 343 CFIndex nextMappedIndex = m_stringLength; 344 for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) { 345 if (mappedIndices[j]) { 346 nextMappedIndex = j; 347 break; 348 } 349 } 350 m_glyphEndOffsets[i] = nextMappedIndex; 351 } 352 } 353 354 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) 355 { 356 if (static_cast<int>(offset) > m_end) 357 offset = m_end; 358 359 if (offset <= m_currentCharacter) 360 return; 361 362 m_currentCharacter = offset; 363 364 size_t runCount = m_complexTextRuns.size(); 365 366 bool ltr = m_run.ltr(); 367 368 unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar; 369 while (m_currentRun < runCount) { 370 const ComplexTextRun& complexTextRun = *m_complexTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun]; 371 size_t glyphCount = complexTextRun.glyphCount(); 372 unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun; 373 while (m_glyphInCurrentRun < glyphCount) { 374 unsigned glyphStartOffset = complexTextRun.indexAt(g); 375 unsigned glyphEndOffset; 376 if (complexTextRun.isMonotonic()) { 377 if (ltr) 378 glyphEndOffset = max<unsigned>(glyphStartOffset, g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.stringLength()); 379 else 380 glyphEndOffset = max<unsigned>(glyphStartOffset, g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.stringLength()); 381 } else 382 glyphEndOffset = complexTextRun.endOffsetAt(g); 383 384 CGSize adjustedAdvance = m_adjustedAdvances[k]; 385 386 if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter) 387 return; 388 389 if (glyphBuffer && !m_characterInCurrentGlyph) 390 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance); 391 392 unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph; 393 m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset; 394 // FIXME: Instead of dividing the glyph's advance equially between the characters, this 395 // could use the glyph's "ligature carets". However, there is no Core Text API to get the 396 // ligature carets. 397 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset); 398 399 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter) 400 return; 401 402 m_numGlyphsSoFar++; 403 m_glyphInCurrentRun++; 404 m_characterInCurrentGlyph = 0; 405 if (ltr) { 406 g++; 407 k++; 408 } else { 409 g--; 410 k--; 411 } 412 } 413 m_currentRun++; 414 m_glyphInCurrentRun = 0; 415 } 416 if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size()) 417 m_runWidthSoFar += m_finalRoundingWidth; 418 } 419 420 void ComplexTextController::adjustGlyphsAndAdvances() 421 { 422 size_t runCount = m_complexTextRuns.size(); 423 for (size_t r = 0; r < runCount; ++r) { 424 ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; 425 unsigned glyphCount = complexTextRun.glyphCount(); 426 const SimpleFontData* fontData = complexTextRun.fontData(); 427 428 const CGGlyph* glyphs = complexTextRun.glyphs(); 429 const CGSize* advances = complexTextRun.advances(); 430 431 bool lastRun = r + 1 == runCount; 432 const UChar* cp = complexTextRun.characters(); 433 CGFloat roundedSpaceWidth = roundCGFloat(fontData->spaceWidth()); 434 bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances(); 435 bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_padding) && !m_run.spacingDisabled(); 436 CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max(); 437 bool isMonotonic = true; 438 439 for (unsigned i = 0; i < glyphCount; i++) { 440 CFIndex characterIndex = complexTextRun.indexAt(i); 441 if (m_run.ltr()) { 442 if (characterIndex < lastCharacterIndex) 443 isMonotonic = false; 444 } else { 445 if (characterIndex > lastCharacterIndex) 446 isMonotonic = false; 447 } 448 UChar ch = *(cp + characterIndex); 449 bool lastGlyph = lastRun && i + 1 == glyphCount; 450 UChar nextCh; 451 if (lastGlyph) 452 nextCh = ' '; 453 else if (i + 1 < glyphCount) 454 nextCh = *(cp + complexTextRun.indexAt(i + 1)); 455 else 456 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0)); 457 458 bool treatAsSpace = Font::treatAsSpace(ch); 459 CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i]; 460 CGSize advance = treatAsSpace ? CGSizeMake(fontData->spaceWidth(), advances[i].height) : advances[i]; 461 462 if (ch == '\t' && m_run.allowTabs()) { 463 float tabWidth = m_font.tabWidth(); 464 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth, tabWidth); 465 } else if (ch == zeroWidthSpace || Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) { 466 advance.width = 0; 467 glyph = fontData->spaceGlyph(); 468 } 469 470 float roundedAdvanceWidth = roundf(advance.width); 471 if (roundsAdvances) 472 advance.width = roundedAdvanceWidth; 473 474 advance.width += fontData->syntheticBoldOffset(); 475 476 // We special case spaces in two ways when applying word rounding. 477 // First, we round spaces to an adjusted width in all fonts. 478 // Second, in fixed-pitch fonts we ensure that all glyphs that 479 // match the width of the space glyph have the same width as the space glyph. 480 if (roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()) && m_run.applyWordRounding()) 481 advance.width = fontData->adjustedSpaceWidth(); 482 483 if (hasExtraSpacing) { 484 // If we're a glyph with an advance, go ahead and add in letter-spacing. 485 // That way we weed out zero width lurkers. This behavior matches the fast text code path. 486 if (advance.width && m_font.letterSpacing()) 487 advance.width += m_font.letterSpacing(); 488 489 // Handle justification and word-spacing. 490 if (glyph == fontData->spaceGlyph()) { 491 // Account for padding. WebCore uses space padding to justify text. 492 // We distribute the specified padding over the available spaces in the run. 493 if (m_padding) { 494 // Use leftover padding if not evenly divisible by number of spaces. 495 if (m_padding < m_padPerSpace) { 496 advance.width += m_padding; 497 m_padding = 0; 498 } else { 499 advance.width += m_padPerSpace; 500 m_padding -= m_padPerSpace; 501 } 502 } 503 504 // Account for word-spacing. 505 if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) 506 advance.width += m_font.wordSpacing(); 507 } 508 } 509 510 // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters 511 // followed by a character defined by isRoundingHackCharacter()) are always an integer width. 512 // We adjust the width of the last character of a "word" to ensure an integer width. 513 // Force characters that are used to determine word boundaries for the rounding hack 514 // to be integer width, so the following words will start on an integer boundary. 515 if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch)) 516 advance.width = ceilCGFloat(advance.width); 517 518 // Check to see if the next character is a "rounding hack character", if so, adjust the 519 // width so that the total run width will be on an integer boundary. 520 if (m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh) || m_run.applyRunRounding() && lastGlyph) { 521 CGFloat totalWidth = m_totalWidth + advance.width; 522 CGFloat extraWidth = ceilCGFloat(totalWidth) - totalWidth; 523 if (m_run.ltr()) 524 advance.width += extraWidth; 525 else { 526 m_totalWidth += extraWidth; 527 if (m_lastRoundingGlyph) 528 m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth; 529 else 530 m_finalRoundingWidth = extraWidth; 531 m_lastRoundingGlyph = m_adjustedAdvances.size() + 1; 532 } 533 } 534 535 m_totalWidth += advance.width; 536 advance.height *= -1; 537 m_adjustedAdvances.append(advance); 538 m_adjustedGlyphs.append(glyph); 539 lastCharacterIndex = characterIndex; 540 } 541 if (!isMonotonic) 542 complexTextRun.setIsNonMonotonic(); 543 } 544 } 545 546 } // namespace WebCore 547