• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
HarfBuzzScopedPtr(T * ptr,DestroyFunction destroy)62     HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy)
63         : m_ptr(ptr)
64         , m_destroy(destroy)
65     {
66         ASSERT(m_destroy);
67     }
~HarfBuzzScopedPtr()68     ~HarfBuzzScopedPtr()
69     {
70         if (m_ptr)
71             (*m_destroy)(m_ptr);
72     }
73 
get()74     T* get() { return m_ptr; }
set(T * 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 
CachedShapingResults(hb_buffer_t * harfBuzzBuffer,const Font * fontData,hb_direction_t dirData,const String & newLocale)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 
~CachedShapingResults()115 CachedShapingResults::~CachedShapingResults()
116 {
117     hb_buffer_destroy(buffer);
118 }
119 
CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator & cacheEntry)120 CachedShapingResultsLRUNode::CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry)
121     : entry(cacheEntry)
122 {
123 }
124 
~CachedShapingResultsLRUNode()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 
HarfBuzzRunCache()145 HarfBuzzRunCache::HarfBuzzRunCache()
146 {
147 }
148 
~HarfBuzzRunCache()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 
insert(const std::wstring & key,CachedShapingResults * data)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 
find(const std::wstring & key) const182 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 
remove(CachedShapingResults * node)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 
moveToBack(CachedShapingResults * node)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 
harfBuzzRunCache()207 HarfBuzzRunCache& harfBuzzRunCache()
208 {
209     DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ());
210     return globalHarfBuzzRunCache;
211 }
212 
harfBuzzPositionToFloat(hb_position_t value)213 static inline float harfBuzzPositionToFloat(hb_position_t value)
214 {
215     return static_cast<float>(value) / (1 << 16);
216 }
217 
countGraphemesInCluster(const UChar * normalizedBuffer,unsigned normalizedBufferLength,uint16_t startIndex,uint16_t endIndex)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 
HarfBuzzRun(const SimpleFontData * fontData,unsigned startIndex,unsigned numCharacters,TextDirection direction,hb_script_t script)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 
HarfBuzzRun(const HarfBuzzRun & rhs)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 
~HarfBuzzRun()264 HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun()
265 {
266 }
267 
applyShapeResult(hb_buffer_t * harfBuzzBuffer)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 
copyShapeResultAndGlyphPositions(const HarfBuzzRun & run)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 
setGlyphAndPositions(unsigned index,uint16_t glyphId,float advance,float offsetX,float offsetY)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 
characterIndexForXPosition(float targetX)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 
xPositionForOffset(unsigned offset)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 
normalizeCharacters(const TextRun & run,unsigned length,UChar * destination,unsigned * destinationLength)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 
HarfBuzzShaper(const Font * font,const TextRun & run,ForTextEmphasisOrNot forTextEmphasis)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 
isWordEnd(unsigned index)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 
determineWordBreakSpacing()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.
setPadding(int padding)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 
setDrawRange(int from,int to)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 
setFontFeatures()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 
shape(GlyphBuffer * glyphBuffer)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 
adjustStartPoint(const FloatPoint & point)559 FloatPoint HarfBuzzShaper::adjustStartPoint(const FloatPoint& point)
560 {
561     return point + m_startOffset;
562 }
563 
handleMultipleUChar(UChar32 character,unsigned clusterLength,const SimpleFontData * currentFontData,const UChar * currentCharacterPosition,const UChar * markCharactersEnd,const UChar * normalizedBufferEnd)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 
collectCandidateRuns(const UChar * normalizedBuffer,size_t bufferLength,const Font * font,Vector<CandidateRun> * runs)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 
matchesAdjacentRun(UScriptCode * scriptExtensions,int length,CandidateRun & adjacentRun)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 
resolveRunBasedOnScriptExtensions(Vector<CandidateRun> & runs,CandidateRun & run,size_t i,size_t length,UScriptCode * scriptExtensions,int extensionsLength,size_t & nextResolvedRun)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 
resolveRunBasedOnScriptValue(Vector<CandidateRun> & runs,CandidateRun & run,size_t i,size_t length,size_t & nextResolvedRun)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 
resolveCandidateRuns(Vector<CandidateRun> & runs)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 
createHarfBuzzRuns()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
ICUScriptToHBScript(UScriptCode script)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 
addHarfBuzzRun(unsigned startCharacter,unsigned endCharacter,const SimpleFontData * fontData,UScriptCode script)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 
toUint16(const UChar * src)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 
shapeHarfBuzzRuns()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*>(&currentFontData->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 
setGlyphPositionsForHarfBuzzRun(HarfBuzzRun * currentRun,hb_buffer_t * harfBuzzBuffer)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 
fillGlyphBufferFromHarfBuzzRun(GlyphBuffer * glyphBuffer,HarfBuzzRun * currentRun,FloatPoint & firstOffsetOfNextRun)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 
fillGlyphBufferForTextEmphasis(GlyphBuffer * glyphBuffer,HarfBuzzRun * currentRun)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 
fillGlyphBuffer(GlyphBuffer * glyphBuffer)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 
offsetForPosition(float targetX)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 
selectionRect(const FloatPoint & point,int height,int from,int to)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