| OLD | NEW | 
|---|
|  | (Empty) | 
| 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 "core/platform/graphics/harfbuzz/HarfBuzzShaper.h" |  | 
| 34 |  | 
| 35 #include "RuntimeEnabledFeatures.h" |  | 
| 36 #include "core/platform/graphics/Font.h" |  | 
| 37 #include "core/platform/graphics/harfbuzz/HarfBuzzFace.h" |  | 
| 38 #include "hb-icu.h" |  | 
| 39 #include "platform/text/SurrogatePairAwareTextIterator.h" |  | 
| 40 #include "wtf/MathExtras.h" |  | 
| 41 #include "wtf/unicode/Unicode.h" |  | 
| 42 #include <unicode/normlzr.h> |  | 
| 43 #include <unicode/uchar.h> |  | 
| 44 |  | 
| 45 #include <list> |  | 
| 46 #include <map> |  | 
| 47 #include <string> |  | 
| 48 |  | 
| 49 namespace WebCore { |  | 
| 50 |  | 
| 51 template<typename T> |  | 
| 52 class HarfBuzzScopedPtr { |  | 
| 53 public: |  | 
| 54     typedef void (*DestroyFunction)(T*); |  | 
| 55 |  | 
| 56     HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) |  | 
| 57         : m_ptr(ptr) |  | 
| 58         , m_destroy(destroy) |  | 
| 59     { |  | 
| 60         ASSERT(m_destroy); |  | 
| 61     } |  | 
| 62     ~HarfBuzzScopedPtr() |  | 
| 63     { |  | 
| 64         if (m_ptr) |  | 
| 65             (*m_destroy)(m_ptr); |  | 
| 66     } |  | 
| 67 |  | 
| 68     T* get() { return m_ptr; } |  | 
| 69     void set(T* ptr) { m_ptr = ptr; } |  | 
| 70 private: |  | 
| 71     T* m_ptr; |  | 
| 72     DestroyFunction m_destroy; |  | 
| 73 }; |  | 
| 74 |  | 
| 75 |  | 
| 76 static const unsigned cHarfBuzzCacheMaxSize = 256; |  | 
| 77 |  | 
| 78 struct CachedShapingResultsLRUNode; |  | 
| 79 struct CachedShapingResults; |  | 
| 80 typedef std::map<std::wstring, CachedShapingResults*> CachedShapingResultsMap; |  | 
| 81 typedef std::list<CachedShapingResultsLRUNode*> CachedShapingResultsLRU; |  | 
| 82 |  | 
| 83 struct CachedShapingResults { |  | 
| 84     CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_di
     rection_t runDir); |  | 
| 85     ~CachedShapingResults(); |  | 
| 86 |  | 
| 87     hb_buffer_t* buffer; |  | 
| 88     Font font; |  | 
| 89     hb_direction_t dir; |  | 
| 90     CachedShapingResultsLRU::iterator lru; |  | 
| 91 }; |  | 
| 92 |  | 
| 93 struct CachedShapingResultsLRUNode { |  | 
| 94     CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEn
     try); |  | 
| 95     ~CachedShapingResultsLRUNode(); |  | 
| 96 |  | 
| 97     CachedShapingResultsMap::iterator entry; |  | 
| 98 }; |  | 
| 99 |  | 
| 100 CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Fo
     nt* fontData, hb_direction_t dirData) |  | 
| 101     : buffer(harfBuzzBuffer) |  | 
| 102     , font(*fontData) |  | 
| 103     , dir(dirData) |  | 
| 104 { |  | 
| 105 } |  | 
| 106 |  | 
| 107 CachedShapingResults::~CachedShapingResults() |  | 
| 108 { |  | 
| 109     hb_buffer_destroy(buffer); |  | 
| 110 } |  | 
| 111 |  | 
| 112 CachedShapingResultsLRUNode::CachedShapingResultsLRUNode(const CachedShapingResu
     ltsMap::iterator& cacheEntry) |  | 
| 113     : entry(cacheEntry) |  | 
| 114 { |  | 
| 115 } |  | 
| 116 |  | 
| 117 CachedShapingResultsLRUNode::~CachedShapingResultsLRUNode() |  | 
| 118 { |  | 
| 119 } |  | 
| 120 |  | 
| 121 class HarfBuzzRunCache { |  | 
| 122 public: |  | 
| 123     HarfBuzzRunCache(); |  | 
| 124     ~HarfBuzzRunCache(); |  | 
| 125 |  | 
| 126     CachedShapingResults* find(const std::wstring& key) const; |  | 
| 127     void remove(CachedShapingResults* node); |  | 
| 128     void moveToBack(CachedShapingResults* node); |  | 
| 129     bool insert(const std::wstring& key, CachedShapingResults* run); |  | 
| 130 |  | 
| 131 private: |  | 
| 132     CachedShapingResultsMap m_harfBuzzRunMap; |  | 
| 133     CachedShapingResultsLRU m_harfBuzzRunLRU; |  | 
| 134 }; |  | 
| 135 |  | 
| 136 |  | 
| 137 HarfBuzzRunCache::HarfBuzzRunCache() |  | 
| 138 { |  | 
| 139 } |  | 
| 140 |  | 
| 141 HarfBuzzRunCache::~HarfBuzzRunCache() |  | 
| 142 { |  | 
| 143     for (CachedShapingResultsMap::iterator it = m_harfBuzzRunMap.begin(); it != 
     m_harfBuzzRunMap.end(); ++it) |  | 
| 144         delete it->second; |  | 
| 145     for (CachedShapingResultsLRU::iterator it = m_harfBuzzRunLRU.begin(); it != 
     m_harfBuzzRunLRU.end(); ++it) |  | 
| 146         delete *it; |  | 
| 147 } |  | 
| 148 |  | 
| 149 bool HarfBuzzRunCache::insert(const std::wstring& key, CachedShapingResults* dat
     a) |  | 
