1 /* 2 * Copyright (c) 2012 Google Inc. All rights reserved. 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "config.h" 33 #include "platform/fonts/harfbuzz/HarfBuzzShaper.h" 34 35 #include "hb.h" 36 #include "platform/LayoutUnit.h" 37 #include "platform/RuntimeEnabledFeatures.h" 38 #include "platform/fonts/Character.h" 39 #include "platform/fonts/Font.h" 40 #include "platform/fonts/GlyphBuffer.h" 41 #include "platform/fonts/harfbuzz/HarfBuzzFace.h" 42 #include "platform/text/SurrogatePairAwareTextIterator.h" 43 #include "platform/text/TextBreakIterator.h" 44 #include "wtf/Compiler.h" 45 #include "wtf/MathExtras.h" 46 #include "wtf/unicode/Unicode.h" 47 #include <unicode/normlzr.h> 48 #include <unicode/uchar.h> 49 #include <unicode/uscript.h> 50 51 #include <list> 52 #include <map> 53 #include <string> 54 55 namespace WebCore { 56 57 template<typename T> 58 class HarfBuzzScopedPtr { 59 public: 60 typedef void (*DestroyFunction)(T*); 61 62 HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) 63 : m_ptr(ptr) 64 , m_destroy(destroy) 65 { 66 ASSERT(m_destroy); 67 } 68 ~HarfBuzzScopedPtr() 69 { 70 if (m_ptr) 71 (*m_destroy)(m_ptr); 72 } 73 74 T* get() { return m_ptr; } 75 void set(T* ptr) { m_ptr = ptr; } 76 private: 77 T* m_ptr; 78 DestroyFunction m_destroy; 79 }; 80 81 82 static const unsigned cHarfBuzzCacheMaxSize = 256; 83 84 struct CachedShapingResultsLRUNode; 85 struct CachedShapingResults; 86 typedef std::map<std::wstring, CachedShapingResults*> CachedShapingResultsMap; 87 typedef std::list<CachedShapingResultsLRUNode*> CachedShapingResultsLRU; 88 89 struct CachedShapingResults { 90 CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_direction_t runDir, const String& newLocale); 91 ~CachedShapingResults(); 92 93 hb_buffer_t* buffer; 94 Font font; 95 hb_direction_t dir; 96 String locale; 97 CachedShapingResultsLRU::iterator lru; 98 }; 99 100 struct CachedShapingResultsLRUNode { 101 CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry); 102 ~CachedShapingResultsLRUNode(); 103 104 CachedShapingResultsMap::iterator entry; 105 }; 106 107 CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* fontData, hb_direction_t dirData, const String& newLocale) 108 : buffer(harfBuzzBuffer) 109 , font(*fontData) 110 , dir(dirData) 111 , locale(newLocale) 112 { 113 } 114 115 CachedShapingResults::~CachedShapingResults() 116 { 117 hb_buffer_destroy(buffer); 118 } 119 120 CachedShapingResultsLRUNode::CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry) 121 : entry(cacheEntry) 122 { 123 } 124 125 CachedShapingResultsLRUNode::~CachedShapingResultsLRUNode() 126 { 127 } 128 129 class HarfBuzzRunCache { 130 public: 131 HarfBuzzRunCache(); 132 ~HarfBuzzRunCache(); 133 134 CachedShapingResults* find(const std::wstring& key) const; 135 void remove(CachedShapingResults* node); 136 void moveToBack(CachedShapingResults* node); 137 bool insert(const std::wstring& key, CachedShapingResults* run); 138 139 private: 140 CachedShapingResultsMap m_harfBuzzRunMap; 141 CachedShapingResultsLRU m_harfBuzzRunLRU; 142 }; 143 144 145 HarfBuzzRunCache::HarfBuzzRunCache() 146 { 147 } 148 149 HarfBuzzRunCache::~HarfBuzzRunCache() 150 { 151 for (CachedShapingResultsMap::iterator it = m_harfBuzzRunMap.begin(); it != m_harfBuzzRunMap.end(); ++it) 152 delete it->second; 153 for (CachedShapingResultsLRU::iterator it = m_harfBuzzRunLRU.begin(); it != m_harfBuzzRunLRU.end(); ++it) 154 delete *it; 155 } 156 157 bool HarfBuzzRunCache::insert(const std::wstring& key, CachedShapingResults* data) 158 { 159 std::pair<CachedShapingResultsMap::iterator, bool> results = 160 m_harfBuzzRunMap.insert(CachedShapingResultsMap::value_type(key, data)); 161 162 if (!results.second) 163 return false; 164 165 CachedShapingResultsLRUNode* node = new CachedShapingResultsLRUNode(results.first); 166 167 m_harfBuzzRunLRU.push_back(node); 168 data->lru = --m_harfBuzzRunLRU.end(); 169 170 if (m_harfBuzzRunMap.size() > cHarfBuzzCacheMaxSize) { 171 CachedShapingResultsLRUNode* lru = m_harfBuzzRunLRU.front(); 172 CachedShapingResults* foo = lru->entry->second; 173 m_harfBuzzRunMap.erase(lru->entry); 174 m_harfBuzzRunLRU.pop_front(); 175 delete foo; 176 delete lru; 177 } 178 179 return true; 180 } 181 182 inline CachedShapingResults* HarfBuzzRunCache::find(const std::wstring& key) const 183 { 184 CachedShapingResultsMap::const_iterator it = m_harfBuzzRunMap.find(key); 185 186 return it != m_harfBuzzRunMap.end() ? it->second : 0; 187 } 188 189 inline void HarfBuzzRunCache::remove(CachedShapingResults* node) 190 { 191 CachedShapingResultsLRUNode* lruNode = *node->lru; 192 193 m_harfBuzzRunLRU.erase(node->lru); 194 m_harfBuzzRunMap.erase(lruNode->entry); 195 delete lruNode; 196 delete node; 197 } 198 199 inline void HarfBuzzRunCache::moveToBack(CachedShapingResults* node) 200 { 201 CachedShapingResultsLRUNode* lruNode = *node->lru; 202 m_harfBuzzRunLRU.erase(node->lru); 203 m_harfBuzzRunLRU.push_back(lruNode); 204 node->lru = --m_harfBuzzRunLRU.end(); 205 } 206 207 HarfBuzzRunCache& harfBuzzRunCache() 208 { 209 DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ()); 210 return globalHarfBuzzRunCache; 211 } 212 213 static inline float harfBuzzPositionToFloat(hb_position_t value) 214 { 215 return static_cast<float>(value) / (1 << 16); 216 } 217 218 static inline unsigned countGraphemesInCluster(const UChar* normalizedBuffer, unsigned normalizedBufferLength, uint16_t startIndex, uint16_t endIndex) 219 { 220 if (startIndex > endIndex) { 221 uint16_t tempIndex = startIndex; 222 startIndex = endIndex; 223 endIndex = tempIndex; 224 } 225 uint16_t length = endIndex - startIndex; 226 ASSERT(static_cast<unsigned>(startIndex + length) <= normalizedBufferLength); 227 TextBreakIterator* cursorPosIterator = cursorMovementIterator(&normalizedBuffer[startIndex], length); 228 229 int cursorPos = cursorPosIterator->current(); 230 int numGraphemes = -1; 231 while (0 <= cursorPos) { 232 cursorPos = cursorPosIterator->next(); 233 numGraphemes++; 234 } 235 return numGraphemes < 0 ? 0 : numGraphemes; 236 } 237 238 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_t script) 239 : m_fontData(fontData) 240 , m_startIndex(startIndex) 241 , m_numCharacters(numCharacters) 242 , m_numGlyphs(0) 243 , m_direction(direction) 244 , m_script(script) 245 , m_width(0) 246 { 247 } 248 249 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const HarfBuzzRun& rhs) 250 : m_fontData(rhs.m_fontData) 251 , m_startIndex(rhs.m_startIndex) 252 , m_numCharacters(rhs.m_numCharacters) 253 , m_numGlyphs(rhs.m_numGlyphs) 254 , m_direction(rhs.m_direction) 255 , m_script(rhs.m_script) 256 , m_glyphs(rhs.m_glyphs) 257 , m_advances(rhs.m_advances) 258 , m_glyphToCharacterIndexes(rhs.m_glyphToCharacterIndexes) 259 , m_offsets(rhs.m_offsets) 260 , m_width(rhs.m_width) 261 { 262 } 263 264 HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun() 265 { 266 } 267 268 inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzBuffer) 269 { 270 m_numGlyphs = hb_buffer_get_length(harfBuzzBuffer); 271 m_glyphs.resize(m_numGlyphs); 272 m_advances.resize(m_numGlyphs); 273 m_glyphToCharacterIndexes.resize(m_numGlyphs); 274 m_offsets.resize(m_numGlyphs); 275 } 276 277 inline void HarfBuzzShaper::HarfBuzzRun::copyShapeResultAndGlyphPositions(const HarfBuzzRun& run) 278 { 279 m_numGlyphs = run.m_numGlyphs; 280 m_glyphs = run.m_glyphs; 281 m_advances = run.m_advances; 282 m_glyphToCharacterIndexes = run.m_glyphToCharacterIndexes; 283 m_offsets = run.m_offsets; 284 m_width = run.m_width; 285 } 286 287 inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY) 288 { 289 m_glyphs[index] = glyphId; 290 m_advances[index] = advance; 291 m_offsets[index] = FloatPoint(offsetX, offsetY); 292 } 293 294 int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX) 295 { 296 ASSERT(targetX <= m_width); 297 float currentX = 0; 298 float currentAdvance = m_advances[0]; 299 unsigned glyphIndex = 0; 300 301 // Sum up advances that belong to a character. 302 while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1]) 303 currentAdvance += m_advances[++glyphIndex]; 304 currentAdvance = currentAdvance / 2.0; 305 if (targetX <= currentAdvance) 306 return rtl() ? m_numCharacters : 0; 307 308 ++glyphIndex; 309 while (glyphIndex < m_numGlyphs) { 310 unsigned prevCharacterIndex = m_glyphToCharacterIndexes[glyphIndex - 1]; 311 float prevAdvance = currentAdvance; 312 currentAdvance = m_advances[glyphIndex]; 313 while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1]) 314 currentAdvance += m_advances[++glyphIndex]; 315 currentAdvance = currentAdvance / 2.0; 316 float nextX = currentX + prevAdvance + currentAdvance; 317 if (currentX <= targetX && targetX <= nextX) 318 return rtl() ? prevCharacterIndex : m_glyphToCharacterIndexes[glyphIndex]; 319 currentX = nextX; 320 ++glyphIndex; 321 } 322 323 return rtl() ? 0 : m_numCharacters; 324 } 325 326 float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset) 327 { 328 ASSERT(offset < m_numCharacters); 329 unsigned glyphIndex = 0; 330 float position = 0; 331 if (rtl()) { 332 while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] > offset) { 333 position += m_advances[glyphIndex]; 334 ++glyphIndex; 335 } 336 // For RTL, we need to return the right side boundary of the character. 337 // Add advance of glyphs which are part of the character. 338 while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1]) { 339 position += m_advances[glyphIndex]; 340 ++glyphIndex; 341 } 342 position += m_advances[glyphIndex]; 343 } else { 344 while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] < offset) { 345 position += m_advances[glyphIndex]; 346 ++glyphIndex; 347 } 348 } 349 return position; 350 } 351 352 static void normalizeCharacters(const TextRun& run, unsigned length, UChar* destination, unsigned* destinationLength) 353 { 354 unsigned position = 0; 355 bool error = false; 356 const UChar* source; 357 String stringFor8BitRun; 358 if (run.is8Bit()) { 359 stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length()); 360 source = stringFor8BitRun.characters16(); 361 } else 362 source = run.characters16(); 363 364 *destinationLength = 0; 365 while (position < length) { 366 UChar32 character; 367 U16_NEXT(source, position, length, character); 368 // Don't normalize tabs as they are not treated as spaces for word-end. 369 if (Character::treatAsSpace(character) && character != '\t') 370 character = ' '; 371 else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) 372 character = zeroWidthSpace; 373 U16_APPEND(destination, *destinationLength, length, character, error); 374 ASSERT_UNUSED(error, !error); 375 } 376 } 377 378 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmphasisOrNot forTextEmphasis) 379 : m_font(font) 380 , m_normalizedBufferLength(0) 381 , m_run(run) 382 , m_wordSpacingAdjustment(font->fontDescription().wordSpacing()) 383 , m_padding(0) 384 , m_padPerWordBreak(0) 385 , m_padError(0) 386 , m_letterSpacing(font->fontDescription().letterSpacing()) 387 , m_fromIndex(0) 388 , m_toIndex(m_run.length()) 389 , m_forTextEmphasis(forTextEmphasis) 390 , m_glyphBoundingBox(std::numeric_limits<float>::max(), std::numeric_limits<float>::min(), std::numeric_limits<float>::min(), std::numeric_limits<float>::max()) 391 { 392 m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]); 393 normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), &m_normalizedBufferLength); 394 setPadding(m_run.expansion()); 395 setFontFeatures(); 396 } 397 398 bool HarfBuzzShaper::isWordEnd(unsigned index) 399 { 400 // This could refer a high-surrogate, but should work. 401 return index && isCodepointSpace(m_normalizedBuffer[index]); 402 } 403 404 int HarfBuzzShaper::determineWordBreakSpacing() 405 { 406 int wordBreakSpacing = m_wordSpacingAdjustment; 407 408 if (m_padding > 0) { 409 int toPad = roundf(m_padPerWordBreak + m_padError); 410 m_padError += m_padPerWordBreak - toPad; 411 412 if (m_padding < toPad) 413 toPad = m_padding; 414 m_padding -= toPad; 415 wordBreakSpacing += toPad; 416 } 417 return wordBreakSpacing; 418 } 419 420 // setPadding sets a number of pixels to be distributed across the TextRun. 421 // WebKit uses this to justify text. 422 void HarfBuzzShaper::setPadding(int padding) 423 { 424 m_padding = padding; 425 m_padError = 0; 426 if (!m_padding) 427 return; 428 429 // If we have padding to distribute, then we try to give an equal 430 // amount to each space. The last space gets the smaller amount, if 431 // any. 432 unsigned numWordEnds = 0; 433 434 for (unsigned i = 0; i < m_normalizedBufferLength; i++) { 435 if (isWordEnd(i)) 436 numWordEnds++; 437 } 438 439 if (numWordEnds) 440 m_padPerWordBreak = m_padding / numWordEnds; 441 else 442 m_padPerWordBreak = 0; 443 } 444 445 446 void HarfBuzzShaper::setDrawRange(int from, int to) 447 { 448 ASSERT_WITH_SECURITY_IMPLICATION(from >= 0); 449 ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length()); 450 m_fromIndex = from; 451 m_toIndex = to; 452 } 453 454 void HarfBuzzShaper::setFontFeatures() 455 { 456 const FontDescription& description = m_font->fontDescription(); 457 if (description.orientation() == Vertical) { 458 static hb_feature_t vert = { HarfBuzzFace::vertTag, 1, 0, static_cast<unsigned>(-1) }; 459 static hb_feature_t vrt2 = { HarfBuzzFace::vrt2Tag, 1, 0, static_cast<unsigned>(-1) }; 460 m_features.append(vert); 461 m_features.append(vrt2); 462 } 463 464 static hb_feature_t noKern = { HB_TAG('k', 'e', 'r', 'n'), 0, 0, static_cast<unsigned>(-1) }; 465 static hb_feature_t noVkrn = { HB_TAG('v', 'k', 'r', 'n'), 0, 0, static_cast<unsigned>(-1) }; 466 switch (description.kerning()) { 467 case FontDescription::NormalKerning: 468 // kern/vkrn are enabled by default 469 break; 470 case FontDescription::NoneKerning: 471 m_features.append(description.orientation() == Vertical ? noVkrn : noKern); 472 break; 473 case FontDescription::AutoKerning: 474 break; 475 } 476 477 static hb_feature_t noClig = { HB_TAG('c', 'l', 'i', 'g'), 0, 0, static_cast<unsigned>(-1) }; 478 static hb_feature_t noLiga = { HB_TAG('l', 'i', 'g', 'a'), 0, 0, static_cast<unsigned>(-1) }; 479 switch (description.commonLigaturesState()) { 480 case FontDescription::DisabledLigaturesState: 481 m_features.append(noLiga); 482 m_features.append(noClig); 483 break; 484 case FontDescription::EnabledLigaturesState: 485 // liga and clig are on by default 486 break; 487 case FontDescription::NormalLigaturesState: 488 break; 489 } 490 static hb_feature_t dlig = { HB_TAG('d', 'l', 'i', 'g'), 1, 0, static_cast<unsigned>(-1) }; 491 switch (description.discretionaryLigaturesState()) { 492 case FontDescription::DisabledLigaturesState: 493 // dlig is off by default 494 break; 495 case FontDescription::EnabledLigaturesState: 496 m_features.append(dlig); 497 break; 498 case FontDescription::NormalLigaturesState: 499 break; 500 } 501 static hb_feature_t hlig = { HB_TAG('h', 'l', 'i', 'g'), 1, 0, static_cast<unsigned>(-1) }; 502 switch (description.historicalLigaturesState()) { 503 case FontDescription::DisabledLigaturesState: 504 // hlig is off by default 505 break; 506 case FontDescription::EnabledLigaturesState: 507 m_features.append(hlig); 508 break; 509 case FontDescription::NormalLigaturesState: 510 break; 511 } 512 static hb_feature_t noCalt = { HB_TAG('c', 'a', 'l', 't'), 0, 0, static_cast<unsigned>(-1) }; 513 switch (description.contextualLigaturesState()) { 514 case FontDescription::DisabledLigaturesState: 515 m_features.append(noCalt); 516 break; 517 case FontDescription::EnabledLigaturesState: 518 // calt is on by default 519 break; 520 case FontDescription::NormalLigaturesState: 521 break; 522 } 523 524 FontFeatureSettings* settings = description.featureSettings(); 525 if (!settings) 526 return; 527 528 unsigned numFeatures = settings->size(); 529 for (unsigned i = 0; i < numFeatures; ++i) { 530 hb_feature_t feature; 531 const AtomicString& tag = settings->at(i).tag(); 532 feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); 533 feature.value = settings->at(i).value(); 534 feature.start = 0; 535 feature.end = static_cast<unsigned>(-1); 536 m_features.append(feature); 537 } 538 } 539 540 bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer) 541 { 542 if (!createHarfBuzzRuns()) 543 return false; 544 545 m_totalWidth = 0; 546 if (!shapeHarfBuzzRuns()) 547 return false; 548 549 if (!RuntimeEnabledFeatures::subpixelFontScalingEnabled()) 550 m_totalWidth = roundf(m_totalWidth); 551 552 if (m_harfBuzzRuns.last()->hasGlyphToCharacterIndexes() 553 && glyphBuffer && !fillGlyphBuffer(glyphBuffer)) 554 return false; 555 556 return true; 557 } 558 559 FloatPoint HarfBuzzShaper::adjustStartPoint(const FloatPoint& point) 560 { 561 return point + m_startOffset; 562 } 563 564 static inline int handleMultipleUChar( 565 UChar32 character, 566 unsigned clusterLength, 567 const SimpleFontData* currentFontData, 568 const UChar* currentCharacterPosition, 569 const UChar* markCharactersEnd, 570 const UChar* normalizedBufferEnd) 571 { 572 if (U_GET_GC_MASK(character) & U_GC_M_MASK) { 573 int markLength = clusterLength; 574 while (markCharactersEnd < normalizedBufferEnd) { 575 UChar32 nextCharacter; 576 int nextCharacterLength = 0; 577 U16_NEXT(markCharactersEnd, nextCharacterLength, normalizedBufferEnd - markCharactersEnd, nextCharacter); 578 if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK)) 579 break; 580 markLength += nextCharacterLength; 581 markCharactersEnd += nextCharacterLength; 582 } 583 584 if (currentFontData->canRenderCombiningCharacterSequence(currentCharacterPosition, markCharactersEnd - currentCharacterPosition)) { 585 return markLength; 586 } 587 } 588 return 0; 589 } 590 591 struct CandidateRun { 592 UChar32 character; 593 unsigned start; 594 unsigned end; 595 const SimpleFontData* fontData; 596 UScriptCode script; 597 }; 598 599 static inline bool collectCandidateRuns(const UChar* normalizedBuffer, 600 size_t bufferLength, const Font* font, Vector<CandidateRun>* runs) 601 { 602 const UChar* normalizedBufferEnd = normalizedBuffer + bufferLength; 603 SurrogatePairAwareTextIterator iterator(normalizedBuffer, 0, bufferLength, bufferLength); 604 UChar32 character; 605 unsigned clusterLength = 0; 606 unsigned startIndexOfCurrentRun = 0; 607 if (!iterator.consume(character, clusterLength)) 608 return false; 609 610 const SimpleFontData* nextFontData = font->glyphDataForCharacter(character, false).fontData; 611 UErrorCode errorCode = U_ZERO_ERROR; 612 UScriptCode nextScript = uscript_getScript(character, &errorCode); 613 if (U_FAILURE(errorCode)) 614 return false; 615 616 do { 617 const UChar* currentCharacterPosition = iterator.characters(); 618 const SimpleFontData* currentFontData = nextFontData; 619 UScriptCode currentScript = nextScript; 620 621 UChar32 lastCharacter = character; 622 for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) { 623 if (Character::treatAsZeroWidthSpace(character)) 624 continue; 625 626 int length = handleMultipleUChar(character, clusterLength, currentFontData, currentCharacterPosition, iterator.characters() + clusterLength, normalizedBufferEnd); 627 if (length) { 628 clusterLength = length; 629 continue; 630 } 631 632 nextFontData = font->glyphDataForCharacter(character, false).fontData; 633 nextScript = uscript_getScript(character, &errorCode); 634 if (U_FAILURE(errorCode)) 635 return false; 636 if (lastCharacter == zeroWidthJoiner) 637 currentFontData = nextFontData; 638 if ((nextFontData != currentFontData) || ((currentScript != nextScript) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, currentScript)))) 639 break; 640 currentCharacterPosition = iterator.characters(); 641 lastCharacter = character; 642 } 643 644 CandidateRun run = { character, startIndexOfCurrentRun, iterator.currentCharacter(), currentFontData, currentScript }; 645 runs->append(run); 646 647 startIndexOfCurrentRun = iterator.currentCharacter(); 648 } while (iterator.consume(character, clusterLength)); 649 650 return true; 651 } 652 653 static inline bool matchesAdjacentRun(UScriptCode* scriptExtensions, int length, 654 CandidateRun& adjacentRun) 655 { 656 for (int i = 0; i < length; i++) { 657 if (scriptExtensions[i] == adjacentRun.script) 658 return true; 659 } 660 return false; 661 } 662 663 static inline void resolveRunBasedOnScriptExtensions(Vector<CandidateRun>& runs, 664 CandidateRun& run, size_t i, size_t length, UScriptCode* scriptExtensions, 665 int extensionsLength, size_t& nextResolvedRun) 666 { 667 // If uscript_getScriptExtensions returns 1 it only contains the script value, 668 // we only care about ScriptExtensions which is indicated by a value >= 2. 669 if (extensionsLength <= 1) 670 return; 671 672 if (i > 0 && matchesAdjacentRun(scriptExtensions, extensionsLength, runs[i - 1])) { 673 run.script = runs[i - 1].script; 674 return; 675 } 676 677 for (size_t j = i + 1; j < length; j++) { 678 if (runs[j].script != USCRIPT_COMMON 679 && runs[j].script != USCRIPT_INHERITED 680 && matchesAdjacentRun(scriptExtensions, extensionsLength, runs[j])) { 681 nextResolvedRun = j; 682 break; 683 } 684 } 685 } 686 687 static inline void resolveRunBasedOnScriptValue(Vector<CandidateRun>& runs, 688 CandidateRun& run, size_t i, size_t length, size_t& nextResolvedRun) 689 { 690 if (run.script != USCRIPT_COMMON) 691 return; 692 693 if (i > 0 && runs[i - 1].script != USCRIPT_COMMON) { 694 run.script = runs[i - 1].script; 695 return; 696 } 697 698 for (size_t j = i + 1; j < length; j++) { 699 if (runs[j].script != USCRIPT_COMMON 700 && runs[j].script != USCRIPT_INHERITED) { 701 nextResolvedRun = j; 702 break; 703 } 704 } 705 } 706 707 static inline bool resolveCandidateRuns(Vector<CandidateRun>& runs) 708 { 709 UScriptCode scriptExtensions[8]; 710 UErrorCode errorCode = U_ZERO_ERROR; 711 size_t length = runs.size(); 712 size_t nextResolvedRun = 0; 713 for (size_t i = 0; i < length; i++) { 714 CandidateRun& run = runs[i]; 715 nextResolvedRun = 0; 716 717 if (run.script == USCRIPT_INHERITED) 718 run.script = i > 0 ? runs[i - 1].script : USCRIPT_COMMON; 719 720 int extensionsLength = uscript_getScriptExtensions(run.character, 721 scriptExtensions, sizeof(scriptExtensions), &errorCode); 722 if (U_FAILURE(errorCode)) 723 return false; 724 725 resolveRunBasedOnScriptExtensions(runs, run, i, length, 726 scriptExtensions, extensionsLength, nextResolvedRun); 727 resolveRunBasedOnScriptValue(runs, run, i, length, 728 nextResolvedRun); 729 for (size_t j = i; j < nextResolvedRun; j++) 730 runs[j].script = runs[nextResolvedRun].script; 731 732 i = std::max(i, nextResolvedRun); 733 } 734 return true; 735 } 736 737 bool HarfBuzzShaper::createHarfBuzzRuns() 738 { 739 Vector<CandidateRun> candidateRuns; 740 if (!collectCandidateRuns(m_normalizedBuffer.get(), 741 m_normalizedBufferLength, m_font, &candidateRuns)) 742 return false; 743 744 if (!resolveCandidateRuns(candidateRuns)) 745 return false; 746 747 size_t length = candidateRuns.size(); 748 for (size_t i = 0; i < length; ) { 749 CandidateRun& run = candidateRuns[i]; 750 CandidateRun lastMatchingRun = run; 751 for (i++; i < length; i++) { 752 if (candidateRuns[i].script != run.script 753 || candidateRuns[i].fontData != run.fontData) 754 break; 755 lastMatchingRun = candidateRuns[i]; 756 } 757 addHarfBuzzRun(run.start, lastMatchingRun.end, run.fontData, run.script); 758 } 759 return !m_harfBuzzRuns.isEmpty(); 760 } 761 762 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built 763 // without hb-icu. See http://crbug.com/356929 764 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) 765 { 766 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) 767 return HB_SCRIPT_INVALID; 768 769 return hb_script_from_string(uscript_getShortName(script), -1); 770 } 771 772 773 void HarfBuzzShaper::addHarfBuzzRun(unsigned startCharacter, 774 unsigned endCharacter, const SimpleFontData* fontData, 775 UScriptCode script) 776 { 777 ASSERT(endCharacter > startCharacter); 778 ASSERT(script != USCRIPT_INVALID_CODE); 779 return m_harfBuzzRuns.append(HarfBuzzRun::create(fontData, 780 startCharacter, endCharacter - startCharacter, 781 m_run.direction(), ICUScriptToHBScript(script))); 782 } 783 784 static const uint16_t* toUint16(const UChar* src) 785 { 786 // FIXME: This relies on undefined behavior however it works on the 787 // current versions of all compilers we care about and avoids making 788 // a copy of the string. 789 COMPILE_ASSERT(sizeof(UChar) == sizeof(uint16_t), UChar_is_the_same_size_as_uint16_t); 790 return reinterpret_cast<const uint16_t*>(src); 791 } 792 793 bool HarfBuzzShaper::shapeHarfBuzzRuns() 794 { 795 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy); 796 797 HarfBuzzRunCache& runCache = harfBuzzRunCache(); 798 const FontDescription& fontDescription = m_font->fontDescription(); 799 const String& localeString = fontDescription.locale(); 800 CString locale = localeString.latin1(); 801 802 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { 803 unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; 804 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); 805 const SimpleFontData* currentFontData = currentRun->fontData(); 806 if (currentFontData->isSVGFont()) 807 return false; 808 809 FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tFontData->platformData()); 810 HarfBuzzFace* face = platformData->harfBuzzFace(); 811 if (!face) 812 return false; 813 814 hb_buffer_set_language(harfBuzzBuffer.get(), hb_language_from_string(locale.data(), locale.length())); 815 hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script()); 816 hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); 817 818 hb_segment_properties_t props; 819 hb_buffer_get_segment_properties(harfBuzzBuffer.get(), &props); 820 821 const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex(); 822 std::wstring key(src, src + currentRun->numCharacters()); 823 824 CachedShapingResults* cachedResults = runCache.find(key); 825 if (cachedResults) { 826 if (cachedResults->dir == props.direction && cachedResults->font == *m_font && cachedResults->locale == localeString) { 827 currentRun->applyShapeResult(cachedResults->buffer); 828 setGlyphPositionsForHarfBuzzRun(currentRun, cachedResults->buffer); 829 830 hb_buffer_clear_contents(harfBuzzBuffer.get()); 831 832 runCache.moveToBack(cachedResults); 833 834 continue; 835 } 836 837 runCache.remove(cachedResults); 838 } 839 840 // Add a space as pre-context to the buffer. This prevents showing dotted-circle 841 // for combining marks at the beginning of runs. 842 static const uint16_t preContext = ' '; 843 hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); 844 845 if (fontDescription.variant() == FontVariantSmallCaps && u_islower(m_normalizedBuffer[currentRun->startIndex()])) { 846 String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()).upper(); 847 ASSERT(!upperText.is8Bit()); // m_normalizedBuffer is 16 bit, therefore upperText is 16 bit, even after we call makeUpper(). 848 hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(upperText.characters16()), currentRun->numCharacters(), 0, currentRun->numCharacters()); 849 } else { 850 hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(m_normalizedBuffer.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun->numCharacters()); 851 } 852 853 if (fontDescription.orientation() == Vertical) 854 face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); 855 856 HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_destroy); 857 858 hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty() ? 0 : m_features.data(), m_features.size()); 859 currentRun->applyShapeResult(harfBuzzBuffer.get()); 860 setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get()); 861 862 runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_font, props.direction, localeString)); 863 864 harfBuzzBuffer.set(hb_buffer_create()); 865 } 866 867 return true; 868 } 869 870 void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer) 871 { 872 const SimpleFontData* currentFontData = currentRun->fontData(); 873 hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); 874 hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzzBuffer, 0); 875 876 if (!currentRun->hasGlyphToCharacterIndexes()) { 877 // FIXME: https://crbug.com/337886 878 ASSERT_NOT_REACHED(); 879 return; 880 } 881 882 unsigned numGlyphs = currentRun->numGlyphs(); 883 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); 884 float totalAdvance = 0; 885 FloatPoint glyphOrigin; 886 887 // HarfBuzz returns the shaping result in visual order. We need not to flip for RTL. 888 for (size_t i = 0; i < numGlyphs; ++i) { 889 bool runEnd = i + 1 == numGlyphs; 890 uint16_t glyph = glyphInfos[i].codepoint; 891 float offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset); 892 float offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset); 893 float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance); 894 895 unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i].cluster; 896 bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster; 897 float spacing = 0; 898 899 glyphToCharacterIndexes[i] = glyphInfos[i].cluster; 900 901 if (isClusterEnd && !Character::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex])) 902 spacing += m_letterSpacing; 903 904 if (isClusterEnd && isWordEnd(currentCharacterIndex)) 905 spacing += determineWordBreakSpacing(); 906 907 if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { 908 currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0); 909 continue; 910 } 911 912 advance += spacing; 913 if (m_run.rtl()) { 914 // In RTL, spacing should be added to left side of glyphs. 915 offsetX += spacing; 916 if (!isClusterEnd) 917 offsetX += m_letterSpacing; 918 } 919 920 currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); 921 922 FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph); 923 glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); 924 m_glyphBoundingBox.unite(glyphBounds); 925 glyphOrigin += FloatSize(advance + offsetX, offsetY); 926 927 totalAdvance += advance; 928 } 929 currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0); 930 m_totalWidth += currentRun->width(); 931 } 932 933 void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun, FloatPoint& firstOffsetOfNextRun) 934 { 935 FloatPoint* offsets = currentRun->offsets(); 936 uint16_t* glyphs = currentRun->glyphs(); 937 float* advances = currentRun->advances(); 938 unsigned numGlyphs = currentRun->numGlyphs(); 939 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); 940 for (unsigned i = 0; i < numGlyphs; ++i) { 941 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i]; 942 FloatPoint& currentOffset = offsets[i]; 943 FloatPoint& nextOffset = (i == numGlyphs - 1) ? firstOffsetOfNextRun : offsets[i + 1]; 944 float glyphAdvanceX = advances[i] + nextOffset.x() - currentOffset.x(); 945 float glyphAdvanceY = nextOffset.y() - currentOffset.y(); 946 if (m_run.rtl()) { 947 if (currentCharacterIndex >= m_toIndex) 948 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); 949 else if (currentCharacterIndex >= m_fromIndex) 950 glyphBuffer->add(glyphs[i], currentRun->fontData(), FloatSize(glyphAdvanceX, glyphAdvanceY)); 951 } else { 952 if (currentCharacterIndex < m_fromIndex) 953 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); 954 else if (currentCharacterIndex < m_toIndex) 955 glyphBuffer->add(glyphs[i], currentRun->fontData(), FloatSize(glyphAdvanceX, glyphAdvanceY)); 956 } 957 } 958 } 959 960 void HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun) 961 { 962 // FIXME: Instead of generating a synthetic GlyphBuffer here which is then used by the 963 // drawEmphasisMarks method of FontFastPath, we should roll our own emphasis mark drawing function. 964 965 float* advances = currentRun->advances(); 966 unsigned numGlyphs = currentRun->numGlyphs(); 967 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); 968 unsigned graphemesInCluster = 1; 969 float clusterAdvance = 0; 970 uint16_t clusterStart; 971 972 // A "cluster" in this context means a cluster as it is used by HarfBuzz: 973 // The minimal group of characters and corresponding glyphs, that cannot be broken 974 // down further from a text shaping point of view. 975 // A cluster can contain multiple glyphs and grapheme clusters, with mutually 976 // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clusters, 977 // then linearly split the sum of corresponding glyph advances by the number of 978 // grapheme clusters in order to find positions for emphasis mark drawing. 979 980 if (m_run.rtl()) 981 clusterStart = currentRun->startIndex() + currentRun->numCharacters(); 982 else 983 clusterStart = currentRun->startIndex() + glyphToCharacterIndexes[0]; 984 985 for (unsigned i = 0; i < numGlyphs; ++i) { 986 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i]; 987 bool isRunEnd = (i + 1 == numGlyphs); 988 bool isClusterEnd = isRunEnd || (currentRun->startIndex() + glyphToCharacterIndexes[i + 1] != currentCharacterIndex); 989 clusterAdvance += advances[i]; 990 991 if (isClusterEnd) { 992 uint16_t clusterEnd; 993 if (m_run.rtl()) 994 clusterEnd = currentCharacterIndex; 995 else 996 clusterEnd = isRunEnd ? currentRun->startIndex() + currentRun->numCharacters() : currentRun->startIndex() + glyphToCharacterIndexes[i + 1]; 997 998 graphemesInCluster = countGraphemesInCluster(m_normalizedBuffer.get(), m_normalizedBufferLength, clusterStart, clusterEnd); 999 if (!graphemesInCluster || !clusterAdvance) 1000 continue; 1001 1002 float glyphAdvanceX = clusterAdvance / graphemesInCluster; 1003 for (unsigned j = 0; j < graphemesInCluster; ++j) { 1004 // Do not put emphasis marks on space, separator, and control characters. 1005 Glyph glyphToAdd = Character::canReceiveTextEmphasis(m_run[currentCharacterIndex]) ? 1 : 0; 1006 glyphBuffer->add(glyphToAdd, currentRun->fontData(), glyphAdvanceX); 1007 } 1008 clusterStart = clusterEnd; 1009 clusterAdvance = 0; 1010 } 1011 } 1012 } 1013 1014 bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer) 1015 { 1016 unsigned numRuns = m_harfBuzzRuns.size(); 1017 if (m_run.rtl()) { 1018 m_startOffset = m_harfBuzzRuns.last()->offsets()[0]; 1019 for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) { 1020 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); 1021 if (!currentRun->hasGlyphToCharacterIndexes()) { 1022 // FIXME: bug 337886, 359664 1023 continue; 1024 } 1025 FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfBuzzRuns[runIndex - 1]->offsets()[0]; 1026 if (m_forTextEmphasis == ForTextEmphasis) 1027 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun); 1028 else 1029 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun); 1030 } 1031 } else { 1032 m_startOffset = m_harfBuzzRuns.first()->offsets()[0]; 1033 for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) { 1034 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); 1035 if (!currentRun->hasGlyphToCharacterIndexes()) { 1036 // FIXME: bug 337886, 359664 1037 continue; 1038 } 1039 FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoint() : m_harfBuzzRuns[runIndex + 1]->offsets()[0]; 1040 if (m_forTextEmphasis == ForTextEmphasis) 1041 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun); 1042 else 1043 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun); 1044 } 1045 } 1046 return glyphBuffer->size(); 1047 } 1048 1049 int HarfBuzzShaper::offsetForPosition(float targetX) 1050 { 1051 int charactersSoFar = 0; 1052 float currentX = 0; 1053 1054 if (m_run.rtl()) { 1055 charactersSoFar = m_normalizedBufferLength; 1056 for (int i = m_harfBuzzRuns.size() - 1; i >= 0; --i) { 1057 charactersSoFar -= m_harfBuzzRuns[i]->numCharacters(); 1058 float nextX = currentX + m_harfBuzzRuns[i]->width(); 1059 float offsetForRun = targetX - currentX; 1060 if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) { 1061 // The x value in question is within this script run. 1062 const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun); 1063 return charactersSoFar + index; 1064 } 1065 currentX = nextX; 1066 } 1067 } else { 1068 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { 1069 float nextX = currentX + m_harfBuzzRuns[i]->width(); 1070 float offsetForRun = targetX - currentX; 1071 if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) { 1072 const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun); 1073 return charactersSoFar + index; 1074 } 1075 charactersSoFar += m_harfBuzzRuns[i]->numCharacters(); 1076 currentX = nextX; 1077 } 1078 } 1079 1080 return charactersSoFar; 1081 } 1082 1083 FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to) 1084 { 1085 float currentX = 0; 1086 float fromX = 0; 1087 float toX = 0; 1088 bool foundFromX = false; 1089 bool foundToX = false; 1090 1091 if (m_run.rtl()) 1092 currentX = m_totalWidth; 1093 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { 1094 if (m_run.rtl()) 1095 currentX -= m_harfBuzzRuns[i]->width(); 1096 int numCharacters = m_harfBuzzRuns[i]->numCharacters(); 1097 if (!foundFromX && from >= 0 && from < numCharacters) { 1098 fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX; 1099 foundFromX = true; 1100 } else 1101 from -= numCharacters; 1102 1103 if (!foundToX && to >= 0 && to < numCharacters) { 1104 toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX; 1105 foundToX = true; 1106 } else 1107 to -= numCharacters; 1108 1109 if (foundFromX && foundToX) 1110 break; 1111 if (!m_run.rtl()) 1112 currentX += m_harfBuzzRuns[i]->width(); 1113 } 1114 1115 // The position in question might be just after the text. 1116 if (!foundFromX) 1117 fromX = 0; 1118 if (!foundToX) 1119 toX = m_run.rtl() ? 0 : m_totalWidth; 1120 1121 if (fromX < toX) { 1122 return Font::pixelSnappedSelectionRect( 1123 point.x() + fromX, point.x() + toX, 1124 point.y(), height); 1125 } 1126 1127 return Font::pixelSnappedSelectionRect( 1128 point.x() + toX, point.x() + fromX, 1129 point.y(), height); 1130 } 1131 1132 } // namespace WebCore 1133