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 "platform/fonts/mac/ComplexTextController.h" 27 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> 37 38 using namespace std; 39 40 namespace WebCore { 41 42 static inline CGFloat roundCGFloat(CGFloat f) 43 { 44 if (sizeof(CGFloat) == sizeof(float)) 45 return roundf(static_cast<float>(f)); 46 return static_cast<CGFloat>(round(f)); 47 } 48 49 static inline CGFloat ceilCGFloat(CGFloat f) 50 { 51 if (sizeof(CGFloat) == sizeof(float)) 52 return ceilf(static_cast<float>(f)); 53 return static_cast<CGFloat>(ceil(f)); 54 } 55 56 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis) 57 : m_font(*font) 58 , m_run(run) 59 , m_isLTROnly(true) 60 , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection) 61 , m_forTextEmphasis(forTextEmphasis) 62 , m_currentCharacter(0) 63 , m_end(run.length()) 64 , m_totalWidth(0) 65 , m_runWidthSoFar(0) 66 , m_numGlyphsSoFar(0) 67 , m_currentRun(0) 68 , m_glyphInCurrentRun(0) 69 , m_characterInCurrentGlyph(0) 70 , m_finalRoundingWidth(0) 71 , m_expansion(run.expansion()) 72 , m_leadingExpansion(0) 73 , m_afterExpansion(!run.allowsLeadingExpansion()) 74 , m_fallbackFonts(fallbackFonts) 75 , m_minGlyphBoundingBoxX(numeric_limits<float>::max()) 76 , m_maxGlyphBoundingBoxX(numeric_limits<float>::min()) 77 , m_minGlyphBoundingBoxY(numeric_limits<float>::max()) 78 , m_maxGlyphBoundingBoxY(numeric_limits<float>::min()) 79 , m_lastRoundingGlyph(0) 80 { 81 if (!m_expansion) 82 m_expansionPerOpportunity = 0; 83 else { 84 bool isAfterExpansion = m_afterExpansion; 85 unsigned expansionOpportunityCount; 86 if (m_run.is8Bit()) 87 expansionOpportunityCount = Character::expansionOpportunityCount(m_run.characters8(), m_end, m_run.direction(), isAfterExpansion); 88 else 89 expansionOpportunityCount = Character::expansionOpportunityCount(m_run.characters16(), m_end, m_run.direction(), isAfterExpansion); 90 if (isAfterExpansion && !m_run.allowsTrailingExpansion()) 91 expansionOpportunityCount--; 92 93 if (!expansionOpportunityCount) 94 m_expansionPerOpportunity = 0; 95 else 96 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; 97 } 98 99 collectComplexTextRuns(); 100 adjustGlyphsAndAdvances(); 101 102 if (!m_isLTROnly) { 103 m_runIndices.reserveInitialCapacity(m_complexTextRuns.size()); 104 105 m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size()); 106 unsigned glyphCountSoFar = 0; 107 for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) { 108 m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar); 109 glyphCountSoFar += m_complexTextRuns[i]->glyphCount(); 110 } 111 } 112 113 m_runWidthSoFar = m_leadingExpansion; 114 } 115 116 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs) 117 { 118 if (h >= m_totalWidth) 119 return m_run.ltr() ? m_end : 0; 120 121 h -= m_leadingExpansion; 122 if (h < 0) 123 return m_run.ltr() ? 0 : m_end; 124 125 CGFloat x = h; 126 127 size_t runCount = m_complexTextRuns.size(); 128 size_t offsetIntoAdjustedGlyphs = 0; 129 130 for (size_t r = 0; r < runCount; ++r) { 131 const ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; 132 for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) { 133 CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width; 134 if (x < adjustedAdvance) { 135 CFIndex hitGlyphStart = complexTextRun.indexAt(j); 136 CFIndex hitGlyphEnd; 137 if (m_run.ltr()) 138 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd())); 139 else 140 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd())); 141 142 // FIXME: Instead of dividing the glyph's advance equally between the characters, this 143 // could use the glyph's "ligature carets". However, there is no Core Text API to get the 144 // ligature carets. 145 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance); 146 int stringLength = complexTextRun.stringLength(); 147 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength); 148 int clusterStart; 149 if (cursorPositionIterator->isBoundary(hitIndex)) 150 clusterStart = hitIndex; 151 else { 152 clusterStart = cursorPositionIterator->preceding(hitIndex); 153 if (clusterStart == TextBreakDone) 154 clusterStart = 0; 155 } 156 157 if (!includePartialGlyphs) 158 return complexTextRun.stringLocation() + clusterStart; 159 160 int clusterEnd = cursorPositionIterator->following(hitIndex); 161 if (clusterEnd == TextBreakDone) 162 clusterEnd = stringLength; 163 164 CGFloat clusterWidth; 165 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns 166 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no 167 // reordering and no font fallback should occur within a CTLine. 168 if (clusterEnd - clusterStart > 1) { 169 clusterWidth = adjustedAdvance; 170 int firstGlyphBeforeCluster = j - 1; 171 while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) { 172 CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width; 173 clusterWidth += width; 174 x += width; 175 firstGlyphBeforeCluster--; 176 } 177 unsigned firstGlyphAfterCluster = j + 1; 178 while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) { 179 clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width; 180 firstGlyphAfterCluster++; 181 } 182 } else { 183 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart); 184 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1); 185 } 186 if (x <= clusterWidth / 2) 187 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd); 188 else 189 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart); 190 } 191 x -= adjustedAdvance; 192 } 193 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount(); 194 } 195 196 ASSERT_NOT_REACHED(); 197 return 0; 198 } 199 200 static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount) 201 { 202 ASSERT(iterator < end); 203 204 markCount = 0; 205 206 baseCharacter = *iterator++; 207 208 if (U16_IS_SURROGATE(baseCharacter)) { 209 if (!U16_IS_LEAD(baseCharacter)) 210 return false; 211 if (iterator == end) 212 return false; 213 UChar trail = *iterator++; 214 if (!U16_IS_TRAIL(trail)) 215 return false; 216 baseCharacter = U16_GET_SUPPLEMENTARY(baseCharacter, trail); 217 } 218 219 // Consume marks. 220 while (iterator < end) { 221 UChar32 nextCharacter; 222 int markLength = 0; 223 U16_NEXT(iterator, markLength, end - iterator, nextCharacter); 224 if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK)) 225 break; 226 markCount += markLength; 227 iterator += markLength; 228 } 229 230 return true; 231 } 232 233 void ComplexTextController::collectComplexTextRuns() 234 { 235 if (!m_end) 236 return; 237 238 // We break up glyph run generation for the string by FontData. 239 const UChar* cp; 240 241 if (m_run.is8Bit()) { 242 String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length()); 243 cp = stringFor8BitRun.characters16(); 244 m_stringsFor8BitRuns.append(stringFor8BitRun); 245 } else 246 cp = m_run.characters16(); 247 248 if (m_font.fontDescription().variant() == FontVariantSmallCaps) 249 m_smallCapsBuffer.resize(m_end); 250 251 unsigned indexOfFontTransition = 0; 252 const UChar* curr = cp; 253 const UChar* end = cp + m_end; 254 255 const SimpleFontData* fontData; 256 bool isMissingGlyph; 257 const SimpleFontData* nextFontData; 258 bool nextIsMissingGlyph; 259 260 unsigned markCount; 261 const UChar* sequenceStart = curr; 262 UChar32 baseCharacter; 263 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount)) 264 return; 265 266 UChar uppercaseCharacter = 0; 267 268 bool isSmallCaps; 269 bool nextIsSmallCaps = m_font.fontDescription().variant() == FontVariantSmallCaps && !(U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK) && (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter; 270 271 if (nextIsSmallCaps) { 272 m_smallCapsBuffer[sequenceStart - cp] = uppercaseCharacter; 273 for (unsigned i = 0; i < markCount; ++i) 274 m_smallCapsBuffer[sequenceStart - cp + i + 1] = sequenceStart[i + 1]; 275 } 276 277 nextIsMissingGlyph = false; 278 nextFontData = m_font.fontDataForCombiningCharacterSequence(sequenceStart, curr - sequenceStart, nextIsSmallCaps ? SmallCapsVariant : NormalVariant); 279 if (!nextFontData) 280 nextIsMissingGlyph = true; 281 282 while (curr < end) { 283 fontData = nextFontData; 284 isMissingGlyph = nextIsMissingGlyph; 285 isSmallCaps = nextIsSmallCaps; 286 int index = curr - cp; 287 288 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount)) 289 return; 290 291 if (m_font.fontDescription().variant()) { 292 nextIsSmallCaps = (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter; 293 if (nextIsSmallCaps) { 294 m_smallCapsBuffer[index] = uppercaseCharacter; 295 for (unsigned i = 0; i < markCount; ++i) 296 m_smallCapsBuffer[index + i + 1] = cp[index + i + 1]; 297 } 298 } 299 300 nextIsMissingGlyph = false; 301 if (baseCharacter == zeroWidthJoiner) 302 nextFontData = fontData; 303 else { 304 nextFontData = m_font.fontDataForCombiningCharacterSequence(cp + index, curr - cp - index, nextIsSmallCaps ? SmallCapsVariant : NormalVariant); 305 if (!nextFontData) 306 nextIsMissingGlyph = true; 307 } 308 309 if (nextFontData != fontData || nextIsMissingGlyph != isMissingGlyph) { 310 int itemStart = static_cast<int>(indexOfFontTransition); 311 int itemLength = index - indexOfFontTransition; 312 collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !isMissingGlyph ? fontData : 0); 313 indexOfFontTransition = index; 314 } 315 } 316 317 int itemLength = m_end - indexOfFontTransition; 318 if (itemLength) { 319 int itemStart = indexOfFontTransition; 320 collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !nextIsMissingGlyph ? nextFontData : 0); 321 } 322 323 if (!m_run.ltr()) 324 m_complexTextRuns.reverse(); 325 } 326 327 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const 328 { 329 return m_coreTextIndices[i]; 330 } 331 332 void ComplexTextController::ComplexTextRun::setIsNonMonotonic() 333 { 334 ASSERT(m_isMonotonic); 335 m_isMonotonic = false; 336 337 Vector<bool, 64> mappedIndices(m_stringLength); 338 for (size_t i = 0; i < m_glyphCount; ++i) { 339 ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength)); 340 mappedIndices[indexAt(i)] = true; 341 } 342 343 m_glyphEndOffsets.grow(m_glyphCount); 344 for (size_t i = 0; i < m_glyphCount; ++i) { 345 CFIndex nextMappedIndex = m_indexEnd; 346 for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) { 347 if (mappedIndices[j]) { 348 nextMappedIndex = j; 349 break; 350 } 351 } 352 m_glyphEndOffsets[i] = nextMappedIndex; 353 } 354 } 355 356 unsigned ComplexTextController::findNextRunIndex(unsigned runIndex) const 357 { 358 const unsigned runOffset = stringEnd(*m_complexTextRuns[runIndex]); 359 360 // Finds the run with the lowest stringBegin() offset that starts at or 361 // after |runOffset|. 362 // 363 // Note that this can't just find a run whose stringBegin() equals the 364 // stringEnd() of the previous run because CoreText on Mac OS X 10.6 does 365 // not return runs covering BiDi control chars, so this has to handle the 366 // resulting gaps. 367 unsigned result = 0; 368 unsigned lowestOffset = UINT_MAX; 369 for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) { 370 unsigned offset = stringBegin(*m_complexTextRuns[i]); 371 if (i != runIndex && offset >= runOffset && offset < lowestOffset) { 372 lowestOffset = offset; 373 result = i; 374 } 375 } 376 377 ASSERT(lowestOffset != UINT_MAX); 378 return result; 379 } 380 381 unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph) 382 { 383 leftmostGlyph = 0; 384 385 size_t runCount = m_complexTextRuns.size(); 386 if (m_currentRun >= runCount) 387 return runCount; 388 389 if (m_isLTROnly) { 390 for (unsigned i = 0; i < m_currentRun; ++i) 391 leftmostGlyph += m_complexTextRuns[i]->glyphCount(); 392 return m_currentRun; 393 } 394 395 if (m_runIndices.isEmpty()) { 396 unsigned firstRun = 0; 397 unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]); 398 for (unsigned i = 1; i < runCount; ++i) { 399 unsigned offset = stringBegin(*m_complexTextRuns[i]); 400 if (offset < firstRunOffset) { 401 firstRun = i; 402 firstRunOffset = offset; 403 } 404 } 405 m_runIndices.uncheckedAppend(firstRun); 406 } 407 408 while (m_runIndices.size() <= m_currentRun) { 409 m_runIndices.uncheckedAppend(findNextRunIndex(m_runIndices.last())); 410 } 411 412 unsigned currentRunIndex = m_runIndices[m_currentRun]; 413 leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex]; 414 return currentRunIndex; 415 } 416 417 unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph) 418 { 419 if (m_isLTROnly) { 420 leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount(); 421 return m_currentRun; 422 } 423 424 m_currentRun++; 425 leftmostGlyph = 0; 426 return indexOfCurrentRun(leftmostGlyph); 427 } 428 429 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const SimpleFontData*>* fallbackFonts) 430 { 431 if (static_cast<int>(offset) > m_end) 432 offset = m_end; 433 434 if (offset <= m_currentCharacter) { 435 m_runWidthSoFar = m_leadingExpansion; 436 m_numGlyphsSoFar = 0; 437 m_currentRun = 0; 438 m_glyphInCurrentRun = 0; 439 m_characterInCurrentGlyph = 0; 440 } 441 442 m_currentCharacter = offset; 443 444 size_t runCount = m_complexTextRuns.size(); 445 446 unsigned leftmostGlyph = 0; 447 unsigned currentRunIndex = indexOfCurrentRun(leftmostGlyph); 448 while (m_currentRun < runCount) { 449 const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex]; 450 bool ltr = complexTextRun.isLTR(); 451 size_t glyphCount = complexTextRun.glyphCount(); 452 unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun; 453 unsigned k = leftmostGlyph + g; 454 if (fallbackFonts && complexTextRun.fontData() != m_font.primaryFont()) 455 fallbackFonts->add(complexTextRun.fontData()); 456 457 while (m_glyphInCurrentRun < glyphCount) { 458 unsigned glyphStartOffset = complexTextRun.indexAt(g); 459 unsigned glyphEndOffset; 460 if (complexTextRun.isMonotonic()) { 461 if (ltr) 462 glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd())); 463 else 464 glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd())); 465 } else 466 glyphEndOffset = complexTextRun.endOffsetAt(g); 467 468 CGSize adjustedAdvance = m_adjustedAdvances[k]; 469 470 if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter) 471 return; 472 473 if (glyphBuffer && !m_characterInCurrentGlyph) 474 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), FloatSize(adjustedAdvance)); 475 476 unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph; 477 m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset; 478 // FIXME: Instead of dividing the glyph's advance equally between the characters, this 479 // could use the glyph's "ligature carets". However, there is no Core Text API to get the 480 // ligature carets. 481 if (glyphStartOffset == glyphEndOffset) { 482 // When there are multiple glyphs per character we need to advance by the full width of the glyph. 483 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph); 484 m_runWidthSoFar += adjustedAdvance.width; 485 } else if (iterationStyle == ByWholeGlyphs) { 486 if (!oldCharacterInCurrentGlyph) 487 m_runWidthSoFar += adjustedAdvance.width; 488 } else 489 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset); 490 491 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter) 492 return; 493 494 m_numGlyphsSoFar++; 495 m_glyphInCurrentRun++; 496 m_characterInCurrentGlyph = 0; 497 if (ltr) { 498 g++; 499 k++; 500 } else { 501 g--; 502 k--; 503 } 504 } 505 currentRunIndex = incrementCurrentRun(leftmostGlyph); 506 m_glyphInCurrentRun = 0; 507 } 508 if (!m_run.ltr() && m_numGlyphsSoFar == m_adjustedAdvances.size()) 509 m_runWidthSoFar += m_finalRoundingWidth; 510 } 511 512 void ComplexTextController::adjustGlyphsAndAdvances() 513 { 514 CGFloat widthSinceLastCommit = 0; 515 size_t runCount = m_complexTextRuns.size(); 516 bool hasExtraSpacing = (m_font.fontDescription().letterSpacing() || m_font.fontDescription().wordSpacing() || m_expansion) && !m_run.spacingDisabled(); 517 for (size_t r = 0; r < runCount; ++r) { 518 ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; 519 unsigned glyphCount = complexTextRun.glyphCount(); 520 const SimpleFontData* fontData = complexTextRun.fontData(); 521 522 if (!complexTextRun.isLTR()) 523 m_isLTROnly = false; 524 525 const CGGlyph* glyphs = complexTextRun.glyphs(); 526 const CGSize* advances = complexTextRun.advances(); 527 528 bool lastRun = r + 1 == runCount; 529 bool roundsAdvances = fontData->platformData().roundsGlyphAdvances(); 530 float spaceWidth = fontData->spaceWidth() - fontData->syntheticBoldOffset(); 531 CGFloat roundedSpaceWidth = roundCGFloat(spaceWidth); 532 const UChar* cp = complexTextRun.characters(); 533 CGPoint glyphOrigin = CGPointZero; 534 CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max(); 535 bool isMonotonic = true; 536 537 for (unsigned i = 0; i < glyphCount; i++) { 538 CFIndex characterIndex = complexTextRun.indexAt(i); 539 if (m_run.ltr()) { 540 if (characterIndex < lastCharacterIndex) 541 isMonotonic = false; 542 } else { 543 if (characterIndex > lastCharacterIndex) 544 isMonotonic = false; 545 } 546 UChar ch = *(cp + characterIndex); 547 bool lastGlyph = lastRun && i + 1 == glyphCount; 548 UChar nextCh; 549 if (lastGlyph) 550 nextCh = ' '; 551 else if (i + 1 < glyphCount) 552 nextCh = *(cp + complexTextRun.indexAt(i + 1)); 553 else 554 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0)); 555 556 bool treatAsSpace = Character::treatAsSpace(ch); 557 CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i]; 558 CGSize advance = treatAsSpace ? CGSizeMake(spaceWidth, advances[i].height) : advances[i]; 559 560 if (ch == '\t' && m_run.allowTabs()) { 561 advance.width = m_font.tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_totalWidth + widthSinceLastCommit); 562 } else if (Character::treatAsZeroWidthSpace(ch) && !treatAsSpace) { 563 advance.width = 0; 564 glyph = fontData->spaceGlyph(); 565 } 566 567 float roundedAdvanceWidth = roundf(advance.width); 568 if (roundsAdvances) 569 advance.width = roundedAdvanceWidth; 570 571 advance.width += fontData->syntheticBoldOffset(); 572 573 574 // We special case spaces in two ways when applying word rounding. 575 // First, we round spaces to an adjusted width in all fonts. 576 // Second, in fixed-pitch fonts we ensure that all glyphs that 577 // match the width of the space glyph have the same width as the space glyph. 578 if (m_run.applyWordRounding() && roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph())) 579 advance.width = fontData->adjustedSpaceWidth(); 580 581 if (hasExtraSpacing) { 582 // If we're a glyph with an advance, go ahead and add in letter-spacing. 583 // That way we weed out zero width lurkers. This behavior matches the fast text code path. 584 if (advance.width && m_font.fontDescription().letterSpacing()) 585 advance.width += m_font.fontDescription().letterSpacing(); 586 587 // Handle justification and word-spacing. 588 if (treatAsSpace || Character::isCJKIdeographOrSymbol(ch)) { 589 // Distribute the run's total expansion evenly over all expansion opportunities in the run. 590 if (m_expansion) { 591 float previousExpansion = m_expansion; 592 if (!treatAsSpace && !m_afterExpansion) { 593 // Take the expansion opportunity before this ideograph. 594 m_expansion -= m_expansionPerOpportunity; 595 float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion); 596 m_totalWidth += expansionAtThisOpportunity; 597 if (m_adjustedAdvances.isEmpty()) 598 m_leadingExpansion = expansionAtThisOpportunity; 599 else 600 m_adjustedAdvances.last().width += expansionAtThisOpportunity; 601 previousExpansion = m_expansion; 602 } 603 if (!lastGlyph || m_run.allowsTrailingExpansion()) { 604 m_expansion -= m_expansionPerOpportunity; 605 advance.width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion); 606 m_afterExpansion = true; 607 } 608 } else 609 m_afterExpansion = false; 610 611 // Account for word-spacing. 612 if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || r > 0) && m_font.fontDescription().wordSpacing()) 613 advance.width += m_font.fontDescription().wordSpacing(); 614 } else 615 m_afterExpansion = false; 616 } 617 618 // Apply rounding hacks if needed. 619 // We adjust the width of the last character of a "word" to ensure an integer width. 620 // Force characters that are used to determine word boundaries for the rounding hack 621 // to be integer width, so the following words will start on an integer boundary. 622 if (m_run.applyWordRounding() && Character::isRoundingHackCharacter(ch)) 623 advance.width = ceilCGFloat(advance.width); 624 625 // Check to see if the next character is a "rounding hack character", if so, adjust the 626 // width so that the total run width will be on an integer boundary. 627 if ((m_run.applyWordRounding() && !lastGlyph && Character::isRoundingHackCharacter(nextCh)) || (m_run.applyRunRounding() && lastGlyph)) { 628 CGFloat totalWidth = widthSinceLastCommit + advance.width; 629 widthSinceLastCommit = ceilCGFloat(totalWidth); 630 CGFloat extraWidth = widthSinceLastCommit - totalWidth; 631 if (m_run.ltr()) 632 advance.width += extraWidth; 633 else { 634 if (m_lastRoundingGlyph) 635 m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth; 636 else 637 m_finalRoundingWidth = extraWidth; 638 m_lastRoundingGlyph = m_adjustedAdvances.size() + 1; 639 } 640 m_totalWidth += widthSinceLastCommit; 641 widthSinceLastCommit = 0; 642 } else 643 widthSinceLastCommit += advance.width; 644 645 // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space. 646 if (m_forTextEmphasis && (!Character::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK))) 647 glyph = 0; 648 649 advance.height *= -1; 650 m_adjustedAdvances.append(advance); 651 m_adjustedGlyphs.append(glyph); 652 653 FloatRect glyphBounds = fontData->boundsForGlyph(glyph); 654 glyphBounds.move(glyphOrigin.x, glyphOrigin.y); 655 m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x()); 656 m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX()); 657 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y()); 658 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY()); 659 glyphOrigin.x += advance.width; 660 glyphOrigin.y += advance.height; 661 662 lastCharacterIndex = characterIndex; 663 } 664 if (!isMonotonic) 665 complexTextRun.setIsNonMonotonic(); 666 } 667 m_totalWidth += widthSinceLastCommit; 668 } 669 670 } // namespace WebCore 671