| 150 { |  | 
| 151     std::pair<CachedShapingResultsMap::iterator, bool> results = |  | 
| 152         m_harfBuzzRunMap.insert(CachedShapingResultsMap::value_type(key, data)); |  | 
| 153 |  | 
| 154     if (!results.second) |  | 
| 155         return false; |  | 
| 156 |  | 
| 157     CachedShapingResultsLRUNode* node = new CachedShapingResultsLRUNode(results.
     first); |  | 
| 158 |  | 
| 159     m_harfBuzzRunLRU.push_back(node); |  | 
| 160     data->lru = --m_harfBuzzRunLRU.end(); |  | 
| 161 |  | 
| 162     if (m_harfBuzzRunMap.size() > cHarfBuzzCacheMaxSize) { |  | 
| 163         CachedShapingResultsLRUNode* lru = m_harfBuzzRunLRU.front(); |  | 
| 164         CachedShapingResults* foo = lru->entry->second; |  | 
| 165         m_harfBuzzRunMap.erase(lru->entry); |  | 
| 166         m_harfBuzzRunLRU.pop_front(); |  | 
| 167         delete foo; |  | 
| 168         delete lru; |  | 
| 169     } |  | 
| 170 |  | 
| 171     return true; |  | 
| 172 } |  | 
| 173 |  | 
| 174 inline CachedShapingResults* HarfBuzzRunCache::find(const std::wstring& key) con
     st |  | 
| 175 { |  | 
| 176     CachedShapingResultsMap::const_iterator it = m_harfBuzzRunMap.find(key); |  | 
| 177 |  | 
| 178     return it != m_harfBuzzRunMap.end() ? it->second : 0; |  | 
| 179 } |  | 
| 180 |  | 
| 181 inline void HarfBuzzRunCache::remove(CachedShapingResults* node) |  | 
| 182 { |  | 
| 183     CachedShapingResultsLRUNode* lruNode = *node->lru; |  | 
| 184 |  | 
| 185     m_harfBuzzRunLRU.erase(node->lru); |  | 
| 186     m_harfBuzzRunMap.erase(lruNode->entry); |  | 
| 187     delete lruNode; |  | 
| 188     delete node; |  | 
| 189 } |  | 
| 190 |  | 
| 191 inline void HarfBuzzRunCache::moveToBack(CachedShapingResults* node) |  | 
| 192 { |  | 
| 193     CachedShapingResultsLRUNode* lruNode = *node->lru; |  | 
| 194     m_harfBuzzRunLRU.erase(node->lru); |  | 
| 195     m_harfBuzzRunLRU.push_back(lruNode); |  | 
| 196     node->lru = --m_harfBuzzRunLRU.end(); |  | 
| 197 } |  | 
| 198 |  | 
| 199 HarfBuzzRunCache& harfBuzzRunCache() |  | 
| 200 { |  | 
| 201     DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ()); |  | 
| 202     return globalHarfBuzzRunCache; |  | 
| 203 } |  | 
| 204 |  | 
| 205 static inline float harfBuzzPositionToFloat(hb_position_t value) |  | 
| 206 { |  | 
| 207     return static_cast<float>(value) / (1 << 16); |  | 
| 208 } |  | 
| 209 |  | 
| 210 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, 
     unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_
     t script) |  | 
| 211     : m_fontData(fontData) |  | 
| 212     , m_startIndex(startIndex) |  | 
| 213     , m_numCharacters(numCharacters) |  | 
| 214     , m_numGlyphs(0) |  | 
| 215     , m_direction(direction) |  | 
| 216     , m_script(script) |  | 
| 217     , m_width(0) |  | 
| 218 { |  | 
| 219 } |  | 
| 220 |  | 
| 221 inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const HarfBuzzRun& rhs) |  | 
| 222     : m_fontData(rhs.m_fontData) |  | 
| 223     , m_startIndex(rhs.m_startIndex) |  | 
| 224     , m_numCharacters(rhs.m_numCharacters) |  | 
| 225     , m_numGlyphs(rhs.m_numGlyphs) |  | 
| 226     , m_direction(rhs.m_direction) |  | 
| 227     , m_script(rhs.m_script) |  | 
| 228     , m_glyphs(rhs.m_glyphs) |  | 
| 229     , m_advances(rhs.m_advances) |  | 
| 230     , m_glyphToCharacterIndexes(rhs.m_glyphToCharacterIndexes) |  | 
| 231     , m_offsets(rhs.m_offsets) |  | 
| 232     , m_width(rhs.m_width) |  | 
| 233 { |  | 
| 234 } |  | 
| 235 |  | 
| 236 HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun() |  | 
| 237 { |  | 
| 238 } |  | 
| 239 |  | 
| 240 inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzB
     uffer) |  | 
| 241 { |  | 
| 242     m_numGlyphs = hb_buffer_get_length(harfBuzzBuffer); |  | 
| 243     m_glyphs.resize(m_numGlyphs); |  | 
| 244     m_advances.resize(m_numGlyphs); |  | 
| 245     m_glyphToCharacterIndexes.resize(m_numGlyphs); |  | 
| 246     m_offsets.resize(m_numGlyphs); |  | 
| 247 } |  | 
| 248 |  | 
| 249 inline void HarfBuzzShaper::HarfBuzzRun::copyShapeResultAndGlyphPositions(const 
     HarfBuzzRun& run) |  | 
| 250 { |  | 
| 251     m_numGlyphs = run.m_numGlyphs; |  | 
| 252     m_glyphs = run.m_glyphs; |  | 
| 253     m_advances = run.m_advances; |  | 
| 254     m_glyphToCharacterIndexes = run.m_glyphToCharacterIndexes; |  | 
| 255     m_offsets = run.m_offsets; |  | 
| 256     m_width = run.m_width; |  | 
| 257 } |  | 
| 258 |  | 
| 259 inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, ui
     nt16_t glyphId, float advance, float offsetX, float offsetY) |  | 
