Bug 1108177 - Implement harfbuzz glyph-extents callback, so that fallback mark positioning works in legacy truetype fonts. r=jdaggett
authorJonathan Kew <jkew@mozilla.com>
Wed, 28 Jan 2015 09:03:28 +0000
changeset 239608 02601ab9f9afcfa06fbc5185e816e6df71accce3
parent 239607 f81de13e4ad2922f052e33fdbd7036f2441ab645
child 239609 0edb544e9ebc2859ba54327d82a380f1715f6a44
push id500
push userjoshua.m.grant@gmail.com
push dateThu, 29 Jan 2015 01:48:36 +0000
reviewersjdaggett
bugs1108177
milestone38.0a1
Bug 1108177 - Implement harfbuzz glyph-extents callback, so that fallback mark positioning works in legacy truetype fonts. r=jdaggett
gfx/thebes/gfxHarfBuzzShaper.cpp
gfx/thebes/gfxHarfBuzzShaper.h
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -48,16 +48,17 @@ gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfx
       mSubtableOffset(0),
       mUVSTableOffset(0),
       mNumLongHMetrics(0),
       mNumLongVMetrics(0),
       mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
       mUseFontGlyphWidths(false),
       mInitialized(false),
       mVerticalInitialized(false),
+      mLoadedLocaGlyf(false),
       mLocaLongOffsets(false)
 {
 }
 
 gfxHarfBuzzShaper::~gfxHarfBuzzShaper()
 {
     if (mCmapTable) {
         hb_blob_destroy(mCmapTable);
@@ -341,77 +342,37 @@ gfxHarfBuzzShaper::GetGlyphVOrigin(hb_co
         } else {
             *aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
                                 int16_t(vorg->defaultVertOriginY));
         }
         return;
     }
 
     if (mVmtxTable) {
-        if (mLocaTable && mGlyfTable) {
-            // TrueType outlines: use glyph bbox + top sidebearing
-            uint32_t offset; // offset of glyph record in the 'glyf' table
-            uint32_t len;
-            const char* data = hb_blob_get_data(mLocaTable, &len);
-            if (mLocaLongOffsets) {
-                if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) {
-                    *aY = 0;
-                    return;
-                }
-                const AutoSwap_PRUint32* offsets =
-                    reinterpret_cast<const AutoSwap_PRUint32*>(data);
-                offset = offsets[aGlyph];
-                if (offset == offsets[aGlyph + 1]) {
-                    // empty glyph
-                    *aY = 0;
-                    return;
-                }
-            } else {
-                if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) {
-                    *aY = 0;
-                    return;
-                }
-                const AutoSwap_PRUint16* offsets =
-                    reinterpret_cast<const AutoSwap_PRUint16*>(data);
-                offset = uint16_t(offsets[aGlyph]);
-                if (offset == uint16_t(offsets[aGlyph + 1])) {
-                    // empty glyph
-                    *aY = 0;
-                    return;
-                }
-                offset *= 2;
-            }
-
-            struct Glyf { // we only need the bounding-box at the beginning
-                          // of the glyph record, not the actual outline data
-                AutoSwap_PRInt16 numberOfContours;
-                AutoSwap_PRInt16 xMin;
-                AutoSwap_PRInt16 yMin;
-                AutoSwap_PRInt16 xMax;
-                AutoSwap_PRInt16 yMax;
-            };
-            data = hb_blob_get_data(mGlyfTable, &len);
-            if (offset + sizeof(Glyf) > len) {
+        bool emptyGlyf;
+        const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf);
+        if (glyf) {
+            if (emptyGlyf) {
                 *aY = 0;
                 return;
             }
-            const Glyf* glyf = reinterpret_cast<const Glyf*>(data + offset);
 
             if (aGlyph >= uint32_t(mNumLongVMetrics)) {
                 aGlyph = mNumLongVMetrics - 1;
             }
             const GlyphMetrics* metrics =
                 reinterpret_cast<const GlyphMetrics*>
                     (hb_blob_get_data(mVmtxTable, nullptr));
             *aY = -FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
                                 (int16_t(metrics->metrics[aGlyph].lsb) +
                                  int16_t(glyf->yMax)));
             return;
         } else {
-            // XXX TODO: CFF outlines - need to get glyph extents.
+            // XXX TODO: not a truetype font; need to get glyph extents
+            // via some other API?
             // For now, fall through to default code below.
         }
     }
 
     // XXX should we consider using OS/2 sTypo* metrics if available?
 
     gfxFontEntry::AutoTable hheaTable(GetFont()->GetFontEntry(),
                                       TRUETYPE_TAG('h','h','e','a'));
