Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(226)

Side by Side Diff: third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp

Issue 1397423004: Improve shaping segmentation for grapheme cluster based font fallback (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: merge TestExpectations with the HarfBuzz rebaselines Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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*>(&currentF 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
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
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698