| 260 { |  | 
| 261     m_glyphs[index] = glyphId; |  | 
| 262     m_advances[index] = advance; |  | 
| 263     m_offsets[index] = FloatPoint(offsetX, offsetY); |  | 
| 264 } |  | 
| 265 |  | 
| 266 int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX) |  | 
| 267 { |  | 
| 268     ASSERT(targetX <= m_width); |  | 
| 269     float currentX = 0; |  | 
| 270     float currentAdvance = m_advances[0]; |  | 
| 271     unsigned glyphIndex = 0; |  | 
| 272 |  | 
| 273     // Sum up advances that belong to a character. |  | 
| 274     while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex]
      == m_glyphToCharacterIndexes[glyphIndex + 1]) |  | 
| 275         currentAdvance += m_advances[++glyphIndex]; |  | 
| 276     currentAdvance = currentAdvance / 2.0; |  | 
| 277     if (targetX <= currentAdvance) |  | 
| 278         return rtl() ? m_numCharacters : 0; |  | 
| 279 |  | 
| 280     ++glyphIndex; |  | 
| 281     while (glyphIndex < m_numGlyphs) { |  | 
| 282         unsigned prevCharacterIndex = m_glyphToCharacterIndexes[glyphIndex - 1]; |  | 
| 283         float prevAdvance = currentAdvance; |  | 
| 284         currentAdvance = m_advances[glyphIndex]; |  | 
| 285         while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIn
     dex] == m_glyphToCharacterIndexes[glyphIndex + 1]) |  | 
| 286             currentAdvance += m_advances[++glyphIndex]; |  | 
| 287         currentAdvance = currentAdvance / 2.0; |  | 
| 288         float nextX = currentX + prevAdvance + currentAdvance; |  | 
| 289         if (currentX <= targetX && targetX <= nextX) |  | 
| 290             return rtl() ? prevCharacterIndex : m_glyphToCharacterIndexes[glyphI
     ndex]; |  | 
| 291         currentX = nextX; |  | 
| 292         prevAdvance = currentAdvance; |  | 
| 293         ++glyphIndex; |  | 
| 294     } |  | 
| 295 |  | 
| 296     return rtl() ? 0 : m_numCharacters; |  | 
| 297 } |  | 
| 298 |  | 
| 299 float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset) |  | 
| 300 { |  | 
| 301     ASSERT(offset < m_numCharacters); |  | 
| 302     unsigned glyphIndex = 0; |  | 
| 303     float position = 0; |  | 
| 304     if (rtl()) { |  | 
| 305         while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex]
      > offset) { |  | 
| 306             position += m_advances[glyphIndex]; |  | 
| 307             ++glyphIndex; |  | 
| 308         } |  | 
| 309         // For RTL, we need to return the right side boundary of the character. |  | 
| 310         // Add advance of glyphs which are part of the character. |  | 
| 311         while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIn
     dex] == m_glyphToCharacterIndexes[glyphIndex + 1]) { |  | 
| 312             position += m_advances[glyphIndex]; |  | 
| 313             ++glyphIndex; |  | 
| 314         } |  | 
| 315         position += m_advances[glyphIndex]; |  | 
| 316     } else { |  | 
| 317         while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex]
      < offset) { |  | 
| 318             position += m_advances[glyphIndex]; |  | 
| 319             ++glyphIndex; |  | 
| 320         } |  | 
| 321     } |  | 
| 322     return position; |  | 
| 323 } |  | 
| 324 |  | 
| 325 static void normalizeCharacters(const TextRun& run, unsigned length, UChar* dest
     ination, unsigned* destinationLength) |  | 
| 326 { |  | 
| 327     unsigned position = 0; |  | 
| 328     bool error = false; |  | 
| 329     const UChar* source; |  | 
| 330     String stringFor8BitRun; |  | 
| 331     if (run.is8Bit()) { |  | 
| 332         stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), ru
     n.length()); |  | 
| 333         source = stringFor8BitRun.characters16(); |  | 
| 334     } else |  | 
| 335         source = run.characters16(); |  | 
| 336 |  | 
| 337     *destinationLength = 0; |  | 
| 338     while (position < length) { |  | 
| 339         UChar32 character; |  | 
| 340         U16_NEXT(source, position, length, character); |  | 
| 341         // Don't normalize tabs as they are not treated as spaces for word-end. |  | 
| 342         if (Font::treatAsSpace(character) && character != '\t') |  | 
| 343             character = ' '; |  | 
| 344         else if (Font::treatAsZeroWidthSpaceInComplexScript(character)) |  | 
| 345             character = zeroWidthSpace; |  | 
| 346         U16_APPEND(destination, *destinationLength, length, character, error); |  | 
| 347         ASSERT_UNUSED(error, !error); |  | 
| 348     } |  | 
| 349 } |  | 
| 350 |  | 
| 351 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run) |  | 
| 352     : m_font(font) |  | 
| 353     , m_normalizedBufferLength(0) |  | 
| 354     , m_run(run) |  | 
| 355     , m_wordSpacingAdjustment(font->wordSpacing()) |  | 
| 356     , m_padding(0) |  | 
| 357     , m_padPerWordBreak(0) |  | 
| 358     , m_padError(0) |  | 
| 359     , m_letterSpacing(font->letterSpacing()) |  | 
| 360     , m_fromIndex(0) |  | 
| 361     , m_toIndex(m_run.length()) |  | 
| 362 { |  | 
| 363     m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]); |  | 
| 364     normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), &m_norm
     alizedBufferLength); |  | 
