| Index: Source/core/platform/graphics/harfbuzz/HarfBuzzShaper.cpp
|
| diff --git a/Source/core/platform/graphics/harfbuzz/HarfBuzzShaper.cpp b/Source/core/platform/graphics/harfbuzz/HarfBuzzShaper.cpp
|
| deleted file mode 100644
|
| index c0d30992c9dc9a639656aef205a7a9275c0f3ee4..0000000000000000000000000000000000000000
|
| --- a/Source/core/platform/graphics/harfbuzz/HarfBuzzShaper.cpp
|
| +++ /dev/null
|
| @@ -1,895 +0,0 @@
|
| -/*
|
| - * Copyright (c) 2012 Google Inc. All rights reserved.
|
| - * Copyright (C) 2013 BlackBerry Limited. All rights reserved.
|
| - *
|
| - * Redistribution and use in source and binary forms, with or without
|
| - * modification, are permitted provided that the following conditions are
|
| - * met:
|
| - *
|
| - * * Redistributions of source code must retain the above copyright
|
| - * notice, this list of conditions and the following disclaimer.
|
| - * * Redistributions in binary form must reproduce the above
|
| - * copyright notice, this list of conditions and the following disclaimer
|
| - * in the documentation and/or other materials provided with the
|
| - * distribution.
|
| - * * Neither the name of Google Inc. nor the names of its
|
| - * contributors may be used to endorse or promote products derived from
|
| - * this software without specific prior written permission.
|
| - *
|
| - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| - */
|
| -
|
| -#include "config.h"
|
| -#include "core/platform/graphics/harfbuzz/HarfBuzzShaper.h"
|
| -
|
| -#include "RuntimeEnabledFeatures.h"
|
| -#include "core/platform/graphics/Font.h"
|
| -#include "core/platform/graphics/harfbuzz/HarfBuzzFace.h"
|
| -#include "hb-icu.h"
|
| -#include "platform/text/SurrogatePairAwareTextIterator.h"
|
| -#include "wtf/MathExtras.h"
|
| -#include "wtf/unicode/Unicode.h"
|
| -#include <unicode/normlzr.h>
|
| -#include <unicode/uchar.h>
|
| -
|
| -#include <list>
|
| -#include <map>
|
| -#include <string>
|
| -
|
| -namespace WebCore {
|
| -
|
| -template<typename T>
|
| -class HarfBuzzScopedPtr {
|
| -public:
|
| - typedef void (*DestroyFunction)(T*);
|
| -
|
| - HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy)
|
| - : m_ptr(ptr)
|
| - , m_destroy(destroy)
|
| - {
|
| - ASSERT(m_destroy);
|
| - }
|
| - ~HarfBuzzScopedPtr()
|
| - {
|
| - if (m_ptr)
|
| - (*m_destroy)(m_ptr);
|
| - }
|
| -
|
| - T* get() { return m_ptr; }
|
| - void set(T* ptr) { m_ptr = ptr; }
|
| -private:
|
| - T* m_ptr;
|
| - DestroyFunction m_destroy;
|
| -};
|
| -
|
| -
|
| -static const unsigned cHarfBuzzCacheMaxSize = 256;
|
| -
|
| -struct CachedShapingResultsLRUNode;
|
| -struct CachedShapingResults;
|
| -typedef std::map<std::wstring, CachedShapingResults*> CachedShapingResultsMap;
|
| -typedef std::list<CachedShapingResultsLRUNode*> CachedShapingResultsLRU;
|
| -
|
| -struct CachedShapingResults {
|
| - CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_direction_t runDir);
|
| - ~CachedShapingResults();
|
| -
|
| - hb_buffer_t* buffer;
|
| - Font font;
|
| - hb_direction_t dir;
|
| - CachedShapingResultsLRU::iterator lru;
|
| -};
|
| -
|
| -struct CachedShapingResultsLRUNode {
|
| - CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry);
|
| - ~CachedShapingResultsLRUNode();
|
| -
|
| - CachedShapingResultsMap::iterator entry;
|
| -};
|
| -
|
| -CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* fontData, hb_direction_t dirData)
|
| - : buffer(harfBuzzBuffer)
|
| - , font(*fontData)
|
| - , dir(dirData)
|
| -{
|
| -}
|
| -
|
| -CachedShapingResults::~CachedShapingResults()
|
| -{
|
| - hb_buffer_destroy(buffer);
|
| -}
|
| -
|
| -CachedShapingResultsLRUNode::CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry)
|
| - : entry(cacheEntry)
|
| -{
|
| -}
|
| -
|
| -CachedShapingResultsLRUNode::~CachedShapingResultsLRUNode()
|
| -{
|
| -}
|
| -
|
| -class HarfBuzzRunCache {
|
| -public:
|
| - HarfBuzzRunCache();
|
| - ~HarfBuzzRunCache();
|
| -
|
| - CachedShapingResults* find(const std::wstring& key) const;
|
| - void remove(CachedShapingResults* node);
|
| - void moveToBack(CachedShapingResults* node);
|
| - bool insert(const std::wstring& key, CachedShapingResults* run);
|
| -
|
| -private:
|
| - CachedShapingResultsMap m_harfBuzzRunMap;
|
| - CachedShapingResultsLRU m_harfBuzzRunLRU;
|
| -};
|
| -
|
| -
|
| -HarfBuzzRunCache::HarfBuzzRunCache()
|
| -{
|
| -}
|
| -
|
| -HarfBuzzRunCache::~HarfBuzzRunCache()
|
| -{
|
| - for (CachedShapingResultsMap::iterator it = m_harfBuzzRunMap.begin(); it != m_harfBuzzRunMap.end(); ++it)
|
| - delete it->second;
|
| - for (CachedShapingResultsLRU::iterator it = m_harfBuzzRunLRU.begin(); it != m_harfBuzzRunLRU.end(); ++it)
|
| - delete *it;
|
| -}
|
| -
|
| -bool HarfBuzzRunCache::insert(const std::wstring& key, CachedShapingResults* data)
|
| -{
|
| - std::pair<CachedShapingResultsMap::iterator, bool> results =
|
| - m_harfBuzzRunMap.insert(CachedShapingResultsMap::value_type(key, data));
|
| -
|
| - if (!results.second)
|
| - return false;
|
| -
|
| - CachedShapingResultsLRUNode* node = new CachedShapingResultsLRUNode(results.first);
|
| -
|
| - m_harfBuzzRunLRU.push_back(node);
|
| - data->lru = --m_harfBuzzRunLRU.end();
|
| -
|
| - if (m_harfBuzzRunMap.size() > cHarfBuzzCacheMaxSize) {
|
| - CachedShapingResultsLRUNode* lru = m_harfBuzzRunLRU.front();
|
| - CachedShapingResults* foo = lru->entry->second;
|
| - m_harfBuzzRunMap.erase(lru->entry);
|
| - m_harfBuzzRunLRU.pop_front();
|
| - delete foo;
|
| - delete lru;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -inline CachedShapingResults* HarfBuzzRunCache::find(const std::wstring& key) const
|
| -{
|
| - CachedShapingResultsMap::const_iterator it = m_harfBuzzRunMap.find(key);
|
| -
|
| - return it != m_harfBuzzRunMap.end() ? it->second : 0;
|
| -}
|
| -
|
| -inline void HarfBuzzRunCache::remove(CachedShapingResults* node)
|
| -{
|
| - CachedShapingResultsLRUNode* lruNode = *node->lru;
|
| -
|
| - m_harfBuzzRunLRU.erase(node->lru);
|
| - m_harfBuzzRunMap.erase(lruNode->entry);
|
| - delete lruNode;
|
| - delete node;
|
| -}
|
| -
|
| -inline void HarfBuzzRunCache::moveToBack(CachedShapingResults* node)
|
| -{
|
| - CachedShapingResultsLRUNode* lruNode = *node->lru;
|
| - m_harfBuzzRunLRU.erase(node->lru);
|
| - m_harfBuzzRunLRU.push_back(lruNode);
|
| - node->lru = --m_harfBuzzRunLRU.end();
|
| -}
|
| -
|
| -HarfBuzzRunCache& harfBuzzRunCache()
|
| -{
|
| - DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ());
|
| - return globalHarfBuzzRunCache;
|
| -}
|
| -
|
| -static inline float harfBuzzPositionToFloat(hb_position_t value)
|
| -{
|
| - return static_cast<float>(value) / (1 << 16);
|
| -}
|
| -
|
| -inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_t script)
|
| - : m_fontData(fontData)
|
| - , m_startIndex(startIndex)
|
| - , m_numCharacters(numCharacters)
|
| - , m_numGlyphs(0)
|
| - , m_direction(direction)
|
| - , m_script(script)
|
| - , m_width(0)
|
| -{
|
| -}
|
| -
|
| -inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const HarfBuzzRun& rhs)
|
| - : m_fontData(rhs.m_fontData)
|
| - , m_startIndex(rhs.m_startIndex)
|
| - , m_numCharacters(rhs.m_numCharacters)
|
| - , m_numGlyphs(rhs.m_numGlyphs)
|
| - , m_direction(rhs.m_direction)
|
| - , m_script(rhs.m_script)
|
| - , m_glyphs(rhs.m_glyphs)
|
| - , m_advances(rhs.m_advances)
|
| - , m_glyphToCharacterIndexes(rhs.m_glyphToCharacterIndexes)
|
| - , m_offsets(rhs.m_offsets)
|
| - , m_width(rhs.m_width)
|
| -{
|
| -}
|
| -
|
| -HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun()
|
| -{
|
| -}
|
| -
|
| -inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzBuffer)
|
| -{
|
| - m_numGlyphs = hb_buffer_get_length(harfBuzzBuffer);
|
| - m_glyphs.resize(m_numGlyphs);
|
| - m_advances.resize(m_numGlyphs);
|
| - m_glyphToCharacterIndexes.resize(m_numGlyphs);
|
| - m_offsets.resize(m_numGlyphs);
|
| -}
|
| -
|
| -inline void HarfBuzzShaper::HarfBuzzRun::copyShapeResultAndGlyphPositions(const HarfBuzzRun& run)
|
| -{
|
| - m_numGlyphs = run.m_numGlyphs;
|
| - m_glyphs = run.m_glyphs;
|
| - m_advances = run.m_advances;
|
| - m_glyphToCharacterIndexes = run.m_glyphToCharacterIndexes;
|
| - m_offsets = run.m_offsets;
|
| - m_width = run.m_width;
|
| -}
|
| -
|
| -inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY)
|
| -{
|
| - m_glyphs[index] = glyphId;
|
| - m_advances[index] = advance;
|
| - m_offsets[index] = FloatPoint(offsetX, offsetY);
|
| -}
|
| -
|
| -int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX)
|
| -{
|
| - ASSERT(targetX <= m_width);
|
| - float currentX = 0;
|
| - float currentAdvance = m_advances[0];
|
| - unsigned glyphIndex = 0;
|
| -
|
| - // Sum up advances that belong to a character.
|
| - while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1])
|
| - currentAdvance += m_advances[++glyphIndex];
|
| - currentAdvance = currentAdvance / 2.0;
|
| - if (targetX <= currentAdvance)
|
| - return rtl() ? m_numCharacters : 0;
|
| -
|
| - ++glyphIndex;
|
| - while (glyphIndex < m_numGlyphs) {
|
| - unsigned prevCharacterIndex = m_glyphToCharacterIndexes[glyphIndex - 1];
|
| - float prevAdvance = currentAdvance;
|
| - currentAdvance = m_advances[glyphIndex];
|
| - while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1])
|
| - currentAdvance += m_advances[++glyphIndex];
|
| - currentAdvance = currentAdvance / 2.0;
|
| - float nextX = currentX + prevAdvance + currentAdvance;
|
| - if (currentX <= targetX && targetX <= nextX)
|
| - return rtl() ? prevCharacterIndex : m_glyphToCharacterIndexes[glyphIndex];
|
| - currentX = nextX;
|
| - prevAdvance = currentAdvance;
|
| - ++glyphIndex;
|
| - }
|
| -
|
| - return rtl() ? 0 : m_numCharacters;
|
| -}
|
| -
|
| -float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset)
|
| -{
|
| - ASSERT(offset < m_numCharacters);
|
| - unsigned glyphIndex = 0;
|
| - float position = 0;
|
| - if (rtl()) {
|
| - while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] > offset) {
|
| - position += m_advances[glyphIndex];
|
| - ++glyphIndex;
|
| - }
|
| - // For RTL, we need to return the right side boundary of the character.
|
| - // Add advance of glyphs which are part of the character.
|
| - while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1]) {
|
| - position += m_advances[glyphIndex];
|
| - ++glyphIndex;
|
| - }
|
| - position += m_advances[glyphIndex];
|
| - } else {
|
| - while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] < offset) {
|
| - position += m_advances[glyphIndex];
|
| - ++glyphIndex;
|
| - }
|
| - }
|
| - return position;
|
| -}
|
| -
|
| -static void normalizeCharacters(const TextRun& run, unsigned length, UChar* destination, unsigned* destinationLength)
|
| -{
|
| - unsigned position = 0;
|
| - bool error = false;
|
| - const UChar* source;
|
| - String stringFor8BitRun;
|
| - if (run.is8Bit()) {
|
| - stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length());
|
| - source = stringFor8BitRun.characters16();
|
| - } else
|
| - source = run.characters16();
|
| -
|
| - *destinationLength = 0;
|
| - while (position < length) {
|
| - UChar32 character;
|
| - U16_NEXT(source, position, length, character);
|
| - // Don't normalize tabs as they are not treated as spaces for word-end.
|
| - if (Font::treatAsSpace(character) && character != '\t')
|
| - character = ' ';
|
| - else if (Font::treatAsZeroWidthSpaceInComplexScript(character))
|
| - character = zeroWidthSpace;
|
| - U16_APPEND(destination, *destinationLength, length, character, error);
|
| - ASSERT_UNUSED(error, !error);
|
| - }
|
| -}
|
| -
|
| -HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run)
|
| - : m_font(font)
|
| - , m_normalizedBufferLength(0)
|
| - , m_run(run)
|
| - , m_wordSpacingAdjustment(font->wordSpacing())
|
| - , m_padding(0)
|
| - , m_padPerWordBreak(0)
|
| - , m_padError(0)
|
| - , m_letterSpacing(font->letterSpacing())
|
| - , m_fromIndex(0)
|
| - , m_toIndex(m_run.length())
|
| -{
|
| - m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]);
|
| - normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), &m_normalizedBufferLength);
|
| - setPadding(m_run.expansion());
|
| - setFontFeatures();
|
| -}
|
| -
|
| -static void normalizeSpacesAndMirrorChars(const UChar* source, unsigned length, UChar* destination, unsigned* destinationLength, HarfBuzzShaper::NormalizeMode normalizeMode)
|
| -{
|
| - unsigned position = 0;
|
| - bool error = false;
|
| - // Iterate characters in source and mirror character if needed.
|
| - *destinationLength = 0;
|
| - while (position < length) {
|
| - UChar32 character;
|
| - U16_NEXT(source, position, length, character);
|
| - // Don't normalize tabs as they are not treated as spaces for word-end
|
| - if (Font::treatAsSpace(character) && character != '\t')
|
| - character = ' ';
|
| - else if (Font::treatAsZeroWidthSpace(character))
|
| - character = zeroWidthSpace;
|
| - else if (normalizeMode == HarfBuzzShaper::NormalizeMirrorChars)
|
| - character = u_charMirror(character);
|
| - U16_APPEND(destination, *destinationLength, length, character, error);
|
| - ASSERT_UNUSED(error, !error);
|
| - }
|
| -}
|
| -
|
| -void HarfBuzzShaper::setNormalizedBuffer(NormalizeMode normalizeMode)
|
| -{
|
| - // Normalize the text run in three ways:
|
| - // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks
|
| - // (U+0300..) are used in the run. This conversion is necessary since most OpenType
|
| - // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in
|
| - // their GSUB tables.
|
| - //
|
| - // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since
|
| - // the API returns FALSE (= not normalized) for complex runs that don't require NFC
|
| - // normalization (e.g., Arabic text). Unless the run contains the diacritical marks,
|
| - // HarfBuzz will do the same thing for us using the GSUB table.
|
| - // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs
|
| - // for characters like '\n' otherwise.
|
| - // 3) Convert mirrored characters such as parenthesis for rtl text.
|
| -
|
| - // Convert to NFC form if the text has diacritical marks.
|
| - icu::UnicodeString normalizedString;
|
| - UErrorCode error = U_ZERO_ERROR;
|
| -
|
| - const UChar* runCharacters;
|
| - String stringFor8BitRun;
|
| - if (m_run.is8Bit()) {
|
| - stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length());
|
| - runCharacters = stringFor8BitRun.characters16();
|
| - } else
|
| - runCharacters = m_run.characters16();
|
| -
|
| - for (int i = 0; i < m_run.length(); ++i) {
|
| - UChar ch = runCharacters[i];
|
| - if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) {
|
| - icu::Normalizer::normalize(icu::UnicodeString(runCharacters,
|
| - m_run.length()), UNORM_NFC, 0 /* no options */,
|
| - normalizedString, error);
|
| - if (U_FAILURE(error))
|
| - normalizedString.remove();
|
| - break;
|
| - }
|
| - }
|
| -
|
| - const UChar* sourceText;
|
| - unsigned sourceLength;
|
| - if (normalizedString.isEmpty()) {
|
| - sourceLength = m_run.length();
|
| - sourceText = runCharacters;
|
| - } else {
|
| - sourceLength = normalizedString.length();
|
| - sourceText = normalizedString.getBuffer();
|
| - }
|
| -
|
| - m_normalizedBuffer = adoptArrayPtr(new UChar[sourceLength + 1]);
|
| - normalizeSpacesAndMirrorChars(sourceText, sourceLength, m_normalizedBuffer.get(), &m_normalizedBufferLength, normalizeMode);
|
| -}
|
| -
|
| -bool HarfBuzzShaper::isWordEnd(unsigned index)
|
| -{
|
| - // This could refer a high-surrogate, but should work.
|
| - return index && isCodepointSpace(m_normalizedBuffer[index]);
|
| -}
|
| -
|
| -int HarfBuzzShaper::determineWordBreakSpacing()
|
| -{
|
| - int wordBreakSpacing = m_wordSpacingAdjustment;
|
| -
|
| - if (m_padding > 0) {
|
| - int toPad = roundf(m_padPerWordBreak + m_padError);
|
| - m_padError += m_padPerWordBreak - toPad;
|
| -
|
| - if (m_padding < toPad)
|
| - toPad = m_padding;
|
| - m_padding -= toPad;
|
| - wordBreakSpacing += toPad;
|
| - }
|
| - return wordBreakSpacing;
|
| -}
|
| -
|
| -// setPadding sets a number of pixels to be distributed across the TextRun.
|
| -// WebKit uses this to justify text.
|
| -void HarfBuzzShaper::setPadding(int padding)
|
| -{
|
| - m_padding = padding;
|
| - m_padError = 0;
|
| - if (!m_padding)
|
| - return;
|
| -
|
| - // If we have padding to distribute, then we try to give an equal
|
| - // amount to each space. The last space gets the smaller amount, if
|
| - // any.
|
| - unsigned numWordEnds = 0;
|
| -
|
| - for (unsigned i = 0; i < m_normalizedBufferLength; i++) {
|
| - if (isWordEnd(i))
|
| - numWordEnds++;
|
| - }
|
| -
|
| - if (numWordEnds)
|
| - m_padPerWordBreak = m_padding / numWordEnds;
|
| - else
|
| - m_padPerWordBreak = 0;
|
| -}
|
| -
|
| -
|
| -void HarfBuzzShaper::setDrawRange(int from, int to)
|
| -{
|
| - ASSERT_WITH_SECURITY_IMPLICATION(from >= 0);
|
| - ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length());
|
| - m_fromIndex = from;
|
| - m_toIndex = to;
|
| -}
|
| -
|
| -void HarfBuzzShaper::setFontFeatures()
|
| -{
|
| - const FontDescription& description = m_font->fontDescription();
|
| - if (description.orientation() == Vertical) {
|
| - static hb_feature_t vert = { HarfBuzzFace::vertTag, 1, 0, static_cast<unsigned>(-1) };
|
| - static hb_feature_t vrt2 = { HarfBuzzFace::vrt2Tag, 1, 0, static_cast<unsigned>(-1) };
|
| - m_features.append(vert);
|
| - m_features.append(vrt2);
|
| - }
|
| -
|
| - static hb_feature_t noKern = { HB_TAG('k', 'e', 'r', 'n'), 0, 0, static_cast<unsigned>(-1) };
|
| - static hb_feature_t noVkrn = { HB_TAG('v', 'k', 'r', 'n'), 0, 0, static_cast<unsigned>(-1) };
|
| - switch (description.kerning()) {
|
| - case FontDescription::NormalKerning:
|
| - // kern/vkrn are enabled by default
|
| - break;
|
| - case FontDescription::NoneKerning:
|
| - m_features.append(description.orientation() == Vertical ? noVkrn : noKern);
|
| - break;
|
| - case FontDescription::AutoKerning:
|
| - break;
|
| - }
|
| -
|
| - FontFeatureSettings* settings = description.featureSettings();
|
| - if (!settings)
|
| - return;
|
| -
|
| - unsigned numFeatures = settings->size();
|
| - for (unsigned i = 0; i < numFeatures; ++i) {
|
| - hb_feature_t feature;
|
| - const AtomicString& tag = settings->at(i).tag();
|
| - feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]);
|
| - feature.value = settings->at(i).value();
|
| - feature.start = 0;
|
| - feature.end = static_cast<unsigned>(-1);
|
| - m_features.append(feature);
|
| - }
|
| -}
|
| -
|
| -bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer)
|
| -{
|
| - if (!collectHarfBuzzRuns())
|
| - return false;
|
| -
|
| - m_totalWidth = 0;
|
| - // WebKit doesn't set direction when calulating widths. Leave the direction setting to
|
| - // HarfBuzz when we are calculating widths (except when directionalOverride() is set).
|
| - if (!shapeHarfBuzzRuns(glyphBuffer || m_run.directionalOverride()))
|
| - return false;
|
| -
|
| - if (!RuntimeEnabledFeatures::subpixelFontScalingEnabled())
|
| - m_totalWidth = roundf(m_totalWidth);
|
| -
|
| - if (glyphBuffer && !fillGlyphBuffer(glyphBuffer))
|
| - return false;
|
| -
|
| - return true;
|
| -}
|
| -
|
| -FloatPoint HarfBuzzShaper::adjustStartPoint(const FloatPoint& point)
|
| -{
|
| - return point + m_startOffset;
|
| -}
|
| -
|
| -bool HarfBuzzShaper::collectHarfBuzzRuns()
|
| -{
|
| - const UChar* normalizedBufferEnd = m_normalizedBuffer.get() + m_normalizedBufferLength;
|
| - SurrogatePairAwareTextIterator iterator(m_normalizedBuffer.get(), 0, m_normalizedBufferLength, m_normalizedBufferLength);
|
| - UChar32 character;
|
| - unsigned clusterLength = 0;
|
| - unsigned startIndexOfCurrentRun = 0;
|
| - if (!iterator.consume(character, clusterLength))
|
| - return false;
|
| -
|
| - const SimpleFontData* nextFontData = m_font->glyphDataForCharacter(character, false).fontData;
|
| - UErrorCode errorCode = U_ZERO_ERROR;
|
| - UScriptCode nextScript = uscript_getScript(character, &errorCode);
|
| - if (U_FAILURE(errorCode))
|
| - return false;
|
| -
|
| - do {
|
| - const UChar* currentCharacterPosition = iterator.characters();
|
| - const SimpleFontData* currentFontData = nextFontData;
|
| - UScriptCode currentScript = nextScript;
|
| -
|
| - for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) {
|
| - if (Font::treatAsZeroWidthSpace(character))
|
| - continue;
|
| -
|
| - if (U_GET_GC_MASK(character) & U_GC_M_MASK) {
|
| - int markLength = clusterLength;
|
| - const UChar* markCharactersEnd = iterator.characters() + clusterLength;
|
| - while (markCharactersEnd < normalizedBufferEnd) {
|
| - UChar32 nextCharacter;
|
| - int nextCharacterLength = 0;
|
| - U16_NEXT(markCharactersEnd, nextCharacterLength, normalizedBufferEnd - markCharactersEnd, nextCharacter);
|
| - if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
|
| - break;
|
| - markLength += nextCharacterLength;
|
| - markCharactersEnd += nextCharacterLength;
|
| - }
|
| -
|
| - if (currentFontData->canRenderCombiningCharacterSequence(currentCharacterPosition, markCharactersEnd - currentCharacterPosition)) {
|
| - clusterLength = markLength;
|
| - continue;
|
| - }
|
| - }
|
| -
|
| - nextFontData = m_font->glyphDataForCharacter(character, false).fontData;
|
| - nextScript = uscript_getScript(character, &errorCode);
|
| - if (U_FAILURE(errorCode))
|
| - return false;
|
| - if ((nextFontData != currentFontData) || ((currentScript != nextScript) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, currentScript))))
|
| - break;
|
| - if (nextScript == USCRIPT_INHERITED)
|
| - nextScript = currentScript;
|
| - currentCharacterPosition = iterator.characters();
|
| - }
|
| - unsigned numCharactersOfCurrentRun = iterator.currentCharacter() - startIndexOfCurrentRun;
|
| - hb_script_t script = hb_icu_script_to_script(currentScript);
|
| - m_harfBuzzRuns.append(HarfBuzzRun::create(currentFontData, startIndexOfCurrentRun, numCharactersOfCurrentRun, m_run.direction(), script));
|
| - currentFontData = nextFontData;
|
| - startIndexOfCurrentRun = iterator.currentCharacter();
|
| - } while (iterator.consume(character, clusterLength));
|
| -
|
| - return !m_harfBuzzRuns.isEmpty();
|
| -}
|
| -
|
| -static const uint16_t* toUint16(const UChar* src)
|
| -{
|
| - // FIXME: This relies on undefined behavior however it works on the
|
| - // current versions of all compilers we care about and avoids making
|
| - // a copy of the string.
|
| - COMPILE_ASSERT(sizeof(UChar) == sizeof(uint16_t), UChar_is_the_same_size_as_uint16_t);
|
| - return reinterpret_cast<const uint16_t*>(src);
|
| -}
|
| -
|
| -bool HarfBuzzShaper::shapeHarfBuzzRuns(bool shouldSetDirection)
|
| -{
|
| - HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy);
|
| -
|
| - hb_buffer_set_unicode_funcs(harfBuzzBuffer.get(), hb_icu_get_unicode_funcs());
|
| - HarfBuzzRunCache& runCache = harfBuzzRunCache();
|
| -
|
| - for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) {
|
| - unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i;
|
| - HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get();
|
| - const SimpleFontData* currentFontData = currentRun->fontData();
|
| - if (currentFontData->isSVGFont())
|
| - return false;
|
| -
|
| - FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tFontData->platformData());
|
| - HarfBuzzFace* face = platformData->harfBuzzFace();
|
| - if (!face)
|
| - return false;
|
| -
|
| - hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script());
|
| - if (shouldSetDirection)
|
| - hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
|
| - else
|
| - // Leaving direction to HarfBuzz to guess is *really* bad, but will do for now.
|
| - hb_buffer_guess_segment_properties(harfBuzzBuffer.get());
|
| -
|
| - hb_segment_properties_t props;
|
| - hb_buffer_get_segment_properties(harfBuzzBuffer.get(), &props);
|
| -
|
| - const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex();
|
| - std::wstring key(src, src + currentRun->numCharacters());
|
| -
|
| - CachedShapingResults* cachedResults = runCache.find(key);
|
| - if (cachedResults) {
|
| - if (cachedResults->dir == props.direction && cachedResults->font == *m_font) {
|
| - currentRun->applyShapeResult(cachedResults->buffer);
|
| - setGlyphPositionsForHarfBuzzRun(currentRun, cachedResults->buffer);
|
| -
|
| - hb_buffer_reset(harfBuzzBuffer.get());
|
| -
|
| - runCache.moveToBack(cachedResults);
|
| -
|
| - continue;
|
| - }
|
| -
|
| - runCache.remove(cachedResults);
|
| - }
|
| -
|
| - // Add a space as pre-context to the buffer. This prevents showing dotted-circle
|
| - // for combining marks at the beginning of runs.
|
| - static const uint16_t preContext = ' ';
|
| - hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0);
|
| -
|
| - if (m_font->isSmallCaps() && u_islower(m_normalizedBuffer[currentRun->startIndex()])) {
|
| - String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()).upper();
|
| - currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).fontData;
|
| - ASSERT(!upperText.is8Bit()); // m_normalizedBuffer is 16 bit, therefore upperText is 16 bit, even after we call makeUpper().
|
| - hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(upperText.characters16()), currentRun->numCharacters(), 0, currentRun->numCharacters());
|
| - } else {
|
| - hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(m_normalizedBuffer.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun->numCharacters());
|
| - }
|
| -
|
| - if (m_font->fontDescription().orientation() == Vertical)
|
| - face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get());
|
| -
|
| - HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_destroy);
|
| -
|
| - hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty() ? 0 : m_features.data(), m_features.size());
|
| - currentRun->applyShapeResult(harfBuzzBuffer.get());
|
| - setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get());
|
| -
|
| - runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_font, props.direction));
|
| -
|
| - harfBuzzBuffer.set(hb_buffer_create());
|
| - hb_buffer_set_unicode_funcs(harfBuzzBuffer.get(), hb_icu_get_unicode_funcs());
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer)
|
| -{
|
| - const SimpleFontData* currentFontData = currentRun->fontData();
|
| - hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0);
|
| - hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzzBuffer, 0);
|
| -
|
| - unsigned numGlyphs = currentRun->numGlyphs();
|
| - uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes();
|
| - float totalAdvance = 0;
|
| -
|
| - // HarfBuzz returns the shaping result in visual order. We need not to flip for RTL.
|
| - for (size_t i = 0; i < numGlyphs; ++i) {
|
| - bool runEnd = i + 1 == numGlyphs;
|
| - uint16_t glyph = glyphInfos[i].codepoint;
|
| - float offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset);
|
| - float offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset);
|
| - float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance);
|
| -
|
| - unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i].cluster;
|
| - bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster;
|
| - float spacing = 0;
|
| -
|
| - glyphToCharacterIndexes[i] = glyphInfos[i].cluster;
|
| -
|
| - if (isClusterEnd && !Font::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex]))
|
| - spacing += m_letterSpacing;
|
| -
|
| - if (isClusterEnd && isWordEnd(currentCharacterIndex))
|
| - spacing += determineWordBreakSpacing();
|
| -
|
| - if (currentFontData->isZeroWidthSpaceGlyph(glyph)) {
|
| - currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0);
|
| - continue;
|
| - }
|
| -
|
| - advance += spacing;
|
| - if (m_run.rtl()) {
|
| - // In RTL, spacing should be added to left side of glyphs.
|
| - offsetX += spacing;
|
| - if (!isClusterEnd)
|
| - offsetX += m_letterSpacing;
|
| - }
|
| -
|
| - currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY);
|
| -
|
| - totalAdvance += advance;
|
| - }
|
| - currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0);
|
| - m_totalWidth += currentRun->width();
|
| -}
|
| -
|
| -void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun, FloatPoint& firstOffsetOfNextRun)
|
| -{
|
| - FloatPoint* offsets = currentRun->offsets();
|
| - uint16_t* glyphs = currentRun->glyphs();
|
| - float* advances = currentRun->advances();
|
| - unsigned numGlyphs = currentRun->numGlyphs();
|
| - uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes();
|
| -
|
| - for (unsigned i = 0; i < numGlyphs; ++i) {
|
| - uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i];
|
| - FloatPoint& currentOffset = offsets[i];
|
| - FloatPoint& nextOffset = (i == numGlyphs - 1) ? firstOffsetOfNextRun : offsets[i + 1];
|
| - float glyphAdvanceX = advances[i] + nextOffset.x() - currentOffset.x();
|
| - float glyphAdvanceY = nextOffset.y() - currentOffset.y();
|
| - if (m_run.rtl()) {
|
| - if (currentCharacterIndex >= m_toIndex)
|
| - m_startOffset.move(glyphAdvanceX, glyphAdvanceY);
|
| - else if (currentCharacterIndex >= m_fromIndex)
|
| - glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY));
|
| - } else {
|
| - if (currentCharacterIndex < m_fromIndex)
|
| - m_startOffset.move(glyphAdvanceX, glyphAdvanceY);
|
| - else if (currentCharacterIndex < m_toIndex)
|
| - glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY));
|
| - }
|
| - }
|
| -}
|
| -
|
| -bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer)
|
| -{
|
| - unsigned numRuns = m_harfBuzzRuns.size();
|
| - if (m_run.rtl()) {
|
| - m_startOffset = m_harfBuzzRuns.last()->offsets()[0];
|
| - for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) {
|
| - HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get();
|
| - FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfBuzzRuns[runIndex - 1]->offsets()[0];
|
| - fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun);
|
| - }
|
| - } else {
|
| - m_startOffset = m_harfBuzzRuns.first()->offsets()[0];
|
| - for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) {
|
| - HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get();
|
| - FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoint() : m_harfBuzzRuns[runIndex + 1]->offsets()[0];
|
| - fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun);
|
| - }
|
| - }
|
| - return glyphBuffer->size();
|
| -}
|
| -
|
| -int HarfBuzzShaper::offsetForPosition(float targetX)
|
| -{
|
| - int charactersSoFar = 0;
|
| - float currentX = 0;
|
| -
|
| - if (m_run.rtl()) {
|
| - charactersSoFar = m_normalizedBufferLength;
|
| - for (int i = m_harfBuzzRuns.size() - 1; i >= 0; --i) {
|
| - charactersSoFar -= m_harfBuzzRuns[i]->numCharacters();
|
| - float nextX = currentX + m_harfBuzzRuns[i]->width();
|
| - float offsetForRun = targetX - currentX;
|
| - if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) {
|
| - // The x value in question is within this script run.
|
| - const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun);
|
| - return charactersSoFar + index;
|
| - }
|
| - currentX = nextX;
|
| - }
|
| - } else {
|
| - for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) {
|
| - float nextX = currentX + m_harfBuzzRuns[i]->width();
|
| - float offsetForRun = targetX - currentX;
|
| - if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) {
|
| - const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun);
|
| - return charactersSoFar + index;
|
| - }
|
| - charactersSoFar += m_harfBuzzRuns[i]->numCharacters();
|
| - currentX = nextX;
|
| - }
|
| - }
|
| -
|
| - return charactersSoFar;
|
| -}
|
| -
|
| -FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to)
|
| -{
|
| - float currentX = 0;
|
| - float fromX = 0;
|
| - float toX = 0;
|
| - bool foundFromX = false;
|
| - bool foundToX = false;
|
| -
|
| - if (m_run.rtl())
|
| - currentX = m_totalWidth;
|
| - for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) {
|
| - if (m_run.rtl())
|
| - currentX -= m_harfBuzzRuns[i]->width();
|
| - int numCharacters = m_harfBuzzRuns[i]->numCharacters();
|
| - if (!foundFromX && from >= 0 && from < numCharacters) {
|
| - fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX;
|
| - foundFromX = true;
|
| - } else
|
| - from -= numCharacters;
|
| -
|
| - if (!foundToX && to >= 0 && to < numCharacters) {
|
| - toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX;
|
| - foundToX = true;
|
| - } else
|
| - to -= numCharacters;
|
| -
|
| - if (foundFromX && foundToX)
|
| - break;
|
| - if (!m_run.rtl())
|
| - currentX += m_harfBuzzRuns[i]->width();
|
| - }
|
| -
|
| - // The position in question might be just after the text.
|
| - if (!foundFromX)
|
| - fromX = 0;
|
| - if (!foundToX)
|
| - toX = m_run.rtl() ? 0 : m_totalWidth;
|
| -
|
| - // Using floorf() and roundf() as the same as mac port.
|
| - if (fromX < toX)
|
| - return FloatRect(floorf(point.x() + fromX), point.y(), roundf(toX - fromX), height);
|
| - return FloatRect(floorf(point.x() + toX), point.y(), roundf(fromX - toX), height);
|
| -}
|
| -
|
| -} // namespace WebCore
|
|
|