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