| 365     setPadding(m_run.expansion()); |  | 
| 366     setFontFeatures(); |  | 
| 367 } |  | 
| 368 |  | 
| 369 static void normalizeSpacesAndMirrorChars(const UChar* source, unsigned length, 
     UChar* destination, unsigned* destinationLength, HarfBuzzShaper::NormalizeMode n
     ormalizeMode) |  | 
| 370 { |  | 
| 371     unsigned position = 0; |  | 
| 372     bool error = false; |  | 
| 373     // Iterate characters in source and mirror character if needed. |  | 
| 374     *destinationLength = 0; |  | 
| 375     while (position < length) { |  | 
| 376         UChar32 character; |  | 
| 377         U16_NEXT(source, position, length, character); |  | 
| 378         // Don't normalize tabs as they are not treated as spaces for word-end |  | 
| 379         if (Font::treatAsSpace(character) && character != '\t') |  | 
| 380             character = ' '; |  | 
| 381         else if (Font::treatAsZeroWidthSpace(character)) |  | 
| 382             character = zeroWidthSpace; |  | 
| 383         else if (normalizeMode == HarfBuzzShaper::NormalizeMirrorChars) |  | 
| 384             character = u_charMirror(character); |  | 
| 385         U16_APPEND(destination, *destinationLength, length, character, error); |  | 
| 386         ASSERT_UNUSED(error, !error); |  | 
| 387     } |  | 
| 388 } |  | 
| 389 |  | 
| 390 void HarfBuzzShaper::setNormalizedBuffer(NormalizeMode normalizeMode) |  | 
| 391 { |  | 
| 392     // Normalize the text run in three ways: |  | 
| 393     // 1) Convert the |originalRun| to NFC normalized form if combining diacriti
     cal marks |  | 
| 394     // (U+0300..) are used in the run. This conversion is necessary since most O
     penType |  | 
| 395     // fonts (e.g., Arial) don't have substitution rules for the diacritical mar
     ks in |  | 
| 396     // their GSUB tables. |  | 
| 397     // |  | 
| 398     // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API h
     ere since |  | 
| 399     // the API returns FALSE (= not normalized) for complex runs that don't requ
     ire NFC |  | 
| 400     // normalization (e.g., Arabic text). Unless the run contains the diacritica
     l marks, |  | 
| 401     // HarfBuzz will do the same thing for us using the GSUB table. |  | 
| 402     // 2) Convert spacing characters into plain spaces, as some fonts will provi
     de glyphs |  | 
| 403     // for characters like '\n' otherwise. |  | 
| 404     // 3) Convert mirrored characters such as parenthesis for rtl text. |  | 
| 405 |  | 
| 406     // Convert to NFC form if the text has diacritical marks. |  | 
| 407     icu::UnicodeString normalizedString; |  | 
| 408     UErrorCode error = U_ZERO_ERROR; |  | 
| 409 |  | 
| 410     const UChar* runCharacters; |  | 
| 411     String stringFor8BitRun; |  | 
| 412     if (m_run.is8Bit()) { |  | 
| 413         stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), 
     m_run.length()); |  | 
| 414         runCharacters = stringFor8BitRun.characters16(); |  | 
| 415     } else |  | 
| 416         runCharacters = m_run.characters16(); |  | 
| 417 |  | 
| 418     for (int i = 0; i < m_run.length(); ++i) { |  | 
| 419         UChar ch = runCharacters[i]; |  | 
| 420         if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) { |  | 
| 421             icu::Normalizer::normalize(icu::UnicodeString(runCharacters, |  | 
| 422                 m_run.length()), UNORM_NFC, 0 /* no options */, |  | 
| 423                 normalizedString, error); |  | 
| 424             if (U_FAILURE(error)) |  | 
| 425                 normalizedString.remove(); |  | 
| 426             break; |  | 
| 427         } |  | 
| 428     } |  | 
| 429 |  | 
| 430     const UChar* sourceText; |  | 
| 431     unsigned sourceLength; |  | 
| 432     if (normalizedString.isEmpty()) { |  | 
| 433         sourceLength = m_run.length(); |  | 
| 434         sourceText = runCharacters; |  | 
| 435     } else { |  | 
| 436         sourceLength = normalizedString.length(); |  | 
| 437         sourceText = normalizedString.getBuffer(); |  | 
| 438     } |  | 
| 439 |  | 
| 440     m_normalizedBuffer = adoptArrayPtr(new UChar[sourceLength + 1]); |  | 
| 441     normalizeSpacesAndMirrorChars(sourceText, sourceLength, m_normalizedBuffer.g
     et(), &m_normalizedBufferLength, normalizeMode); |  | 
