Chromium Code Reviews| Index: Source/platform/fonts/shaping/HarfBuzzShaper.cpp |
| diff --git a/Source/platform/fonts/shaping/HarfBuzzShaper.cpp b/Source/platform/fonts/shaping/HarfBuzzShaper.cpp |
| index 22e181f679e9dc53fcc06f0451fa188630533f70..e73837961bc7fd662fec5ecf8f827e8e6379c1af 100644 |
| --- a/Source/platform/fonts/shaping/HarfBuzzShaper.cpp |
| +++ b/Source/platform/fonts/shaping/HarfBuzzShaper.cpp |
| @@ -552,13 +552,15 @@ static void normalizeCharacters(const TextRun& run, unsigned length, UChar* dest |
| 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 (run.normalizeSpace() && Character::isNormalizedCanvasSpaceCharacter(character)) |
| - character = spaceCharacter; |
| - else if (Character::treatAsSpace(character) && character != tabulationCharacter) |
| - character = spaceCharacter; |
| - else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) |
| - character = zeroWidthSpaceCharacter; |
| + if (!(character == tabulationCharacter && run.allowTabs())) { |
| + // Don't normalize tabs as they are not treated as spaces for word-end. |
| + if (run.normalizeSpace() && Character::isNormalizedCanvasSpaceCharacter(character)) |
| + character = spaceCharacter; |
| + else if (Character::treatAsSpace(character) && character != tabulationCharacter) |
| + character = spaceCharacter; |
| + else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) |
| + character = zeroWidthSpaceCharacter; |
| + } |
| U16_APPEND(destination, *destinationLength, length, character, error); |
| ASSERT_UNUSED(error, !error); |
| @@ -732,8 +734,34 @@ struct CandidateRun { |
| UScriptCode script; |
| }; |
| +static void collectCandidateRunsForTabulationCharacters(UTF16TextIterator& iterator, |
| + Vector<CandidateRun>* runs, unsigned& startIndexOfCurrentRun, |
| + UChar32& character, const SimpleFontData* currentFontData) |
| +{ |
| + ASSERT(character == tabulationCharacter); |
| + for (iterator.advance(); iterator.consume(character); iterator.advance()) { |
| + if (character != tabulationCharacter) |
| + break; |
| + } |
| + CandidateRun run = { tabulationCharacter, startIndexOfCurrentRun, static_cast<unsigned>(iterator.offset()), currentFontData, USCRIPT_COMMON }; |
| + runs->append(run); |
| + startIndexOfCurrentRun = iterator.offset(); |
| +} |
| + |
| +static inline bool updateCharacterInfo(const Font* font, UChar32 character, const TextRun& textRun, |
|
eae
2015/07/20 17:39:24
Update makes it sound like it has state, how about
kojii
2015/07/21 01:11:47
Glad you suggested this! I once did this but it ma
|
| + const SimpleFontData*& fontData, UScriptCode& script) |
| +{ |
| + fontData = font->glyphDataForCharacter(character, false, textRun.normalizeSpace()).fontData; |
| + UErrorCode errorCode = U_ZERO_ERROR; |
| + script = uscript_getScript(character, &errorCode); |
| + if (U_FAILURE(errorCode)) |
| + return false; |
| + return true; |
| +} |
| + |
| static inline bool collectCandidateRuns(const UChar* normalizedBuffer, |
| - size_t bufferLength, const Font* font, Vector<CandidateRun>* runs, bool isSpaceNormalize) |
| + size_t bufferLength, const Font* font, Vector<CandidateRun>* runs, |
| + const TextRun& textRun) |
| { |
| UTF16TextIterator iterator(normalizedBuffer, bufferLength); |
| UChar32 character; |
| @@ -742,21 +770,33 @@ static inline bool collectCandidateRuns(const UChar* normalizedBuffer, |
| if (!iterator.consume(character)) |
| return false; |
| - const SimpleFontData* nextFontData = font->glyphDataForCharacter(character, false, isSpaceNormalize).fontData; |
| - UErrorCode errorCode = U_ZERO_ERROR; |
| - UScriptCode nextScript = uscript_getScript(character, &errorCode); |
| - if (U_FAILURE(errorCode)) |
| + const SimpleFontData* nextFontData; |
| + UScriptCode nextScript; |
| + if (!updateCharacterInfo(font, character, textRun, nextFontData, nextScript)) |
| return false; |
| do { |
| + // Tab characters need to go to its own run because the run doesn't use Harfbuzz. |
| + if (character == tabulationCharacter && textRun.allowTabs()) { |
|
eae
2015/07/20 17:39:24
How about adding UNLIKELY macros around the tab ch
kojii
2015/07/21 01:11:47
Thought UNLIKELY() is the past. Thanks, learned on
|
| + collectCandidateRunsForTabulationCharacters(iterator, runs, |
| + startIndexOfCurrentRun, character, nextFontData); |
| + if (iterator.atEnd()) |
| + break; |
| + if (!updateCharacterInfo(font, character, textRun, nextFontData, nextScript)) |
| + return false; |
| + } |
| + |
| const UChar* currentCharacterPosition = iterator.characters(); |
| const SimpleFontData* currentFontData = nextFontData; |
| UScriptCode currentScript = nextScript; |
| UChar32 lastCharacter = character; |
| for (iterator.advance(); iterator.consume(character); iterator.advance()) { |
| - if (Character::treatAsZeroWidthSpace(character)) |
| + if (Character::treatAsZeroWidthSpace(character)) { |
| + if (character == tabulationCharacter && textRun.allowTabs()) |
|
eae
2015/07/20 17:39:24
I'd move this check out of the treatAsZeroWidthSpa
kojii
2015/07/21 01:11:47
That's better to read. treatAsZeroWidthSpace() che
|
| + break; |
| continue; |
| + } |
| if ((U_GET_GC_MASK(character) & U_GC_M_MASK) |
| && (Character::isUnicodeVariationSelector(character) |
| || currentFontData->canRenderCombiningCharacterSequence( |
| @@ -764,9 +804,7 @@ static inline bool collectCandidateRuns(const UChar* normalizedBuffer, |
| iterator.glyphEnd() - currentCharacterPosition))) |
| continue; |
| - nextFontData = font->glyphDataForCharacter(character, false, isSpaceNormalize).fontData; |
| - nextScript = uscript_getScript(character, &errorCode); |
| - if (U_FAILURE(errorCode)) |
| + if (!updateCharacterInfo(font, character, textRun, nextFontData, nextScript)) |
| return false; |
| if (lastCharacter == zeroWidthJoinerCharacter) |
| currentFontData = nextFontData; |
| @@ -780,7 +818,7 @@ static inline bool collectCandidateRuns(const UChar* normalizedBuffer, |
| runs->append(run); |
| startIndexOfCurrentRun = iterator.offset(); |
| - } while (iterator.consume(character)); |
| + } while (!iterator.atEnd()); |
| return true; |
| } |
| @@ -893,7 +931,7 @@ bool HarfBuzzShaper::createHarfBuzzRuns() |
| Vector<CandidateRun> candidateRuns; |
| if (!collectCandidateRuns(m_normalizedBuffer.get(), |
| - m_normalizedBufferLength, m_font, &candidateRuns, m_textRun.normalizeSpace())) |
| + m_normalizedBufferLength, m_font, &candidateRuns, m_textRun)) |
| return false; |
| if (!resolveCandidateRuns(candidateRuns)) |
| @@ -902,10 +940,16 @@ bool HarfBuzzShaper::createHarfBuzzRuns() |
| size_t length = candidateRuns.size(); |
| for (size_t i = 0; i < length; ) { |
| CandidateRun& run = candidateRuns[i]; |
| + if (run.character == tabulationCharacter && m_textRun.allowTabs()) { |
|
eae
2015/07/20 17:39:24
Is this really needed? Isn't the extra condition i
kojii
2015/07/21 01:11:47
Yeah, this check is needed if the first run is tab
|
| + addHarfBuzzRun(run.start, run.end, run.fontData, run.script); |
| + ++i; |
| + continue; |
| + } |
| CandidateRun lastMatchingRun = run; |
| for (i++; i < length; i++) { |
| if (candidateRuns[i].script != run.script |
| - || candidateRuns[i].fontData != run.fontData) |
| + || candidateRuns[i].fontData != run.fontData |
| + || candidateRuns[i].character == tabulationCharacter) |
| break; |
| lastMatchingRun = candidateRuns[i]; |
| } |
| @@ -958,12 +1002,12 @@ static const uint16_t* toUint16(const UChar* src) |
| } |
| static inline void addToHarfBuzzBufferInternal(hb_buffer_t* buffer, |
| - const FontDescription& fontDescription, const UChar* normalizedBuffer, |
| - unsigned startIndex, unsigned numCharacters) |
| + const FontDescription& fontDescription, const UChar* characters, |
| + unsigned numCharacters) |
| { |
| if (fontDescription.variant() == FontVariantSmallCaps |
| - && u_islower(normalizedBuffer[startIndex])) { |
| - String upperText = String(normalizedBuffer + startIndex, numCharacters) |
| + && u_islower(characters[0])) { |
| + String upperText = String(characters, numCharacters) |
| .upper(); |
| // TextRun is 16 bit, therefore upperText is 16 bit, even after we call |
| // makeUpper(). |
| @@ -971,7 +1015,7 @@ static inline void addToHarfBuzzBufferInternal(hb_buffer_t* buffer, |
| hb_buffer_add_utf16(buffer, toUint16(upperText.characters16()), |
| numCharacters, 0, numCharacters); |
| } else { |
| - hb_buffer_add_utf16(buffer, toUint16(normalizedBuffer + startIndex), |
| + hb_buffer_add_utf16(buffer, toUint16(characters), |
| numCharacters, 0, numCharacters); |
| } |
| } |
| @@ -991,6 +1035,13 @@ PassRefPtr<ShapeResult> HarfBuzzShaper::shapeHarfBuzzRuns() |
| for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { |
| unsigned runIndex = m_textRun.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; |
| const HarfBuzzRun* currentRun = &m_harfBuzzRuns[runIndex]; |
| + ASSERT(m_normalizedBufferLength >= currentRun->m_startIndex + currentRun->m_numCharacters); |
| + const UChar* characters = m_normalizedBuffer.get() + currentRun->m_startIndex; |
| + |
| + if (characters[0] == tabulationCharacter && m_textRun.allowTabs()) { |
| + shapeResultForTabulationCharacters(result.get(), i, currentRun); |
| + continue; |
| + } |
| const SimpleFontData* currentFontData = currentRun->m_fontData; |
| FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tFontData->platformData()); |
| @@ -1008,8 +1059,7 @@ PassRefPtr<ShapeResult> HarfBuzzShaper::shapeHarfBuzzRuns() |
| hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); |
| addToHarfBuzzBufferInternal(harfBuzzBuffer.get(), |
| - fontDescription, m_normalizedBuffer.get(), currentRun->m_startIndex, |
| - currentRun->m_numCharacters); |
| + fontDescription, characters, currentRun->m_numCharacters); |
| if (fontDescription.isVerticalAnyUpright()) |
| face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); |
| @@ -1067,6 +1117,7 @@ void HarfBuzzShaper::shapeResult(ShapeResult* result, unsigned index, |
| float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance - glyphPositions[i].y_advance); |
| unsigned currentCharacterIndex = currentRun->m_startIndex + glyphInfos[i].cluster; |
| RELEASE_ASSERT(m_normalizedBufferLength > currentCharacterIndex); |
| + ASSERT(m_normalizedBuffer[currentCharacterIndex] != tabulationCharacter || !m_textRun.allowTabs()); |
| bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster; |
| float spacing = 0; |
| @@ -1103,6 +1154,27 @@ void HarfBuzzShaper::shapeResult(ShapeResult* result, unsigned index, |
| result->m_fallbackFonts.add(const_cast<SimpleFontData*>(fallbackFont)); |
| } |
| +void HarfBuzzShaper::shapeResultForTabulationCharacters(ShapeResult* result, unsigned index, const HarfBuzzRun* currentRun) |
| +{ |
| + const SimpleFontData& fontData = *currentRun->m_fontData; |
| + ShapeResult::RunInfo* run = new ShapeResult::RunInfo(currentRun->m_fontData, |
| + currentRun->m_direction, currentRun->m_script, currentRun->m_startIndex, |
| + currentRun->m_numCharacters, currentRun->m_numCharacters); |
| + result->m_runs[index] = run; |
| + result->m_numGlyphs += currentRun->m_numCharacters; |
| + float position = m_textRun.xPos() + result->m_width; |
| + float positionStart = position; |
| + for (size_t i = 0; i < currentRun->m_numCharacters; ++i) { |
| + ASSERT(m_normalizedBuffer[currentRun->m_startIndex + i] == tabulationCharacter); |
| + float advance = m_font->tabWidth(fontData, m_textRun.tabSize(), position); |
| + run->m_glyphData[i].characterIndex = i; |
| + run->setGlyphAndPositions(i, fontData.spaceGlyph(), advance, 0, 0); |
| + position += advance; |
| + } |
| + run->m_width = position - positionStart; |
| + result->m_width += run->m_width; |
| +} |
| + |
| float HarfBuzzShaper::adjustSpacing(ShapeResult::RunInfo* run, size_t glyphIndex, unsigned currentCharacterIndex, float& offset, float& totalAdvance) |
| { |
| float spacing = 0; |