Bug 1065002 pt 1.2 - Add CreateVerticalMetrics to gfxFont, to read/synthesize metrics for vertical layout. r=jdaggett
authorJonathan Kew <jkew@mozilla.com>
Tue, 30 Sep 2014 07:38:06 +0100
changeset 207845 adbf88894825a28a5af5a2a3d7cc2efd3321aa46
parent 207844 acfc96bde64d5944b42994a0f093b8535b23a3d2
child 207846 c9c0762bbe7806eae5d25208c74438c54fa0b9e9
push id27568
push usercbook@mozilla.com
push dateTue, 30 Sep 2014 13:03:13 +0000
treeherdermozilla-central@4475aa556e69 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs1065002
milestone35.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 1065002 pt 1.2 - Add CreateVerticalMetrics to gfxFont, to read/synthesize metrics for vertical layout. r=jdaggett
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3075,16 +3075,19 @@ gfxFont::InitMetricsFromSfntTables(Metri
                 mFontEntry->HasFontTable(TRUETYPE_TAG('M','A','T','H'))) {
                 SET_SIGNED(maxAscent, os2->sTypoAscender);
                 SET_SIGNED(maxDescent, - int16_t(os2->sTypoDescender));
                 SET_SIGNED(externalLeading, os2->sTypoLineGap);
             }
         }
     }
 
+#undef SET_SIGNED
+#undef SET_UNSIGNED
+
     mIsValid = true;
 
     return true;
 }
 
 static double
 RoundToNearestMultiple(double aValue, double aFraction)
 {
@@ -3200,16 +3203,162 @@ gfxFont::SanitizeMetrics(gfxFont::Metric
     }
 
     // If overline is larger than the ascent, the line should be resized.
     if (aMetrics->underlineSize > aMetrics->maxAscent) {
         aMetrics->underlineSize = aMetrics->maxAscent;
     }
 }
 