| 442 } |  | 
| 443 |  | 
| 444 bool HarfBuzzShaper::isWordEnd(unsigned index) |  | 
| 445 { |  | 
| 446     // This could refer a high-surrogate, but should work. |  | 
| 447     return index && isCodepointSpace(m_normalizedBuffer[index]); |  | 
| 448 } |  | 
| 449 |  | 
| 450 int HarfBuzzShaper::determineWordBreakSpacing() |  | 
| 451 { |  | 
| 452     int wordBreakSpacing = m_wordSpacingAdjustment; |  | 
| 453 |  | 
| 454     if (m_padding > 0) { |  | 
| 455         int toPad = roundf(m_padPerWordBreak + m_padError); |  | 
| 456         m_padError += m_padPerWordBreak - toPad; |  | 
| 457 |  | 
| 458         if (m_padding < toPad) |  | 
| 459             toPad = m_padding; |  | 
| 460         m_padding -= toPad; |  | 
| 461         wordBreakSpacing += toPad; |  | 
| 462     } |  | 
| 463     return wordBreakSpacing; |  | 
| 464 } |  | 
| 465 |  | 
| 466 // setPadding sets a number of pixels to be distributed across the TextRun. |  | 
| 467 // WebKit uses this to justify text. |  | 
| 468 void HarfBuzzShaper::setPadding(int padding) |  | 
| 469 { |  | 
| 470     m_padding = padding; |  | 
| 471     m_padError = 0; |  | 
| 472     if (!m_padding) |  | 
| 473         return; |  | 
| 474 |  | 
| 475     // If we have padding to distribute, then we try to give an equal |  | 
| 476     // amount to each space. The last space gets the smaller amount, if |  | 
| 477     // any. |  | 
| 478     unsigned numWordEnds = 0; |  | 
| 479 |  | 
| 480     for (unsigned i = 0; i < m_normalizedBufferLength; i++) { |  | 
| 481         if (isWordEnd(i)) |  | 
| 482             numWordEnds++; |  | 
| 483     } |  | 
| 484 |  | 
| 485     if (numWordEnds) |  | 
| 486         m_padPerWordBreak = m_padding / numWordEnds; |  | 
| 487     else |  | 
| 488         m_padPerWordBreak = 0; |  | 
| 489 } |  | 
| 490 |  | 
| 491 |  | 
| 492 void HarfBuzzShaper::setDrawRange(int from, int to) |  | 
| 493 { |  | 
| 494     ASSERT_WITH_SECURITY_IMPLICATION(from >= 0); |  | 
| 495     ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length()); |  | 
| 496     m_fromIndex = from; |  | 
| 497     m_toIndex = to; |  | 
| 498 } |  | 
| 499 |  | 
| 500 void HarfBuzzShaper::setFontFeatures() |  | 
| 501 { |  | 
| 502     const FontDescription& description = m_font->fontDescription(); |  | 
| 503     if (description.orientation() == Vertical) { |  | 
| 504         static hb_feature_t vert = { HarfBuzzFace::vertTag, 1, 0, static_cast<un
     signed>(-1) }; |  | 
| 505         static hb_feature_t vrt2 = { HarfBuzzFace::vrt2Tag, 1, 0, static_cast<un
     signed>(-1) }; |  | 
| 506         m_features.append(vert); |  | 
| 507         m_features.append(vrt2); |  | 
| 508     } |  | 
| 509 |  | 
| 510     static hb_feature_t noKern = { HB_TAG('k', 'e', 'r', 'n'), 0, 0, static_cast
     <unsigned>(-1) }; |  | 
| 511     static hb_feature_t noVkrn = { HB_TAG('v', 'k', 'r', 'n'), 0, 0, static_cast
     <unsigned>(-1) }; |  | 
| 512     switch (description.kerning()) { |  | 
| 513     case FontDescription::NormalKerning: |  | 
| 514         // kern/vkrn are enabled by default |  | 
| 515         break; |  | 
| 516     case FontDescription::NoneKerning: |  | 
| 517         m_features.append(description.orientation() == Vertical ? noVkrn : noKer
     n); |  | 
| 518         break; |  | 
| 519     case FontDescription::AutoKerning: |  | 
| 520         break; |  | 
| 521     } |  | 
| 522 |  | 
| 523     FontFeatureSettings* settings = description.featureSettings(); |  | 
| 524     if (!settings) |  | 
| 525         return; |  | 
| 526 |  | 
| 527     unsigned numFeatures = settings->size(); |  | 
| 528     for (unsigned i = 0; i < numFeatures; ++i) { |  | 
| 529         hb_feature_t feature; |  | 
| 530         const AtomicString& tag = settings->at(i).tag(); |  | 
| 531         feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); |  | 
| 532         feature.value = settings->at(i).value(); |  | 
| 533         feature.start = 0; |  | 
| 534         feature.end = static_cast<unsigned>(-1); |  | 
| 535         m_features.append(feature); |  | 
| 536     } |  | 
| 537 } |  | 
| 538 |  | 
| 539 bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer) |  | 
| 540 { |  | 
| 541     if (!collectHarfBuzzRuns()) |  | 
| 542         return false; |  | 
| 543 |  | 
| 544     m_totalWidth = 0; |  | 
| 545     // WebKit doesn't set direction when calulating widths. Leave the direction 
     setting to |  | 
| 546     // HarfBuzz when we are calculating widths (except when directionalOverride(
     ) is set). |  | 