@@ -427,16 +388,122 @@ gfxHarfBuzzShaper::GetGlyphVOrigin(hb_co
         }
     }
 
     NS_NOTREACHED("we shouldn't be here!");
     *aY = -FloatToFixed(GetFont()->GetAdjustedSize() / 2);
 }
 
 static hb_bool_t
+HBGetGlyphExtents(hb_font_t *font, void *font_data,
+                  hb_codepoint_t glyph,
+                  hb_glyph_extents_t *extents,
+                  void *user_data)
+{
+    const gfxHarfBuzzShaper::FontCallbackData *fcd =
+        static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+    return fcd->mShaper->GetGlyphExtents(glyph, extents);
+}
+
+// Find the data for glyph ID |aGlyph| in the 'glyf' table, if present.
+// Returns null if not found, otherwise pointer to the beginning of the
+// glyph's data. Sets aEmptyGlyf true if there is no actual data;
+// otherwise, it's guaranteed that we can read at least the bounding box.
+const gfxHarfBuzzShaper::Glyf*
+gfxHarfBuzzShaper::FindGlyf(hb_codepoint_t aGlyph, bool *aEmptyGlyf) const
+{
+    if (!mLoadedLocaGlyf) {
+        mLoadedLocaGlyf = true; // only try this once; if it fails, this
+                                // isn't a truetype font
+        gfxFontEntry *entry = mFont->GetFontEntry();
+        uint32_t len;
+        gfxFontEntry::AutoTable headTable(entry,
+                                          TRUETYPE_TAG('h','e','a','d'));
+        const HeadTable* head =
+            reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
+                                                                &len));
+        if (len < sizeof(HeadTable)) {
+            return nullptr;
+        }
+        mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0;
+        mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l','o','c','a'));
+        mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g','l','y','f'));
+    }
+
+    if (!mLocaTable || !mGlyfTable) {
+        // it's not a truetype font
+        return nullptr;
+    }
+
+    uint32_t offset; // offset of glyph record in the 'glyf' table
+    uint32_t len;
+    const char* data = hb_blob_get_data(mLocaTable, &len);
+    if (mLocaLongOffsets) {
+        if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) {
+            return nullptr;
+        }
+        const AutoSwap_PRUint32* offsets =
+            reinterpret_cast<const AutoSwap_PRUint32*>(data);
+        offset = offsets[aGlyph];
+        *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
+    } else {
+        if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) {
+            return nullptr;
+        }
+        const AutoSwap_PRUint16* offsets =
+            reinterpret_cast<const AutoSwap_PRUint16*>(data);
+        offset = uint16_t(offsets[aGlyph]);
+        *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
+        offset *= 2;
+    }
+
+    data = hb_blob_get_data(mGlyfTable, &len);
+    if (offset + sizeof(Glyf) > len) {
+        return nullptr;
+    }
+
+    return reinterpret_cast<const Glyf*>(data + offset);
+}
+
+hb_bool_t
+gfxHarfBuzzShaper::GetGlyphExtents(hb_codepoint_t aGlyph,
+                                   hb_glyph_extents_t *aExtents) const
+{
+    bool emptyGlyf;
+    const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf);
+    if (!glyf) {
+        // TODO: for non-truetype fonts, get extents some other way?
+        return false;
+    }
+
+    if (emptyGlyf) {
+        aExtents->x_bearing = 0;
+        aExtents->y_bearing = 0;
+        aExtents->width = 0;
+        aExtents->height = 0;
+        return true;
+    }
+
+    double f = mFont->FUnitsToDevUnitsFactor();
+    aExtents->x_bearing = FloatToFixed(int16_t(glyf->xMin) * f);
+    aExtents->width =
+        FloatToFixed((int16_t(glyf->xMax) - int16_t(glyf->xMin)) * f);
+
+    // Our y-coordinates are positive-downwards, whereas harfbuzz assumes
+    // positive-upwards; hence the apparently-reversed subtractions here.
+    aExtents->y_bearing =
+        FloatToFixed(int16_t(glyf->yMax) * f -
+                     mFont->GetHorizontalMetrics().emAscent);
+    aExtents->height =
+        FloatToFixed((int16_t(glyf->yMin) - int16_t(glyf->yMax)) * f);
+
+    return true;
+}
+
+static hb_bool_t
 HBGetContourPoint(hb_font_t *font, void *font_data,
                   unsigned int point_index, hb_codepoint_t glyph,
                   hb_position_t *x, hb_position_t *y,
                   void *user_data)
 {
     /* not yet implemented - no support for used of hinted contour points
        to fine-tune anchor positions in GPOS AnchorFormat2 */
     return false;
@@ -1061,16 +1128,19 @@ gfxHarfBuzzShaper::Initialize()
                                                HBGetGlyphVAdvance,
                                                nullptr, nullptr);
         hb_font_funcs_set_glyph_h_origin_func(sHBFontFuncs,
                                               HBGetGlyphHOrigin,
                                               nullptr, nullptr);
         hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs,
                                               HBGetGlyphVOrigin,
                                               nullptr, nullptr);
