2 * Copyright (c) 2012 Google Inc. All rights reserved.
3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "platform/fonts/harfbuzz/HarfBuzzShaper.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>
58 class HarfBuzzScopedPtr {
60 typedef void (*DestroyFunction)(T*);
62 HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy)
74 T* get() { return m_ptr; }
75 void set(T* ptr) { m_ptr = ptr; }
78 DestroyFunction m_destroy;
82 static const unsigned cHarfBuzzCacheMaxSize = 256;
84 struct CachedShapingResultsLRUNode;
85 struct CachedShapingResults;
86 typedef std::map<std::wstring, CachedShapingResults*> CachedShapingResultsMap;
87 typedef std::list<CachedShapingResultsLRUNode*> CachedShapingResultsLRU;
89 struct CachedShapingResults {
90 CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_direction_t runDir, const String& newLocale);
91 ~CachedShapingResults();
97 CachedShapingResultsLRU::iterator lru;
100 struct CachedShapingResultsLRUNode {
101 CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry);
102 ~CachedShapingResultsLRUNode();
104 CachedShapingResultsMap::iterator entry;
107 CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* fontData, hb_direction_t dirData, const String& newLocale)
108 : buffer(harfBuzzBuffer)
115 CachedShapingResults::~CachedShapingResults()
117 hb_buffer_destroy(buffer);
120 CachedShapingResultsLRUNode::CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry)
125 CachedShapingResultsLRUNode::~CachedShapingResultsLRUNode()
129 class HarfBuzzRunCache {
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);
140 CachedShapingResultsMap m_harfBuzzRunMap;
141 CachedShapingResultsLRU m_harfBuzzRunLRU;
145 HarfBuzzRunCache::HarfBuzzRunCache()
149 HarfBuzzRunCache::~HarfBuzzRunCache()
151 for (CachedShapingResultsMap::iterator it = m_harfBuzzRunMap.begin(); it != m_harfBuzzRunMap.end(); ++it)
153 for (CachedShapingResultsLRU::iterator it = m_harfBuzzRunLRU.begin(); it != m_harfBuzzRunLRU.end(); ++it)
157 bool HarfBuzzRunCache::insert(const std::wstring& key, CachedShapingResults* data)
159 std::pair<CachedShapingResultsMap::iterator, bool> results =
160 m_harfBuzzRunMap.insert(CachedShapingResultsMap::value_type(key, data));
165 CachedShapingResultsLRUNode* node = new CachedShapingResultsLRUNode(results.first);
167 m_harfBuzzRunLRU.push_back(node);
168 data->lru = --m_harfBuzzRunLRU.end();
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();
182 inline CachedShapingResults* HarfBuzzRunCache::find(const std::wstring& key) const
184 CachedShapingResultsMap::const_iterator it = m_harfBuzzRunMap.find(key);
186 return it != m_harfBuzzRunMap.end() ? it->second : 0;
189 inline void HarfBuzzRunCache::remove(CachedShapingResults* node)
191 CachedShapingResultsLRUNode* lruNode = *node->lru;
193 m_harfBuzzRunLRU.erase(node->lru);
194 m_harfBuzzRunMap.erase(lruNode->entry);
199 inline void HarfBuzzRunCache::moveToBack(CachedShapingResults* node)
201 CachedShapingResultsLRUNode* lruNode = *node->lru;
202 m_harfBuzzRunLRU.erase(node->lru);
203 m_harfBuzzRunLRU.push_back(lruNode);
204 node->lru = --m_harfBuzzRunLRU.end();
207 HarfBuzzRunCache& harfBuzzRunCache()
209 DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ());
210 return globalHarfBuzzRunCache;
213 static inline float harfBuzzPositionToFloat(hb_position_t value)
215 return static_cast<float>(value) / (1 << 16);
218 static inline unsigned countGraphemesInCluster(const UChar* normalizedBuffer, unsigned normalizedBufferLength, uint16_t startIndex, uint16_t endIndex)
220 if (startIndex > endIndex) {
221 uint16_t tempIndex = startIndex;
222 startIndex = endIndex;
223 endIndex = tempIndex;
225 uint16_t length = endIndex - startIndex;
226 ASSERT(static_cast<unsigned>(startIndex + length) <= normalizedBufferLength);
227 TextBreakIterator* cursorPosIterator = cursorMovementIterator(&normalizedBuffer[startIndex], length);
229 int cursorPos = cursorPosIterator->current();
230 int numGraphemes = -1;
231 while (0 <= cursorPos) {
232 cursorPos = cursorPosIterator->next();
235 return numGraphemes < 0 ? 0 : numGraphemes;
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)
243 , m_direction(direction)
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)
264 HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun()
268 inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzBuffer)
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);
277 inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY)
279 m_glyphs[index] = glyphId;
280 m_advances[index] = advance;
281 m_offsets[index] = FloatPoint(offsetX, offsetY);
284 int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX)
286 ASSERT(targetX <= m_width);
288 float currentAdvance = m_advances[0];
289 unsigned glyphIndex = 0;
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;
298 currentX = currentAdvance;
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];
314 return rtl() ? 0 : m_numCharacters;
317 float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset)
319 ASSERT(offset < m_numCharacters);
320 unsigned glyphIndex = 0;
323 while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] > offset) {
324 position += m_advances[glyphIndex];
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];
333 position += m_advances[glyphIndex];
335 while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] < offset) {
336 position += m_advances[glyphIndex];
343 static void normalizeCharacters(const TextRun& run, unsigned length, UChar* destination, unsigned* destinationLength)
345 unsigned position = 0;
348 String stringFor8BitRun;
350 stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length());
351 source = stringFor8BitRun.characters16();
353 source = run.characters16();
355 *destinationLength = 0;
356 while (position < length) {
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))
362 else if (Character::treatAsSpace(character) && character != characterTabulation)
364 else if (Character::treatAsZeroWidthSpaceInComplexScript(character))
365 character = zeroWidthSpace;
367 U16_APPEND(destination, *destinationLength, length, character, error);
368 ASSERT_UNUSED(error, !error);
372 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmphasisOrNot forTextEmphasis, HashSet<const SimpleFontData*>* fallbackFonts)
374 , m_normalizedBufferLength(0)
376 , m_wordSpacingAdjustment(font->fontDescription().wordSpacing())
378 , m_padPerWordBreak(0)
380 , m_letterSpacing(font->fontDescription().letterSpacing())
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)
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());
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)
396 return c == space || c == noBreakSpace || c == newlineCharacter;
399 static inline bool isWordEnd(const UChar* normalizedBuffer, unsigned index)
401 // This could refer a high-surrogate, but should work.
402 return index && isCodepointSpace(normalizedBuffer[index]);
405 int HarfBuzzShaper::determineWordBreakSpacing()
407 int wordBreakSpacing = m_wordSpacingAdjustment;
410 int toPad = roundf(m_padPerWordBreak + m_padError);
411 m_padError += m_padPerWordBreak - toPad;
413 if (m_padding < toPad)
416 wordBreakSpacing += toPad;
418 return wordBreakSpacing;
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)
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
433 unsigned numWordEnds = 0;
435 for (unsigned i = 0; i < m_normalizedBufferLength; i++) {
436 if (isWordEnd(m_normalizedBuffer.get(), i))
441 m_padPerWordBreak = m_padding / numWordEnds;
443 m_padPerWordBreak = 0;
447 void HarfBuzzShaper::setDrawRange(int from, int to)
449 ASSERT_WITH_SECURITY_IMPLICATION(from >= 0);
450 ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length());
455 void HarfBuzzShaper::setFontFeatures()
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);
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
471 case FontDescription::NoneKerning:
472 m_features.append(description.orientation() == Vertical ? noVkrn : noKern);
474 case FontDescription::AutoKerning:
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);
485 case FontDescription::EnabledLigaturesState:
486 // liga and clig are on by default
488 case FontDescription::NormalLigaturesState:
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
496 case FontDescription::EnabledLigaturesState:
497 m_features.append(dlig);
499 case FontDescription::NormalLigaturesState:
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
507 case FontDescription::EnabledLigaturesState:
508 m_features.append(hlig);
510 case FontDescription::NormalLigaturesState:
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);
518 case FontDescription::EnabledLigaturesState:
519 // calt is on by default
521 case FontDescription::NormalLigaturesState:
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()) {
530 m_features.append(hwid);
533 m_features.append(twid);
536 m_features.append(qwid);
542 FontFeatureSettings* settings = description.featureSettings();
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();
553 feature.end = static_cast<unsigned>(-1);
554 m_features.append(feature);
558 bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer)
560 if (!createHarfBuzzRuns())
564 if (!shapeHarfBuzzRuns())
567 if (!RuntimeEnabledFeatures::subpixelFontScalingEnabled())
568 m_totalWidth = roundf(m_totalWidth);
570 if (m_harfBuzzRuns.last()->hasGlyphToCharacterIndexes()
571 && glyphBuffer && !fillGlyphBuffer(glyphBuffer))
577 FloatPoint HarfBuzzShaper::adjustStartPoint(const FloatPoint& point)
579 return point + m_startOffset;
582 static inline int handleMultipleUChar(
584 unsigned clusterLength,
585 const SimpleFontData* currentFontData,
586 const UChar* currentCharacterPosition,
587 const UChar* markCharactersEnd,
588 const UChar* normalizedBufferEnd)
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))
598 markLength += nextCharacterLength;
599 markCharactersEnd += nextCharacterLength;
602 if (currentFontData->canRenderCombiningCharacterSequence(currentCharacterPosition, markCharactersEnd - currentCharacterPosition)) {
609 struct CandidateRun {
613 const SimpleFontData* fontData;
617 static inline bool collectCandidateRuns(const UChar* normalizedBuffer,
618 size_t bufferLength, const Font* font, Vector<CandidateRun>* runs, bool isSpaceNormalize)
620 const UChar* normalizedBufferEnd = normalizedBuffer + bufferLength;
621 SurrogatePairAwareTextIterator iterator(normalizedBuffer, 0, bufferLength, bufferLength);
623 unsigned clusterLength = 0;
624 unsigned startIndexOfCurrentRun = 0;
626 if (!iterator.consume(character, clusterLength))
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))
636 const UChar* currentCharacterPosition = iterator.characters();
637 const SimpleFontData* currentFontData = nextFontData;
638 UScriptCode currentScript = nextScript;
640 UChar32 lastCharacter = character;
641 for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) {
642 if (Character::treatAsZeroWidthSpace(character))
645 int length = handleMultipleUChar(character, clusterLength, currentFontData, currentCharacterPosition, iterator.characters() + clusterLength, normalizedBufferEnd);
647 clusterLength = length;
651 nextFontData = font->glyphDataForCharacter(character, false, isSpaceNormalize).fontData;
652 nextScript = uscript_getScript(character, &errorCode);
653 if (U_FAILURE(errorCode))
655 if (lastCharacter == zeroWidthJoiner)
656 currentFontData = nextFontData;
657 if ((nextFontData != currentFontData) || ((currentScript != nextScript) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, currentScript))))
659 currentCharacterPosition = iterator.characters();
660 lastCharacter = character;
663 CandidateRun run = { character, startIndexOfCurrentRun, iterator.currentCharacter(), currentFontData, currentScript };
666 startIndexOfCurrentRun = iterator.currentCharacter();
667 } while (iterator.consume(character, clusterLength));
672 static inline bool matchesAdjacentRun(UScriptCode* scriptExtensions, int length,
673 CandidateRun& adjacentRun)
675 for (int i = 0; i < length; i++) {
676 if (scriptExtensions[i] == adjacentRun.script)
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)
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)
691 if (i > 0 && matchesAdjacentRun(scriptExtensions, extensionsLength, runs[i - 1])) {
692 run.script = runs[i - 1].script;
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])) {
706 static inline void resolveRunBasedOnScriptValue(Vector<CandidateRun>& runs,
707 CandidateRun& run, size_t i, size_t length, size_t& nextResolvedRun)
709 if (run.script != USCRIPT_COMMON)
712 if (i > 0 && runs[i - 1].script != USCRIPT_COMMON) {
713 run.script = runs[i - 1].script;
717 for (size_t j = i + 1; j < length; j++) {
718 if (runs[j].script != USCRIPT_COMMON
719 && runs[j].script != USCRIPT_INHERITED) {
726 static inline bool resolveCandidateRuns(Vector<CandidateRun>& runs)
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];
736 if (run.script == USCRIPT_INHERITED)
737 run.script = i > 0 ? runs[i - 1].script : USCRIPT_COMMON;
739 int extensionsLength = uscript_getScriptExtensions(run.character,
740 scriptExtensions, sizeof(scriptExtensions), &errorCode);
741 if (U_FAILURE(errorCode))
744 resolveRunBasedOnScriptExtensions(runs, run, i, length,
745 scriptExtensions, extensionsLength, nextResolvedRun);
746 resolveRunBasedOnScriptValue(runs, run, i, length,
748 for (size_t j = i; j < nextResolvedRun; j++)
749 runs[j].script = runs[nextResolvedRun].script;
751 i = std::max(i, nextResolvedRun);
756 bool HarfBuzzShaper::createHarfBuzzRuns()
758 Vector<CandidateRun> candidateRuns;
759 if (!collectCandidateRuns(m_normalizedBuffer.get(),
760 m_normalizedBufferLength, m_font, &candidateRuns, m_run.normalizeSpace()))
763 if (!resolveCandidateRuns(candidateRuns))
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)
774 lastMatchingRun = candidateRuns[i];
776 addHarfBuzzRun(run.start, lastMatchingRun.end, run.fontData, run.script);
778 return !m_harfBuzzRuns.isEmpty();
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)
785 if (UNLIKELY(script == USCRIPT_INVALID_CODE))
786 return HB_SCRIPT_INVALID;
788 return hb_script_from_string(uscript_getShortName(script), -1);
791 static inline hb_direction_t TextDirectionToHBDirection(TextDirection dir)
793 return dir == RTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
797 void HarfBuzzShaper::addHarfBuzzRun(unsigned startCharacter,
798 unsigned endCharacter, const SimpleFontData* fontData,
801 ASSERT(endCharacter > startCharacter);
802 ASSERT(script != USCRIPT_INVALID_CODE);
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)));
811 static const uint16_t* toUint16(const UChar* src)
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);
820 bool HarfBuzzShaper::shapeHarfBuzzRuns()
822 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy);
824 HarfBuzzRunCache& runCache = harfBuzzRunCache();
825 const FontDescription& fontDescription = m_font->fontDescription();
826 const String& localeString = fontDescription.locale();
827 CString locale = localeString.latin1();
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())
836 FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tFontData->platformData());
837 HarfBuzzFace* face = platformData->harfBuzzFace();
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());
845 const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex();
846 std::wstring key(src, src + currentRun->numCharacters());
848 CachedShapingResults* cachedResults = runCache.find(key);
850 if (cachedResults->dir == currentRun->direction() && cachedResults->font == *m_font && cachedResults->locale == localeString) {
851 currentRun->applyShapeResult(cachedResults->buffer);
852 setGlyphPositionsForHarfBuzzRun(currentRun, cachedResults->buffer);
854 hb_buffer_clear_contents(harfBuzzBuffer.get());
856 runCache.moveToBack(cachedResults);
861 runCache.remove(cachedResults);
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);
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());
874 hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(m_normalizedBuffer.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun->numCharacters());
877 if (fontDescription.orientation() == Vertical)
878 face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get());
880 HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_destroy);
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());
886 runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_font, currentRun->direction(), localeString));
888 harfBuzzBuffer.set(hb_buffer_create());
894 void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer)
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);
900 if (!currentRun->hasGlyphToCharacterIndexes()) {
901 // FIXME: https://crbug.com/337886
902 ASSERT_NOT_REACHED();
906 unsigned numGlyphs = currentRun->numGlyphs();
907 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes();
908 float totalAdvance = 0;
909 FloatPoint glyphOrigin;
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);
919 unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i].cluster;
920 bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster;
923 glyphToCharacterIndexes[i] = glyphInfos[i].cluster;
925 if (isClusterEnd && !Character::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex]))
926 spacing += m_letterSpacing;
928 if (isClusterEnd && isWordEnd(m_normalizedBuffer.get(), currentCharacterIndex))
929 spacing += determineWordBreakSpacing();
931 if (currentFontData->isZeroWidthSpaceGlyph(glyph)) {
932 currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0);
938 // In RTL, spacing should be added to left side of glyphs.
941 offsetX += m_letterSpacing;
944 currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY);
946 FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph);
947 glyphBounds.move(glyphOrigin.x(), glyphOrigin.y());
948 m_glyphBoundingBox.unite(glyphBounds);
949 glyphOrigin += FloatSize(advance + offsetX, offsetY);
951 totalAdvance += advance;
953 currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0);
954 m_totalWidth += currentRun->width();
957 void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun, FloatPoint& firstOffsetOfNextRun)
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();
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));
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));
984 void HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun)
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.
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;
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.
1005 clusterStart = currentRun->startIndex() + currentRun->numCharacters();
1007 clusterStart = currentRun->startIndex() + glyphToCharacterIndexes[0];
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];
1016 uint16_t clusterEnd;
1018 clusterEnd = currentCharacterIndex;
1020 clusterEnd = isRunEnd ? currentRun->startIndex() + currentRun->numCharacters() : currentRun->startIndex() + glyphToCharacterIndexes[i + 1];
1022 graphemesInCluster = countGraphemesInCluster(m_normalizedBuffer.get(), m_normalizedBufferLength, clusterStart, clusterEnd);
1023 if (!graphemesInCluster || !clusterAdvance)
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);
1032 clusterStart = clusterEnd;
1038 bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer)
1040 unsigned numRuns = m_harfBuzzRuns.size();
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
1049 FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfBuzzRuns[runIndex - 1]->offsets()[0];
1050 if (m_forTextEmphasis == ForTextEmphasis)
1051 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun);
1053 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun);
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
1063 FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoint() : m_harfBuzzRuns[runIndex + 1]->offsets()[0];
1064 if (m_forTextEmphasis == ForTextEmphasis)
1065 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun);
1067 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun);
1070 return glyphBuffer->size();
1073 int HarfBuzzShaper::offsetForPosition(float targetX)
1075 int charactersSoFar = 0;
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;
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;
1099 charactersSoFar += m_harfBuzzRuns[i]->numCharacters();
1104 return charactersSoFar;
1107 FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to)
1112 bool foundFromX = false;
1113 bool foundToX = false;
1116 currentX = m_totalWidth;
1117 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) {
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;
1125 from -= numCharacters;
1127 if (!foundToX && to >= 0 && to < numCharacters) {
1128 toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX;
1131 to -= numCharacters;
1133 if (foundFromX && foundToX)
1136 currentX += m_harfBuzzRuns[i]->width();
1139 // The position in question might be just after the text.
1143 toX = m_run.rtl() ? 0 : m_totalWidth;
1146 return Font::pixelSnappedSelectionRect(
1147 point.x() + fromX, point.x() + toX,
1151 return Font::pixelSnappedSelectionRect(
1152 point.x() + toX, point.x() + fromX,
1156 } // namespace blink