| 547     if (!shapeHarfBuzzRuns(glyphBuffer || m_run.directionalOverride())) |  | 
| 548         return false; |  | 
| 549 |  | 
| 550     if (!RuntimeEnabledFeatures::subpixelFontScalingEnabled()) |  | 
| 551         m_totalWidth = roundf(m_totalWidth); |  | 
| 552 |  | 
| 553     if (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 bool HarfBuzzShaper::collectHarfBuzzRuns() |  | 
| 565 { |  | 
| 566     const UChar* normalizedBufferEnd = m_normalizedBuffer.get() + m_normalizedBu
     fferLength; |  | 
| 567     SurrogatePairAwareTextIterator iterator(m_normalizedBuffer.get(), 0, m_norma
     lizedBufferLength, m_normalizedBufferLength); |  | 
| 568     UChar32 character; |  | 
| 569     unsigned clusterLength = 0; |  | 
| 570     unsigned startIndexOfCurrentRun = 0; |  | 
| 571     if (!iterator.consume(character, clusterLength)) |  | 
| 572         return false; |  | 
| 573 |  | 
| 574     const SimpleFontData* nextFontData = m_font->glyphDataForCharacter(character
     , false).fontData; |  | 
| 575     UErrorCode errorCode = U_ZERO_ERROR; |  | 
| 576     UScriptCode nextScript = uscript_getScript(character, &errorCode); |  | 
| 577     if (U_FAILURE(errorCode)) |  | 
| 578         return false; |  | 
| 579 |  | 
| 580     do { |  | 
| 581         const UChar* currentCharacterPosition = iterator.characters(); |  | 
| 582         const SimpleFontData* currentFontData = nextFontData; |  | 
| 583         UScriptCode currentScript = nextScript; |  | 
| 584 |  | 
| 585         for (iterator.advance(clusterLength); iterator.consume(character, cluste
     rLength); iterator.advance(clusterLength)) { |  | 
| 586             if (Font::treatAsZeroWidthSpace(character)) |  | 
| 587                 continue; |  | 
| 588 |  | 
| 589             if (U_GET_GC_MASK(character) & U_GC_M_MASK) { |  | 
| 590                 int markLength = clusterLength; |  | 
| 591                 const UChar* markCharactersEnd = iterator.characters() + cluster
     Length; |  | 
| 592                 while (markCharactersEnd < normalizedBufferEnd) { |  | 
| 593                     UChar32 nextCharacter; |  | 
| 594                     int nextCharacterLength = 0; |  | 
| 595                     U16_NEXT(markCharactersEnd, nextCharacterLength, normalizedB
     ufferEnd - 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(current
     CharacterPosition, markCharactersEnd - currentCharacterPosition)) { |  | 
| 603                     clusterLength = markLength; |  | 
| 604                     continue; |  | 
| 605                 } |  | 
| 606             } |  | 
| 607 |  | 
| 608             nextFontData = m_font->glyphDataForCharacter(character, false).fontD
     ata; |  | 
| 609             nextScript = uscript_getScript(character, &errorCode); |  | 
| 610             if (U_FAILURE(errorCode)) |  | 
| 611                 return false; |  | 
| 612             if ((nextFontData != currentFontData) || ((currentScript != nextScri
     pt) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, curre
     ntScript)))) |  | 
| 613                 break; |  | 
| 614             if (nextScript == USCRIPT_INHERITED) |  | 
| 615                 nextScript = currentScript; |  | 
| 616             currentCharacterPosition = iterator.characters(); |  | 
| 617         } |  | 
| 618         unsigned numCharactersOfCurrentRun = iterator.currentCharacter() - start
     IndexOfCurrentRun; |  | 
| 619         hb_script_t script = hb_icu_script_to_script(currentScript); |  | 
| 620         m_harfBuzzRuns.append(HarfBuzzRun::create(currentFontData, startIndexOfC
     urrentRun, numCharactersOfCurrentRun, m_run.direction(), script)); |  | 
| 621         currentFontData = nextFontData; |  | 
| 622         startIndexOfCurrentRun = iterator.currentCharacter(); |  | 
| 623     } while (iterator.consume(character, clusterLength)); |  | 
| 624 |  | 
| 625     return !m_harfBuzzRuns.isEmpty(); |  | 
| 626 } |  | 
| 627 |  | 
| 628 static const uint16_t* toUint16(const UChar* src) |  | 
| 629 { |  | 
| 630     // FIXME: This relies on undefined behavior however it works on the |  | 
| 631     // current versions of all compilers we care about and avoids making |  | 
| 632     // a copy of the string. |  | 
| 633     COMPILE_ASSERT(sizeof(UChar) == sizeof(uint16_t), UChar_is_the_same_size_as_
     uint16_t); |  | 
| 634     return reinterpret_cast<const uint16_t*>(src); |  | 
| 635 } |  | 
| 636 |  | 
| 637 bool HarfBuzzShaper::shapeHarfBuzzRuns(bool shouldSetDirection) |  | 
| 638 { |  | 
| 639     HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_
     destroy); |  | 
| 640 |  | 
| 641     hb_buffer_set_unicode_funcs(harfBuzzBuffer.get(), hb_icu_get_unicode_funcs()
     ); |  | 
| 642     HarfBuzzRunCache& runCache = harfBuzzRunCache(); |  | 
| 643 |  | 
| 644     for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { |  | 
| 645         unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; |  | 
| 646         HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); |  | 
| 647         const SimpleFontData* currentFontData = currentRun->fontData(); |  | 
| 648         if (currentFontData->isSVGFont()) |  | 
| 649             return false; |  | 
| 650 |  | 
| 651         FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tF
     ontData->platformData()); |  | 
| 652         HarfBuzzFace* face = platformData->harfBuzzFace(); |  | 
| 653         if (!face) |  | 
| 654             return false; |  | 
| 655 |  | 
| 656         hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script()); |  | 
| 657         if (shouldSetDirection) |  | 
| 658             hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->rtl() ? HB
     _DIRECTION_RTL : HB_DIRECTION_LTR); |  | 
| 659         else |  | 
| 660             // Leaving direction to HarfBuzz to guess is *really* bad, but will 
     do for now. |  | 
| 661             hb_buffer_guess_segment_properties(harfBuzzBuffer.get()); |  | 
| 662 |  | 
| 663         hb_segment_properties_t props; |  | 
| 664         hb_buffer_get_segment_properties(harfBuzzBuffer.get(), &props); |  | 
| 665 |  | 
| 666         const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex(); |  | 
| 667         std::wstring key(src, src + currentRun->numCharacters()); |  | 
| 668 |  | 
| 669         CachedShapingResults* cachedResults = runCache.find(key); |  | 
| 670         if (cachedResults) { |  | 
| 671             if (cachedResults->dir == props.direction && cachedResults->font == 
     *m_font) { |  | 
| 672                 currentRun->applyShapeResult(cachedResults->buffer); |  | 
| 673                 setGlyphPositionsForHarfBuzzRun(currentRun, cachedResults->buffe
     r); |  | 
| 674 |  | 
| 675                 hb_buffer_reset(harfBuzzBuffer.get()); |  | 
| 676 |  | 
| 677                 runCache.moveToBack(cachedResults); |  | 
| 678 |  | 
| 679                 continue; |  | 
| 680             } |  | 
| 681 |  | 
| 682             runCache.remove(cachedResults); |  | 
| 683         } |  | 
| 684 |  | 
| 685         // Add a space as pre-context to the buffer. This prevents showing dotte
     d-circle |  | 
