Bug 1431305 - Handle fallback (rotated) rendering of characters with Vertical_Orientation=Tr when the font does not support them via 'vert', nor is there a vertical presentation form encoded in Unicode. r=m_kato
authorJonathan Kew <jkew@mozilla.com>
Fri, 19 Jan 2018 10:46:53 +0000
changeset 454411 d76b88de07ca4fff02828d24db2dbce6e4f3d7e4
parent 454410 af59a3ed2fcc7fd1d1c91da2598573ee110a1935
child 454412 794abd47aa0cbcc2608b7e5c38f14227d9c5dd8e
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1431305
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1431305 - Handle fallback (rotated) rendering of characters with Vertical_Orientation=Tr when the font does not support them via 'vert', nor is there a vertical presentation form encoded in Unicode. r=m_kato
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontEntry.cpp
gfx/thebes/gfxHarfBuzzShaper.cpp
gfx/thebes/gfxHarfBuzzShaper.h
gfx/thebes/gfxTextRun.cpp
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1517,16 +1517,50 @@ gfxFont::SupportsSubSuperscript(uint32_t
     hb_set_intersect(defaultGlyphsInRun, inputGlyphs);
     uint32_t intersectionSize = hb_set_get_population(defaultGlyphsInRun);
     hb_set_destroy(defaultGlyphsInRun);
 
     return origSize == intersectionSize;
 }
 
 bool
+gfxFont::FeatureWillHandleChar(Script aRunScript, uint32_t aFeature,
+                               uint32_t aUnicode)
+{
+    if (!SupportsFeature(aRunScript, aFeature)) {
+        return false;
+    }
+
+    // xxx - for graphite, don't really know how to sniff lookups so bail
+    if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
+        return true;
+    }
+
+    if (!mHarfBuzzShaper) {
+        mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
+    }
+    gfxHarfBuzzShaper* shaper =
+        static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
+    if (!shaper->Initialize()) {
+        return false;
+    }
+
+    // get the hbset containing input glyphs for the feature
+    const hb_set_t *inputGlyphs =
+        mFontEntry->InputsForOpenTypeFeature(aRunScript, aFeature);
+
+    if (aUnicode == 0xa0) {
+        aUnicode = ' ';
+    }
+
+    hb_codepoint_t gid = shaper->GetNominalGlyph(aUnicode);
+    return hb_set_has(inputGlyphs, gid);
+}
+
+bool
 gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn)
 {
     aFeatureOn = false;
 
     if (mStyle.featureSettings.IsEmpty() &&
         GetFontEntry()->mFeatureSettings.IsEmpty()) {
         return false;
     }
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1588,16 +1588,20 @@ public:
                                 uint32_t aLength,
                                 Script aRunScript);
 
     bool SupportsSubSuperscript(uint32_t aSubSuperscript,
                                 const char16_t *aString,
                                 uint32_t aLength,
                                 Script aRunScript);
 