+        hb_font_funcs_set_glyph_extents_func(sHBFontFuncs,
+                                             HBGetGlyphExtents,
+                                             nullptr, nullptr);
         hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
                                                    HBGetContourPoint,
                                                    nullptr, nullptr);
         hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
                                                HBGetHKerning,
                                                nullptr, nullptr);
 
         sHBUnicodeFuncs =
@@ -1220,30 +1290,16 @@ gfxHarfBuzzShaper::InitializeVertical()
                               sizeof(VORGrec)) {
                 // VORG table is an unknown version, or not large enough
                 // to be valid -- discard it.
                 NS_WARNING("discarding invalid VORG table");
                 hb_blob_destroy(mVORGTable);
                 mVORGTable = nullptr;
             }
         }
-    } else if (mVmtxTable) {
-        // Otherwise, try to load loca and glyf tables so that we can read
-        // bounding boxes (needed to support vertical glyph origin).
-        uint32_t len;
-        gfxFontEntry::AutoTable headTable(entry,
-                                          TRUETYPE_TAG('h','e','a','d'));
-        const HeadTable* head =
-            reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
-                                                                &len));
-        if (len >= sizeof(HeadTable)) {
-            mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0;
-            mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l','o','c','a'));
-            mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g','l','y','f'));
-        }
     }
 
     return true;
 }
 
 bool
 gfxHarfBuzzShaper::ShapeText(gfxContext      *aContext,
                              const char16_t *aText,
--- a/gfx/thebes/gfxHarfBuzzShaper.h
+++ b/gfx/thebes/gfxHarfBuzzShaper.h
@@ -68,16 +68,19 @@ public:
     HBGetGlyphVOrigin(hb_font_t *font, void *font_data,
                       hb_codepoint_t glyph,
                       hb_position_t *x, hb_position_t *y,
                       void *user_data);
 
     hb_position_t GetHKerning(uint16_t aFirstGlyph,
                               uint16_t aSecondGlyph) const;
 
+    hb_bool_t GetGlyphExtents(hb_codepoint_t aGlyph,
+                              hb_glyph_extents_t *aExtents) const;
+
     static hb_script_t
     GetHBScriptUsedForShaping(int32_t aScript) {
         // Decide what harfbuzz script code will be used for shaping
         hb_script_t hbScript;
         if (aScript <= MOZ_SCRIPT_INHERITED) {
             // For unresolved "common" or "inherited" runs,
             // default to Latin for now.
             hbScript = HB_SCRIPT_LATIN;
@@ -102,16 +105,27 @@ protected:
     nscoord GetGlyphPositions(gfxContext *aContext,
                               hb_buffer_t *aBuffer,
                               nsTArray<nsPoint>& aPositions,
                               uint32_t aAppUnitsPerDevUnit);
 
     bool InitializeVertical();
     bool LoadHmtxTable();
 
+    struct Glyf { // we only need the bounding-box at the beginning
+                  // of the glyph record, not the actual outline data
+        AutoSwap_PRInt16 numberOfContours;
+        AutoSwap_PRInt16 xMin;
+        AutoSwap_PRInt16 yMin;
+        AutoSwap_PRInt16 xMax;
+        AutoSwap_PRInt16 yMax;
+    };
+
+    const Glyf *FindGlyf(hb_codepoint_t aGlyph, bool *aEmptyGlyf) const;
+
     // harfbuzz face object: we acquire a reference from the font entry
     // on shaper creation, and release it in our destructor
     hb_face_t         *mHBFace;
 
     // size-specific font object, owned by the gfxHarfBuzzShaper
     hb_font_t         *mHBFont;
 
     FontCallbackData   mCallbackData;
@@ -156,12 +170,15 @@ protected:
     // directly
     bool mUseFontGetGlyph;
     // Whether the font implements GetGlyphWidth, or we should read tables
     // directly to get ideal widths
     bool mUseFontGlyphWidths;
 
     bool mInitialized;
     bool mVerticalInitialized;
-    bool mLocaLongOffsets;
+
+    // these are set from the FindGlyf callback on first use of the glyf data
+    mutable bool mLoadedLocaGlyf;
+    mutable bool mLocaLongOffsets;
 };
 
 #endif /* GFX_HARFBUZZSHAPER_H */