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 "RenderBlock.h" 31#include "RenderText.h" 32#include "TextBreakIterator.h" 33#include "TextRun.h" 34#if !PLATFORM(IOS) 35#include <ApplicationServices/ApplicationServices.h> 36#else 37#include <CoreText/CoreText.h> 38#endif 39#include <wtf/StdLibExtras.h> 40#include <wtf/unicode/CharacterNames.h> 41 42namespace WebCore { 43 44class TextLayout { 45public: 46 static bool isNeeded(RenderText* text, const Font& font) 47 { 48 TextRun run = RenderBlock::constructTextRun(text, font, text, text->style()); 49 return font.codePath(run) == Font::Complex; 50 } 51 52 TextLayout(RenderText* text, const Font& font, float xPos) 53 : m_font(font) 54 , m_run(constructTextRun(text, font, xPos)) 55 , m_controller(adoptPtr(new ComplexTextController(&m_font, m_run, true))) 56 { 57 } 58 59 float width(unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts) 60 { 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; 68 } 69 70private: 71 static TextRun constructTextRun(RenderText* text, const Font& font, float xPos) 72 { 73 TextRun run = RenderBlock::constructTextRun(text, font, text, text->style()); 74 run.setCharactersLength(text->textLength()); 75 ASSERT(run.charactersLength() >= run.length()); 76 77 run.setXPos(xPos); 78 return run; 79 } 80 81 // ComplexTextController has only references to its Font and TextRun so they must be kept alive here. 82 Font m_font; 83 TextRun m_run; 84 OwnPtr<ComplexTextController> m_controller; 85}; 86 87PassOwnPtr<TextLayout> Font::createLayout(RenderText* text, float xPos, bool collapseWhiteSpace) const 88{ 89 if (!collapseWhiteSpace || !TextLayout::isNeeded(text, *this)) 90 return nullptr; 91 return adoptPtr(new TextLayout(text, *this, xPos)); 92} 93 94void Font::deleteLayout(TextLayout* layout) 95{ 96 delete layout; 97} 98 99float Font::width(TextLayout& layout, unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts) 100{ 101 return layout.width(from, len, fallbackFonts); 102} 103 104static inline CGFloat roundCGFloat(CGFloat f) 105{ 106 if (sizeof(CGFloat) == sizeof(float)) 107 return roundf(static_cast<float>(f)); 108 return static_cast<CGFloat>(round(f)); 109} 110 111static inline CGFloat ceilCGFloat(CGFloat f) 112{ 113 if (sizeof(CGFloat) == sizeof(float)) 114 return ceilf(static_cast<float>(f)); 115 return static_cast<CGFloat>(ceil(f)); 116} 117 118ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis) 119 : m_font(*font) 120 , m_run(run) 121 , m_isLTROnly(true) 122 , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection) 123 , m_forTextEmphasis(forTextEmphasis) 124 , m_currentCharacter(0) 125 , m_end(run.length()) 126 , m_totalWidth(0) 127 , m_runWidthSoFar(0) 128 , m_numGlyphsSoFar(0) 129 , m_currentRun(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) 142{ 143 if (!m_expansion) 144 m_expansionPerOpportunity = 0; 145 else { 146 bool isAfterExpansion = m_afterExpansion; 147 unsigned expansionOpportunityCount; 148 if (m_run.is8Bit()) 149 expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters8(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion); 150 else 151 expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters16(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion); 152 if (isAfterExpansion && !m_run.allowsTrailingExpansion()) 153 expansionOpportunityCount--; 154 155 if (!expansionOpportunityCount) 156 m_expansionPerOpportunity = 0; 157 else 158 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; 159 } 160 161 collectComplexTextRuns(); 162 adjustGlyphsAndAdvances(); 163 164 if (!m_isLTROnly) { 165 m_runIndices.reserveInitialCapacity(m_complexTextRuns.size()); 166 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(); 172 } 173 } 174 175 m_runWidthSoFar = m_leadingExpansion; 176} 177 178int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs) 179{ 180 if (h >= m_totalWidth) 181 return m_run.ltr() ? m_end : 0; 182 183 h -= m_leadingExpansion; 184 if (h < 0) 185 return m_run.ltr() ? 0 : m_end; 186 187 CGFloat x = h; 188 189 size_t runCount = m_complexTextRuns.size(); 190 size_t offsetIntoAdjustedGlyphs = 0; 191 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; 197 if (!index) 198 adjustedAdvance += complexTextRun.initialAdvance().width; 199 if (x < adjustedAdvance) { 200 CFIndex hitGlyphStart = complexTextRun.indexAt(j); 201 CFIndex hitGlyphEnd; 202 if (m_run.ltr()) 203 hitGlyphEnd = std::max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd())); 204 else 205 hitGlyphEnd = std::max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd())); 206 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 209 // ligature carets. 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)); 213 int clusterStart; 214 if (isTextBreak(cursorPositionIterator, hitIndex)) 215 clusterStart = hitIndex; 216 else { 217 clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex); 218 if (clusterStart == TextBreakDone) 219 clusterStart = 0; 220 } 221 222 if (!includePartialGlyphs) 223 return complexTextRun.stringLocation() + clusterStart; 224 225 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex); 226 if (clusterEnd == TextBreakDone) 227 clusterEnd = stringLength; 228 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; 239 x += width; 240 firstGlyphBeforeCluster--; 241 } 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++; 246 } 247 } else { 248 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart); 249 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1); 250 } 251 if (x <= clusterWidth / 2) 252 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd); 253 else 254 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart); 255 } 256 x -= adjustedAdvance; 257 } 258 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount(); 259 } 260 261 ASSERT_NOT_REACHED(); 262 return 0; 263} 264 265static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount) 266{ 267 ASSERT(iterator < end); 268 269 markCount = 0; 270 271 unsigned i = 0; 272 unsigned remainingCharacters = end - iterator; 273 U16_NEXT(iterator, i, remainingCharacters, baseCharacter); 274 iterator = iterator + i; 275 276 if (U_IS_SURROGATE(baseCharacter)) 277 return false; 278 279 // Consume marks. 280 while (iterator < end) { 281 UChar32 nextCharacter; 282 int markLength = 0; 283 U16_NEXT(iterator, markLength, end - iterator, nextCharacter); 284 if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK)) 285 break; 286 markCount += markLength; 287 iterator += markLength; 288 } 289 290 return true; 291} 292 293void ComplexTextController::collectComplexTextRuns() 294{ 295 if (!m_end) 296 return; 297 298 // We break up glyph run generation for the string by FontData. 299 const UChar* cp; 300 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); 305 } else 306 cp = m_run.characters16(); 307 308 if (m_font.isSmallCaps()) 309 m_smallCapsBuffer.resize(m_end); 310 311 unsigned indexOfFontTransition = 0; 312 const UChar* curr = cp; 313 const UChar* end = cp + m_end; 314 315 const SimpleFontData* fontData; 316 bool isMissingGlyph; 317 const SimpleFontData* nextFontData; 318 bool nextIsMissingGlyph; 319 320 unsigned markCount; 321 const UChar* sequenceStart = curr; 322 UChar32 baseCharacter; 323 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount)) 324 return; 325 326 UChar uppercaseCharacter = 0; 327 328 bool isSmallCaps; 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); 331 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]; 336 } 337 338 nextIsMissingGlyph = false; 339 nextFontData = m_font.fontDataForCombiningCharacterSequence(sequenceStart, curr - sequenceStart, nextIsSmallCaps ? SmallCapsVariant : NormalVariant); 340 if (!nextFontData) 341 nextIsMissingGlyph = true; 342 343 while (curr < end) { 344 fontData = nextFontData; 345 isMissingGlyph = nextIsMissingGlyph; 346 isSmallCaps = nextIsSmallCaps; 347 int index = curr - cp; 348 349 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount)) 350 return; 351 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]; 360 } 361 } 362 363 nextIsMissingGlyph = false; 364 if (baseCharacter == zeroWidthJoiner) 365 nextFontData = fontData; 366 else { 367 nextFontData = m_font.fontDataForCombiningCharacterSequence(cp + index, curr - cp - index, nextIsSmallCaps ? SmallCapsVariant : NormalVariant); 368 if (!nextFontData) 369 nextIsMissingGlyph = true; 370 } 371 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; 377 } 378 } 379 380 int itemLength = m_end - indexOfFontTransition; 381 if (itemLength) { 382 int itemStart = indexOfFontTransition; 383 collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !nextIsMissingGlyph ? nextFontData : 0); 384 } 385 386 if (!m_run.ltr()) 387 m_complexTextRuns.reverse(); 388} 389 390CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const 391{ 392 ASSERT(i < m_glyphCount); 393 394 return m_coreTextIndices[i]; 395} 396 397void ComplexTextController::ComplexTextRun::setIsNonMonotonic() 398{ 399 ASSERT(m_isMonotonic); 400 m_isMonotonic = false; 401 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; 406 } 407 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]) { 413 nextMappedIndex = j; 414 break; 415 } 416 } 417 m_glyphEndOffsets[i] = nextMappedIndex; 418 } 419} 420 421unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph) 422{ 423 leftmostGlyph = 0; 424 425 size_t runCount = m_complexTextRuns.size(); 426 if (m_currentRun >= runCount) 427 return runCount; 428 429 if (m_isLTROnly) { 430 for (unsigned i = 0; i < m_currentRun; ++i) 431 leftmostGlyph += m_complexTextRuns[i]->glyphCount(); 432 return m_currentRun; 433 } 434 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) { 441 firstRun = i; 442 firstRunOffset = offset; 443 } 444 } 445 m_runIndices.uncheckedAppend(firstRun); 446 } 447 448 while (m_runIndices.size() <= m_currentRun) { 449 unsigned offset = stringEnd(*m_complexTextRuns[m_runIndices.last()]); 450 451 for (unsigned i = 0; i < runCount; ++i) { 452 if (offset == stringBegin(*m_complexTextRuns[i])) { 453 m_runIndices.uncheckedAppend(i); 454 break; 455 } 456 } 457 } 458 459 unsigned currentRunIndex = m_runIndices[m_currentRun]; 460 leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex]; 461 return currentRunIndex; 462} 463 464unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph) 465{ 466 if (m_isLTROnly) { 467 leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount(); 468 return m_currentRun; 469 } 470 471 m_currentRun++; 472 leftmostGlyph = 0; 473 return indexOfCurrentRun(leftmostGlyph); 474} 475 476void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const SimpleFontData*>* fallbackFonts) 477{ 478 if (static_cast<int>(offset) > m_end) 479 offset = m_end; 480 481 if (offset <= m_currentCharacter) { 482 m_runWidthSoFar = m_leadingExpansion; 483 m_numGlyphsSoFar = 0; 484 m_currentRun = 0; 485 m_glyphInCurrentRun = 0; 486 m_characterInCurrentGlyph = 0; 487 } 488 489 m_currentCharacter = offset; 490 491 size_t runCount = m_complexTextRuns.size(); 492 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()); 503 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()); 509 510 while (m_glyphInCurrentRun < glyphCount) { 511 unsigned glyphStartOffset = complexTextRun.indexAt(g); 512 unsigned glyphEndOffset; 513 if (complexTextRun.isMonotonic()) { 514 if (ltr) 515 glyphEndOffset = std::max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd())); 516 else 517 glyphEndOffset = std::max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd())); 518 } else 519 glyphEndOffset = complexTextRun.endOffsetAt(g); 520 521 CGSize adjustedAdvance = m_adjustedAdvances[k]; 522 523 if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter) 524 return; 525 526 if (glyphBuffer && !m_characterInCurrentGlyph) 527 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance, complexTextRun.indexAt(m_glyphInCurrentRun)); 528 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 533 // ligature carets. 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; 541 } else 542 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset); 543 544 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter) 545 return; 546 547 m_numGlyphsSoFar++; 548 m_glyphInCurrentRun++; 549 m_characterInCurrentGlyph = 0; 550 if (ltr) { 551 g++; 552 k++; 553 } else { 554 g--; 555 k--; 556 } 557 } 558 currentRunIndex = incrementCurrentRun(leftmostGlyph); 559 m_glyphInCurrentRun = 0; 560 } 561 if (!m_run.ltr() && m_numGlyphsSoFar == m_adjustedAdvances.size()) 562 m_runWidthSoFar += m_finalRoundingWidth; 563} 564 565void ComplexTextController::adjustGlyphsAndAdvances() 566{ 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(); 574#if PLATFORM(IOS) 575 bool isEmoji = fontData->platformData().m_isEmoji; 576#endif 577 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; 585 } 586 widthSinceLastCommit += complexTextRun.initialAdvance().width; 587 588 if (!complexTextRun.isLTR()) 589 m_isLTROnly = false; 590 591 const CGGlyph* glyphs = complexTextRun.glyphs(); 592 const CGSize* advances = complexTextRun.advances(); 593 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; 602 603 for (unsigned i = 0; i < glyphCount; i++) { 604 CFIndex characterIndex = complexTextRun.indexAt(i); 605 if (m_run.ltr()) { 606 if (characterIndex < lastCharacterIndex) 607 isMonotonic = false; 608 } else { 609 if (characterIndex > lastCharacterIndex) 610 isMonotonic = false; 611 } 612 UChar ch = *(cp + characterIndex); 613 bool lastGlyph = lastRun && i + 1 == glyphCount; 614 UChar nextCh; 615 if (lastGlyph) 616 nextCh = ' '; 617 else if (i + 1 < glyphCount) 618 nextCh = *(cp + complexTextRun.indexAt(i + 1)); 619 else 620 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0)); 621 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]; 625#if PLATFORM(IOS) 626 if (isEmoji && advance.width) 627 advance.width = fontData->widthForGlyph(glyph); 628#endif 629 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) { 633 advance.width = 0; 634 glyph = fontData->spaceGlyph(); 635 } 636 637 float roundedAdvanceWidth = roundf(advance.width); 638 if (roundsAdvances) 639 advance.width = roundedAdvanceWidth; 640 641 advance.width += fontData->syntheticBoldOffset(); 642 643 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(); 650 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(); 656 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. 660 if (m_expansion) { 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; 669 else 670 m_adjustedAdvances.last().width += expansionAtThisOpportunity; 671 previousExpansion = m_expansion; 672 } 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; 677 } 678 } else 679 m_afterExpansion = false; 680 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(); 684 } else 685 m_afterExpansion = false; 686 } 687 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); 694 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; 701 if (m_run.ltr()) 702 advance.width += extraWidth; 703 else { 704 if (m_lastRoundingGlyph) 705 m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth; 706 else 707 m_finalRoundingWidth = extraWidth; 708 m_lastRoundingGlyph = m_adjustedAdvances.size() + 1; 709 } 710 m_totalWidth += widthSinceLastCommit; 711 widthSinceLastCommit = 0; 712 } else 713 widthSinceLastCommit += advance.width; 714 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))) 717 glyph = 0; 718 719 advance.height *= -1; 720 m_adjustedAdvances.append(advance); 721 m_adjustedGlyphs.append(glyph); 722 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; 731 732 lastCharacterIndex = characterIndex; 733 } 734 if (!isMonotonic) 735 complexTextRun.setIsNonMonotonic(); 736 } 737 m_totalWidth += widthSinceLastCommit; 738} 739 740} // namespace WebCore 741