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