diff options
15 files changed, 833 insertions, 640 deletions
diff --git a/third_party/WebKit/Source/platform/blink_platform.gypi b/third_party/WebKit/Source/platform/blink_platform.gypi index e47addd..0aaf6ce 100644 --- a/third_party/WebKit/Source/platform/blink_platform.gypi +++ b/third_party/WebKit/Source/platform/blink_platform.gypi @@ -432,6 +432,11 @@ 'fonts/shaping/RunSegmenter.h', 'fonts/shaping/RunSegmenter.cpp', 'fonts/shaping/ShapeCache.h', + 'fonts/shaping/ShapeResult.cpp', + 'fonts/shaping/ShapeResult.h', + 'fonts/shaping/ShapeResultInlineHeaders.h', + 'fonts/shaping/ShapeResultTestInfo.cpp', + 'fonts/shaping/ShapeResultTestInfo.h', 'fonts/shaping/Shaper.cpp', 'fonts/shaping/Shaper.h', 'fonts/shaping/SimpleShaper.cpp', diff --git a/third_party/WebKit/Source/platform/fonts/Font.cpp b/third_party/WebKit/Source/platform/fonts/Font.cpp index 79f7842a..0d9f480 100644 --- a/third_party/WebKit/Source/platform/fonts/Font.cpp +++ b/third_party/WebKit/Source/platform/fonts/Font.cpp @@ -35,6 +35,7 @@ #include "platform/fonts/GlyphBuffer.h" #include "platform/fonts/GlyphPageTreeNode.h" #include "platform/fonts/SimpleFontData.h" +#include "platform/fonts/shaping/CachingWordShaper.h" #include "platform/fonts/shaping/HarfBuzzFace.h" #include "platform/fonts/shaping/HarfBuzzShaper.h" #include "platform/fonts/shaping/SimpleShaper.h" diff --git a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShapeIterator.h b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShapeIterator.h index d66080e3..65d2ee7 100644 --- a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShapeIterator.h +++ b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShapeIterator.h @@ -26,6 +26,7 @@ #ifndef CachingWordShapeIterator_h #define CachingWordShapeIterator_h +#include "platform/fonts/Font.h" #include "platform/fonts/SimpleFontData.h" #include "platform/fonts/shaping/CachingWordShapeIterator.h" #include "platform/fonts/shaping/HarfBuzzShaper.h" diff --git a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp index 51d2360..048cc5d 100644 --- a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp +++ b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp @@ -7,6 +7,7 @@ #include "platform/fonts/FontCache.h" #include "platform/fonts/GlyphBuffer.h" #include "platform/fonts/shaping/CachingWordShapeIterator.h" +#include "platform/fonts/shaping/ShapeResultTestInfo.h" #include <gtest/gtest.h> namespace blink { @@ -42,6 +43,11 @@ protected: hb_script_t script = HB_SCRIPT_INVALID; }; +static inline ShapeResultTestInfo* testInfo(RefPtr<ShapeResult>& result) +{ + return static_cast<ShapeResultTestInfo*>(result.get()); +} + TEST_F(CachingWordShaperTest, LatinLeftToRightByWord) { TextRun textRun(reinterpret_cast<const LChar*>("ABC DEF."), 8); @@ -49,19 +55,19 @@ TEST_F(CachingWordShaperTest, LatinLeftToRightByWord) RefPtr<ShapeResult> result; CachingWordShapeIterator iterator(cache, textRun, font); ASSERT_TRUE(iterator.next(&result)); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(3u, numGlyphs); EXPECT_EQ(HB_SCRIPT_LATIN, script); ASSERT_TRUE(iterator.next(&result)); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_COMMON, script); ASSERT_TRUE(iterator.next(&result)); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(4u, numGlyphs); EXPECT_EQ(HB_SCRIPT_LATIN, script); @@ -78,21 +84,21 @@ TEST_F(CachingWordShaperTest, CommonAccentLeftToRightByWord) RefPtr<ShapeResult> result; CachingWordShapeIterator iterator(cache, textRun, font); ASSERT_TRUE(iterator.next(&result)); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, offset + startIndex); EXPECT_EQ(3u, numGlyphs); EXPECT_EQ(HB_SCRIPT_COMMON, script); offset += result->numCharacters(); ASSERT_TRUE(iterator.next(&result)); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(3u, offset + startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_COMMON, script); offset += result->numCharacters(); ASSERT_TRUE(iterator.next(&result)); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(4u, offset + startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_COMMON, script); diff --git a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp index c118ab6..0678568 100644 --- a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp +++ b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp @@ -32,7 +32,6 @@ #include "config.h" #include "platform/fonts/shaping/HarfBuzzShaper.h" -#include "hb.h" #include "platform/Logging.h" #include "platform/fonts/Character.h" #include "platform/fonts/Font.h" @@ -41,549 +40,20 @@ #include "platform/fonts/UTF16TextIterator.h" #include "platform/fonts/shaping/HarfBuzzFace.h" #include "platform/fonts/shaping/RunSegmenter.h" +#include "platform/fonts/shaping/ShapeResultInlineHeaders.h" #include "platform/text/TextBreakIterator.h" #include "wtf/Compiler.h" #include "wtf/MathExtras.h" #include "wtf/text/Unicode.h" #include <algorithm> +#include <hb.h> #include <unicode/normlzr.h> #include <unicode/uchar.h> #include <unicode/uscript.h> namespace blink { -struct HarfBuzzRunGlyphData { - uint16_t glyph; - uint16_t characterIndex; - float advance; - FloatSize offset; -}; - -struct ShapeResult::RunInfo { - RunInfo(const SimpleFontData* font, hb_direction_t dir, hb_script_t script, - unsigned startIndex, unsigned numGlyphs, unsigned numCharacters) - : m_fontData(const_cast<SimpleFontData*>(font)), m_direction(dir), m_script(script) - , m_startIndex(startIndex), m_numCharacters(numCharacters) - , m_numGlyphs(numGlyphs), m_width(0.0f) - { - m_glyphData.resize(m_numGlyphs); - } - - bool rtl() const { return HB_DIRECTION_IS_BACKWARD(m_direction); } - float xPositionForVisualOffset(unsigned) const; - float xPositionForOffset(unsigned) const; - int characterIndexForXPosition(float) const; - void setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, - float offsetX, float offsetY); - - void addAdvance(unsigned index, float advance) - { - m_glyphData[index].advance += advance; - } - - size_t glyphToCharacterIndex(size_t i) const - { - return m_startIndex + m_glyphData[i].characterIndex; - } - - RefPtr<SimpleFontData> m_fontData; - hb_direction_t m_direction; - hb_script_t m_script; - Vector<HarfBuzzRunGlyphData> m_glyphData; - unsigned m_startIndex; - unsigned m_numCharacters; - unsigned m_numGlyphs; - float m_width; -}; - -float ShapeResult::RunInfo::xPositionForVisualOffset(unsigned offset) const -{ - ASSERT(offset < m_numCharacters); - if (rtl()) - offset = m_numCharacters - offset - 1; - return xPositionForOffset(offset); -} - -float ShapeResult::RunInfo::xPositionForOffset(unsigned offset) const -{ - ASSERT(offset <= m_numCharacters); - unsigned glyphIndex = 0; - float position = 0; - if (rtl()) { - while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterIndex > offset) { - position += m_glyphData[glyphIndex].advance; - ++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_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) { - position += m_glyphData[glyphIndex].advance; - ++glyphIndex; - } - position += m_glyphData[glyphIndex].advance; - } else { - while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterIndex < offset) { - position += m_glyphData[glyphIndex].advance; - ++glyphIndex; - } - } - return position; -} - -int ShapeResult::RunInfo::characterIndexForXPosition(float targetX) const -{ - ASSERT(targetX <= m_width); - float currentX = 0; - float currentAdvance = m_glyphData[0].advance; - unsigned glyphIndex = 0; - - // Sum up advances that belong to the first character. - while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) - currentAdvance += m_glyphData[++glyphIndex].advance; - currentAdvance = currentAdvance / 2.0; - if (targetX <= currentAdvance) - return rtl() ? m_numCharacters : 0; - - currentX = currentAdvance; - ++glyphIndex; - while (glyphIndex < m_numGlyphs) { - unsigned prevCharacterIndex = m_glyphData[glyphIndex - 1].characterIndex; - float prevAdvance = currentAdvance; - currentAdvance = m_glyphData[glyphIndex].advance; - while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) - currentAdvance += m_glyphData[++glyphIndex].advance; - currentAdvance = currentAdvance / 2.0; - float nextX = currentX + prevAdvance + currentAdvance; - if (currentX <= targetX && targetX <= nextX) - return rtl() ? prevCharacterIndex : m_glyphData[glyphIndex].characterIndex; - currentX = nextX; - ++glyphIndex; - } - - return rtl() ? 0 : m_numCharacters; -} - -void ShapeResult::RunInfo::setGlyphAndPositions(unsigned index, - uint16_t glyphId, float advance, float offsetX, float offsetY) -{ - HarfBuzzRunGlyphData& data = m_glyphData[index]; - data.glyph = glyphId; - data.advance = advance; - data.offset = FloatSize(offsetX, offsetY); -} - -ShapeResult::ShapeResult(const Font* font, unsigned numCharacters, TextDirection direction) - : m_width(0) - , m_primaryFont(const_cast<SimpleFontData*>(font->primaryFont())) - , m_numCharacters(numCharacters) - , m_numGlyphs(0) - , m_direction(direction) -{ -} - -ShapeResult::~ShapeResult() -{ -} - -static inline void addGlyphToBuffer(GlyphBuffer* glyphBuffer, float advance, - hb_direction_t direction, const SimpleFontData* fontData, - const HarfBuzzRunGlyphData& glyphData) -{ - FloatPoint startOffset = HB_DIRECTION_IS_HORIZONTAL(direction) - ? FloatPoint(advance, 0) - : FloatPoint(0, advance); - glyphBuffer->add(glyphData.glyph, fontData, startOffset + glyphData.offset); -} - -template<TextDirection direction> -float ShapeResult::fillGlyphBufferForRun(GlyphBuffer* glyphBuffer, - const RunInfo* run, float initialAdvance, unsigned from, unsigned to, - unsigned runOffset) -{ - if (!run) - return 0; - float advanceSoFar = initialAdvance; - unsigned numGlyphs = run->m_numGlyphs; - for (unsigned i = 0; i < numGlyphs; ++i) { - const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; - uint16_t currentCharacterIndex = run->m_startIndex + - glyphData.characterIndex + runOffset; - if ((direction == RTL && currentCharacterIndex >= to) - || (direction == LTR && currentCharacterIndex < from)) { - advanceSoFar += glyphData.advance; - } else if ((direction == RTL && currentCharacterIndex >= from) - || (direction == LTR && currentCharacterIndex < to)) { - addGlyphToBuffer(glyphBuffer, advanceSoFar, run->m_direction, - run->m_fontData.get(), glyphData); - advanceSoFar += glyphData.advance; - } - } - return advanceSoFar - initialAdvance; -} - -static inline unsigned countGraphemesInCluster(const UChar* str, - unsigned strLength, uint16_t startIndex, uint16_t endIndex) -{ - if (startIndex > endIndex) { - uint16_t tempIndex = startIndex; - startIndex = endIndex; - endIndex = tempIndex; - } - uint16_t length = endIndex - startIndex; - ASSERT(static_cast<unsigned>(startIndex + length) <= strLength); - TextBreakIterator* cursorPosIterator = cursorMovementIterator(&str[startIndex], length); - - int cursorPos = cursorPosIterator->current(); - int numGraphemes = -1; - while (0 <= cursorPos) { - cursorPos = cursorPosIterator->next(); - numGraphemes++; - } - return std::max(0, numGraphemes); -} - -static inline void addEmphasisMark(GlyphBuffer* buffer, - const GlyphData* emphasisData, FloatPoint glyphCenter, - float midGlyphOffset) -{ - ASSERT(buffer); - ASSERT(emphasisData); - - const SimpleFontData* emphasisFontData = emphasisData->fontData; - ASSERT(emphasisFontData); - - bool isVertical = emphasisFontData->platformData().isVerticalAnyUpright() - && emphasisFontData->verticalData(); - - if (!isVertical) { - buffer->add(emphasisData->glyph, emphasisFontData, - midGlyphOffset - glyphCenter.x()); - } else { - buffer->add(emphasisData->glyph, emphasisFontData, - FloatPoint(-glyphCenter.x(), midGlyphOffset - glyphCenter.y())); - } -} - -float ShapeResult::fillGlyphBufferForTextEmphasisRun(GlyphBuffer* glyphBuffer, - const RunInfo* run, const TextRun& textRun, const GlyphData* emphasisData, - float initialAdvance, unsigned from, unsigned to, unsigned runOffset) -{ - if (!run) - return 0; - - unsigned graphemesInCluster = 1; - float clusterAdvance = 0; - - FloatPoint glyphCenter = emphasisData->fontData-> - boundsForGlyph(emphasisData->glyph).center(); - - TextDirection direction = textRun.direction(); - - // A "cluster" in this context means a cluster as it is used by HarfBuzz: - // The minimal group of characters and corresponding glyphs, that cannot be broken - // down further from a text shaping point of view. - // A cluster can contain multiple glyphs and grapheme clusters, with mutually - // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clusters, - // then linearly split the sum of corresponding glyph advances by the number of - // grapheme clusters in order to find positions for emphasis mark drawing. - uint16_t clusterStart = direction == RTL - ? run->m_startIndex + run->m_numCharacters + runOffset - : run->glyphToCharacterIndex(0) + runOffset; - - float advanceSoFar = initialAdvance; - unsigned numGlyphs = run->m_numGlyphs; - for (unsigned i = 0; i < numGlyphs; ++i) { - const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; - uint16_t currentCharacterIndex = run->m_startIndex + glyphData.characterIndex + runOffset; - bool isRunEnd = (i + 1 == numGlyphs); - bool isClusterEnd = isRunEnd || (run->glyphToCharacterIndex(i + 1) + runOffset != currentCharacterIndex); - - if ((direction == RTL && currentCharacterIndex >= to) || (direction != RTL && currentCharacterIndex < from)) { - advanceSoFar += glyphData.advance; - direction == RTL ? --clusterStart : ++clusterStart; - continue; - } - - clusterAdvance += glyphData.advance; - - if (textRun.is8Bit()) { - float glyphAdvanceX = glyphData.advance; - if (Character::canReceiveTextEmphasis(textRun[currentCharacterIndex])) { - addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, advanceSoFar + glyphAdvanceX / 2); - } - advanceSoFar += glyphAdvanceX; - } else if (isClusterEnd) { - uint16_t clusterEnd; - if (direction == RTL) - clusterEnd = currentCharacterIndex; - else - clusterEnd = isRunEnd ? run->m_startIndex + run->m_numCharacters + runOffset : run->glyphToCharacterIndex(i + 1) + runOffset; - - graphemesInCluster = countGraphemesInCluster(textRun.characters16(), textRun.charactersLength(), clusterStart, clusterEnd); - if (!graphemesInCluster || !clusterAdvance) - continue; - - float glyphAdvanceX = clusterAdvance / graphemesInCluster; - for (unsigned j = 0; j < graphemesInCluster; ++j) { - // Do not put emphasis marks on space, separator, and control characters. - if (Character::canReceiveTextEmphasis(textRun[currentCharacterIndex])) - addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, advanceSoFar + glyphAdvanceX / 2); - advanceSoFar += glyphAdvanceX; - } - clusterStart = clusterEnd; - clusterAdvance = 0; - } - } - return advanceSoFar - initialAdvance; -} - -float ShapeResult::fillGlyphBuffer(Vector<RefPtr<ShapeResult>>& results, - GlyphBuffer* glyphBuffer, const TextRun& textRun, - unsigned from, unsigned to) -{ - float advance = 0; - if (textRun.rtl()) { - unsigned wordOffset = textRun.length(); - for (unsigned j = 0; j < results.size(); j++) { - unsigned resolvedIndex = results.size() - 1 - j; - RefPtr<ShapeResult>& wordResult = results[resolvedIndex]; - for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { - advance += wordResult->fillGlyphBufferForRun<RTL>(glyphBuffer, - wordResult->m_runs[i].get(), advance, from, to, - wordOffset - wordResult->numCharacters()); - } - wordOffset -= wordResult->numCharacters(); - } - } else { - unsigned wordOffset = 0; - for (unsigned j = 0; j < results.size(); j++) { - RefPtr<ShapeResult>& wordResult = results[j]; - for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { - advance += wordResult->fillGlyphBufferForRun<LTR>(glyphBuffer, - wordResult->m_runs[i].get(), advance, from, to, wordOffset); - } - wordOffset += wordResult->numCharacters(); - } - } - - return advance; -} - -float ShapeResult::fillGlyphBufferForTextEmphasis( - Vector<RefPtr<ShapeResult>>& results, GlyphBuffer* glyphBuffer, - const TextRun& textRun, const GlyphData* emphasisData, - unsigned from, unsigned to) -{ - float advance = 0; - unsigned wordOffset = textRun.rtl() ? textRun.length() : 0; - for (unsigned j = 0; j < results.size(); j++) { - unsigned resolvedIndex = textRun.rtl() ? results.size() - 1 - j : j; - RefPtr<ShapeResult>& wordResult = results[resolvedIndex]; - for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { - unsigned resolvedOffset = wordOffset - - (textRun.rtl() ? wordResult->numCharacters() : 0); - advance += wordResult->fillGlyphBufferForTextEmphasisRun( - glyphBuffer, wordResult->m_runs[i].get(), textRun, emphasisData, - advance, from, to, resolvedOffset); - } - wordOffset += wordResult->numCharacters() * (textRun.rtl() ? -1 : 1); - } - - return advance; -} - -FloatRect ShapeResult::selectionRect(Vector<RefPtr<ShapeResult>>& results, - TextDirection direction, float totalWidth, const FloatPoint& point, - int height, unsigned absoluteFrom, unsigned absoluteTo) -{ - float currentX = 0; - float fromX = 0; - float toX = 0; - bool foundFromX = false; - bool foundToX = false; - - if (direction == RTL) - currentX = totalWidth; - - // The absoluteFrom and absoluteTo arguments represent the start/end offset - // for the entire run, from/to are continuously updated to be relative to - // the current word (ShapeResult instance). - int from = absoluteFrom; - int to = absoluteTo; - - unsigned totalNumCharacters = 0; - for (unsigned j = 0; j < results.size(); j++) { - RefPtr<ShapeResult> result = results[j]; - if (direction == RTL) { - // Convert logical offsets to visual offsets, because results are in - // logical order while runs are in visual order. - if (!foundFromX && from >= 0 && static_cast<unsigned>(from) < result->numCharacters()) - from = result->numCharacters() - from - 1; - if (!foundToX && to >= 0 && static_cast<unsigned>(to) < result->numCharacters()) - to = result->numCharacters() - to - 1; - currentX -= result->width(); - } - for (unsigned i = 0; i < result->m_runs.size(); i++) { - if (!result->m_runs[i]) - continue; - ASSERT((direction == RTL) == result->m_runs[i]->rtl()); - int numCharacters = result->m_runs[i]->m_numCharacters; - if (!foundFromX && from >= 0 && from < numCharacters) { - fromX = result->m_runs[i]->xPositionForVisualOffset(from) + currentX; - foundFromX = true; - } else { - from -= numCharacters; - } - - if (!foundToX && to >= 0 && to < numCharacters) { - toX = result->m_runs[i]->xPositionForVisualOffset(to) + currentX; - foundToX = true; - } else { - to -= numCharacters; - } - - if (foundFromX && foundToX) - break; - currentX += result->m_runs[i]->m_width; - } - if (direction == RTL) - currentX -= result->width(); - totalNumCharacters += result->numCharacters(); - } - - // The position in question might be just after the text. - if (!foundFromX && absoluteFrom == totalNumCharacters) { - fromX = direction == RTL ? 0 : totalWidth; - foundFromX = true; - } - if (!foundToX && absoluteTo == totalNumCharacters) { - toX = direction == RTL ? 0 : totalWidth; - foundToX = true; - } - if (!foundFromX) - fromX = 0; - if (!foundToX) - toX = direction == RTL ? 0 : totalWidth; - - // None of our runs is part of the selection, possibly invalid arguments. - if (!foundToX && !foundFromX) - fromX = toX = 0; - if (fromX < toX) - return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); - return FloatRect(point.x() + toX, point.y(), fromX - toX, height); -} - -int ShapeResult::offsetForPosition(Vector<RefPtr<ShapeResult>>& results, - const TextRun& run, float targetX) -{ - unsigned totalOffset; - if (run.rtl()) { - totalOffset = run.length(); - for (unsigned i = results.size(); i; --i) { - const RefPtr<ShapeResult>& wordResult = results[i - 1]; - if (!wordResult) - continue; - totalOffset -= wordResult->numCharacters(); - if (targetX >= 0 && targetX <= wordResult->width()) { - int offsetForWord = wordResult->offsetForPosition(targetX); - return totalOffset + offsetForWord; - } - targetX -= wordResult->width(); - } - } else { - totalOffset = 0; - for (auto& wordResult : results) { - if (!wordResult) - continue; - int offsetForWord = wordResult->offsetForPosition(targetX); - ASSERT(offsetForWord >= 0); - totalOffset += offsetForWord; - if (targetX >= 0 && targetX <= wordResult->width()) - return totalOffset; - targetX -= wordResult->width(); - } - } - return totalOffset; -} - -int ShapeResult::offsetForPosition(float targetX) -{ - int charactersSoFar = 0; - float currentX = 0; - - if (m_direction == RTL) { - charactersSoFar = m_numCharacters; - for (unsigned i = 0; i < m_runs.size(); ++i) { - if (!m_runs[i]) - continue; - charactersSoFar -= m_runs[i]->m_numCharacters; - float nextX = currentX + m_runs[i]->m_width; - float offsetForRun = targetX - currentX; - if (offsetForRun >= 0 && offsetForRun <= m_runs[i]->m_width) { - // The x value in question is within this script run. - const unsigned index = m_runs[i]->characterIndexForXPosition(offsetForRun); - return charactersSoFar + index; - } - currentX = nextX; - } - } else { - for (unsigned i = 0; i < m_runs.size(); ++i) { - if (!m_runs[i]) - continue; - float nextX = currentX + m_runs[i]->m_width; - float offsetForRun = targetX - currentX; - if (offsetForRun >= 0 && offsetForRun <= m_runs[i]->m_width) { - const unsigned index = m_runs[i]->characterIndexForXPosition(offsetForRun); - return charactersSoFar + index; - } - charactersSoFar += m_runs[i]->m_numCharacters; - currentX = nextX; - } - } - - return charactersSoFar; -} - -void ShapeResult::fallbackFonts(HashSet<const SimpleFontData*>* fallback) const -{ - ASSERT(fallback); - ASSERT(m_primaryFont); - for (unsigned i = 0; i < m_runs.size(); ++i) { - if (m_runs[i] && m_runs[i]->m_fontData != m_primaryFont - && !m_runs[i]->m_fontData->isTextOrientationFallbackOf(m_primaryFont.get())) { - fallback->add(m_runs[i]->m_fontData.get()); - } - } -} - -unsigned ShapeResult::numberOfRunsForTesting() const -{ - return m_runs.size(); -} - -bool ShapeResult::runInfoForTesting(unsigned runIndex, unsigned& startIndex, - unsigned& numGlyphs, hb_script_t& script) -{ - if (runIndex < m_runs.size() && m_runs[runIndex]) { - startIndex = m_runs[runIndex]->m_startIndex; - numGlyphs = m_runs[runIndex]->m_numGlyphs; - script = m_runs[runIndex]->m_script; - return true; - } - return false; -} - -uint16_t ShapeResult::glyphForTesting(unsigned runIndex, size_t glyphIndex) -{ - return m_runs[runIndex]->m_glyphData[glyphIndex].glyph; -} - -float ShapeResult::advanceForTesting(unsigned runIndex, size_t glyphIndex) -{ - return m_runs[runIndex]->m_glyphData[glyphIndex].advance; -} - template<typename T> class HarfBuzzScopedPtr { public: diff --git a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.h b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.h index 71141f6..ea7817b 100644 --- a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.h +++ b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.h @@ -32,6 +32,7 @@ #define HarfBuzzShaper_h #include "hb.h" +#include "platform/fonts/shaping/ShapeResult.h" #include "platform/fonts/shaping/Shaper.h" #include "platform/geometry/FloatPoint.h" #include "platform/geometry/FloatRect.h" @@ -52,70 +53,6 @@ class GlyphBuffer; class SimpleFontData; class HarfBuzzShaper; -class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> { -public: - static PassRefPtr<ShapeResult> create(const Font* font, - unsigned numCharacters, TextDirection direction) - { - return adoptRef(new ShapeResult(font, numCharacters, direction)); - } - static PassRefPtr<ShapeResult> createForTabulationCharacters(const Font*, - const TextRun&, float positionOffset, unsigned count); - ~ShapeResult(); - - float width() { return m_width; } - FloatRect bounds() { return m_glyphBoundingBox; } - unsigned numCharacters() const { return m_numCharacters; } - void fallbackFonts(HashSet<const SimpleFontData*>*) const; - - static int offsetForPosition(Vector<RefPtr<ShapeResult>>&, - const TextRun&, float targetX); - static float fillGlyphBuffer(Vector<RefPtr<ShapeResult>>&, - GlyphBuffer*, const TextRun&, unsigned from, unsigned to); - static float fillGlyphBufferForTextEmphasis(Vector<RefPtr<ShapeResult>>&, - GlyphBuffer*, const TextRun&, const GlyphData* emphasisData, - unsigned from, unsigned to); - static FloatRect selectionRect(Vector<RefPtr<ShapeResult>>&, - TextDirection, float totalWidth, const FloatPoint&, int height, - unsigned from, unsigned to); - - unsigned numberOfRunsForTesting() const; - bool runInfoForTesting(unsigned runIndex, unsigned& startIndex, - unsigned& numGlyphs, hb_script_t&); - uint16_t glyphForTesting(unsigned runIndex, size_t glyphIndex); - float advanceForTesting(unsigned runIndex, size_t glyphIndex); - -private: - struct RunInfo; - - ShapeResult(const Font*, unsigned numCharacters, TextDirection); - - int offsetForPosition(float targetX); - template<TextDirection> - float fillGlyphBufferForRun(GlyphBuffer*, const RunInfo*, - float initialAdvance, unsigned from, unsigned to, unsigned runOffset); - - float fillGlyphBufferForTextEmphasisRun(GlyphBuffer*, const RunInfo*, - const TextRun&, const GlyphData*, float initialAdvance, - unsigned from, unsigned to, unsigned runOffset); - - float m_width; - FloatRect m_glyphBoundingBox; - Vector<OwnPtr<RunInfo>> m_runs; - RefPtr<SimpleFontData> m_primaryFont; - - unsigned m_numCharacters; - unsigned m_numGlyphs : 31; - - // Overall direction for the TextRun, dictates which order each individual - // sub run (represented by RunInfo structs in the m_runs vector) can have a - // different text direction. - unsigned m_direction : 1; - - friend class HarfBuzzShaper; -}; - - // Shaping text runs is split into several stages: Run segmentation, shaping the // initial segment, identify shaped and non-shaped sequences of the shaping // result, and processing sub-runs by trying to shape them with a fallback font diff --git a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaperTest.cpp b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaperTest.cpp index e09e2fa..2d239f4 100644 --- a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaperTest.cpp +++ b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaperTest.cpp @@ -8,6 +8,7 @@ #include "platform/fonts/Font.h" #include "platform/fonts/FontCache.h" #include "platform/fonts/GlyphPage.h" +#include "platform/fonts/shaping/ShapeResultTestInfo.h" #include "platform/text/TextRun.h" #include "wtf/Vector.h" #include <gtest/gtest.h> @@ -37,6 +38,10 @@ protected: hb_script_t script = HB_SCRIPT_INVALID; }; +static inline ShapeResultTestInfo* testInfo(RefPtr<ShapeResult>& result) +{ + return static_cast<ShapeResultTestInfo*>(result.get()); +} TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsLatin) { @@ -44,8 +49,8 @@ TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsLatin) HarfBuzzShaper shaper(font, latinCommon); RefPtr<ShapeResult> result = shaper.shapeResult(); - ASSERT_EQ(1u, result->numberOfRunsForTesting()); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_EQ(1u, testInfo(result)->numberOfRunsForTesting()); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(8u, numGlyphs); EXPECT_EQ(HB_SCRIPT_LATIN, script); @@ -57,8 +62,8 @@ TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsLeadingCommon) HarfBuzzShaper shaper(font, leadingCommon); RefPtr<ShapeResult> result = shaper.shapeResult(); - ASSERT_EQ(1u, result->numberOfRunsForTesting()); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_EQ(1u, testInfo(result)->numberOfRunsForTesting()); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(8u, numGlyphs); EXPECT_EQ(HB_SCRIPT_LATIN, script); @@ -83,8 +88,8 @@ TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsUnicodeVariants) HarfBuzzShaper shaper(font, run); RefPtr<ShapeResult> result = shaper.shapeResult(); - EXPECT_EQ(1u, result->numberOfRunsForTesting()) << test.name; - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)) << test.name; + EXPECT_EQ(1u, testInfo(result)->numberOfRunsForTesting()) << test.name; + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)) << test.name; EXPECT_EQ(0u, startIndex) << test.name; if (numGlyphs == 2) { // If the specified VS is not in the font, it's mapped to .notdef. @@ -93,9 +98,9 @@ TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsUnicodeVariants) // OpenType recommends Glyph ID 3 for a space; not a hard requirement though. // https://www.microsoft.com/typography/otspec/recom.htm #if !OS(MACOSX) - EXPECT_EQ(3u, result->glyphForTesting(0, 1)) << test.name; + EXPECT_EQ(3u, testInfo(result)->glyphForTesting(0, 1)) << test.name; #endif - EXPECT_EQ(0.f, result->advanceForTesting(0, 1)) << test.name; + EXPECT_EQ(0.f, testInfo(result)->advanceForTesting(0, 1)) << test.name; } else { EXPECT_EQ(1u, numGlyphs) << test.name; } @@ -110,13 +115,13 @@ TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsDevanagariCommon) HarfBuzzShaper shaper(font, devanagariCommonLatin); RefPtr<ShapeResult> result = shaper.shapeResult(); - ASSERT_EQ(2u, result->numberOfRunsForTesting()); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_EQ(2u, testInfo(result)->numberOfRunsForTesting()); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_DEVANAGARI, script); - ASSERT_TRUE(result->runInfoForTesting(1, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(1, startIndex, numGlyphs, script)); EXPECT_EQ(3u, startIndex); EXPECT_EQ(3u, numGlyphs); EXPECT_EQ(HB_SCRIPT_DEVANAGARI, script); @@ -129,18 +134,18 @@ TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsDevanagariCommonLatinCommon) HarfBuzzShaper shaper(font, devanagariCommonLatin); RefPtr<ShapeResult> result = shaper.shapeResult(); - ASSERT_EQ(3u, result->numberOfRunsForTesting()); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_EQ(3u, testInfo(result)->numberOfRunsForTesting()); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_DEVANAGARI, script); - ASSERT_TRUE(result->runInfoForTesting(1, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(1, startIndex, numGlyphs, script)); EXPECT_EQ(3u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_DEVANAGARI, script); - ASSERT_TRUE(result->runInfoForTesting(2, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(2, startIndex, numGlyphs, script)); EXPECT_EQ(4u, startIndex); EXPECT_EQ(3u, numGlyphs); EXPECT_EQ(HB_SCRIPT_LATIN, script); @@ -153,23 +158,23 @@ TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsArabicThaiHanLatin) HarfBuzzShaper shaper(font, mixed); RefPtr<ShapeResult> result = shaper.shapeResult(); - ASSERT_EQ(4u, result->numberOfRunsForTesting()); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_EQ(4u, testInfo(result)->numberOfRunsForTesting()); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(3u, numGlyphs); EXPECT_EQ(HB_SCRIPT_ARABIC, script); - ASSERT_TRUE(result->runInfoForTesting(1, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(1, startIndex, numGlyphs, script)); EXPECT_EQ(3u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_THAI, script); - ASSERT_TRUE(result->runInfoForTesting(2, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(2, startIndex, numGlyphs, script)); EXPECT_EQ(4u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_HAN, script); - ASSERT_TRUE(result->runInfoForTesting(3, startIndex, numGlyphs, script)); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(3, startIndex, numGlyphs, script)); EXPECT_EQ(5u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_LATIN, script); @@ -182,8 +187,8 @@ TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsArabic) HarfBuzzShaper shaper(font, arabic); RefPtr<ShapeResult> result = shaper.shapeResult(); - ASSERT_EQ(1u, result->numberOfRunsForTesting()); - ASSERT_TRUE(result->runInfoForTesting(0, startIndex, numGlyphs, script)); + ASSERT_EQ(1u, testInfo(result)->numberOfRunsForTesting()); + ASSERT_TRUE(testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(3u, numGlyphs); EXPECT_EQ(HB_SCRIPT_ARABIC, script); diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeCache.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeCache.h index ac8278c..9755ed3 100644 --- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeCache.h +++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeCache.h @@ -27,7 +27,7 @@ #ifndef ShapeCache_h #define ShapeCache_h -#include "platform/fonts/shaping/HarfBuzzShaper.h" +#include "platform/fonts/shaping/ShapeResult.h" #include "platform/text/TextRun.h" #include "wtf/Forward.h" #include "wtf/HashFunctions.h" @@ -40,7 +40,6 @@ namespace blink { class Font; class GlyphBuffer; class SimpleFontData; -class HarfBuzzShaper; struct ShapeCacheEntry { ShapeCacheEntry() @@ -171,10 +170,8 @@ private: value = &addResult.storedValue->value; } - // Cache hit: ramp up by sampling the next few words. - if (!isNewEntry) { + if (!isNewEntry) return value; - } if (m_singleCharMap.size() + m_shortStringMap.size() < s_maxSize) { return value; diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp new file mode 100644 index 0000000..1d015a7 --- /dev/null +++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp @@ -0,0 +1,502 @@ +/* + * 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 "platform/fonts/shaping/ShapeResult.h" + +#include "platform/fonts/Font.h" +#include "platform/fonts/GlyphBuffer.h" +#include "platform/fonts/shaping/ShapeResultInlineHeaders.h" +#include "platform/text/TextBreakIterator.h" + +namespace blink { + +float ShapeResult::RunInfo::xPositionForVisualOffset(unsigned offset) const +{ + ASSERT(offset < m_numCharacters); + if (rtl()) + offset = m_numCharacters - offset - 1; + return xPositionForOffset(offset); +} + +float ShapeResult::RunInfo::xPositionForOffset(unsigned offset) const +{ + ASSERT(offset <= m_numCharacters); + unsigned glyphIndex = 0; + float position = 0; + if (rtl()) { + while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterIndex > offset) { + position += m_glyphData[glyphIndex].advance; + ++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_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) { + position += m_glyphData[glyphIndex].advance; + ++glyphIndex; + } + position += m_glyphData[glyphIndex].advance; + } else { + while (glyphIndex < m_numGlyphs && m_glyphData[glyphIndex].characterIndex < offset) { + position += m_glyphData[glyphIndex].advance; + ++glyphIndex; + } + } + return position; +} + +int ShapeResult::RunInfo::characterIndexForXPosition(float targetX) const +{ + ASSERT(targetX <= m_width); + float currentX = 0; + float currentAdvance = m_glyphData[0].advance; + unsigned glyphIndex = 0; + + // Sum up advances that belong to the first character. + while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) + currentAdvance += m_glyphData[++glyphIndex].advance; + currentAdvance = currentAdvance / 2.0; + if (targetX <= currentAdvance) + return rtl() ? m_numCharacters : 0; + + currentX = currentAdvance; + ++glyphIndex; + while (glyphIndex < m_numGlyphs) { + unsigned prevCharacterIndex = m_glyphData[glyphIndex - 1].characterIndex; + float prevAdvance = currentAdvance; + currentAdvance = m_glyphData[glyphIndex].advance; + while (glyphIndex < m_numGlyphs - 1 && m_glyphData[glyphIndex].characterIndex == m_glyphData[glyphIndex + 1].characterIndex) + currentAdvance += m_glyphData[++glyphIndex].advance; + currentAdvance = currentAdvance / 2.0; + float nextX = currentX + prevAdvance + currentAdvance; + if (currentX <= targetX && targetX <= nextX) + return rtl() ? prevCharacterIndex : m_glyphData[glyphIndex].characterIndex; + currentX = nextX; + ++glyphIndex; + } + + return rtl() ? 0 : m_numCharacters; +} + +void ShapeResult::RunInfo::setGlyphAndPositions(unsigned index, + uint16_t glyphId, float advance, float offsetX, float offsetY) +{ + HarfBuzzRunGlyphData& data = m_glyphData[index]; + data.glyph = glyphId; + data.advance = advance; + data.offset = FloatSize(offsetX, offsetY); +} + +ShapeResult::ShapeResult(const Font* font, unsigned numCharacters, TextDirection direction) + : m_width(0) + , m_primaryFont(const_cast<SimpleFontData*>(font->primaryFont())) + , m_numCharacters(numCharacters) + , m_numGlyphs(0) + , m_direction(direction) +{ +} + +ShapeResult::~ShapeResult() +{ +} + +static inline void addGlyphToBuffer(GlyphBuffer* glyphBuffer, float advance, + hb_direction_t direction, const SimpleFontData* fontData, + const HarfBuzzRunGlyphData& glyphData) +{ + FloatPoint startOffset = HB_DIRECTION_IS_HORIZONTAL(direction) + ? FloatPoint(advance, 0) + : FloatPoint(0, advance); + glyphBuffer->add(glyphData.glyph, fontData, startOffset + glyphData.offset); +} + +template<TextDirection direction> +float ShapeResult::fillGlyphBufferForRun(GlyphBuffer* glyphBuffer, + const RunInfo* run, float initialAdvance, unsigned from, unsigned to, + unsigned runOffset) +{ + if (!run) + return 0; + float advanceSoFar = initialAdvance; + unsigned numGlyphs = run->m_numGlyphs; + for (unsigned i = 0; i < numGlyphs; ++i) { + const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; + uint16_t currentCharacterIndex = run->m_startIndex + + glyphData.characterIndex + runOffset; + if ((direction == RTL && currentCharacterIndex >= to) + || (direction == LTR && currentCharacterIndex < from)) { + advanceSoFar += glyphData.advance; + } else if ((direction == RTL && currentCharacterIndex >= from) + || (direction == LTR && currentCharacterIndex < to)) { + addGlyphToBuffer(glyphBuffer, advanceSoFar, run->m_direction, + run->m_fontData.get(), glyphData); + advanceSoFar += glyphData.advance; + } + } + return advanceSoFar - initialAdvance; +} + +static inline unsigned countGraphemesInCluster(const UChar* str, + unsigned strLength, uint16_t startIndex, uint16_t endIndex) +{ + if (startIndex > endIndex) { + uint16_t tempIndex = startIndex; + startIndex = endIndex; + endIndex = tempIndex; + } + uint16_t length = endIndex - startIndex; + ASSERT(static_cast<unsigned>(startIndex + length) <= strLength); + TextBreakIterator* cursorPosIterator = cursorMovementIterator(&str[startIndex], length); + + int cursorPos = cursorPosIterator->current(); + int numGraphemes = -1; + while (0 <= cursorPos) { + cursorPos = cursorPosIterator->next(); + numGraphemes++; + } + return std::max(0, numGraphemes); +} + +static inline void addEmphasisMark(GlyphBuffer* buffer, + const GlyphData* emphasisData, FloatPoint glyphCenter, + float midGlyphOffset) +{ + ASSERT(buffer); + ASSERT(emphasisData); + + const SimpleFontData* emphasisFontData = emphasisData->fontData; + ASSERT(emphasisFontData); + + bool isVertical = emphasisFontData->platformData().isVerticalAnyUpright() + && emphasisFontData->verticalData(); + + if (!isVertical) { + buffer->add(emphasisData->glyph, emphasisFontData, + midGlyphOffset - glyphCenter.x()); + } else { + buffer->add(emphasisData->glyph, emphasisFontData, + FloatPoint(-glyphCenter.x(), midGlyphOffset - glyphCenter.y())); + } +} + +float ShapeResult::fillGlyphBufferForTextEmphasisRun(GlyphBuffer* glyphBuffer, + const RunInfo* run, const TextRun& textRun, const GlyphData* emphasisData, + float initialAdvance, unsigned from, unsigned to, unsigned runOffset) +{ + if (!run) + return 0; + + unsigned graphemesInCluster = 1; + float clusterAdvance = 0; + + FloatPoint glyphCenter = emphasisData->fontData-> + boundsForGlyph(emphasisData->glyph).center(); + + TextDirection direction = textRun.direction(); + + // A "cluster" in this context means a cluster as it is used by HarfBuzz: + // The minimal group of characters and corresponding glyphs, that cannot be broken + // down further from a text shaping point of view. + // A cluster can contain multiple glyphs and grapheme clusters, with mutually + // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clusters, + // then linearly split the sum of corresponding glyph advances by the number of + // grapheme clusters in order to find positions for emphasis mark drawing. + uint16_t clusterStart = direction == RTL + ? run->m_startIndex + run->m_numCharacters + runOffset + : run->glyphToCharacterIndex(0) + runOffset; + + float advanceSoFar = initialAdvance; + unsigned numGlyphs = run->m_numGlyphs; + for (unsigned i = 0; i < numGlyphs; ++i) { + const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; + uint16_t currentCharacterIndex = run->m_startIndex + glyphData.characterIndex + runOffset; + bool isRunEnd = (i + 1 == numGlyphs); + bool isClusterEnd = isRunEnd || (run->glyphToCharacterIndex(i + 1) + runOffset != currentCharacterIndex); + + if ((direction == RTL && currentCharacterIndex >= to) || (direction != RTL && currentCharacterIndex < from)) { + advanceSoFar += glyphData.advance; + direction == RTL ? --clusterStart : ++clusterStart; + continue; + } + + clusterAdvance += glyphData.advance; + + if (textRun.is8Bit()) { + float glyphAdvanceX = glyphData.advance; + if (Character::canReceiveTextEmphasis(textRun[currentCharacterIndex])) { + addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, advanceSoFar + glyphAdvanceX / 2); + } + advanceSoFar += glyphAdvanceX; + } else if (isClusterEnd) { + uint16_t clusterEnd; + if (direction == RTL) + clusterEnd = currentCharacterIndex; + else + clusterEnd = isRunEnd ? run->m_startIndex + run->m_numCharacters + runOffset : run->glyphToCharacterIndex(i + 1) + runOffset; + + graphemesInCluster = countGraphemesInCluster(textRun.characters16(), textRun.charactersLength(), clusterStart, clusterEnd); + if (!graphemesInCluster || !clusterAdvance) + continue; + + float glyphAdvanceX = clusterAdvance / graphemesInCluster; + for (unsigned j = 0; j < graphemesInCluster; ++j) { + // Do not put emphasis marks on space, separator, and control characters. + if (Character::canReceiveTextEmphasis(textRun[currentCharacterIndex])) + addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, advanceSoFar + glyphAdvanceX / 2); + advanceSoFar += glyphAdvanceX; + } + clusterStart = clusterEnd; + clusterAdvance = 0; + } + } + return advanceSoFar - initialAdvance; +} + +float ShapeResult::fillGlyphBuffer(Vector<RefPtr<ShapeResult>>& results, + GlyphBuffer* glyphBuffer, const TextRun& textRun, + unsigned from, unsigned to) +{ + float advance = 0; + if (textRun.rtl()) { + unsigned wordOffset = textRun.length(); + for (unsigned j = 0; j < results.size(); j++) { + unsigned resolvedIndex = results.size() - 1 - j; + RefPtr<ShapeResult>& wordResult = results[resolvedIndex]; + for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { + advance += wordResult->fillGlyphBufferForRun<RTL>(glyphBuffer, + wordResult->m_runs[i].get(), advance, from, to, + wordOffset - wordResult->numCharacters()); + } + wordOffset -= wordResult->numCharacters(); + } + } else { + unsigned wordOffset = 0; + for (unsigned j = 0; j < results.size(); j++) { + RefPtr<ShapeResult>& wordResult = results[j]; + for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { + advance += wordResult->fillGlyphBufferForRun<LTR>(glyphBuffer, + wordResult->m_runs[i].get(), advance, from, to, wordOffset); + } + wordOffset += wordResult->numCharacters(); + } + } + + return advance; +} + +float ShapeResult::fillGlyphBufferForTextEmphasis( + Vector<RefPtr<ShapeResult>>& results, GlyphBuffer* glyphBuffer, + const TextRun& textRun, const GlyphData* emphasisData, + unsigned from, unsigned to) +{ + float advance = 0; + unsigned wordOffset = textRun.rtl() ? textRun.length() : 0; + for (unsigned j = 0; j < results.size(); j++) { + unsigned resolvedIndex = textRun.rtl() ? results.size() - 1 - j : j; + RefPtr<ShapeResult>& wordResult = results[resolvedIndex]; + for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { + unsigned resolvedOffset = wordOffset - + (textRun.rtl() ? wordResult->numCharacters() : 0); + advance += wordResult->fillGlyphBufferForTextEmphasisRun( + glyphBuffer, wordResult->m_runs[i].get(), textRun, emphasisData, + advance, from, to, resolvedOffset); + } + wordOffset += wordResult->numCharacters() * (textRun.rtl() ? -1 : 1); + } + + return advance; +} + +FloatRect ShapeResult::selectionRect(Vector<RefPtr<ShapeResult>>& results, + TextDirection direction, float totalWidth, const FloatPoint& point, + int height, unsigned absoluteFrom, unsigned absoluteTo) +{ + float currentX = 0; + float fromX = 0; + float toX = 0; + bool foundFromX = false; + bool foundToX = false; + + if (direction == RTL) + currentX = totalWidth; + + // The absoluteFrom and absoluteTo arguments represent the start/end offset + // for the entire run, from/to are continuously updated to be relative to + // the current word (ShapeResult instance). + int from = absoluteFrom; + int to = absoluteTo; + + unsigned totalNumCharacters = 0; + for (unsigned j = 0; j < results.size(); j++) { + RefPtr<ShapeResult> result = results[j]; + if (direction == RTL) { + // Convert logical offsets to visual offsets, because results are in + // logical order while runs are in visual order. + if (!foundFromX && from >= 0 && static_cast<unsigned>(from) < result->numCharacters()) + from = result->numCharacters() - from - 1; + if (!foundToX && to >= 0 && static_cast<unsigned>(to) < result->numCharacters()) + to = result->numCharacters() - to - 1; + currentX -= result->width(); + } + for (unsigned i = 0; i < result->m_runs.size(); i++) { + if (!result->m_runs[i]) + continue; + ASSERT((direction == RTL) == result->m_runs[i]->rtl()); + int numCharacters = result->m_runs[i]->m_numCharacters; + if (!foundFromX && from >= 0 && from < numCharacters) { + fromX = result->m_runs[i]->xPositionForVisualOffset(from) + currentX; + foundFromX = true; + } else { + from -= numCharacters; + } + + if (!foundToX && to >= 0 && to < numCharacters) { + toX = result->m_runs[i]->xPositionForVisualOffset(to) + currentX; + foundToX = true; + } else { + to -= numCharacters; + } + + if (foundFromX && foundToX) + break; + currentX += result->m_runs[i]->m_width; + } + if (direction == RTL) + currentX -= result->width(); + totalNumCharacters += result->numCharacters(); + } + + // The position in question might be just after the text. + if (!foundFromX && absoluteFrom == totalNumCharacters) { + fromX = direction == RTL ? 0 : totalWidth; + foundFromX = true; + } + if (!foundToX && absoluteTo == totalNumCharacters) { + toX = direction == RTL ? 0 : totalWidth; + foundToX = true; + } + if (!foundFromX) + fromX = 0; + if (!foundToX) + toX = direction == RTL ? 0 : totalWidth; + + // None of our runs is part of the selection, possibly invalid arguments. + if (!foundToX && !foundFromX) + fromX = toX = 0; + if (fromX < toX) + return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); + return FloatRect(point.x() + toX, point.y(), fromX - toX, height); +} + +int ShapeResult::offsetForPosition(Vector<RefPtr<ShapeResult>>& results, + const TextRun& run, float targetX) +{ + unsigned totalOffset; + if (run.rtl()) { + totalOffset = run.length(); + for (unsigned i = results.size(); i; --i) { + const RefPtr<ShapeResult>& wordResult = results[i - 1]; + if (!wordResult) + continue; + totalOffset -= wordResult->numCharacters(); + if (targetX >= 0 && targetX <= wordResult->width()) { + int offsetForWord = wordResult->offsetForPosition(targetX); + return totalOffset + offsetForWord; + } + targetX -= wordResult->width(); + } + } else { + totalOffset = 0; + for (auto& wordResult : results) { + if (!wordResult) + continue; + int offsetForWord = wordResult->offsetForPosition(targetX); + ASSERT(offsetForWord >= 0); + totalOffset += offsetForWord; + if (targetX >= 0 && targetX <= wordResult->width()) + return totalOffset; + targetX -= wordResult->width(); + } + } + return totalOffset; +} + +int ShapeResult::offsetForPosition(float targetX) +{ + int charactersSoFar = 0; + float currentX = 0; + + if (m_direction == RTL) { + charactersSoFar = m_numCharacters; + for (unsigned i = 0; i < m_runs.size(); ++i) { + if (!m_runs[i]) + continue; + charactersSoFar -= m_runs[i]->m_numCharacters; + float nextX = currentX + m_runs[i]->m_width; + float offsetForRun = targetX - currentX; + if (offsetForRun >= 0 && offsetForRun <= m_runs[i]->m_width) { + // The x value in question is within this script run. + const unsigned index = m_runs[i]->characterIndexForXPosition(offsetForRun); + return charactersSoFar + index; + } + currentX = nextX; + } + } else { + for (unsigned i = 0; i < m_runs.size(); ++i) { + if (!m_runs[i]) + continue; + float nextX = currentX + m_runs[i]->m_width; + float offsetForRun = targetX - currentX; + if (offsetForRun >= 0 && offsetForRun <= m_runs[i]->m_width) { + const unsigned index = m_runs[i]->characterIndexForXPosition(offsetForRun); + return charactersSoFar + index; + } + charactersSoFar += m_runs[i]->m_numCharacters; + currentX = nextX; + } + } + + return charactersSoFar; +} + +void ShapeResult::fallbackFonts(HashSet<const SimpleFontData*>* fallback) const +{ + ASSERT(fallback); + ASSERT(m_primaryFont); + for (unsigned i = 0; i < m_runs.size(); ++i) { + if (m_runs[i] && m_runs[i]->m_fontData != m_primaryFont + && !m_runs[i]->m_fontData->isTextOrientationFallbackOf(m_primaryFont.get())) { + fallback->add(m_runs[i]->m_fontData.get()); + } + } +} + +} // namespace blink diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h new file mode 100644 index 0000000..811232c --- /dev/null +++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2012 Google Inc. 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. + */ + +#ifndef ShapeResult_h +#define ShapeResult_h + +#include "platform/geometry/FloatPoint.h" +#include "platform/geometry/FloatRect.h" +#include "platform/text/TextRun.h" +#include "wtf/HashSet.h" +#include "wtf/OwnPtr.h" +#include "wtf/Vector.h" + +namespace blink { + +class Font; +class GlyphBuffer; +class SimpleFontData; +class HarfBuzzShaper; +struct GlyphData; + +class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> { +public: + static PassRefPtr<ShapeResult> create(const Font* font, + unsigned numCharacters, TextDirection direction) + { + return adoptRef(new ShapeResult(font, numCharacters, direction)); + } + static PassRefPtr<ShapeResult> createForTabulationCharacters(const Font*, + const TextRun&, float positionOffset, unsigned count); + ~ShapeResult(); + + float width() { return m_width; } + FloatRect bounds() { return m_glyphBoundingBox; } + unsigned numCharacters() const { return m_numCharacters; } + void fallbackFonts(HashSet<const SimpleFontData*>*) const; + + static int offsetForPosition(Vector<RefPtr<ShapeResult>>&, + const TextRun&, float targetX); + static float fillGlyphBuffer(Vector<RefPtr<ShapeResult>>&, + GlyphBuffer*, const TextRun&, unsigned from, unsigned to); + static float fillGlyphBufferForTextEmphasis(Vector<RefPtr<ShapeResult>>&, + GlyphBuffer*, const TextRun&, const GlyphData* emphasisData, + unsigned from, unsigned to); + static FloatRect selectionRect(Vector<RefPtr<ShapeResult>>&, + TextDirection, float totalWidth, const FloatPoint&, int height, + unsigned from, unsigned to); + +protected: + struct RunInfo; + + ShapeResult(const Font*, unsigned numCharacters, TextDirection); + + int offsetForPosition(float targetX); + template<TextDirection> + float fillGlyphBufferForRun(GlyphBuffer*, const RunInfo*, + float initialAdvance, unsigned from, unsigned to, unsigned runOffset); + + float fillGlyphBufferForTextEmphasisRun(GlyphBuffer*, const RunInfo*, + const TextRun&, const GlyphData*, float initialAdvance, + unsigned from, unsigned to, unsigned runOffset); + + float m_width; + FloatRect m_glyphBoundingBox; + Vector<OwnPtr<RunInfo>> m_runs; + RefPtr<SimpleFontData> m_primaryFont; + + unsigned m_numCharacters; + unsigned m_numGlyphs : 31; + + // Overall direction for the TextRun, dictates which order each individual + // sub run (represented by RunInfo structs in the m_runs vector) can have a + // different text direction. + unsigned m_direction : 1; + + friend class HarfBuzzShaper; +}; + +} // namespace blink + +#endif // ShapeResult_h diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h new file mode 100644 index 0000000..c0985a2 --- /dev/null +++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#ifndef ShapeResultInlineHeaders_h +#define ShapeResultInlineHeaders_h + +#include "platform/fonts/shaping/ShapeResult.h" + +#include <hb.h> + +namespace blink { + +class Font; +class GlyphBuffer; +class SimpleFontData; +class HarfBuzzShaper; + +struct HarfBuzzRunGlyphData { + uint16_t glyph; + uint16_t characterIndex; + float advance; + FloatSize offset; +}; + +struct ShapeResult::RunInfo { + RunInfo(const SimpleFontData* font, hb_direction_t dir, hb_script_t script, + unsigned startIndex, unsigned numGlyphs, unsigned numCharacters) + : m_fontData(const_cast<SimpleFontData*>(font)), m_direction(dir) + , m_script(script), m_startIndex(startIndex) + , m_numCharacters(numCharacters) , m_numGlyphs(numGlyphs), m_width(0.0f) + { + m_glyphData.resize(m_numGlyphs); + } + + bool rtl() const { return HB_DIRECTION_IS_BACKWARD(m_direction); } + float xPositionForVisualOffset(unsigned) const; + float xPositionForOffset(unsigned) const; + int characterIndexForXPosition(float) const; + void setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, + float offsetX, float offsetY); + + void addAdvance(unsigned index, float advance) + { + m_glyphData[index].advance += advance; + } + + size_t glyphToCharacterIndex(size_t i) const + { + return m_startIndex + m_glyphData[i].characterIndex; + } + + RefPtr<SimpleFontData> m_fontData; + hb_direction_t m_direction; + hb_script_t m_script; + Vector<HarfBuzzRunGlyphData> m_glyphData; + unsigned m_startIndex; + unsigned m_numCharacters; + unsigned m_numGlyphs; + float m_width; +}; + +} // namespace blink + +#endif // ShapeResultInlineHeaders_h diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultTestInfo.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultTestInfo.cpp new file mode 100644 index 0000000..e9fedda --- /dev/null +++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultTestInfo.cpp @@ -0,0 +1,42 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" +#include "platform/fonts/shaping/ShapeResultTestInfo.h" + +#include "platform/fonts/Font.h" +#include "platform/fonts/shaping/ShapeResultInlineHeaders.h" + +namespace blink { + +unsigned ShapeResultTestInfo::numberOfRunsForTesting() const +{ + return m_runs.size(); +} + +bool ShapeResultTestInfo::runInfoForTesting(unsigned runIndex, + unsigned& startIndex, unsigned& numGlyphs, hb_script_t& script) +{ + if (runIndex < m_runs.size() && m_runs[runIndex]) { + startIndex = m_runs[runIndex]->m_startIndex; + numGlyphs = m_runs[runIndex]->m_numGlyphs; + script = m_runs[runIndex]->m_script; + return true; + } + return false; +} + +uint16_t ShapeResultTestInfo::glyphForTesting(unsigned runIndex, + size_t glyphIndex) +{ + return m_runs[runIndex]->m_glyphData[glyphIndex].glyph; +} + +float ShapeResultTestInfo::advanceForTesting(unsigned runIndex, + size_t glyphIndex) +{ + return m_runs[runIndex]->m_glyphData[glyphIndex].advance; +} + +} // namespace blink diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultTestInfo.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultTestInfo.h new file mode 100644 index 0000000..d8dd283 --- /dev/null +++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultTestInfo.h @@ -0,0 +1,25 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ShapeResultTestInfo_h +#define ShapeResultTestInfo_h + +#include "platform/fonts/shaping/HarfBuzzShaper.h" + +#include <hb.h> + +namespace blink { + +class PLATFORM_EXPORT ShapeResultTestInfo : public ShapeResult { +public: + unsigned numberOfRunsForTesting() const; + bool runInfoForTesting(unsigned runIndex, unsigned& startIndex, + unsigned& numGlyphs, hb_script_t&); + uint16_t glyphForTesting(unsigned runIndex, size_t glyphIndex); + float advanceForTesting(unsigned runIndex, size_t glyphIndex); +}; + +} // namespace blink + +#endif // ShapeResultTestInfo_h diff --git a/third_party/WebKit/Source/platform/fonts/shaping/Shaper.cpp b/third_party/WebKit/Source/platform/fonts/shaping/Shaper.cpp index d3c0db4..3a63f49 100644 --- a/third_party/WebKit/Source/platform/fonts/shaping/Shaper.cpp +++ b/third_party/WebKit/Source/platform/fonts/shaping/Shaper.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "platform/fonts/shaping/Shaper.h" +#include "platform/fonts/Font.h" #include "platform/fonts/GlyphBuffer.h" #include "platform/fonts/GlyphPage.h" #include "platform/text/TextRun.h" @@ -74,4 +75,14 @@ void Shaper::addEmphasisMark(GlyphBuffer* buffer, float midGlyphOffset) const } } +void Shaper::trackNonPrimaryFallbackFont(const SimpleFontData* fontData) +{ + ASSERT(m_fallbackFonts); + + if (fontData == m_font->primaryFont()) + return; + + m_fallbackFonts->add(fontData); +} + } // namespace blink diff --git a/third_party/WebKit/Source/platform/fonts/shaping/Shaper.h b/third_party/WebKit/Source/platform/fonts/shaping/Shaper.h index 42fc1fe..ba3b5fb 100644 --- a/third_party/WebKit/Source/platform/fonts/shaping/Shaper.h +++ b/third_party/WebKit/Source/platform/fonts/shaping/Shaper.h @@ -32,12 +32,13 @@ #define Shaper_h #include "platform/PlatformExport.h" -#include "platform/fonts/Font.h" +#include "platform/geometry/FloatPoint.h" #include "wtf/HashSet.h" namespace blink { class FloatRect; +class Font; class GlyphBuffer; class SimpleFontData; class TextRun; @@ -70,16 +71,6 @@ private: FloatPoint m_emphasisGlyphCenter; }; -inline void Shaper::trackNonPrimaryFallbackFont(const SimpleFontData* fontData) -{ - ASSERT(m_fallbackFonts); - - if (fontData == m_font->primaryFont()) - return; - - m_fallbackFonts->add(fontData); -} - } // namespace blink #endif // Shaper_h |