| 686         // for combining marks at the beginning of runs. |  | 
| 687         static const uint16_t preContext = ' '; |  | 
| 688         hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); |  | 
| 689 |  | 
| 690         if (m_font->isSmallCaps() && u_islower(m_normalizedBuffer[currentRun->st
     artIndex()])) { |  | 
| 691             String upperText = String(m_normalizedBuffer.get() + currentRun->sta
     rtIndex(), currentRun->numCharacters()).upper(); |  | 
| 692             currentFontData = m_font->glyphDataForCharacter(upperText[0], false,
      SmallCapsVariant).fontData; |  | 
| 693             ASSERT(!upperText.is8Bit()); // m_normalizedBuffer is 16 bit, theref
     ore upperText is 16 bit, even after we call makeUpper(). |  | 
| 694             hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(upperText.charact
     ers16()), currentRun->numCharacters(), 0, currentRun->numCharacters()); |  | 
| 695         } else { |  | 
| 696             hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(m_normalizedBuffe
     r.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun-
     >numCharacters()); |  | 
| 697         } |  | 
| 698 |  | 
| 699         if (m_font->fontDescription().orientation() == Vertical) |  | 
| 700             face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); |  | 
| 701 |  | 
| 702         HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_de
     stroy); |  | 
| 703 |  | 
| 704         hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty() 
     ? 0 : m_features.data(), m_features.size()); |  | 
| 705         currentRun->applyShapeResult(harfBuzzBuffer.get()); |  | 
| 706         setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get()); |  | 
| 707 |  | 
| 708         runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_fo
     nt, props.direction)); |  | 
| 709 |  | 
| 710         harfBuzzBuffer.set(hb_buffer_create()); |  | 
| 711         hb_buffer_set_unicode_funcs(harfBuzzBuffer.get(), hb_icu_get_unicode_fun
     cs()); |  | 
| 712     } |  | 
| 713 |  | 
| 714     return true; |  | 
| 715 } |  | 
| 716 |  | 
| 717 void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb
     _buffer_t* harfBuzzBuffer) |  | 
| 718 { |  | 
| 719     const SimpleFontData* currentFontData = currentRun->fontData(); |  | 
| 720     hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); |  | 
| 721     hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzz
     Buffer, 0); |  | 
| 722 |  | 
| 723     unsigned numGlyphs = currentRun->numGlyphs(); |  | 
| 724     uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); |  | 
| 725     float totalAdvance = 0; |  | 
| 726 |  | 
| 727     // HarfBuzz returns the shaping result in visual order. We need not to flip 
     for RTL. |  | 
| 728     for (size_t i = 0; i < numGlyphs; ++i) { |  | 
| 729         bool runEnd = i + 1 == numGlyphs; |  | 
| 730         uint16_t glyph = glyphInfos[i].codepoint; |  | 
| 731         float offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset); |  | 
| 732         float offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset); |  | 
| 733         float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance); |  | 
| 734 |  | 
| 735         unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i
     ].cluster; |  | 
| 736         bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1]
     .cluster; |  | 
| 737         float spacing = 0; |  | 
| 738 |  | 
| 739         glyphToCharacterIndexes[i] = glyphInfos[i].cluster; |  | 
| 740 |  | 
| 741         if (isClusterEnd && !Font::treatAsZeroWidthSpace(m_normalizedBuffer[curr
     entCharacterIndex])) |  | 
| 742             spacing += m_letterSpacing; |  | 
| 743 |  | 
| 744         if (isClusterEnd && isWordEnd(currentCharacterIndex)) |  | 
| 745             spacing += determineWordBreakSpacing(); |  | 
| 746 |  | 
| 747         if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { |  | 
| 748             currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0); |  | 
| 749             continue; |  | 
| 750         } |  | 
| 751 |  | 
| 752         advance += spacing; |  | 
| 753         if (m_run.rtl()) { |  | 
| 754             // In RTL, spacing should be added to left side of glyphs. |  | 
| 755             offsetX += spacing; |  | 
| 756             if (!isClusterEnd) |  | 
| 757                 offsetX += m_letterSpacing; |  | 
| 758         } |  | 
| 759 |  | 
| 760         currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); |  | 
| 761 |  | 
| 762         totalAdvance += advance; |  | 
| 763     } |  | 
| 764     currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0); |  | 
| 765     m_totalWidth += currentRun->width(); |  | 
| 766 } |  | 
| 767 |  | 
| 768 void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, Ha
     rfBuzzRun* currentRun, FloatPoint& firstOffsetOfNextRun) |  | 
| 769 { |  | 
| 770     FloatPoint* offsets = currentRun->offsets(); |  | 
| 771     uint16_t* glyphs = currentRun->glyphs(); |  | 
| 772     float* advances = currentRun->advances(); |  | 
| 773     unsigned numGlyphs = currentRun->numGlyphs(); |  | 
| 774     uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); |  | 
| 775 |  | 
| 776     for (unsigned i = 0; i < numGlyphs; ++i) { |  | 
| 777         uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToChara
     cterIndexes[i]; |  | 
| 778         FloatPoint& currentOffset = offsets[i]; |  | 
| 779         FloatPoint& nextOffset = (i == numGlyphs - 1) ? firstOffsetOfNextRun : o
     ffsets[i + 1]; |  | 
| 780         float glyphAdvanceX = advances[i] + nextOffset.x() - currentOffset.x(); |  | 
| 781         float glyphAdvanceY = nextOffset.y() - currentOffset.y(); |  | 
| 782         if (m_run.rtl()) { |  | 
| 783             if (currentCharacterIndex >= m_toIndex) |  | 
| 784                 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); |  | 
| 785             else if (currentCharacterIndex >= m_fromIndex) |  | 
| 786                 glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphB
     ufferAdvance(glyphAdvanceX, glyphAdvanceY)); |  | 
