| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 Google Inc. All rights reserved. | 2 * Copyright (c) 2012 Google Inc. All rights reserved. |
| 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. | 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are | 6 * modification, are permitted provided that the following conditions are |
| 7 * met: | 7 * met: |
| 8 * | 8 * |
| 9 * * Redistributions of source code must retain the above copyright | 9 * * Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 */ | 30 */ |
| 31 | 31 |
| 32 #include "config.h" | 32 #include "config.h" |
| 33 #include "platform/fonts/shaping/HarfBuzzShaper.h" | 33 #include "platform/fonts/shaping/HarfBuzzShaper.h" |
| 34 | 34 |
| 35 #include "hb.h" | 35 #include "hb.h" |
| 36 #include "platform/LayoutUnit.h" | 36 #include "platform/LayoutUnit.h" |
| 37 #include "platform/Logging.h" |
| 37 #include "platform/RuntimeEnabledFeatures.h" | 38 #include "platform/RuntimeEnabledFeatures.h" |
| 38 #include "platform/fonts/Character.h" | 39 #include "platform/fonts/Character.h" |
| 39 #include "platform/fonts/Font.h" | 40 #include "platform/fonts/Font.h" |
| 41 #include "platform/fonts/FontFallbackIterator.h" |
| 40 #include "platform/fonts/GlyphBuffer.h" | 42 #include "platform/fonts/GlyphBuffer.h" |
| 41 #include "platform/fonts/UTF16TextIterator.h" | 43 #include "platform/fonts/UTF16TextIterator.h" |
| 42 #include "platform/fonts/shaping/HarfBuzzFace.h" | 44 #include "platform/fonts/shaping/HarfBuzzFace.h" |
| 45 #include "platform/fonts/shaping/RunSegmenter.h" |
| 43 #include "platform/text/TextBreakIterator.h" | 46 #include "platform/text/TextBreakIterator.h" |
| 44 #include "wtf/Compiler.h" | 47 #include "wtf/Compiler.h" |
| 45 #include "wtf/MathExtras.h" | 48 #include "wtf/MathExtras.h" |
| 46 #include "wtf/text/Unicode.h" | 49 #include "wtf/text/Unicode.h" |
| 47 | 50 |
| 48 #include <algorithm> | 51 #include <algorithm> |
| 49 #include <list> | 52 #include <list> |
| 50 #include <map> | 53 #include <map> |
| 51 #include <string> | 54 #include <string> |
| 52 #include <unicode/normlzr.h> | 55 #include <unicode/normlzr.h> |
| (...skipping 719 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 772 hb_feature_t feature; | 775 hb_feature_t feature; |
| 773 const AtomicString& tag = settings->at(i).tag(); | 776 const AtomicString& tag = settings->at(i).tag(); |
| 774 feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); | 777 feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); |
| 775 feature.value = settings->at(i).value(); | 778 feature.value = settings->at(i).value(); |
| 776 feature.start = 0; | 779 feature.start = 0; |
| 777 feature.end = static_cast<unsigned>(-1); | 780 feature.end = static_cast<unsigned>(-1); |
| 778 m_features.append(feature); | 781 m_features.append(feature); |
| 779 } | 782 } |
| 780 } | 783 } |
| 781 | 784 |
| 782 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult() | |
| 783 { | |
| 784 if (!createHarfBuzzRuns()) | |
| 785 return nullptr; | |
| 786 return shapeHarfBuzzRuns(); | |
| 787 } | |
| 788 | |
| 789 struct CandidateRun { | |
| 790 UChar32 character; | |
| 791 unsigned start; | |
| 792 unsigned end; | |
| 793 const SimpleFontData* fontData; | |
| 794 UScriptCode script; | |
| 795 }; | |
| 796 | |
| 797 static inline bool collectCandidateRuns(const UChar* normalizedBuffer, | |
| 798 size_t bufferLength, const Font* font, Vector<CandidateRun>* runs, bool isSp
aceNormalize) | |
| 799 { | |
| 800 UTF16TextIterator iterator(normalizedBuffer, bufferLength); | |
| 801 UChar32 character; | |
| 802 unsigned startIndexOfCurrentRun = 0; | |
| 803 | |
| 804 if (!iterator.consume(character)) | |
| 805 return false; | |
| 806 | |
| 807 const SimpleFontData* nextFontData = font->glyphDataForCharacter(character,
false, isSpaceNormalize).fontData; | |
| 808 UErrorCode errorCode = U_ZERO_ERROR; | |
| 809 UScriptCode nextScript = uscript_getScript(character, &errorCode); | |
| 810 if (U_FAILURE(errorCode)) | |
| 811 return false; | |
| 812 | |
| 813 do { | |
| 814 const UChar* currentCharacterPosition = iterator.characters(); | |
| 815 const SimpleFontData* currentFontData = nextFontData; | |
| 816 UScriptCode currentScript = nextScript; | |
| 817 | |
| 818 UChar32 lastCharacter = character; | |
| 819 for (iterator.advance(); iterator.consume(character); iterator.advance()
) { | |
| 820 if (Character::treatAsZeroWidthSpace(character)) | |
| 821 continue; | |
| 822 if ((U_GET_GC_MASK(character) & U_GC_M_MASK) | |
| 823 && (Character::isUnicodeVariationSelector(character) | |
| 824 || currentFontData->canRenderCombiningCharacterSequence( | |
| 825 currentCharacterPosition, | |
| 826 iterator.glyphEnd() - currentCharacterPosition))) | |
| 827 continue; | |
| 828 | |
| 829 nextFontData = font->glyphDataForCharacter(character, false, isSpace
Normalize).fontData; | |
| 830 nextScript = uscript_getScript(character, &errorCode); | |
| 831 if (U_FAILURE(errorCode)) | |
| 832 return false; | |
| 833 if (lastCharacter == zeroWidthJoinerCharacter) | |
| 834 currentFontData = nextFontData; | |
| 835 if ((nextFontData != currentFontData) || ((currentScript != nextScri
pt) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, curre
ntScript)))) | |
| 836 break; | |
| 837 currentCharacterPosition = iterator.characters(); | |
| 838 lastCharacter = character; | |
| 839 } | |
| 840 | |
| 841 CandidateRun run = { lastCharacter, startIndexOfCurrentRun, static_cast<
unsigned>(iterator.offset()), currentFontData, currentScript }; | |
| 842 runs->append(run); | |
| 843 | |
| 844 startIndexOfCurrentRun = iterator.offset(); | |
| 845 } while (iterator.consume(character)); | |
| 846 | |
| 847 return true; | |
| 848 } | |
| 849 | |
| 850 static inline bool matchesAdjacentRun(UScriptCode* scriptExtensions, int length, | |
| 851 CandidateRun& adjacentRun) | |
| 852 { | |
| 853 for (int i = 0; i < length; i++) { | |
| 854 if (scriptExtensions[i] == adjacentRun.script) | |
| 855 return true; | |
| 856 } | |
| 857 return false; | |
| 858 } | |
| 859 | |
| 860 static inline void resolveRunBasedOnScriptExtensions(Vector<CandidateRun>& runs, | |
| 861 CandidateRun& run, size_t i, size_t length, UScriptCode* scriptExtensions, | |
| 862 int extensionsLength, size_t& nextResolvedRun) | |
| 863 { | |
| 864 // If uscript_getScriptExtensions returns 1 it only contains the script valu
e, | |
| 865 // we only care about ScriptExtensions which is indicated by a value >= 2. | |
| 866 if (extensionsLength <= 1) | |
| 867 return; | |
| 868 | |
| 869 if (i > 0 && matchesAdjacentRun(scriptExtensions, extensionsLength, runs[i -
1])) { | |
| 870 run.script = runs[i - 1].script; | |
| 871 return; | |
| 872 } | |
| 873 | |
| 874 for (size_t j = i + 1; j < length; j++) { | |
| 875 if (runs[j].script != USCRIPT_COMMON | |
| 876 && runs[j].script != USCRIPT_INHERITED | |
| 877 && matchesAdjacentRun(scriptExtensions, extensionsLength, runs[j]))
{ | |
| 878 nextResolvedRun = j; | |
| 879 break; | |
| 880 } | |
| 881 } | |
| 882 } | |
| 883 | |
| 884 static inline void resolveRunBasedOnScriptValue(Vector<CandidateRun>& runs, | |
| 885 CandidateRun& run, size_t i, size_t length, size_t& nextResolvedRun) | |
| 886 { | |
| 887 if (run.script != USCRIPT_COMMON) | |
| 888 return; | |
| 889 | |
| 890 if (i > 0 && runs[i - 1].script != USCRIPT_COMMON) { | |
| 891 run.script = runs[i - 1].script; | |
| 892 return; | |
| 893 } | |
| 894 | |
| 895 for (size_t j = i + 1; j < length; j++) { | |
| 896 if (runs[j].script != USCRIPT_COMMON | |
| 897 && runs[j].script != USCRIPT_INHERITED) { | |
| 898 nextResolvedRun = j; | |
| 899 break; | |
| 900 } | |
| 901 } | |
| 902 } | |
| 903 | |
| 904 static inline bool resolveCandidateRuns(Vector<CandidateRun>& runs) | |
| 905 { | |
| 906 UScriptCode scriptExtensions[USCRIPT_CODE_LIMIT]; | |
| 907 UErrorCode errorCode = U_ZERO_ERROR; | |
| 908 size_t length = runs.size(); | |
| 909 for (size_t i = 0; i < length; i++) { | |
| 910 CandidateRun& run = runs[i]; | |
| 911 size_t nextResolvedRun = 0; | |
| 912 | |
| 913 if (run.script == USCRIPT_INHERITED) | |
| 914 run.script = i > 0 ? runs[i - 1].script : USCRIPT_COMMON; | |
| 915 | |
| 916 int extensionsLength = uscript_getScriptExtensions(run.character, | |
| 917 scriptExtensions, sizeof(scriptExtensions) / sizeof(scriptExtensions
[0]), | |
| 918 &errorCode); | |
| 919 if (U_FAILURE(errorCode)) | |
| 920 return false; | |
| 921 | |
| 922 resolveRunBasedOnScriptExtensions(runs, run, i, length, | |
| 923 scriptExtensions, extensionsLength, nextResolvedRun); | |
| 924 resolveRunBasedOnScriptValue(runs, run, i, length, | |
| 925 nextResolvedRun); | |
| 926 for (size_t j = i; j < nextResolvedRun; j++) | |
| 927 runs[j].script = runs[nextResolvedRun].script; | |
| 928 | |
| 929 i = std::max(i, nextResolvedRun); | |
| 930 } | |
| 931 return true; | |
| 932 } | |
| 933 | |
| 934 // For ideographic (CJK) documents, 90-95% of calls from width() are one charact
er length | |
| 935 // because most characters have break opportunities both before and after. | |
| 936 bool HarfBuzzShaper::createHarfBuzzRunsForSingleCharacter() | |
| 937 { | |
| 938 ASSERT(m_normalizedBufferLength == 1); | |
| 939 UChar32 character = m_normalizedBuffer[0]; | |
| 940 if (!U16_IS_SINGLE(character)) | |
| 941 return false; | |
| 942 const SimpleFontData* fontData = m_font->glyphDataForCharacter(character, fa
lse, m_textRun.normalizeSpace()).fontData; | |
| 943 UErrorCode errorCode = U_ZERO_ERROR; | |
| 944 UScriptCode script = uscript_getScript(character, &errorCode); | |
| 945 if (U_FAILURE(errorCode)) | |
| 946 return false; | |
| 947 addHarfBuzzRun(0, 1, fontData, script); | |
| 948 return true; | |
| 949 } | |
| 950 | |
| 951 bool HarfBuzzShaper::createHarfBuzzRuns() | |
| 952 { | |
| 953 if (m_normalizedBufferLength == 1) | |
| 954 return createHarfBuzzRunsForSingleCharacter(); | |
| 955 | |
| 956 Vector<CandidateRun> candidateRuns; | |
| 957 if (!collectCandidateRuns(m_normalizedBuffer.get(), | |
| 958 m_normalizedBufferLength, m_font, &candidateRuns, m_textRun.normalizeSpa
ce())) | |
| 959 return false; | |
| 960 | |
| 961 if (!resolveCandidateRuns(candidateRuns)) | |
| 962 return false; | |
| 963 | |
| 964 size_t length = candidateRuns.size(); | |
| 965 for (size_t i = 0; i < length; ) { | |
| 966 CandidateRun& run = candidateRuns[i]; | |
| 967 CandidateRun lastMatchingRun = run; | |
| 968 for (i++; i < length; i++) { | |
| 969 if (candidateRuns[i].script != run.script | |
| 970 || candidateRuns[i].fontData != run.fontData) | |
| 971 break; | |
| 972 lastMatchingRun = candidateRuns[i]; | |
| 973 } | |
| 974 addHarfBuzzRun(run.start, lastMatchingRun.end, run.fontData, run.script)
; | |
| 975 } | |
| 976 return !m_harfBuzzRuns.isEmpty(); | |
| 977 } | |
| 978 | |
| 979 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built | 785 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built |
| 980 // without hb-icu. See http://crbug.com/356929 | 786 // without hb-icu. See http://crbug.com/356929 |
| 981 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) | 787 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) |
| 982 { | 788 { |
| 983 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) | 789 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) |
| 984 return HB_SCRIPT_INVALID; | 790 return HB_SCRIPT_INVALID; |
| 985 | 791 |
| 986 return hb_script_from_string(uscript_getShortName(script), -1); | 792 return hb_script_from_string(uscript_getShortName(script), -1); |
| 987 } | 793 } |
| 988 | 794 |
| 989 static inline hb_direction_t TextDirectionToHBDirection(TextDirection dir, FontO
rientation orientation, const SimpleFontData* fontData) | 795 static inline hb_direction_t TextDirectionToHBDirection(TextDirection dir, FontO
rientation orientation, const SimpleFontData* fontData) |
| 990 { | 796 { |
| 991 hb_direction_t harfBuzzDirection = isVerticalAnyUpright(orientation) && !fon
tData->isTextOrientationFallback() ? HB_DIRECTION_TTB : HB_DIRECTION_LTR; | 797 hb_direction_t harfBuzzDirection = isVerticalAnyUpright(orientation) && !fon
tData->isTextOrientationFallback() ? HB_DIRECTION_TTB : HB_DIRECTION_LTR; |
| 992 return dir == RTL ? HB_DIRECTION_REVERSE(harfBuzzDirection) : harfBuzzDirect
ion; | 798 return dir == RTL ? HB_DIRECTION_REVERSE(harfBuzzDirection) : harfBuzzDirect
ion; |
| 993 } | 799 } |
| 994 | 800 |
| 995 void HarfBuzzShaper::addHarfBuzzRun(unsigned startCharacter, | |
| 996 unsigned endCharacter, const SimpleFontData* fontData, | |
| 997 UScriptCode script) | |
| 998 { | |
| 999 ASSERT(endCharacter > startCharacter); | |
| 1000 ASSERT(script != USCRIPT_INVALID_CODE); | |
| 1001 | |
| 1002 hb_direction_t direction = TextDirectionToHBDirection(m_textRun.direction(), | |
| 1003 m_font->fontDescription().orientation(), fontData); | |
| 1004 HarfBuzzRun harfBuzzRun = { | |
| 1005 fontData, startCharacter, endCharacter - startCharacter, | |
| 1006 direction, ICUScriptToHBScript(script) | |
| 1007 }; | |
| 1008 m_harfBuzzRuns.append(harfBuzzRun); | |
| 1009 } | |
| 1010 | |
| 1011 static const uint16_t* toUint16(const UChar* src) | 801 static const uint16_t* toUint16(const UChar* src) |
| 1012 { | 802 { |
| 1013 // FIXME: This relies on undefined behavior however it works on the | 803 // FIXME: This relies on undefined behavior however it works on the |
| 1014 // current versions of all compilers we care about and avoids making | 804 // current versions of all compilers we care about and avoids making |
| 1015 // a copy of the string. | 805 // a copy of the string. |
| 1016 static_assert(sizeof(UChar) == sizeof(uint16_t), "UChar should be the same s
ize as uint16_t"); | 806 static_assert(sizeof(UChar) == sizeof(uint16_t), "UChar should be the same s
ize as uint16_t"); |
| 1017 return reinterpret_cast<const uint16_t*>(src); | 807 return reinterpret_cast<const uint16_t*>(src); |
| 1018 } | 808 } |
| 1019 | 809 |
| 1020 static inline void addToHarfBuzzBufferInternal(hb_buffer_t* buffer, | 810 static inline void addToHarfBuzzBufferInternal(hb_buffer_t* buffer, |
| 1021 const FontDescription& fontDescription, const UChar* normalizedBuffer, | 811 const FontDescription& fontDescription, const UChar* normalizedBuffer, |
| 1022 unsigned startIndex, unsigned numCharacters) | 812 unsigned startIndex, unsigned numCharacters) |
| 1023 { | 813 { |
| 1024 if (fontDescription.variant() == FontVariantSmallCaps | 814 // TODO: Revisit whether we can always fill the hb_buffer_t with the |
| 1025 && u_islower(normalizedBuffer[startIndex])) { | 815 // full run text, but only specify startIndex and numCharacters for the part |
| 816 // to be shaped. Then simplify/change the complicated index computations in |
| 817 // extractShapeResults(). |
| 818 if (fontDescription.variant() == FontVariantSmallCaps) { |
| 1026 String upperText = String(normalizedBuffer + startIndex, numCharacters) | 819 String upperText = String(normalizedBuffer + startIndex, numCharacters) |
| 1027 .upper(); | 820 .upper(); |
| 1028 // TextRun is 16 bit, therefore upperText is 16 bit, even after we call | 821 // TextRun is 16 bit, therefore upperText is 16 bit, even after we call |
| 1029 // makeUpper(). | 822 // makeUpper(). |
| 1030 ASSERT(!upperText.is8Bit()); | 823 ASSERT(!upperText.is8Bit()); |
| 1031 hb_buffer_add_utf16(buffer, toUint16(upperText.characters16()), | 824 hb_buffer_add_utf16(buffer, toUint16(upperText.characters16()), |
| 1032 numCharacters, 0, numCharacters); | 825 numCharacters, 0, numCharacters); |
| 1033 } else { | 826 } else { |
| 1034 hb_buffer_add_utf16(buffer, toUint16(normalizedBuffer + startIndex), | 827 hb_buffer_add_utf16(buffer, toUint16(normalizedBuffer + startIndex), |
| 1035 numCharacters, 0, numCharacters); | 828 numCharacters, 0, numCharacters); |
| 1036 } | 829 } |
| 1037 } | 830 } |
| 1038 | 831 |
| 1039 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeHarfBuzzRuns() | 832 inline bool HarfBuzzShaper::shapeRange(hb_buffer_t* harfBuzzBuffer, |
| 833 unsigned startIndex, |
| 834 unsigned numCharacters, |
| 835 const SimpleFontData* currentFont, |
| 836 unsigned currentFontRangeFrom, |
| 837 unsigned currentFontRangeTo, |
| 838 UScriptCode currentRunScript, |
| 839 hb_language_t language) |
| 840 { |
| 841 const FontPlatformData* platformData = &(currentFont->platformData()); |
| 842 HarfBuzzFace* face = platformData->harfBuzzFace(); |
| 843 if (!face) { |
| 844 WTF_LOG_ERROR("Could not create HarfBuzzFace from FontPlatformData."); |
| 845 return false; |
| 846 } |
| 847 |
| 848 hb_buffer_set_language(harfBuzzBuffer, language); |
| 849 hb_buffer_set_script(harfBuzzBuffer, ICUScriptToHBScript(currentRunScript)); |
| 850 hb_buffer_set_direction(harfBuzzBuffer, TextDirectionToHBDirection(m_textRun
.direction(), |
| 851 m_font->fontDescription().orientation(), currentFont)); |
| 852 |
| 853 // Add a space as pre-context to the buffer. This prevents showing dotted-ci
rcle |
| 854 // for combining marks at the beginning of runs. |
| 855 static const uint16_t preContext = spaceCharacter; |
| 856 hb_buffer_add_utf16(harfBuzzBuffer, &preContext, 1, 1, 0); |
| 857 |
| 858 addToHarfBuzzBufferInternal(harfBuzzBuffer, |
| 859 m_font->fontDescription(), m_normalizedBuffer.get(), startIndex, |
| 860 numCharacters); |
| 861 |
| 862 HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(currentFontRangeF
rom, currentFontRangeTo), hb_font_destroy); |
| 863 hb_shape(harfBuzzFont.get(), harfBuzzBuffer, m_features.isEmpty() ? 0 : m_fe
atures.data(), m_features.size()); |
| 864 |
| 865 return true; |
| 866 } |
| 867 |
| 868 bool HarfBuzzShaper::extractShapeResults(hb_buffer_t* harfBuzzBuffer, |
| 869 ShapeResult* shapeResult, |
| 870 bool& fontCycleQueued, const HolesQueueItem& currentQueueItem, |
| 871 const SimpleFontData* currentFont, |
| 872 UScriptCode currentRunScript, |
| 873 bool isLastResort) |
| 874 { |
| 875 enum ClusterResult { |
| 876 Shaped, |
| 877 NotDef, |
| 878 Unknown |
| 879 }; |
| 880 ClusterResult currentClusterResult = Unknown; |
| 881 ClusterResult previousClusterResult = Unknown; |
| 882 unsigned previousCluster = 0; |
| 883 unsigned currentCluster = 0; |
| 884 |
| 885 // Find first notdef glyph in harfBuzzBuffer. |
| 886 unsigned numGlyphs = hb_buffer_get_length(harfBuzzBuffer); |
| 887 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); |
| 888 |
| 889 unsigned lastChangePosition = 0; |
| 890 |
| 891 if (!numGlyphs) { |
| 892 WTF_LOG_ERROR("HarfBuzz returned empty glyph buffer after shaping."); |
| 893 return false; |
| 894 } |
| 895 |
| 896 for (unsigned glyphIndex = 0; glyphIndex <= numGlyphs; ++glyphIndex) { |
| 897 // Iterating by clusters, check for when the state switches from shaped |
| 898 // to non-shaped and vice versa. Taking into account the edge cases of |
| 899 // beginning of the run and end of the run. |
| 900 previousCluster = currentCluster; |
| 901 currentCluster = glyphInfo[glyphIndex].cluster; |
| 902 |
| 903 if (glyphIndex < numGlyphs) { |
| 904 // Still the same cluster, merge shaping status. |
| 905 if (previousCluster == currentCluster && glyphIndex != 0) { |
| 906 if (glyphInfo[glyphIndex].codepoint == 0) { |
| 907 currentClusterResult = NotDef; |
| 908 } else { |
| 909 // We can only call the current cluster fully shapped, if |
| 910 // all characters that are part of it are shaped, so update |
| 911 // currentClusterResult to Shaped only if the previous |
| 912 // characters have been shaped, too. |
| 913 currentClusterResult = currentClusterResult == Shaped ? Shap
ed : NotDef; |
| 914 } |
| 915 continue; |
| 916 } |
| 917 // We've moved to a new cluster. |
| 918 previousClusterResult = currentClusterResult; |
| 919 currentClusterResult = glyphInfo[glyphIndex].codepoint == 0 ? NotDef
: Shaped; |
| 920 } else { |
| 921 // The code below operates on the "flanks"/changes between NotDef |
| 922 // and Shaped. In order to keep the code below from explictly |
| 923 // dealing with character indices and run end, we explicitly |
| 924 // terminate the cluster/run here by setting the result value to the |
| 925 // opposite of what it was, leading to atChange turning true. |
| 926 previousClusterResult = currentClusterResult; |
| 927 currentClusterResult = currentClusterResult == NotDef ? Shaped : Not
Def; |
| 928 } |
| 929 |
| 930 bool atChange = (previousClusterResult != currentClusterResult) && previ
ousClusterResult != Unknown; |
| 931 if (!atChange) |
| 932 continue; |
| 933 |
| 934 // Compute the range indices of consecutive shaped or .notdef glyphs. |
| 935 // Cluster information for RTL runs becomes reversed, e.g. character 0 |
| 936 // has cluster index 5 in a run of 6 characters. |
| 937 unsigned numCharacters = 0; |
| 938 unsigned numGlyphsToInsert = 0; |
| 939 unsigned startIndex = 0; |
| 940 if (HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(harfBuzzBuffer))) { |
| 941 startIndex = currentQueueItem.m_startIndex + glyphInfo[lastChangePos
ition].cluster; |
| 942 if (glyphIndex == numGlyphs) { |
| 943 numCharacters = currentQueueItem.m_numCharacters - glyphInfo[las
tChangePosition].cluster; |
| 944 numGlyphsToInsert = numGlyphs - lastChangePosition; |
| 945 } else { |
| 946 numCharacters = glyphInfo[glyphIndex].cluster - glyphInfo[lastCh
angePosition].cluster; |
| 947 numGlyphsToInsert = glyphIndex - lastChangePosition; |
| 948 } |
| 949 } else { |
| 950 // Direction Backwards |
| 951 startIndex = currentQueueItem.m_startIndex + glyphInfo[glyphIndex -
1].cluster; |
| 952 if (lastChangePosition == 0) { |
| 953 numCharacters = currentQueueItem.m_numCharacters - glyphInfo[gly
phIndex - 1].cluster; |
| 954 } else { |
| 955 numCharacters = glyphInfo[lastChangePosition - 1].cluster - glyp
hInfo[glyphIndex - 1].cluster; |
| 956 } |
| 957 numGlyphsToInsert = glyphIndex - lastChangePosition; |
| 958 } |
| 959 |
| 960 if (currentClusterResult == Shaped && !isLastResort) { |
| 961 // Now it's clear that we need to continue processing. |
| 962 if (!fontCycleQueued) { |
| 963 appendToHolesQueue(HolesQueueNextFont, 0, 0); |
| 964 fontCycleQueued = true; |
| 965 } |
| 966 |
| 967 // Here we need to put character positions. |
| 968 ASSERT(numCharacters); |
| 969 appendToHolesQueue(HolesQueueRange, startIndex, numCharacters); |
| 970 } |
| 971 |
| 972 // If numCharacters is 0, that means we hit a NotDef before shaping the |
| 973 // whole grapheme. We do not append it here. For the next glyph we |
| 974 // encounter, atChange will be true, and the characters corresponding to |
| 975 // the grapheme will be added to the TODO queue again, attempting to |
| 976 // shape the whole grapheme with the next font. |
| 977 // When we're getting here with the last resort font, we have no other |
| 978 // choice than adding boxes to the ShapeResult. |
| 979 if ((currentClusterResult == NotDef && numCharacters) || isLastResort) { |
| 980 // Here we need to specify glyph positions. |
| 981 OwnPtr<ShapeResult::RunInfo> run = adoptPtr(new ShapeResult::RunInfo
(currentFont, |
| 982 TextDirectionToHBDirection(m_textRun.direction(), |
| 983 m_font->fontDescription().orientation(), currentFont), |
| 984 ICUScriptToHBScript(currentRunScript), |
| 985 startIndex, |
| 986 numGlyphsToInsert, numCharacters)); |
| 987 insertRunIntoShapeResult(shapeResult, run.release(), lastChangePosit
ion, numGlyphsToInsert, currentQueueItem.m_startIndex, harfBuzzBuffer); |
| 988 } |
| 989 lastChangePosition = glyphIndex; |
| 990 } |
| 991 return true; |
| 992 } |
| 993 |
| 994 static inline const SimpleFontData* fontDataAdjustedForOrientation(const SimpleF
ontData* originalFont, |
| 995 FontOrientation runOrientation, |
| 996 OrientationIterator::RenderOrientation renderOrientation) |
| 997 { |
| 998 if (!isVerticalBaseline(runOrientation)) |
| 999 return originalFont; |
| 1000 |
| 1001 if (runOrientation == FontOrientation::VerticalRotated |
| 1002 || (runOrientation == FontOrientation::VerticalMixed && renderOrientatio
n == OrientationIterator::OrientationRotateSideways)) |
| 1003 return originalFont->verticalRightOrientationFontData().get(); |
| 1004 |
| 1005 return originalFont; |
| 1006 } |
| 1007 |
| 1008 bool HarfBuzzShaper::collectFallbackHintChars(Vector<UChar32>& hint, bool needsL
ist) |
| 1009 { |
| 1010 if (!m_holesQueue.size()) |
| 1011 return false; |
| 1012 |
| 1013 hint.clear(); |
| 1014 |
| 1015 size_t numCharsAdded = 0; |
| 1016 for (auto it = m_holesQueue.begin(); it != m_holesQueue.end(); ++it) { |
| 1017 if (it->m_action == HolesQueueNextFont) |
| 1018 break; |
| 1019 |
| 1020 UChar32 hintChar; |
| 1021 RELEASE_ASSERT(it->m_startIndex + it->m_numCharacters <= m_normalizedBuf
ferLength); |
| 1022 UTF16TextIterator iterator(m_normalizedBuffer.get() + it->m_startIndex,
it->m_numCharacters); |
| 1023 while (iterator.consume(hintChar)) { |
| 1024 hint.append(hintChar); |
| 1025 numCharsAdded++; |
| 1026 if (!needsList) |
| 1027 break; |
| 1028 iterator.advance(); |
| 1029 } |
| 1030 } |
| 1031 return numCharsAdded > 0; |
| 1032 } |
| 1033 |
| 1034 void HarfBuzzShaper::appendToHolesQueue(HolesQueueItemAction action, |
| 1035 unsigned startIndex, |
| 1036 unsigned numCharacters) |
| 1037 { |
| 1038 m_holesQueue.append(HolesQueueItem(action, startIndex, numCharacters)); |
| 1039 } |
| 1040 |
| 1041 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult() |
| 1040 { | 1042 { |
| 1041 RefPtr<ShapeResult> result = ShapeResult::create(m_font, | 1043 RefPtr<ShapeResult> result = ShapeResult::create(m_font, |
| 1042 m_normalizedBufferLength, m_textRun.direction()); | 1044 m_normalizedBufferLength, m_textRun.direction()); |
| 1043 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_
destroy); | 1045 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_
destroy); |
| 1044 | 1046 |
| 1045 const FontDescription& fontDescription = m_font->fontDescription(); | 1047 const FontDescription& fontDescription = m_font->fontDescription(); |
| 1046 const String& localeString = fontDescription.locale(); | 1048 const String& localeString = fontDescription.locale(); |
| 1047 CString locale = localeString.latin1(); | 1049 CString locale = localeString.latin1(); |
| 1048 const hb_language_t language = hb_language_from_string(locale.data(), locale
.length()); | 1050 const hb_language_t language = hb_language_from_string(locale.data(), locale
.length()); |
| 1049 | 1051 |
| 1050 result->m_runs.resize(m_harfBuzzRuns.size()); | 1052 RunSegmenter::RunSegmenterRange segmentRange = { |
| 1051 for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { | 1053 0, |
| 1052 unsigned runIndex = m_textRun.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; | 1054 0, |
| 1053 const HarfBuzzRun* currentRun = &m_harfBuzzRuns[runIndex]; | 1055 USCRIPT_INVALID_CODE, |
| 1056 OrientationIterator::OrientationInvalid, |
| 1057 SmallCapsIterator::SmallCapsSameCase }; |
| 1058 RunSegmenter runSegmenter( |
| 1059 m_normalizedBuffer.get(), |
| 1060 m_normalizedBufferLength, |
| 1061 m_font->fontDescription().orientation(), |
| 1062 fontDescription.variant()); |
| 1054 | 1063 |
| 1055 const SimpleFontData* currentFontData = currentRun->m_fontData; | 1064 Vector<UChar32> fallbackCharsHint; |
| 1056 FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tF
ontData->platformData()); | |
| 1057 HarfBuzzFace* face = platformData->harfBuzzFace(); | |
| 1058 if (!face) | |
| 1059 return nullptr; | |
| 1060 | 1065 |
| 1061 hb_buffer_set_language(harfBuzzBuffer.get(), language); | 1066 // TODO: Check whether this treatAsZerowidthspace from the previous script |
| 1062 hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->m_script); | 1067 // segmentation plays a role here, does the new scriptRuniterator handle tha
t correctly? |
| 1063 hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->m_direction); | 1068 while (runSegmenter.consume(&segmentRange)) { |
| 1069 RefPtr<FontFallbackIterator> fallbackIterator = m_font->createFontFallba
ckIterator(); |
| 1064 | 1070 |
| 1065 // Add a space as pre-context to the buffer. This prevents showing dotte
d-circle | 1071 appendToHolesQueue(HolesQueueNextFont, 0, 0); |
| 1066 // for combining marks at the beginning of runs. | 1072 appendToHolesQueue(HolesQueueRange, segmentRange.start, segmentRange.end
- segmentRange.start); |
| 1067 static const uint16_t preContext = spaceCharacter; | |
| 1068 hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); | |
| 1069 | 1073 |
| 1070 addToHarfBuzzBufferInternal(harfBuzzBuffer.get(), | 1074 const SimpleFontData* currentFont = nullptr; |
| 1071 fontDescription, m_normalizedBuffer.get(), currentRun->m_startIndex, | 1075 unsigned currentFontRangeFrom = 0; |
| 1072 currentRun->m_numCharacters); | 1076 unsigned currentFontRangeTo = 0; |
| 1073 | 1077 |
| 1074 HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_de
stroy); | 1078 bool fontCycleQueued = false; |
| 1075 hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty()
? 0 : m_features.data(), m_features.size()); | 1079 while (m_holesQueue.size()) { |
| 1076 shapeResult(result.get(), i, currentRun, harfBuzzBuffer.get()); | 1080 HolesQueueItem currentQueueItem = m_holesQueue.takeFirst(); |
| 1077 | 1081 |
| 1078 hb_buffer_reset(harfBuzzBuffer.get()); | 1082 if (currentQueueItem.m_action == HolesQueueNextFont) { |
| 1083 // For now, we're building a character list with which we probe |
| 1084 // for needed fonts depending on the declared unicode-range of a |
| 1085 // segmented CSS font. Alternatively, we can build a fake font |
| 1086 // for the shaper and check whether any glyphs were found, or |
| 1087 // define a new API on the shaper which will give us coverage |
| 1088 // information? |
| 1089 if (!collectFallbackHintChars(fallbackCharsHint, fallbackIterato
r->needsHintList())) { |
| 1090 // Give up shaping since we cannot retrieve a font fallback |
| 1091 // font without a hintlist. |
| 1092 m_holesQueue.clear(); |
| 1093 break; |
| 1094 } |
| 1095 |
| 1096 FontDataRange nextFontDataRange = fallbackIterator->next(fallbac
kCharsHint); |
| 1097 currentFont = nextFontDataRange.fontData().get(); |
| 1098 currentFontRangeFrom = nextFontDataRange.from(); |
| 1099 currentFontRangeTo = nextFontDataRange.to(); |
| 1100 if (!currentFont) { |
| 1101 ASSERT(!m_holesQueue.size()); |
| 1102 break; |
| 1103 } |
| 1104 fontCycleQueued = false; |
| 1105 continue; |
| 1106 } |
| 1107 |
| 1108 // TODO crbug.com/522964: Only use smallCapsFontData when the font d
oes not support true smcp. The spec |
| 1109 // says: "To match the surrounding text, a font may provide alternat
e glyphs for caseless characters when |
| 1110 // these features are enabled but when a user agent simulates small
capitals, it must not attempt to |
| 1111 // simulate alternates for codepoints which are considered caseless.
" |
| 1112 const SimpleFontData* smallcapsAdjustedFont = segmentRange.smallCaps
Behavior == SmallCapsIterator::SmallCapsUppercaseNeeded |
| 1113 ? currentFont->smallCapsFontData(fontDescription).get() |
| 1114 : currentFont; |
| 1115 |
| 1116 // Compatibility with SimpleFontData approach of keeping a flag for
overriding drawing direction. |
| 1117 // TODO: crbug.com/506224 This should go away in favor of storing th
at information elsewhere, for example in |
| 1118 // ShapeResult. |
| 1119 const SimpleFontData* directionAndSmallCapsAdjustedFont = fontDataAd
justedForOrientation(smallcapsAdjustedFont, |
| 1120 m_font->fontDescription().orientation(), |
| 1121 segmentRange.renderOrientation); |
| 1122 |
| 1123 if (!shapeRange(harfBuzzBuffer.get(), |
| 1124 currentQueueItem.m_startIndex, |
| 1125 currentQueueItem.m_numCharacters, |
| 1126 directionAndSmallCapsAdjustedFont, |
| 1127 currentFontRangeFrom, |
| 1128 currentFontRangeTo, |
| 1129 segmentRange.script, |
| 1130 language)) |
| 1131 WTF_LOG_ERROR("Shaping range failed."); |
| 1132 |
| 1133 if (!extractShapeResults(harfBuzzBuffer.get(), |
| 1134 result.get(), |
| 1135 fontCycleQueued, |
| 1136 currentQueueItem, |
| 1137 directionAndSmallCapsAdjustedFont, |
| 1138 segmentRange.script, |
| 1139 !fallbackIterator->hasNext())) |
| 1140 WTF_LOG_ERROR("Shape result extraction failed."); |
| 1141 |
| 1142 hb_buffer_reset(harfBuzzBuffer.get()); |
| 1143 } |
| 1079 } | 1144 } |
| 1080 | |
| 1081 // We should have consumed all expansion opportunities. | |
| 1082 // Failures here means that our logic does not match to the one in expansion
OpportunityCount(). | |
| 1083 // FIXME: Ideally, we should ASSERT(!m_expansionOpportunityCount) here to en
sure that, | |
| 1084 // or unify the two logics (and the one in SimplePath too,) but there are so
me cases where our impl | |
| 1085 // does not support justification very well yet such as U+3099, and it'll ca
use the ASSERT to fail. | |
| 1086 // It's to be fixed because they're very rarely used, and a broken justifica
tion is still somewhat readable. | |
| 1087 | |
| 1088 return result.release(); | 1145 return result.release(); |
| 1089 } | 1146 } |
| 1090 | 1147 |
| 1091 void HarfBuzzShaper::shapeResult(ShapeResult* result, unsigned index, | 1148 // TODO crbug.com/542701: This should be a method on ShapeResult. |
| 1092 const HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer) | 1149 void HarfBuzzShaper::insertRunIntoShapeResult(ShapeResult* result, |
| 1150 PassOwnPtr<ShapeResult::RunInfo> runToInsert, unsigned startGlyph, unsigned
numGlyphs, |
| 1151 int bufferStartCharIndex, hb_buffer_t* harfBuzzBuffer) |
| 1093 { | 1152 { |
| 1094 unsigned numGlyphs = hb_buffer_get_length(harfBuzzBuffer); | 1153 ASSERT(numGlyphs > 0); |
| 1095 if (!numGlyphs) { | 1154 OwnPtr<ShapeResult::RunInfo> run(runToInsert); |
| 1096 result->m_runs[index] = nullptr; | |
| 1097 return; | |
| 1098 } | |
| 1099 | 1155 |
| 1100 OwnPtr<ShapeResult::RunInfo> run = adoptPtr(new ShapeResult::RunInfo(current
Run->m_fontData, | 1156 const SimpleFontData* currentFontData = run->m_fontData.get(); |
| 1101 currentRun->m_direction, currentRun->m_script, currentRun->m_startIndex, | |
| 1102 numGlyphs, currentRun->m_numCharacters)); | |
| 1103 | |
| 1104 const SimpleFontData* currentFontData = currentRun->m_fontData; | |
| 1105 hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); | 1157 hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); |
| 1106 hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzz
Buffer, 0); | 1158 hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzz
Buffer, 0); |
| 1107 | 1159 |
| 1108 float totalAdvance = 0.0f; | 1160 float totalAdvance = 0.0f; |
| 1109 FloatPoint glyphOrigin; | 1161 FloatPoint glyphOrigin; |
| 1110 float offsetX, offsetY; | 1162 float offsetX, offsetY; |
| 1111 float* directionOffset = m_font->fontDescription().isVerticalAnyUpright() ?
&offsetY : &offsetX; | 1163 float* directionOffset = m_font->fontDescription().isVerticalAnyUpright() ?
&offsetY : &offsetX; |
| 1112 | 1164 |
| 1113 // HarfBuzz returns result in visual order, no need to flip for RTL. | 1165 // HarfBuzz returns result in visual order, no need to flip for RTL. |
| 1114 for (size_t i = 0; i < numGlyphs; ++i) { | 1166 for (unsigned i = 0; i < numGlyphs; ++i) { |
| 1115 bool runEnd = i + 1 == numGlyphs; | 1167 bool runEnd = i + 1 == numGlyphs; |
| 1116 uint16_t glyph = glyphInfos[i].codepoint; | 1168 uint16_t glyph = glyphInfos[startGlyph + i].codepoint; |
| 1117 offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset); | 1169 offsetX = harfBuzzPositionToFloat(glyphPositions[startGlyph + i].x_offse
t); |
| 1118 offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset); | 1170 offsetY = -harfBuzzPositionToFloat(glyphPositions[startGlyph + i].y_offs
et); |
| 1119 | 1171 |
| 1120 // One out of x_advance and y_advance is zero, depending on | 1172 // One out of x_advance and y_advance is zero, depending on |
| 1121 // whether the buffer direction is horizontal or vertical. | 1173 // whether the buffer direction is horizontal or vertical. |
| 1122 float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance - gl
yphPositions[i].y_advance); | 1174 float advance = harfBuzzPositionToFloat(glyphPositions[startGlyph + i].x
_advance - glyphPositions[startGlyph + i].y_advance); |
| 1123 unsigned currentCharacterIndex = currentRun->m_startIndex + glyphInfos[i
].cluster; | 1175 unsigned currentCharacterIndex = bufferStartCharIndex + glyphInfos[start
Glyph + i].cluster; |
| 1124 RELEASE_ASSERT(m_normalizedBufferLength > currentCharacterIndex); | 1176 RELEASE_ASSERT(m_normalizedBufferLength > currentCharacterIndex); |
| 1125 bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1]
.cluster; | 1177 bool isClusterEnd = runEnd || glyphInfos[startGlyph + i].cluster != glyp
hInfos[startGlyph + i + 1].cluster; |
| 1126 float spacing = 0; | 1178 float spacing = 0; |
| 1127 | 1179 |
| 1128 run->m_glyphData[i].characterIndex = glyphInfos[i].cluster; | 1180 // The characterIndex of one ShapeResult run is normalized to the run's |
| 1181 // startIndex and length. TODO crbug.com/542703: Consider changing that |
| 1182 // and instead pass the whole run to hb_buffer_t each time. |
| 1183 run->m_glyphData.resize(numGlyphs); |
| 1184 if (HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(harfBuzzBuffer))) { |
| 1185 run->m_glyphData[i].characterIndex = glyphInfos[startGlyph + i].clus
ter - glyphInfos[startGlyph].cluster; |
| 1186 } else { |
| 1187 run->m_glyphData[i].characterIndex = glyphInfos[startGlyph + i].clus
ter - glyphInfos[startGlyph + numGlyphs - 1].cluster; |
| 1188 } |
| 1129 | 1189 |
| 1130 if (isClusterEnd) | 1190 if (isClusterEnd) |
| 1131 spacing += adjustSpacing(run.get(), i, currentCharacterIndex, *direc
tionOffset, totalAdvance); | 1191 spacing += adjustSpacing(run.get(), i, currentCharacterIndex, *direc
tionOffset, totalAdvance); |
| 1132 | 1192 |
| 1133 if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { | 1193 if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { |
| 1134 run->setGlyphAndPositions(i, glyph, 0, 0, 0); | 1194 run->setGlyphAndPositions(i, glyph, 0, 0, 0); |
| 1135 continue; | 1195 continue; |
| 1136 } | 1196 } |
| 1137 | 1197 |
| 1138 advance += spacing; | 1198 advance += spacing; |
| 1139 if (m_textRun.rtl()) { | 1199 if (m_textRun.rtl()) { |
| 1140 // In RTL, spacing should be added to left side of glyphs. | 1200 // In RTL, spacing should be added to left side of glyphs. |
| 1141 *directionOffset += spacing; | 1201 *directionOffset += spacing; |
| 1142 if (!isClusterEnd) | 1202 if (!isClusterEnd) |
| 1143 *directionOffset += m_letterSpacing; | 1203 *directionOffset += m_letterSpacing; |
| 1144 } | 1204 } |
| 1145 | 1205 |
| 1146 run->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); | 1206 run->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); |
| 1147 totalAdvance += advance; | 1207 totalAdvance += advance; |
| 1148 | 1208 |
| 1149 FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph); | 1209 FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph); |
| 1150 glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); | 1210 glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); |
| 1151 result->m_glyphBoundingBox.unite(glyphBounds); | 1211 result->m_glyphBoundingBox.unite(glyphBounds); |
| 1152 glyphOrigin += FloatSize(advance + offsetX, offsetY); | 1212 glyphOrigin += FloatSize(advance + offsetX, offsetY); |
| 1153 } | 1213 } |
| 1154 | |
| 1155 run->m_width = std::max(0.0f, totalAdvance); | 1214 run->m_width = std::max(0.0f, totalAdvance); |
| 1156 result->m_width += run->m_width; | 1215 result->m_width += run->m_width; |
| 1157 result->m_numGlyphs += numGlyphs; | 1216 result->m_numGlyphs += numGlyphs; |
| 1158 result->m_runs[index] = run.release(); | 1217 |
| 1218 // The runs are stored in result->m_runs in visual order. For LTR, we place |
| 1219 // the run to be inserted before the next run with a bigger character |
| 1220 // start index. For RTL, we place the run before the next run with a lower |
| 1221 // character index. Otherwise, for both directions, at the end. |
| 1222 if (HB_DIRECTION_IS_FORWARD(run->m_direction)) { |
| 1223 for (size_t pos = 0; pos < result->m_runs.size(); ++pos) { |
| 1224 if (result->m_runs.at(pos)->m_startIndex > run->m_startIndex) { |
| 1225 result->m_runs.insert(pos, run.release()); |
| 1226 break; |
| 1227 } |
| 1228 } |
| 1229 } else { |
| 1230 for (size_t pos = 0; pos < result->m_runs.size(); ++pos) { |
| 1231 if (result->m_runs.at(pos)->m_startIndex < run->m_startIndex) { |
| 1232 result->m_runs.insert(pos, run.release()); |
| 1233 break; |
| 1234 } |
| 1235 } |
| 1236 } |
| 1237 // If we didn't find an existing slot to place it, append. |
| 1238 if (run) { |
| 1239 result->m_runs.append(run.release()); |
| 1240 } |
| 1159 } | 1241 } |
| 1160 | 1242 |
| 1161 PassRefPtr<ShapeResult> ShapeResult::createForTabulationCharacters(const Font* f
ont, | 1243 PassRefPtr<ShapeResult> ShapeResult::createForTabulationCharacters(const Font* f
ont, |
| 1162 const TextRun& textRun, float positionOffset, unsigned count) | 1244 const TextRun& textRun, float positionOffset, unsigned count) |
| 1163 { | 1245 { |
| 1164 const SimpleFontData* fontData = font->primaryFont(); | 1246 const SimpleFontData* fontData = font->primaryFont(); |
| 1165 OwnPtr<ShapeResult::RunInfo> run = adoptPtr(new ShapeResult::RunInfo(fontDat
a, | 1247 OwnPtr<ShapeResult::RunInfo> run = adoptPtr(new ShapeResult::RunInfo(fontDat
a, |
| 1166 // Tab characters are always LTR or RTL, not TTB, even when isVerticalAn
yUpright(). | 1248 // Tab characters are always LTR or RTL, not TTB, even when isVerticalAn
yUpright(). |
| 1167 textRun.rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR, | 1249 textRun.rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR, |
| 1168 HB_SCRIPT_COMMON, 0, count, count)); | 1250 HB_SCRIPT_COMMON, 0, count, count)); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1232 if (!m_expansionOpportunityCount) | 1314 if (!m_expansionOpportunityCount) |
| 1233 return spacing; | 1315 return spacing; |
| 1234 } | 1316 } |
| 1235 | 1317 |
| 1236 // Don't need to check m_textRun.allowsTrailingExpansion() since it's covere
d by !m_expansionOpportunityCount above | 1318 // Don't need to check m_textRun.allowsTrailingExpansion() since it's covere
d by !m_expansionOpportunityCount above |
| 1237 spacing += nextExpansionPerOpportunity(); | 1319 spacing += nextExpansionPerOpportunity(); |
| 1238 m_isAfterExpansion = true; | 1320 m_isAfterExpansion = true; |
| 1239 return spacing; | 1321 return spacing; |
| 1240 } | 1322 } |
| 1241 | 1323 |
| 1324 |
| 1242 } // namespace blink | 1325 } // namespace blink |
| OLD | NEW |