+    // whether the specified feature will apply to the given character
+    bool FeatureWillHandleChar(Script aRunScript, uint32_t aFeature,
+                               uint32_t aUnicode);
+
     // Subclasses may choose to look up glyph ids for characters.
     // If they do not override this, gfxHarfBuzzShaper will fetch the cmap
     // table and use that.
     virtual bool ProvidesGetGlyph() const {
         return false;
     }
     // Map unicode character to glyph ID.
     // Only used if ProvidesGetGlyph() returns true.
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -843,17 +843,18 @@ gfxFontEntry::SupportsOpenTypeFeature(Sc
 const hb_set_t*
 gfxFontEntry::InputsForOpenTypeFeature(Script aScript, uint32_t aFeatureTag)
 {
     if (!mFeatureInputs) {
         mFeatureInputs = MakeUnique<nsDataHashtable<nsUint32HashKey,hb_set_t*>>();
     }
 
     NS_ASSERTION(aFeatureTag == HB_TAG('s','u','p','s') ||
-                 aFeatureTag == HB_TAG('s','u','b','s'),
+                 aFeatureTag == HB_TAG('s','u','b','s') ||
+                 aFeatureTag == HB_TAG('v','e','r','t'),
                  "use of unknown feature tag");
 
     uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
     hb_set_t *inputGlyphs;
     if (mFeatureInputs->Get(scriptFeature, &inputGlyphs)) {
         return inputGlyphs;
     }
 
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -204,18 +204,18 @@ gfxHarfBuzzShaper::GetVariationGlyph(hb_
 static int
 VertFormsGlyphCompare(const void* aKey, const void* aElem)
 {
     return int(*((hb_codepoint_t*)(aKey))) - int(*((uint16_t*)(aElem)));
 }
 
 // Return a vertical presentation-form codepoint corresponding to the
 // given Unicode value, or 0 if no such form is available.
-static hb_codepoint_t
-GetVerticalPresentationForm(hb_codepoint_t unicode)
+hb_codepoint_t
+gfxHarfBuzzShaper::GetVerticalPresentationForm(hb_codepoint_t aUnicode)
 {
     static const uint16_t sVerticalForms[][2] = {
         { 0x2013, 0xfe32 }, // EN DASH
         { 0x2014, 0xfe31 }, // EM DASH
         { 0x2025, 0xfe30 }, // TWO DOT LEADER
         { 0x2026, 0xfe19 }, // HORIZONTAL ELLIPSIS
         { 0x3001, 0xfe11 }, // IDEOGRAPHIC COMMA
         { 0x3002, 0xfe12 }, // IDEOGRAPHIC FULL STOP
@@ -243,17 +243,17 @@ GetVerticalPresentationForm(hb_codepoint
         { 0xff1f, 0xfe16 }, // FULLWIDTH QUESTION MARK
         { 0xff3b, 0xfe47 }, // FULLWIDTH LEFT SQUARE BRACKET
         { 0xff3d, 0xfe48 }, // FULLWIDTH RIGHT SQUARE BRACKET
         { 0xff3f, 0xfe33 }, // FULLWIDTH LOW LINE
         { 0xff5b, 0xfe37 }, // FULLWIDTH LEFT CURLY BRACKET
         { 0xff5d, 0xfe38 }  // FULLWIDTH RIGHT CURLY BRACKET
     };
     const uint16_t* charPair =
-        static_cast<const uint16_t*>(bsearch(&unicode,
+        static_cast<const uint16_t*>(bsearch(&aUnicode,
                                              sVerticalForms,
                                              ArrayLength(sVerticalForms),
                                              sizeof(sVerticalForms[0]),
                                              VertFormsGlyphCompare));
     return charPair ? charPair[1] : 0;
 }
 
 static hb_bool_t
@@ -261,17 +261,18 @@ HBGetNominalGlyph(hb_font_t *font, void 
                   hb_codepoint_t unicode,
                   hb_codepoint_t *glyph,
                   void *user_data)
 {
     const gfxHarfBuzzShaper::FontCallbackData *fcd =
         static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
 
     if (fcd->mShaper->UseVerticalPresentationForms()) {
-        hb_codepoint_t verticalForm = GetVerticalPresentationForm(unicode);
+        hb_codepoint_t verticalForm =
+            gfxHarfBuzzShaper::GetVerticalPresentationForm(unicode);
         if (verticalForm) {
             *glyph = fcd->mShaper->GetNominalGlyph(verticalForm);
             if (*glyph != 0) {
                 return true;
             }
         }
         // fall back to the non-vertical form if we didn't find an alternate
     }
@@ -285,17 +286,18 @@ HBGetVariationGlyph(hb_font_t *font, voi
                     hb_codepoint_t unicode, hb_codepoint_t variation_selector,
                     hb_codepoint_t *glyph,
                     void *user_data)
 {
     const gfxHarfBuzzShaper::FontCallbackData *fcd =
         static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
 
     if (fcd->mShaper->UseVerticalPresentationForms()) {
-        hb_codepoint_t verticalForm = GetVerticalPresentationForm(unicode);
+        hb_codepoint_t verticalForm =
+            gfxHarfBuzzShaper::GetVerticalPresentationForm(unicode);
         if (verticalForm) {
             *glyph = fcd->mShaper->GetVariationGlyph(verticalForm,
                                                      variation_selector);
             if (*glyph != 0) {
                 return true;
             }
         }
         // fall back to the non-vertical form if we didn't find an alternate
--- a/gfx/thebes/gfxHarfBuzzShaper.h
+++ b/gfx/thebes/gfxHarfBuzzShaper.h
@@ -92,16 +92,19 @@ public:
             hbScript = HB_SCRIPT_LATIN;
         } else {
             hbScript =
                 hb_script_t(mozilla::unicode::GetScriptTagForCode(aScript));
         }
         return hbScript;
     }
 
+    static hb_codepoint_t
+    GetVerticalPresentationForm(hb_codepoint_t aUnicode);
+
 protected:
     nsresult SetGlyphsFromRun(gfxShapedText  *aShapedText,
                               uint32_t        aOffset,
                               uint32_t        aLength,
                               const char16_t *aText,
                               bool            aVertical,
                               RoundingFlags   aRounding);
 
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -3175,20 +3175,35 @@ void gfxFontGroup::ComputeRanges(nsTArra
         prevCh = ch;
 
         ShapedTextFlags orient = aOrientation;
         if (aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
             // For CSS text-orientation:mixed, we need to resolve orientation
             // on a per-character basis using the UTR50 orientation property.
             switch (GetVerticalOrientation(ch)) {
             case VERTICAL_ORIENTATION_U:
-            case VERTICAL_ORIENTATION_Tr:
             case VERTICAL_ORIENTATION_Tu:
                 orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
                 break;
+            case VERTICAL_ORIENTATION_Tr: {
+                // We check for a vertical presentation form first as that's
+                // likely to be cheaper than inspecting lookups to see if the
+                // 'vert' feature is going to handle this character, and if the
+                // presentation form is available then it will be used as
+                // fallback if needed, so it's OK if the feature is missing.
+                uint32_t v = gfxHarfBuzzShaper::GetVerticalPresentationForm(ch);
+                orient = (!font ||
+                          (v && font->HasCharacter(v)) ||
+                          font->FeatureWillHandleChar(aRunScript,
+                                                      HB_TAG('v','e','r','t'),
+                                                      ch))
+                         ? ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
+                         : ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
+                break;
+            }
             case VERTICAL_ORIENTATION_R:
                 orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
                 break;
             }
         }
 
         if (lastRangeIndex == -1) {
             // first char ==> make a new range