| 787         } else { |  | 
| 788             if (currentCharacterIndex < m_fromIndex) |  | 
| 789                 m_startOffset.move(glyphAdvanceX, glyphAdvanceY); |  | 
| 790             else if (currentCharacterIndex < m_toIndex) |  | 
| 791                 glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphB
     ufferAdvance(glyphAdvanceX, glyphAdvanceY)); |  | 
| 792         } |  | 
| 793     } |  | 
| 794 } |  | 
| 795 |  | 
| 796 bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer) |  | 
| 797 { |  | 
| 798     unsigned numRuns = m_harfBuzzRuns.size(); |  | 
| 799     if (m_run.rtl()) { |  | 
| 800         m_startOffset = m_harfBuzzRuns.last()->offsets()[0]; |  | 
| 801         for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) { |  | 
| 802             HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); |  | 
| 803             FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfB
     uzzRuns[runIndex - 1]->offsets()[0]; |  | 
| 804             fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetO
     fNextRun); |  | 
| 805         } |  | 
| 806     } else { |  | 
| 807         m_startOffset = m_harfBuzzRuns.first()->offsets()[0]; |  | 
| 808         for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) { |  | 
| 809             HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); |  | 
| 810             FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoi
     nt() : m_harfBuzzRuns[runIndex + 1]->offsets()[0]; |  | 
| 811             fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetO
     fNextRun); |  | 
| 812         } |  | 
| 813     } |  | 
| 814     return glyphBuffer->size(); |  | 
| 815 } |  | 
| 816 |  | 
| 817 int HarfBuzzShaper::offsetForPosition(float targetX) |  | 
| 818 { |  | 
| 819     int charactersSoFar = 0; |  | 
| 820     float currentX = 0; |  | 
| 821 |  | 
| 822     if (m_run.rtl()) { |  | 
| 823         charactersSoFar = m_normalizedBufferLength; |  | 
| 824         for (int i = m_harfBuzzRuns.size() - 1; i >= 0; --i) { |  | 
| 825             charactersSoFar -= m_harfBuzzRuns[i]->numCharacters(); |  | 
| 826             float nextX = currentX + m_harfBuzzRuns[i]->width(); |  | 
| 827             float offsetForRun = targetX - currentX; |  | 
| 828             if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width())
      { |  | 
| 829                 // The x value in question is within this script run. |  | 
| 830                 const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosi
     tion(offsetForRun); |  | 
| 831                 return charactersSoFar + index; |  | 
| 832             } |  | 
| 833             currentX = nextX; |  | 
| 834         } |  | 
| 835     } else { |  | 
| 836         for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { |  | 
| 837             float nextX = currentX + m_harfBuzzRuns[i]->width(); |  | 
| 838             float offsetForRun = targetX - currentX; |  | 
| 839             if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width())
      { |  | 
| 840                 const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosi
     tion(offsetForRun); |  | 
| 841                 return charactersSoFar + index; |  | 
| 842             } |  | 
| 843             charactersSoFar += m_harfBuzzRuns[i]->numCharacters(); |  | 
| 844             currentX = nextX; |  | 
| 845         } |  | 
| 846     } |  | 
| 847 |  | 
| 848     return charactersSoFar; |  | 
| 849 } |  | 
| 850 |  | 
| 851 FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int
      from, int to) |  | 
| 852 { |  | 
| 853     float currentX = 0; |  | 
| 854     float fromX = 0; |  | 
| 855     float toX = 0; |  | 
| 856     bool foundFromX = false; |  | 
| 857     bool foundToX = false; |  | 
| 858 |  | 
| 859     if (m_run.rtl()) |  | 
| 860         currentX = m_totalWidth; |  | 
| 861     for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { |  | 
| 862         if (m_run.rtl()) |  | 
| 863             currentX -= m_harfBuzzRuns[i]->width(); |  | 
| 864         int numCharacters = m_harfBuzzRuns[i]->numCharacters(); |  | 
| 865         if (!foundFromX && from >= 0 && from < numCharacters) { |  | 
| 866             fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX; |  | 
| 867             foundFromX = true; |  | 
| 868         } else |  | 
| 869             from -= numCharacters; |  | 
| 870 |  | 
| 871         if (!foundToX && to >= 0 && to < numCharacters) { |  | 
| 872             toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX; |  | 
| 873             foundToX = true; |  | 
| 874         } else |  | 
| 875             to -= numCharacters; |  | 
| 876 |  | 
| 877         if (foundFromX && foundToX) |  | 
| 878             break; |  | 
| 879         if (!m_run.rtl()) |  | 
| 880             currentX += m_harfBuzzRuns[i]->width(); |  | 
| 881     } |  | 
| 882 |  | 
| 883     // The position in question might be just after the text. |  | 
| 884     if (!foundFromX) |  | 
| 885         fromX = 0; |  | 
| 886     if (!foundToX) |  | 
| 887         toX = m_run.rtl() ? 0 : m_totalWidth; |  | 
| 888 |  | 
| 889     // Using floorf() and roundf() as the same as mac port. |  | 
| 890     if (fromX < toX) |  | 
| 891         return FloatRect(floorf(point.x() + fromX), point.y(), roundf(toX - from
     X), height); |  | 
| 892     return FloatRect(floorf(point.x() + toX), point.y(), roundf(fromX - toX), he
     ight); |  | 
| 893 } |  | 
| 894 |  | 
| 895 } // namespace WebCore |  | 
| OLD | NEW | 
|---|