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 blink { 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, hb_direction_t 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::setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY) 278 { 279 m_glyphs[index] = glyphId; 280 m_advances[index] = advance; 281 m_offsets[index] = FloatPoint(offsetX, offsetY); 282 } 283 284 int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX) 285 { 286 ASSERT(targetX <= m_width); 287 float currentX = 0; 288 float currentAdvance = m_advances[0]; 289 unsigned glyphIndex = 0; 290 291 // Sum up advances that belong to a character. 292 while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1]) 293 currentAdvance += m_advances[++glyphIndex]; 294 currentAdvance = currentAdvance / 2.0; 295 if (targetX <= currentAdvance) 296 return rtl() ? m_numCharacters : 0; 297 298 currentX = currentAdvance; 299 ++glyphIndex; 300 while (glyphIndex < m_numGlyphs) { 301 unsigned prevCharacterIndex = m_glyphToCharacterIndexes[glyphIndex - 1]; 302 float prevAdvance = currentAdvance; 303 currentAdvance = m_advances[glyphIndex]; 304 while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1]) 305 currentAdvance += m_advances[++glyphIndex]; 306 currentAdvance = currentAdvance / 2.0; 307 float nextX = currentX + prevAdvance + currentAdvance; 308 if (currentX <= targetX && targetX <= nextX) 309 return rtl() ? prevCharacterIndex : m_glyphToCharacterIndexes[glyphIndex]; 310 currentX = nextX; 311 ++glyphIndex; 312 } 313 314 return rtl() ? 0 : m_numCharacters; 315 } 316 317 float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset) 318 { 319 ASSERT(offset < m_numCharacters); 320 unsigned glyphIndex = 0; 321 float position = 0; 322 if (rtl()) { 323 while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] > offset) { 324 position += m_advances[glyphIndex]; 325 ++glyphIndex; 326 } 327 // For RTL, we need to return the right side boundary of the character. 328 // Add advance of glyphs which are part of the character. 329 while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1]) { 330 position += m_advances[glyphIndex]; 331 ++glyphIndex; 332 } 333 position += m_advances[glyphIndex]; 334 } else { 335 while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] < offset) { 336 position += m_advances[glyphIndex]; 337 ++glyphIndex; 338 } 339 } 340 return position; 341 } 342 343 static void normalizeCharacters(const TextRun& run, unsigned length, UChar* destination, unsigned* destinationLength) 344 { 345 unsigned position = 0; 346 bool error = false; 347 const UChar* source; 348 String stringFor8BitRun; 349 if (run.is8Bit()) { 350 stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length()); 351 source = stringFor8BitRun.characters16(); 352 } else 353 source = run.characters16(); 354 355 *destinationLength = 0; 356 while (position < length) { 357 UChar32 character; 358 U16_NEXT(source, position, length, character); 359 // Don't normalize tabs as they are not treated as spaces for word-end. 360 if (run.normalizeSpace() && Character::isNormalizedCanvasSpaceCharacter(character)) 361 character = space; 362 else if (Character::treatAsSpace(character) && character != characterTabulation) 363 character = space; 364 else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) 365 character = zeroWidthSpace; 366 367 U16_APPEND(destination, *destinationLength, length, character, error); 368 ASSERT_UNUSED(error, !error); 369 } 370 } 371 372 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmphasisOrNot forTextEmphasis, HashSet<const SimpleFontData*>* fallbackFonts) 373 : m_font(font) 374 , m_normalizedBufferLength(0) 375 , m_run(run) 376 , m_wordSpacingAdjustment(font->fontDescription().wordSpacing()) 377 , m_padding(0) 378 , m_padPerWordBreak(0) 379 , m_padError(0) 380 , m_letterSpacing(font->fontDescription().letterSpacing()) 381 , m_fromIndex(0) 382 , m_toIndex(m_run.length()) 383 , m_forTextEmphasis(forTextEmphasis) 384 , m_glyphBoundingBox(std::numeric_limits<float>::max(), std::numeric_limits<float>::min(), std::numeric_limits<float>::min(), std::numeric_limits<float>::max()) 385 , m_fallbackFonts(fallbackFonts) 386 { 387 m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]); 388 normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), &m_normalizedBufferLength); 389 setPadding(m_run.expansion()); 390 setFontFeatures(); 391 } 392 393 // In complex text word-spacing affects each line-break, space (U+0020) and non-breaking space (U+00A0). 394 static inline bool isCodepointSpace(UChar c) 395 { 396 return c == space || c == noBreakSpace || c == newlineCharacter; 397 } 398 399 static inline bool isWordEnd(const UChar* normalizedBuffer, unsigned index) 400 { 401 // This could refer a high-surrogate, but should work. 402 return index && isCodepointSpace(normalizedBuffer[index]); 403 } 404 405 int HarfBuzzShaper::determineWordBreakSpacing() 406 { 407 int wordBreakSpacing = m_wordSpacingAdjustment; 408 409 if (m_padding > 0) { 410 int toPad = roundf(m_padPerWordBreak + m_padError); 411 m_padError += m_padPerWordBreak - toPad; 412 413 if (m_padding < toPad) 414 toPad = m_padding; 415 m_padding -= toPad; 416 wordBreakSpacing += toPad; 417 } 418 return wordBreakSpacing; 419 } 420 421 // setPadding sets a number of pixels to be distributed across the TextRun. 422 // WebKit uses this to justify text. 423 void HarfBuzzShaper::setPadding(int padding) 424 { 425 m_padding = padding; 426 m_padError = 0; 427 if (!m_padding) 428 return; 429 430 // If we have padding to distribute, then we try to give an equal 431 // amount to each space. The last space gets the smaller amount, if 432 // any. 433 unsigned numWordEnds = 0; 434 435 for (unsigned i = 0; i < m_normalizedBufferLength; i++) { 436 if (isWordEnd(m_normalizedBuffer.get(), i)) 437 numWordEnds++; 438 } 439 440 if (numWordEnds) 441 m_padPerWordBreak = m_padding / numWordEnds; 442 else 443 m_padPerWordBreak = 0; 444 } 445 446 447 void HarfBuzzShaper::setDrawRange(int from, int to) 448 { 449 ASSERT_WITH_SECURITY_IMPLICATION(from >= 0); 450 ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length()); 451 m_fromIndex = from; 452 m_toIndex = to; 453 } 454 455 void HarfBuzzShaper::setFontFeatures() 456 { 457 const FontDescription& description = m_font->fontDescription(); 458 if (description.orientation() == Vertical) { 459 static hb_feature_t vert = { HarfBuzzFace::vertTag, 1, 0, static_cast<unsigned>(-1) }; 460 static hb_feature_t vrt2 = { HarfBuzzFace::vrt2Tag, 1, 0, static_cast<unsigned>(-1) }; 461 m_features.append(vert); 462 m_features.append(vrt2); 463 } 464 465 static hb_feature_t noKern = { HB_TAG('k', 'e', 'r', 'n'), 0, 0, static_cast<unsigned>(-1) }; 466 static hb_feature_t noVkrn = { HB_TAG('v', 'k', 'r', 'n'), 0, 0, static_cast<unsigned>(-1) }; 467 switch (description.kerning()) { 468 case FontDescription::NormalKerning: 469 // kern/vkrn are enabled by default 470 break; 471 case FontDescription::NoneKerning: 472 m_features.append(description.orientation() == Vertical ? noVkrn : noKern); 473 break; 474 case FontDescription::AutoKerning: 475 break; 476 } 477 478 static hb_feature_t noClig = { HB_TAG('c', 'l', 'i', 'g'), 0, 0, static_cast<unsigned>(-1) }; 479 static hb_feature_t noLiga = { HB_TAG('l', 'i', 'g', 'a'), 0, 0, static_cast<unsigned>(-1) }; 480 switch (description.commonLigaturesState()) { 481 case FontDescription::DisabledLigaturesState: 482 m_features.append(noLiga); 483 m_features.append(noClig); 484 break; 485 case FontDescription::EnabledLigaturesState: 486 // liga and clig are on by default 487 break; 488 case FontDescription::NormalLigaturesState: 489 break; 490 } 491 static hb_feature_t dlig = { HB_TAG('d', 'l', 'i', 'g'), 1, 0, static_cast<unsigned>(-1) }; 492 switch (description.discretionaryLigaturesState()) { 493 case FontDescription::DisabledLigaturesState: 494 // dlig is off by default 495 break; 496 case FontDescription::EnabledLigaturesState: 497 m_features.append(dlig); 498 break; 499 case FontDescription::NormalLigaturesState: 500 break; 501 } 502 static hb_feature_t hlig = { HB_TAG('h', 'l', 'i', 'g'), 1, 0, static_cast<unsigned>(-1) }; 503 switch (description.historicalLigaturesState()) { 504 case FontDescription::DisabledLigaturesState: 505 // hlig is off by default 506 break; 507 case FontDescription::EnabledLigaturesState: 508 m_features.append(hlig); 509 break; 510 case FontDescription::NormalLigaturesState: 511 break; 512 } 513 static hb_feature_t noCalt = { HB_TAG('c', 'a', 'l', 't'), 0, 0, static_cast<unsigned>(-1) }; 514 switch (description.contextualLigaturesState()) { 515 case FontDescription::DisabledLigaturesState: 516 m_features.append(noCalt); 517 break; 518 case FontDescription::EnabledLigaturesState: 519 // calt is on by default 520 break; 521 case FontDescription::NormalLigaturesState: 522 break; 523 } 524 525 static hb_feature_t hwid = { HB_TAG('h', 'w', 'i', 'd'), 1, 0, static_cast<unsigned>(-1) }; 526 static hb_feature_t twid = { HB_TAG('t', 'w', 'i', 'd'), 1, 0, static_cast<unsigned>(-1) }; 527 static hb_feature_t qwid = { HB_TAG('d', 'w', 'i', 'd'), 1, 0, static_cast<unsigned>(-1) }; 528 switch (description.widthVariant()) { 529 case HalfWidth: 530 m_features.append(hwid); 531 break; 532 case ThirdWidth: 533 m_features.append(twid); 534 break; 535 case QuarterWidth: 536 m_features.append(qwid); 537 break; 538 case RegularWidth: 539 break; 540 } 541 542 FontFeatureSettings* settings = description.featureSettings(); 543 if (!settings) 544 return; 545 546 unsigned numFeatures = settings->size(); 547 for (unsigned i = 0; i < numFeatures; ++i) { 548 hb_feature_t feature; 549 const AtomicString& tag = settings->at(i).tag(); 550 feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); 551 feature.value = settings->at(i).value(); 552 feature.start = 0; 553 feature.end = static_cast<unsigned>(-1); 554 m_features.append(feature); 555 } 556 } 557 558 bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer) 559 { 560 if (!createHarfBuzzRuns()) 561 return false; 562 563 m_totalWidth = 0; 564 if (!shapeHarfBuzzRuns()) 565 return false; 566 567 if (!RuntimeEnabledFeatures::subpixelFontScalingEnabled()) 568 m_totalWidth = roundf(m_totalWidth); 569 570 if (m_harfBuzzRuns.last()->hasGlyphToCharacterIndexes() 571 && glyphBuffer && !fillGlyphBuffer(glyphBuffer)) 572 return false; 573 574 return true; 575 } 576 577 FloatPoint HarfBuzzShaper::adjustStartPoint(const FloatPoint& point) 578 { 579 return point + m_startOffset; 580 } 581 582 static inline int handleMultipleUChar( 583 UChar32 character, 584 unsigned clusterLength, 585 const SimpleFontData* currentFontData, 586 const UChar* currentCharacterPosition, 587 const UChar* markCharactersEnd, 588 const UChar* normalizedBufferEnd) 589 { 590 if (U_GET_GC_MASK(character) & U_GC_M_MASK) { 591 int markLength = clusterLength; 592 while (markCharactersEnd < normalizedBufferEnd) { 593 UChar32 nextCharacter; 594 int nextCharacterLength = 0; 595 U16_NEXT(markCharactersEnd, nextCharacterLength, normalizedBufferEnd - markCharactersEnd, nextCharacter); 596 if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK)) 597 break; 598 markLength += nextCharacterLength; 599 markCharactersEnd += nextCharacterLength; 600 } 601 602 if (currentFontData->canRenderCombiningCharacterSequence(currentCharacterPosition, markCharactersEnd - currentCharacterPosition)) { 603 return markLength; 604 } 605 } 606 return 0; 607 } 608 609 struct CandidateRun { 610 UChar32 character; 611 unsigned start; 612 unsigned end; 613 const SimpleFontData* fontData; 614 UScriptCode script; 615 }; 616 617 static inline bool collectCandidateRuns(const UChar* normalizedBuffer, 618 size_t bufferLength, const Font* font, Vector<CandidateRun>* runs, bool isSpaceNormalize) 619 { 620 const UChar* normalizedBufferEnd = normalizedBuffer + bufferLength; 621 SurrogatePairAwareTextIterator iterator(normalizedBuffer, 0, bufferLength, bufferLength); 622 UChar32 character; 623 unsigned clusterLength = 0; 624 unsigned startIndexOfCurrentRun = 0; 625 626 if (!iterator.consume(character, clusterLength)) 627 return false; 628 629 const SimpleFontData* nextFontData = font->glyphDataForCharacter(character, false, isSpaceNormalize).fontData; 630 UErrorCode errorCode = U_ZERO_ERROR; 631 UScriptCode nextScript = uscript_getScript(character, &errorCode); 632 if (U_FAILURE(errorCode)) 633 return false; 634 635 do { 636 const UChar* currentCharacterPosition = iterator.characters(); 637 const SimpleFontData* currentFontData = nextFontData; 638 UScriptCode currentScript = nextScript; 639 640 UChar32 lastCharacter = character; 641 for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) { 642 if (Character::treatAsZeroWidthSpace(character)) 643 continue; 644 645 int length = handleMultipleUChar(character, clusterLength, currentFontData, currentCharacterPosition, iterator.characters() + clusterLength, normalizedBufferEnd); 646 if (length) { 647 clusterLength = length; 648 continue; 649 } 650 651 nextFontData = font->glyphDataForCharacter(character, false, isSpaceNormalize).fontData; 652 nextScript = uscript_getScript(character, &errorCode); 653 if (U_FAILURE(errorCode)) 654 return false; 655 if (lastCharacter == zeroWidthJoiner) 656 currentFontData = nextFontData; 657 if ((nextFontData != currentFontData) || ((currentScript != nextScript) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, currentScript)))) 658 break; 659 currentCharacterPosition = iterator.characters(); 660 lastCharacter = character; 661 } 662 663 CandidateRun run = { character, startIndexOfCurrentRun, iterator.currentCharacter(), currentFontData, currentScript }; 664 runs->append(run); 665 666 startIndexOfCurrentRun = iterator.currentCharacter(); 667 } while (iterator.consume(character, clusterLength)); 668 669 return true; 670 } 671 672 static inline bool matchesAdjacentRun(UScriptCode* scriptExtensions, int length, 673 CandidateRun& adjacentRun) 674 { 675 for (int i = 0; i < length; i++) { 676 if (scriptExtensions[i] == adjacentRun.script) 677 return true; 678 } 679 return false; 680 } 681 682 static inline void resolveRunBasedOnScriptExtensions(Vector<CandidateRun>& runs, 683 CandidateRun& run, size_t i, size_t length, UScriptCode* scriptExtensions, 684 int extensionsLength, size_t& nextResolvedRun) 685 { 686 // If uscript_getScriptExtensions returns 1 it only contains the script value, 687 // we only care about ScriptExtensions which is indicated by a value >= 2. 688 if (extensionsLength <= 1) 689 return; 690 691 if (i > 0 && matchesAdjacentRun(scriptExtensions, extensionsLength, runs[i - 1])) { 692 run.script = runs[i - 1].script; 693 return; 694 } 695 696 for (size_t j = i + 1; j < length; j++) { 697 if (runs[j].script != USCRIPT_COMMON 698 && runs[j].script != USCRIPT_INHERITED 699 && matchesAdjacentRun(scriptExtensions, extensionsLength, runs[j])) { 700 nextResolvedRun = j; 701 break; 702 } 703 } 704 } 705 706 static inline void resolveRunBasedOnScriptValue(Vector<CandidateRun>& runs, 707 CandidateRun& run, size_t i, size_t length, size_t& nextResolvedRun) 708 { 709 if (run.script != USCRIPT_COMMON) 710 return; 711 712 if (i > 0 && runs[i - 1].script != USCRIPT_COMMON) { 713 run.script = runs[i - 1].script; 714 return; 715 } 716 717 for (size_t j = i + 1; j < length; j++) { 718 if (runs[j].script != USCRIPT_COMMON 719 && runs[j].script != USCRIPT_INHERITED) { 720 nextResolvedRun = j; 721 break; 722 } 723 } 724 } 725 726 static inline bool resolveCandidateRuns(Vector<CandidateRun>& runs) 727 { 728 UScriptCode scriptExtensions[8]; 729 UErrorCode errorCode = U_ZERO_ERROR; 730 size_t length = runs.size(); 731 size_t nextResolvedRun = 0; 732 for (size_t i = 0; i < length; i++) { 733 CandidateRun& run = runs[i]; 734 nextResolvedRun = 0; 735 736 if (run.script == USCRIPT_INHERITED) 737 run.script = i > 0 ? runs[i - 1].script : USCRIPT_COMMON; 738 739 int extensionsLength = uscript_getScriptExtensions(run.character, 740 scriptExtensions, sizeof(scriptExtensions), &errorCode); 741 if (U_FAILURE(errorCode)) 742 return false; 743 744 resolveRunBasedOnScriptExtensions(runs, run, i, length, 745 scriptExtensions, extensionsLength, nextResolvedRun); 746 resolveRunBasedOnScriptValue(runs, run, i, length, 747 nextResolvedRun); 748 for (size_t j = i; j < nextResolvedRun; j++) 749 runs[j].script = runs[nextResolvedRun].script; 750 751 i = std::max(i, nextResolvedRun); 752 } 753 return true; 754 } 755 756 bool HarfBuzzShaper::createHarfBuzzRuns() 757 { 758 Vector<CandidateRun> candidateRuns; 759 if (!collectCandidateRuns(m_normalizedBuffer.get(), 760 m_normalizedBufferLength, m_font, &candidateRuns, m_run.normalizeSpace())) 761 return false; 762 763 if (!resolveCandidateRuns(candidateRuns)) 764 return false; 765 766 size_t length = candidateRuns.size(); 767 for (size_t i = 0; i < length; ) { 768 CandidateRun& run = candidateRuns[i]; 769 CandidateRun lastMatchingRun = run; 770 for (i++; i < length; i++) { 771 if (candidateRuns[i].script != run.script 772 || candidateRuns[i].fontData != run.fontData) 773 break; 774 lastMatchingRun = candidateRuns[i]; 775 } 776 addHarfBuzzRun(run.start, lastMatchingRun.end, run.fontData, run.script); 777 } 778 return !m_harfBuzzRuns.isEmpty(); 779 } 780 781 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built 782 // without hb-icu. See http://crbug.com/356929 783 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) 784 { 785 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) 786 return HB_SCRIPT_INVALID; 787 788 return hb_script_from_string(uscript_getShortName(script), -1); 789 } 790 791 static inline hb_direction_t TextDirectionToHBDirection(TextDirection dir) 792 { 793 return dir == RTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR; 794 } 795 796 797 void HarfBuzzShaper::addHarfBuzzRun(unsigned startCharacter, 798 unsigned endCharacter, const SimpleFontData* fontData, 799 UScriptCode script) 800 { 801 ASSERT(endCharacter > startCharacter); 802 ASSERT(script != USCRIPT_INVALID_CODE); 803 if (m_fallbackFonts) 804 m_fallbackFonts->add(fontData); 805 return m_harfBuzzRuns.append(HarfBuzzRun::create(fontData, 806 startCharacter, endCharacter - startCharacter, 807 TextDirectionToHBDirection(m_run.direction()), 808 ICUScriptToHBScript(script))); 809 } 810 811 static const uint16_t* toUint16(const UChar* src) 812 { 813 // FIXME: This relies on undefined behavior however it works on the 814 // current versions of all compilers we care about and avoids making 815 // a copy of the string. 816 COMPILE_ASSERT(sizeof(UChar) == sizeof(uint16_t), UChar_is_the_same_size_as_uint16_t); 817 return reinterpret_cast<const uint16_t*>(src); 818 } 819 820 bool HarfBuzzShaper::shapeHarfBuzzRuns() 821 { 822 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy); 823 824 HarfBuzzRunCache& runCache = harfBuzzRunCache(); 825 const FontDescription& fontDescription = m_font->fontDescription(); 826 const String& localeString = fontDescription.locale(); 827 CString locale = localeString.latin1(); 828 829 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { 830 unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; 831 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); 832 const SimpleFontData* currentFontData = currentRun->fontData(); 833 if (currentFontData->isSVGFont()) 834 return false; 835 836 FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tFontData->platformData()); 837 HarfBuzzFace* face = platformData->harfBuzzFace(); 838 if (!face) 839 return false; 840 841 hb_buffer_set_language(harfBuzzBuffer.get(), hb_language_from_string(locale.data(), locale.length())); 842 hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script()); 843 hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->direction()); 844 845 const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex(); 846 std::wstring key(src, src + currentRun->numCharacters()); 847 848 CachedShapingResults* cachedResults = runCache.find(key); 849 if (cachedResults) { 850 if (cachedResults->dir == currentRun->direction() && cachedResults->font == *m_font && cachedResults->locale == localeString) { 851 currentRun->applyShapeResult(cachedResults->buffer); 852 setGlyphPositionsForHarfBuzzRun(currentRun, cachedResults->buffer); 853 854 hb_buffer_clear_contents(harfBuzzBuffer.get()); 855 856 runCache.moveToBack(cachedResults); 857 858 continue; 859 } 860 861 runCache.remove(cachedResults); 862 } 863 864 // Add a space as pre-context to the buffer. This prevents showing dotted-circle 865 // for combining marks at the beginning of runs. 866 static const uint16_t preContext = space; 867 hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); 868 869 if (fontDescription.variant() == FontVariantSmallCaps && u_islower(m_normalizedBuffer[currentRun->startIndex()])) { 870 String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()).upper(); 871 ASSERT(!upperText.is8Bit()); // m_normalizedBuffer is 16 bit, therefore upperText is 16 bit, even after we call makeUpper(). 872 hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(upperText.characters16()), currentRun->numCharacters(), 0, currentRun->numCharacters()); 873 } else { 874 hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(m_normalizedBuffer.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun->numCharacters()); 875 } 876 877 if (fontDescription.orientation() == Vertical) 878 face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); 879 880 HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_destroy); 881 882 hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty() ? 0 : m_features.data(), m_features.size()); 883 currentRun->applyShapeResult(harfBuzzBuffer.get()); 884 setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get()); 885 886 runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_font, currentRun->direction(), localeString)); 887 888 harfBuzzBuffer.set(hb_buffer_create()); 889 } 890 891 return true; 892 } 893 894 void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer) 895 { 896 const SimpleFontData* currentFontData = currentRun->fontData(); 897 hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); 898 hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzzBuffer, 0); 899 900 if (!currentRun->hasGlyphToCharacterIndexes()) { 901 // FIXME: https://crbug.com/337886 902 ASSERT_NOT_REACHED(); 903 return; 904 } 905 906 unsigned numGlyphs = currentRun->numGlyphs(); 907 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); 908 float totalAdvance = 0; 909 FloatPoint glyphOrigin; 910 911 // HarfBuzz returns the shaping result in visual order. We need not to flip for RTL. 912 for (size_t i = 0; i < numGlyphs; ++i) { 913 bool runEnd = i + 1 == numGlyphs; 914 uint16_t glyph = glyphInfos[i].codepoint; 915 float offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset); 916 float offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset); 917 float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance); 918 919 unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i].cluster; 920 bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster; 921 float spacing = 0; 922 923 glyphToCharacterIndexes[i] = glyphInfos[i].cluster; 924 925 if (isClusterEnd && !Character::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex])) 926 spacing += m_letterSpacing; 927 928 if (isClusterEnd && isWordEnd(m_normalizedBuffer.get(), currentCharacterIndex)) 929 spacing += determineWordBreakSpacing(); 930 931 if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { 932 currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0); 933 continue; 934 } 935 936 advance += spacing; 937 if (m_run.rtl()) { 938 // In RTL, spacing should be added to left side of glyphs. 939 offsetX += spacing; 940 if (!isClusterEnd) 941 offsetX += m_letterSpacing; 942 } 943 944 currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); 945 946 FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph); 947 glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); 948 m_glyphBoundingBox.unite(glyphBounds); 949 glyphOrigin += FloatSize(advance + offsetX, offsetY); 950 951 totalAdvance += advance; 952 } 953 currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0); 954 m_totalWidth += currentRun->width(); 955 } 956 957 void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun, FloatPoint& firstOffsetOfNextRun) 958 { 959 FloatPoint* offsets = currentRun->offsets(); 960 uint16_t* glyphs = currentRun->glyphs(); 961 float* advances = currentRun->advances(); 962 unsigned numGlyphs = currentRun->numGlyphs(); 963 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); 964 for (unsigned i = 0; i < numGlyphs; ++i) { 965 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i]; 966 FloatPoint& currentOffset = offsets[i]; 967 FloatPoint& nextOffset = (i == numGlyphs - 1) ? firstOffsetOfNextRun : offsets[i + 1]; 968 float glyphAdvanceX = advances[i] + nextOffset.x() - currentOffset.x(); 969 float glyphAdvanceY = nextOffset.y() - currentOffset.y(); 970 if (m_run.rtl()) { 971 if (currentCharacterIndex >= m_toIndex) 972 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); 973 else if (currentCharacterIndex >= m_fromIndex) 974 glyphBuffer->add(glyphs[i], currentRun->fontData(), FloatSize(glyphAdvanceX, glyphAdvanceY)); 975 } else { 976 if (currentCharacterIndex < m_fromIndex) 977 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); 978 else if (currentCharacterIndex < m_toIndex) 979 glyphBuffer->add(glyphs[i], currentRun->fontData(), FloatSize(glyphAdvanceX, glyphAdvanceY)); 980 } 981 } 982 } 983 984 void HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun) 985 { 986 // FIXME: Instead of generating a synthetic GlyphBuffer here which is then used by the 987 // drawEmphasisMarks method of FontFastPath, we should roll our own emphasis mark drawing function. 988 989 float* advances = currentRun->advances(); 990 unsigned numGlyphs = currentRun->numGlyphs(); 991 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); 992 unsigned graphemesInCluster = 1; 993 float clusterAdvance = 0; 994 uint16_t clusterStart; 995 996 // A "cluster" in this context means a cluster as it is used by HarfBuzz: 997 // The minimal group of characters and corresponding glyphs, that cannot be broken 998 // down further from a text shaping point of view. 999 // A cluster can contain multiple glyphs and grapheme clusters, with mutually 1000 // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clusters, 1001 // then linearly split the sum of corresponding glyph advances by the number of 1002 // grapheme clusters in order to find positions for emphasis mark drawing. 1003 1004 if (m_run.rtl()) 1005 clusterStart = currentRun->startIndex() + currentRun->numCharacters(); 1006 else 1007 clusterStart = currentRun->startIndex() + glyphToCharacterIndexes[0]; 1008 1009 for (unsigned i = 0; i < numGlyphs; ++i) { 1010 uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i]; 1011 bool isRunEnd = (i + 1 == numGlyphs); 1012 bool isClusterEnd = isRunEnd || (currentRun->startIndex() + glyphToCharacterIndexes[i + 1] != currentCharacterIndex); 1013 clusterAdvance += advances[i]; 1014 1015 if (isClusterEnd) { 1016 uint16_t clusterEnd; 1017 if (m_run.rtl()) 1018 clusterEnd = currentCharacterIndex; 1019 else 1020 clusterEnd = isRunEnd ? currentRun->startIndex() + currentRun->numCharacters() : currentRun->startIndex() + glyphToCharacterIndexes[i + 1]; 1021 1022 graphemesInCluster = countGraphemesInCluster(m_normalizedBuffer.get(), m_normalizedBufferLength, clusterStart, clusterEnd); 1023 if (!graphemesInCluster || !clusterAdvance) 1024 continue; 1025 1026 float glyphAdvanceX = clusterAdvance / graphemesInCluster; 1027 for (unsigned j = 0; j < graphemesInCluster; ++j) { 1028 // Do not put emphasis marks on space, separator, and control characters. 1029 Glyph glyphToAdd = Character::canReceiveTextEmphasis(m_run[currentCharacterIndex]) ? 1 : 0; 1030 glyphBuffer->add(glyphToAdd, currentRun->fontData(), glyphAdvanceX); 1031 } 1032 clusterStart = clusterEnd; 1033 clusterAdvance = 0; 1034 } 1035 } 1036 } 1037 1038 bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer) 1039 { 1040 unsigned numRuns = m_harfBuzzRuns.size(); 1041 if (m_run.rtl()) { 1042 m_startOffset = m_harfBuzzRuns.last()->offsets()[0]; 1043 for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) { 1044 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); 1045 if (!currentRun->hasGlyphToCharacterIndexes()) { 1046 // FIXME: bug 337886, 359664 1047 continue; 1048 } 1049 FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfBuzzRuns[runIndex - 1]->offsets()[0]; 1050 if (m_forTextEmphasis == ForTextEmphasis) 1051 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun); 1052 else 1053 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun); 1054 } 1055 } else { 1056 m_startOffset = m_harfBuzzRuns.first()->offsets()[0]; 1057 for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) { 1058 HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); 1059 if (!currentRun->hasGlyphToCharacterIndexes()) { 1060 // FIXME: bug 337886, 359664 1061 continue; 1062 } 1063 FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoint() : m_harfBuzzRuns[runIndex + 1]->offsets()[0]; 1064 if (m_forTextEmphasis == ForTextEmphasis) 1065 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun); 1066 else 1067 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun); 1068 } 1069 } 1070 return glyphBuffer->size(); 1071 } 1072 1073 int HarfBuzzShaper::offsetForPosition(float targetX) 1074 { 1075 int charactersSoFar = 0; 1076 float currentX = 0; 1077 1078 if (m_run.rtl()) { 1079 charactersSoFar = m_normalizedBufferLength; 1080 for (int i = m_harfBuzzRuns.size() - 1; i >= 0; --i) { 1081 charactersSoFar -= m_harfBuzzRuns[i]->numCharacters(); 1082 float nextX = currentX + m_harfBuzzRuns[i]->width(); 1083 float offsetForRun = targetX - currentX; 1084 if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) { 1085 // The x value in question is within this script run. 1086 const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun); 1087 return charactersSoFar + index; 1088 } 1089 currentX = nextX; 1090 } 1091 } else { 1092 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { 1093 float nextX = currentX + m_harfBuzzRuns[i]->width(); 1094 float offsetForRun = targetX - currentX; 1095 if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) { 1096 const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun); 1097 return charactersSoFar + index; 1098 } 1099 charactersSoFar += m_harfBuzzRuns[i]->numCharacters(); 1100 currentX = nextX; 1101 } 1102 } 1103 1104 return charactersSoFar; 1105 } 1106 1107 FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to) 1108 { 1109 float currentX = 0; 1110 float fromX = 0; 1111 float toX = 0; 1112 bool foundFromX = false; 1113 bool foundToX = false; 1114 1115 if (m_run.rtl()) 1116 currentX = m_totalWidth; 1117 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { 1118 if (m_run.rtl()) 1119 currentX -= m_harfBuzzRuns[i]->width(); 1120 int numCharacters = m_harfBuzzRuns[i]->numCharacters(); 1121 if (!foundFromX && from >= 0 && from < numCharacters) { 1122 fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX; 1123 foundFromX = true; 1124 } else 1125 from -= numCharacters; 1126 1127 if (!foundToX && to >= 0 && to < numCharacters) { 1128 toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX; 1129 foundToX = true; 1130 } else 1131 to -= numCharacters; 1132 1133 if (foundFromX && foundToX) 1134 break; 1135 if (!m_run.rtl()) 1136 currentX += m_harfBuzzRuns[i]->width(); 1137 } 1138 1139 // The position in question might be just after the text. 1140 if (!foundFromX) 1141 fromX = 0; 1142 if (!foundToX) 1143 toX = m_run.rtl() ? 0 : m_totalWidth; 1144 1145 if (fromX < toX) { 1146 return Font::pixelSnappedSelectionRect( 1147 point.x() + fromX, point.x() + toX, 1148 point.y(), height); 1149 } 1150 1151 return Font::pixelSnappedSelectionRect( 1152 point.x() + toX, point.x() + fromX, 1153 point.y(), height); 1154 } 1155 1156 } // namespace blink 1157