+// Create a Metrics record to be used for vertical layout. This should never
+// fail, as we've already decided this is a valid font. We do not have the
+// option of marking it invalid (as can happen if we're unable to read
+// horizontal metrics), because that could break a font that we're already
+// using for horizontal text.
+// So we will synthesize *something* usable here even if there aren't any of the
+// usual font tables (which can happen in the case of a legacy bitmap or Type1
+// font for which the platform-specific backend used platform APIs instead of
+// sfnt tables to create the horizontal metrics).
+const gfxFont::Metrics*
+gfxFont::CreateVerticalMetrics()
+{
+    const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
+    const uint32_t kVheaTableTag = TRUETYPE_TAG('v','h','e','a');
+    const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t');
+    const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
+    uint32_t len;
+
+    Metrics* metrics = new Metrics;
+    ::memset(metrics, 0, sizeof(Metrics));
+
+    // Some basic defaults, in case the font lacks any real metrics tables.
+    // TODO: consider what rounding (if any) we should apply to these.
+    metrics->emHeight = GetAdjustedSize();
+    metrics->emAscent = metrics->emHeight / 2;
+    metrics->emDescent = metrics->emHeight - metrics->emAscent;
+
+    metrics->maxAscent = metrics->emAscent;
+    metrics->maxDescent = metrics->emDescent;
+
+    const float UNINITIALIZED_LEADING = -10000.0f;
+    metrics->externalLeading = UNINITIALIZED_LEADING;
+
+#define SET_UNSIGNED(field,src) metrics->field = uint16_t(src) * mFUnitsConvFactor
+#define SET_SIGNED(field,src)   metrics->field = int16_t(src) * mFUnitsConvFactor
+
+    gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
+    if (os2Table) {
+        const OS2Table *os2 =
+            reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
+        // These fields should always be present in any valid OS/2 table
+        if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
+            SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
+            SET_SIGNED(aveCharWidth, int16_t(os2->sTypoAscender) -
+                                     int16_t(os2->sTypoDescender));
+            metrics->maxAscent =
+                std::max(metrics->maxAscent, int16_t(os2->xAvgCharWidth) *
+                                             gfxFloat(mFUnitsConvFactor));
+            metrics->maxDescent =
+                std::max(metrics->maxDescent, int16_t(os2->xAvgCharWidth) *
+                                              gfxFloat(mFUnitsConvFactor));
+        }
+    }
+
+    // If we didn't set aveCharWidth from OS/2, try to read 'hhea' metrics
+    // and use the line height from its ascent/descent.
+    if (!metrics->aveCharWidth) {
+        gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
+        if (hheaTable) {
+            const HheaTable* hhea =
+                reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable,
+                                                                    &len));
+            if (len >= sizeof(HheaTable)) {
+                SET_SIGNED(aveCharWidth, int16_t(hhea->ascender) -
+                                         int16_t(hhea->descender));
+                metrics->maxAscent = metrics->aveCharWidth / 2;
+                metrics->maxDescent =
+                    metrics->aveCharWidth - metrics->maxAscent;
+            }
+        }
+    }
+
+    // Read real vertical metrics if available.
+    gfxFontEntry::AutoTable vheaTable(mFontEntry, kVheaTableTag);
+    if (vheaTable) {
+        const HheaTable* vhea =
+            reinterpret_cast<const HheaTable*>(hb_blob_get_data(vheaTable,
+                                                                &len));
+        if (len >= sizeof(HheaTable)) {
+            SET_UNSIGNED(maxAdvance, vhea->advanceWidthMax);
+            SET_SIGNED(maxAscent, vhea->ascender);
+            SET_SIGNED(maxDescent, -int16_t(vhea->descender));
+            SET_SIGNED(externalLeading, vhea->lineGap);
+        }
+    }
+
+    // If we didn't set aveCharWidth above, we must be dealing with a non-sfnt
+    // font of some kind (Type1, bitmap, vector, ...), so fall back to using
+    // whatever the platform backend figured out for horizontal layout.
+    // And if we haven't set externalLeading yet, then copy that from the
+    // horizontal metrics as well, to help consistency of CSS line-height.
+    if (!metrics->aveCharWidth ||
+        metrics->externalLeading == UNINITIALIZED_LEADING) {
+        const Metrics& horizMetrics = GetHorizontalMetrics();
+        if (!metrics->aveCharWidth) {
+            metrics->aveCharWidth = horizMetrics.maxAscent + horizMetrics.maxDescent;
+        }
+        if (metrics->externalLeading == UNINITIALIZED_LEADING) {
+            metrics->externalLeading = horizMetrics.externalLeading;
+        }
+    }
+
+    // Get underline thickness from the 'post' table if available.
+    gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
+    if (postTable) {
+        const PostTable *post =
+            reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable,
+                                                                &len));
+        if (len >= offsetof(PostTable, underlineThickness) +
+                       sizeof(uint16_t)) {
+            SET_UNSIGNED(underlineSize, post->underlineThickness);
+            // Also use for strikeout if we didn't find that in OS/2 above.
+            if (!metrics->strikeoutSize) {
+                metrics->strikeoutSize = metrics->underlineSize;
+            }
+        }
+    }
+
+#undef SET_UNSIGNED
+#undef SET_SIGNED
+
+    // If we didn't read this from a vhea table, it will still be zero.
+    // In any case, let's make sure it is not less than the value we've
+    // come up with for aveCharWidth.
+    metrics->maxAdvance = std::max(metrics->maxAdvance, metrics->aveCharWidth);
+
+    // Thickness of underline and strikeout may have been read from tables,
+    // but in case they were not present, ensure a minimum of 1 pixel.
+    // We synthesize our own positions, as font metrics don't provide these
+    // for vertical layout.
+    metrics->underlineSize = std::max(1.0, metrics->underlineSize);
+    metrics->underlineOffset = 0; // XXX to be adjusted
+
+    metrics->strikeoutSize = std::max(1.0, metrics->strikeoutSize);
+    metrics->strikeoutOffset =
+        metrics->maxDescent - 0.5 * metrics->strikeoutSize;
+
+    // Somewhat arbitrary values for now, subject to future refinement...
+    metrics->spaceWidth = metrics->aveCharWidth;
+    metrics->zeroOrAveCharWidth = metrics->aveCharWidth;
+    metrics->maxHeight = metrics->maxAscent + metrics->maxDescent;
+    metrics->xHeight = metrics->emHeight / 2;
+
+    return metrics;
+}
+
 gfxFloat
 gfxFont::SynthesizeSpaceWidth(uint32_t aCh)
 {
     // return an appropriate width for various Unicode space characters
     // that we "fake" if they're not actually present in the font;
     // returns negative value if the char is not a known space.
     switch (aCh) {
     case 0x2000:                                 // en quad
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1736,16 +1736,18 @@ public:
 
     // return a cloned font resized and offset to simulate sub/superscript glyphs
     virtual already_AddRefed<gfxFont>
     GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel);
 
 protected:
     virtual const Metrics& GetHorizontalMetrics() = 0;
 
+    const Metrics* CreateVerticalMetrics();
+
     // Output a single glyph at *aPt, which is updated by the glyph's advance.
     // Normal glyphs are simply accumulated in aBuffer until it is full and
     // gets flushed, but SVG or color-font glyphs will instead be rendered
     // directly to the destination (found from the buffer's parameters).
     void DrawOneGlyph(uint32_t           aGlyphID,
                       double             aAdvance,
                       gfxPoint          *aPt,
                       GlyphBufferAzure&  aBuffer,
@@ -1973,16 +1975,19 @@ protected:
     // we create either or both of these shapers when needed, depending
     // whether the font has graphite tables, and whether graphite shaping
     // is actually enabled
     nsAutoPtr<gfxFontShaper>   mHarfBuzzShaper;
     nsAutoPtr<gfxFontShaper>   mGraphiteShaper;
 
     mozilla::RefPtr<mozilla::gfx::ScaledFont> mAzureScaledFont;
 
+    // For vertical metrics, created on demand.
+    nsAutoPtr<const Metrics> mVerticalMetrics;
+
     // Helper for subclasses that want to initialize standard metrics from the
     // tables of sfnt (TrueType/OpenType) fonts.
     // This will use mFUnitsConvFactor if it is already set, else compute it
     // from mAdjustedSize and the unitsPerEm in the font's 'head' table.
     // Returns TRUE and sets mIsValid=TRUE if successful;
     // Returns TRUE but leaves mIsValid=FALSE if the font seems to be broken.
     // Returns FALSE if the font does not appear to be an sfnt at all,
     // and should be handled (if possible) using other APIs.