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"
35 #include "RuntimeEnabledFeatures.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>
57 class HarfBuzzScopedPtr {
59 typedef void (*DestroyFunction)(T*);
61 HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy)
73 T* get() { return m_ptr; }
74 void set(T* ptr) { m_ptr = ptr; }
77 DestroyFunction m_destroy;
81 static const unsigned cHarfBuzzCacheMaxSize = 256;
83 struct CachedShapingResultsLRUNode;
84 struct CachedShapingResults;
85 typedef std::map<std::wstring, CachedShapingResults*> CachedShapingResultsMap;
86 typedef std::list<CachedShapingResultsLRUNode*> CachedShapingResultsLRU;
88 struct CachedShapingResults {
89 CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_direction_t runDir);
90 ~CachedShapingResults();
95 CachedShapingResultsLRU::iterator lru;
98 struct CachedShapingResultsLRUNode {
99 CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry);
100 ~CachedShapingResultsLRUNode();
102 CachedShapingResultsMap::iterator entry;
105 CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* fontData, hb_direction_t dirData)
106 : buffer(harfBuzzBuffer)
112 CachedShapingResults::~CachedShapingResults()
114 hb_buffer_destroy(buffer);
117 CachedShapingResultsLRUNode::CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry)
122 CachedShapingResultsLRUNode::~CachedShapingResultsLRUNode()
126 class HarfBuzzRunCache {
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);
137 CachedShapingResultsMap m_harfBuzzRunMap;
138 CachedShapingResultsLRU m_harfBuzzRunLRU;
142 HarfBuzzRunCache::HarfBuzzRunCache()
146 HarfBuzzRunCache::~HarfBuzzRunCache()
148 for (CachedShapingResultsMap::iterator it = m_harfBuzzRunMap.begin(); it != m_harfBuzzRunMap.end(); ++it)
150 for (CachedShapingResultsLRU::iterator it = m_harfBuzzRunLRU.begin(); it != m_harfBuzzRunLRU.end(); ++it)
154 bool HarfBuzzRunCache::insert(const std::wstring& key, CachedShapingResults* data)
156 std::pair<CachedShapingResultsMap::iterator, bool> results =
157 m_harfBuzzRunMap.insert(CachedShapingResultsMap::value_type(key, data));
162 CachedShapingResultsLRUNode* node = new CachedShapingResultsLRUNode(results.first);
164 m_harfBuzzRunLRU.push_back(node);
165 data->lru = --m_harfBuzzRunLRU.end();
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();
179 inline CachedShapingResults* HarfBuzzRunCache::find(const std::wstring& key) const
181 CachedShapingResultsMap::const_iterator it = m_harfBuzzRunMap.find(key);
183 return it != m_harfBuzzRunMap.end() ? it->second : 0;
186 inline void HarfBuzzRunCache::remove(CachedShapingResults* node)
188 CachedShapingResultsLRUNode* lruNode = *node->lru;
190 m_harfBuzzRunLRU.erase(node->lru);
191 m_harfBuzzRunMap.erase(lruNode->entry);
196 inline void HarfBuzzRunCache::moveToBack(CachedShapingResults* node)
198 CachedShapingResultsLRUNode* lruNode = *node->lru;
199 m_harfBuzzRunLRU.erase(node->lru);
200 m_harfBuzzRunLRU.push_back(lruNode);
201 node->lru = --m_harfBuzzRunLRU.end();
204 HarfBuzzRunCache& harfBuzzRunCache()
206 DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ());
207 return globalHarfBuzzRunCache;
210 static inline float harfBuzzPositionToFloat(hb_position_t value)
212 return static_cast<float>(value) / (1 << 16);
215 static inline unsigned countGraphemesInCluster(const UChar* normalizedBuffer, unsigned normalizedBufferLength, uint16_t startIndex, uint16_t endIndex)
217 if (startIndex > endIndex) {
218 uint16_t tempIndex = startIndex;
219 startIndex = endIndex;
220 endIndex = tempIndex;
222 uint16_t length = endIndex - startIndex;
223 ASSERT(static_cast<unsigned>(startIndex + length) <= normalizedBufferLength);
224 TextBreakIterator* cursorPosIterator = cursorMovementIterator(&normalizedBuffer[startIndex], length);
226 int cursorPos = cursorPosIterator->current();
227 int numGraphemes = -1;
228 while (0 <= cursorPos) {
229 cursorPos = cursorPosIterator->next();
232 return numGraphemes < 0 ? 0 : numGraphemes;
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)
240 , m_direction(direction)
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)
261 HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun()
265 inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzBuffer)
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);
274 inline void HarfBuzzShaper::HarfBuzzRun::copyShapeResultAndGlyphPositions(const HarfBuzzRun& run)
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;
284 inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY)
286 m_glyphs[index] = glyphId;
287 m_advances[index] = advance;
288 m_offsets[index] = FloatPoint(offsetX, offsetY);
291 int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX)
293 ASSERT(targetX <= m_width);
295 float currentAdvance = m_advances[0];
296 unsigned glyphIndex = 0;
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;
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];
320 return rtl() ? 0 : m_numCharacters;
323 float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset)
325 ASSERT(offset < m_numCharacters);
326 unsigned glyphIndex = 0;
329 while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] > offset) {
330 position += m_advances[glyphIndex];
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];
339 position += m_advances[glyphIndex];
341 while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] < offset) {
342 position += m_advances[glyphIndex];
349 static void normalizeCharacters(const TextRun& run, unsigned length, UChar* destination, unsigned* destinationLength)
351 unsigned position = 0;
354 String stringFor8BitRun;
356 stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length());
357 source = stringFor8BitRun.characters16();
359 source = run.characters16();
361 *destinationLength = 0;
362 while (position < length) {
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')
368 else if (Character::treatAsZeroWidthSpaceInComplexScript(character))
369 character = zeroWidthSpace;
370 U16_APPEND(destination, *destinationLength, length, character, error);
371 ASSERT_UNUSED(error, !error);
375 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmphasisOrNot forTextEmphasis)
377 , m_normalizedBufferLength(0)
379 , m_wordSpacingAdjustment(font->fontDescription().wordSpacing())
381 , m_padPerWordBreak(0)
383 , m_letterSpacing(font->fontDescription().letterSpacing())
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())
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());
395 bool HarfBuzzShaper::isWordEnd(unsigned index)
397 // This could refer a high-surrogate, but should work.
398 return index && isCodepointSpace(m_normalizedBuffer[index]);
401 int HarfBuzzShaper::determineWordBreakSpacing()
403 int wordBreakSpacing = m_wordSpacingAdjustment;
406 int toPad = roundf(m_padPerWordBreak + m_padError);
407 m_padError += m_padPerWordBreak - toPad;
409 if (m_padding < toPad)
412 wordBreakSpacing += toPad;
414 return wordBreakSpacing;
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)
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
429 unsigned numWordEnds = 0;
431 for (unsigned i = 0; i < m_normalizedBufferLength; i++) {
437 m_padPerWordBreak = m_padding / numWordEnds;
439 m_padPerWordBreak = 0;
443 void HarfBuzzShaper::setDrawRange(int from, int to)
445 ASSERT_WITH_SECURITY_IMPLICATION(from >= 0);
446 ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length());
451 void HarfBuzzShaper::setFontFeatures()
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);
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
467 case FontDescription::NoneKerning:
468 m_features.append(description.orientation() == Vertical ? noVkrn : noKern);
470 case FontDescription::AutoKerning:
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);
481 case FontDescription::EnabledLigaturesState:
482 // liga and clig are on by default
484 case FontDescription::NormalLigaturesState:
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
492 case FontDescription::EnabledLigaturesState:
493 m_features.append(dlig);
495 case FontDescription::NormalLigaturesState:
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
503 case FontDescription::EnabledLigaturesState:
504 m_features.append(hlig);
506 case FontDescription::NormalLigaturesState:
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);
514 case FontDescription::EnabledLigaturesState:
515 // calt is on by default
517 case FontDescription::NormalLigaturesState:
521 FontFeatureSettings* settings = description.featureSettings();
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();
532 feature.end = static_cast<unsigned>(-1);
533 m_features.append(feature);
537 bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer)
539 if (!createHarfBuzzRuns())
543 if (!shapeHarfBuzzRuns())
546 if (!RuntimeEnabledFeatures::subpixelFontScalingEnabled())
547 m_totalWidth = roundf(m_totalWidth);
549 if (m_harfBuzzRuns.last()->hasGlyphToCharacterIndexes()
550 && glyphBuffer && !fillGlyphBuffer(glyphBuffer))
556 FloatPoint HarfBuzzShaper::adjustStartPoint(const FloatPoint& point)
558 return point + m_startOffset;
561 static inline int handleMultipleUChar(
563 unsigned clusterLength,
564 const SimpleFontData* currentFontData,
565 const UChar* currentCharacterPosition,
566 const UChar* markCharactersEnd,
567 const UChar* normalizedBufferEnd)
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))
577 markLength += nextCharacterLength;
578 markCharactersEnd += nextCharacterLength;
581 if (currentFontData->canRenderCombiningCharacterSequence(currentCharacterPosition, markCharactersEnd - currentCharacterPosition)) {
588 struct CandidateRun {
592 const SimpleFontData* fontData;
596 static inline bool collectCandidateRuns(const UChar* normalizedBuffer,
597 size_t bufferLength, const Font* font, Vector<CandidateRun>* runs)
599 const UChar* normalizedBufferEnd = normalizedBuffer + bufferLength;
600 SurrogatePairAwareTextIterator iterator(normalizedBuffer, 0, bufferLength, bufferLength);
602 unsigned clusterLength = 0;
603 unsigned startIndexOfCurrentRun = 0;
604 if (!iterator.consume(character, clusterLength))
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))
614 const UChar* currentCharacterPosition = iterator.characters();
615 const SimpleFontData* currentFontData = nextFontData;
616 UScriptCode currentScript = nextScript;
618 UChar32 lastCharacter = character;
619 for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) {
620 if (Character::treatAsZeroWidthSpace(character))
623 int length = handleMultipleUChar(character, clusterLength, currentFontData, currentCharacterPosition, iterator.characters() + clusterLength, normalizedBufferEnd);
625 clusterLength = length;
629 nextFontData = font->glyphDataForCharacter(character, false).fontData;
630 nextScript = uscript_getScript(character, &errorCode);
631 if (U_FAILURE(errorCode))
633 if (lastCharacter == zeroWidthJoiner)
634 currentFontData = nextFontData;
635 if ((nextFontData != currentFontData) || ((currentScript != nextScript) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, currentScript))))
637 currentCharacterPosition = iterator.characters();
638 lastCharacter = character;
641 CandidateRun run = { character, startIndexOfCurrentRun, iterator.currentCharacter(), currentFontData, currentScript };
644 startIndexOfCurrentRun = iterator.currentCharacter();
645 } while (iterator.consume(character, clusterLength));
650 static inline bool matchesAdjacentRun(UScriptCode* scriptExtensions, int length,
651 CandidateRun& adjacentRun)
653 for (int i = 0; i < length; i++) {
654 if (scriptExtensions[i] == adjacentRun.script)
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)
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)
669 if (i > 0 && matchesAdjacentRun(scriptExtensions, extensionsLength, runs[i - 1])) {
670 run.script = runs[i - 1].script;
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])) {
684 static inline void resolveRunBasedOnScriptValue(Vector<CandidateRun>& runs,
685 CandidateRun& run, size_t i, size_t length, size_t& nextResolvedRun)
687 if (run.script != USCRIPT_COMMON)
690 if (i > 0 && runs[i - 1].script != USCRIPT_COMMON) {
691 run.script = runs[i - 1].script;
695 for (size_t j = i + 1; j < length; j++) {
696 if (runs[j].script != USCRIPT_COMMON
697 && runs[j].script != USCRIPT_INHERITED) {
704 static inline bool resolveCandidateRuns(Vector<CandidateRun>& runs)
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];
714 if (run.script == USCRIPT_INHERITED)
715 run.script = i > 0 ? runs[i - 1].script : USCRIPT_COMMON;
717 int extensionsLength = uscript_getScriptExtensions(run.character,
718 scriptExtensions, sizeof(scriptExtensions), &errorCode);
719 if (U_FAILURE(errorCode))
722 resolveRunBasedOnScriptExtensions(runs, run, i, length,
723 scriptExtensions, extensionsLength, nextResolvedRun);
724 resolveRunBasedOnScriptValue(runs, run, i, length,
726 for (size_t j = i; j < nextResolvedRun; j++)
727 runs[j].script = runs[nextResolvedRun].script;
729 i = std::max(i, nextResolvedRun);
734 bool HarfBuzzShaper::createHarfBuzzRuns()
736 Vector<CandidateRun> candidateRuns;
737 if (!collectCandidateRuns(m_normalizedBuffer.get(),
738 m_normalizedBufferLength, m_font, &candidateRuns))
741 if (!resolveCandidateRuns(candidateRuns))
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)
752 lastMatchingRun = candidateRuns[i];
754 addHarfBuzzRun(run.start, lastMatchingRun.end, run.fontData, run.script);
756 return !m_harfBuzzRuns.isEmpty();
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)
763 if (UNLIKELY(script == USCRIPT_INVALID_CODE))
764 return HB_SCRIPT_INVALID;
766 return hb_script_from_string(uscript_getShortName(script), -1);
770 void HarfBuzzShaper::addHarfBuzzRun(unsigned startCharacter,
771 unsigned endCharacter, const SimpleFontData* fontData,
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)));
781 static const uint16_t* toUint16(const UChar* src)
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);
790 bool HarfBuzzShaper::shapeHarfBuzzRuns()
792 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy);
794 HarfBuzzRunCache& runCache = harfBuzzRunCache();
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())
803 FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tFontData->platformData());
804 HarfBuzzFace* face = platformData->harfBuzzFace();
808 hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script());
809 hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
811 hb_segment_properties_t props;
812 hb_buffer_get_segment_properties(harfBuzzBuffer.get(), &props);
814 const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex();
815 std::wstring key(src, src + currentRun->numCharacters());
817 CachedShapingResults* cachedResults = runCache.find(key);
819 if (cachedResults->dir == props.direction && cachedResults->font == *m_font) {
820 currentRun->applyShapeResult(cachedResults->buffer);
821 setGlyphPositionsForHarfBuzzRun(currentRun, cachedResults->buffer);
823 hb_buffer_clear_contents(harfBuzzBuffer.get());
825 runCache.moveToBack(cachedResults);
830 runCache.remove(cachedResults);
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);
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());
843 hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(m_normalizedBuffer.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun->numCharacters());
846 if (m_font->fontDescription().orientation() == Vertical)
847 face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get());
849 HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_destroy);
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());
855 runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_font, props.direction));
857 harfBuzzBuffer.set(hb_buffer_create());
863 void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer)
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);
869 if (!currentRun->hasGlyphToCharacterIndexes()) {
870 // FIXME: https://crbug.com/337886
871 ASSERT_NOT_REACHED();
875 unsigned numGlyphs = currentRun->numGlyphs();
876 uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes();
877 float totalAdvance = 0;
878 FloatPoint glyphOrigin;
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);
888 unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i].cluster;
889 bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster;
892 glyphToCharacterIndexes[i] = glyphInfos[i].cluster;
894 if (isClusterEnd && !Character::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex]))
895 spacing += m_letterSpacing;
897 if (isClusterEnd && isWordEnd(currentCharacterIndex))
898 spacing += determineWordBreakSpacing();
900 if (currentFontData->isZeroWidthSpaceGlyph(glyph)) {
901 currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0);
907 // In RTL, spacing should be added to left side of glyphs.
910 offsetX += m_letterSpacing;
913 currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY);
915 FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph);
916 glyphBounds.move(glyphOrigin.x(), glyphOrigin.y());
917 m_glyphBoundingBox.unite(glyphBounds);
918 glyphOrigin += FloatSize(advance + offsetX, offsetY);
920 totalAdvance += advance;
922 currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0);
923 m_totalWidth += currentRun->width();
926 void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun, FloatPoint& firstOffsetOfNextRun)
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();
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));
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));
953 void HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun)
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.
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;
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.
974 clusterStart = currentRun->startIndex() + currentRun->numCharacters();
976 clusterStart = currentRun->startIndex() + glyphToCharacterIndexes[0];
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];
987 clusterEnd = currentCharacterIndex;
989 clusterEnd = isRunEnd ? currentRun->startIndex() + currentRun->numCharacters() : currentRun->startIndex() + glyphToCharacterIndexes[i + 1];
991 graphemesInCluster = countGraphemesInCluster(m_normalizedBuffer.get(), m_normalizedBufferLength, clusterStart, clusterEnd);
992 if (!graphemesInCluster || !clusterAdvance)
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));
1001 clusterStart = clusterEnd;
1007 bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer)
1009 unsigned numRuns = m_harfBuzzRuns.size();
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
1018 FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfBuzzRuns[runIndex - 1]->offsets()[0];
1019 if (m_forTextEmphasis == ForTextEmphasis)
1020 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun);
1022 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun);
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
1032 FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoint() : m_harfBuzzRuns[runIndex + 1]->offsets()[0];
1033 if (m_forTextEmphasis == ForTextEmphasis)
1034 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun);
1036 fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun);
1039 return glyphBuffer->size();
1042 int HarfBuzzShaper::offsetForPosition(float targetX)
1044 int charactersSoFar = 0;
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;
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;
1068 charactersSoFar += m_harfBuzzRuns[i]->numCharacters();
1073 return charactersSoFar;
1076 FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to)
1081 bool foundFromX = false;
1082 bool foundToX = false;
1085 currentX = m_totalWidth;
1086 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) {
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;
1094 from -= numCharacters;
1096 if (!foundToX && to >= 0 && to < numCharacters) {
1097 toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX;
1100 to -= numCharacters;
1102 if (foundFromX && foundToX)
1105 currentX += m_harfBuzzRuns[i]->width();
1108 // The position in question might be just after the text.
1112 toX = m_run.rtl() ? 0 : m_totalWidth;
1115 return Font::pixelSnappedSelectionRect(
1116 point.x() + fromX, point.x() + toX,
1120 return Font::pixelSnappedSelectionRect(
1121 point.x() + toX, point.x() + fromX,
1125 } // namespace WebCore