Add support for kEmbeddedBitmapText_Flag to DirectWrite.

R=reed@google.com

Review URL: https://codereview.chromium.org/263503004

git-svn-id: http://skia.googlecode.com/svn/trunk@14518 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/ports/SkFontHost_win_dw.cpp b/src/ports/SkFontHost_win_dw.cpp
index 36ed4d4..cd32fdb 100644
--- a/src/ports/SkFontHost_win_dw.cpp
+++ b/src/ports/SkFontHost_win_dw.cpp
@@ -22,7 +22,10 @@
 #include "SkGlyph.h"
 #include "SkHRESULT.h"
 #include "SkMaskGamma.h"
+#include "SkMatrix22.h"
 #include "SkOnce.h"
+#include "SkOTTable_EBLC.h"
+#include "SkOTTable_EBSC.h"
 #include "SkOTTable_head.h"
 #include "SkOTTable_hhea.h"
 #include "SkOTTable_OS_2.h"
@@ -449,7 +452,22 @@
     const void* drawDWMask(const SkGlyph& glyph);
 
     SkTDArray<uint8_t> fBits;
+    /** The total matrix without the text height scale. */
+    SkMatrix fSkXform;
+    /** The total matrix without the text height scale. */
     DWRITE_MATRIX fXform;
+    /** The non-rotational part of total matrix without the text height scale.
+     *  This is used to find the magnitude of gdi compatible advances.
+     */
+    DWRITE_MATRIX fGsA;
+    /** The inverse of the rotational part of the total matrix.
+     *  This is used to find the direction of gdi compatible advances.
+     */
+    SkMatrix fG_inv;
+    /** The text size to render with. */
+    SkScalar fTextSizeRender;
+    /** The text size to measure with. */
+    SkScalar fTextSizeMeasure;
     SkAutoTUnref<DWriteFontTypeface> fTypeface;
     int fGlyphCount;
     DWRITE_RENDERING_MODE fRenderingMode;
@@ -570,32 +588,224 @@
            wcscmp(dwFaceFontNameChar.get(), dwFontNameChar.get()) == 0;
 }
 
+class AutoDWriteTable {
+public:
+    AutoDWriteTable(IDWriteFontFace* fontFace, UINT32 beTag) : fFontFace(fontFace), fExists(FALSE) {
+        // Any errors are ignored, user must check fExists anyway.
+        fontFace->TryGetFontTable(beTag,
+            reinterpret_cast<const void **>(&fData), &fSize, &fLock, &fExists);
+    }
+    ~AutoDWriteTable() {
+        if (fExists) {
+            fFontFace->ReleaseFontTable(fLock);
+        }
+    }
+
+    const uint8_t* fData;
+    UINT32 fSize;
+    BOOL fExists;
+private:
+    // Borrowed reference, the user must ensure the fontFace stays alive.
+    IDWriteFontFace* fFontFace;
+    void* fLock;
+};
+template<typename T> class AutoTDWriteTable : public AutoDWriteTable {
+public:
+    static const UINT32 tag = DWRITE_MAKE_OPENTYPE_TAG(T::TAG0, T::TAG1, T::TAG2, T::TAG3);
+    AutoTDWriteTable(IDWriteFontFace* fontFace) : AutoDWriteTable(fontFace, tag) { }
+
+    const T* get() const { return reinterpret_cast<const T*>(fData); }
+    const T* operator->() const { return reinterpret_cast<const T*>(fData); }
+};
+
+static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) {
+    {
+        AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get());
+        if (!eblc.fExists) {
+            return false;
+        }
+        if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) {
+            return false;
+        }
+        if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) {
+            return false;
+        }
+
+        uint32_t numSizes = SkEndianSwap32(eblc->numSizes);
+        if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
+                         sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes)
+        {
+            return false;
+        }
+
+        const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable =
+                SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get());
+        for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
+            if (sizeTable->ppemX == size && sizeTable->ppemY == size) {
+                // TODO: determine if we should dig through IndexSubTableArray/IndexSubTable
+                // to determine the actual number of glyphs with bitmaps.
+
+                // TODO: Ensure that the bitmaps actually cover a significant portion of the strike.
+
+                //TODO: Endure that the bitmaps are bi-level.
+                if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) {
+                    return true;
+                }
+            }
+        }
+    }
+
+    {
+        AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteFontFace.get());
+        if (!ebsc.fExists) {
+            return false;
+        }
+        if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) {
+            return false;
+        }
+        if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) {
+            return false;
+        }
+
+        uint32_t numSizes = SkEndianSwap32(ebsc->numSizes);
+        if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
+                         sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes)
+        {
+            return false;
+        }
+
+        const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable =
+                SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get());
+        for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
+            if (scaleTable->ppemX == size && scaleTable->ppemY == size) {
+                // EBSC tables are normally only found in bitmap only fonts.
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+static bool bothZero(SkScalar a, SkScalar b) {
+    return 0 == a && 0 == b;
+}
+
+// returns false if there is any non-90-rotation or skew
+static bool isAxisAligned(const SkScalerContext::Rec& rec) {
+    return 0 == rec.fPreSkewX &&
+           (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
+            bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
+}
+
 SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
                                        const SkDescriptor* desc)
         : SkScalerContext(typeface, desc)
         , fTypeface(SkRef(typeface))
         , fGlyphCount(-1) {
 
-    fXform.m11 = SkScalarToFloat(fRec.fPost2x2[0][0]);
-    fXform.m12 = SkScalarToFloat(fRec.fPost2x2[1][0]);
-    fXform.m21 = SkScalarToFloat(fRec.fPost2x2[0][1]);
-    fXform.m22 = SkScalarToFloat(fRec.fPost2x2[1][1]);
-    fXform.dx = 0;
-    fXform.dy = 0;
+    // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC
+    // except when bi-level rendering is requested or there are embedded
+    // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
+    //
+    // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
+    // this. As a result, determine the actual size of the text and then see if
+    // there are any embedded bi-level bitmaps of that size. If there are, then
+    // force bitmaps by requesting bi-level rendering.
+    //
+    // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
+    // square pixels and only uses ppemY. Therefore the transform must track any
+    // non-uniform x-scale.
+    //
+    // Also, rotated glyphs should have the same absolute advance widths as
+    // horizontal glyphs and the subpixel flag should not affect glyph shapes.
 
-    if (SkMask::kBW_Format == fRec.fMaskFormat) {
+    // A is the total matrix.
+    SkMatrix A;
+    fRec.getSingleMatrix(&A);
+
+    // h is where A maps the horizontal baseline.
+    SkPoint h = SkPoint::Make(SK_Scalar1, 0);
+    A.mapPoints(&h, 1);
+
+    // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
+    SkMatrix G;
+    SkComputeGivensRotation(h, &G);
+
+    // GA is the matrix A with rotation removed.
+    SkMatrix GA(G);
+    GA.preConcat(A);
+
+    // realTextSize is the actual device size we want (as opposed to the size the user requested).
+    // gdiTextSize is the size we request when GDI compatible.
+    // If the scale is negative, this means the matrix will do the flip anyway.
+    SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
+    // Due to floating point math, the lower bits are suspect. Round carefully.
+    SkScalar roundedTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
+    SkScalar gdiTextSize = SkScalarFloorToScalar(roundedTextSize);
+    if (gdiTextSize == 0) {
+        gdiTextSize = SK_Scalar1;
+    }
+
+    bool hasBitmap = fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag &&
+                     hasBitmapStrike(typeface, SkScalarTruncToInt(gdiTextSize));
+    bool axisAligned = isAxisAligned(fRec);
+    bool isBiLevel = SkMask::kBW_Format == fRec.fMaskFormat || (hasBitmap && axisAligned);
+
+    if (isBiLevel) {
+        fTextSizeRender = gdiTextSize;
         fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
         fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
+        fTextSizeMeasure = gdiTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
-    } else {
+    } else if (hasBitmap) {
+        // If rotated but the horizontal text would have used a bitmap,
+        // render high quality rotated glyphs using the bitmap metrics.
+        fTextSizeRender = gdiTextSize;
         fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
+        fTextSizeMeasure = gdiTextSize;
+        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
+    } else {
+        fTextSizeRender = realTextSize;
+        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
+        fTextSizeMeasure = realTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
     }
 
     if (this->isSubpixel()) {
+        fTextSizeMeasure = realTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
     }
+
+    // Remove the realTextSize, as that is the text height scale currently in A.
+    SkScalar scale = SkScalarInvert(realTextSize);
+
+    // fSkXform is the total matrix A without the text height scale.
+    fSkXform = A;
+    fSkXform.preScale(scale, scale); //remove the text height scale.
+
+    fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
+    fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
+    fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
+    fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
+    fXform.dx = 0;
+    fXform.dy = 0;
+
+    // GsA is the non-rotational part of A without the text height scale.
+    SkMatrix GsA(GA);
+    GsA.preScale(scale, scale); //remove text height scale, G is rotational so reorders with scale.
+
+    fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX));
+    fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
+    fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX));
+    fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY));
+
+    // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational.
+    fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
+                  -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
+                  G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
 }
 
 SkScalerContext_DW::~SkScalerContext_DW() {
@@ -631,9 +841,9 @@
         DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
     {
         HRVM(fTypeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics(
-                 fRec.fTextSize,
+                 fTextSizeMeasure,
                  1.0f, // pixelsPerDip
-                 &fXform,
+                 &fGsA,
                  DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode,
                  &glyphId, 1,
                  &gm),
@@ -645,7 +855,7 @@
 
     DWRITE_FONT_METRICS dwfm;
     fTypeface->fDWriteFontFace->GetMetrics(&dwfm);
-    SkScalar advanceX = SkScalarMulDiv(fRec.fTextSize,
+    SkScalar advanceX = SkScalarMulDiv(fTextSizeMeasure,
                                        SkIntToScalar(gm.advanceWidth),
                                        SkIntToScalar(dwfm.designUnitsPerEm));
 
@@ -654,9 +864,13 @@
     }
 
     SkVector vecs[1] = { { advanceX, 0 } };
-    SkMatrix mat;
-    fRec.getMatrixFrom2x2(&mat);
-    mat.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
+    if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
+        DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
+    {
+        fG_inv.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
+    } else {
+        fSkXform.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
+    }
 
     glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX);
     glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY);
@@ -683,7 +897,7 @@
     run.glyphCount = 1;
     run.glyphAdvances = &advance;
     run.fontFace = fTypeface->fDWriteFontFace.get();
-    run.fontEmSize = SkScalarToFloat(fRec.fTextSize);
+    run.fontEmSize = SkScalarToFloat(fTextSizeRender);
     run.bidiLevel = 0;
     run.glyphIndices = &glyphId;
     run.isSideways = FALSE;
@@ -728,7 +942,7 @@
         DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
     {
         fTypeface->fDWriteFontFace->GetGdiCompatibleMetrics(
-             fRec.fTextSize,
+             fTextSizeRender,
              1.0f, // pixelsPerDip
              &fXform,
              &dwfm);
@@ -738,28 +952,28 @@
 
     SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm);
     if (mx) {
-        mx->fTop = -fRec.fTextSize * SkIntToScalar(dwfm.ascent) / upem;
+        mx->fTop = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
         mx->fAscent = mx->fTop;
-        mx->fDescent = fRec.fTextSize * SkIntToScalar(dwfm.descent) / upem;
+        mx->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem;
         mx->fBottom = mx->fDescent;
-        mx->fLeading = fRec.fTextSize * SkIntToScalar(dwfm.lineGap) / upem;
-        mx->fXHeight = fRec.fTextSize * SkIntToScalar(dwfm.xHeight) / upem;
-        mx->fUnderlineThickness = fRec.fTextSize * SkIntToScalar(dwfm.underlinePosition) / upem;
-        mx->fUnderlinePosition = -(fRec.fTextSize * SkIntToScalar(dwfm.underlineThickness) / upem);
+        mx->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem;
+        mx->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem;
+        mx->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem;
+        mx->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem);
 
         mx->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
         mx->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
     }
 
     if (my) {
-        my->fTop = -fRec.fTextSize * SkIntToScalar(dwfm.ascent) / upem;
+        my->fTop = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
         my->fAscent = my->fTop;
-        my->fDescent = fRec.fTextSize * SkIntToScalar(dwfm.descent) / upem;
+        my->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem;
         my->fBottom = my->fDescent;
-        my->fLeading = fRec.fTextSize * SkIntToScalar(dwfm.lineGap) / upem;
-        my->fXHeight = fRec.fTextSize * SkIntToScalar(dwfm.xHeight) / upem;
-        my->fUnderlineThickness = fRec.fTextSize * SkIntToScalar(dwfm.underlinePosition) / upem;
-        my->fUnderlinePosition = -(fRec.fTextSize * SkIntToScalar(dwfm.underlineThickness) / upem);
+        my->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem;
+        my->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem;
+        my->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem;
+        my->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem);
 
         my->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
         my->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
@@ -888,7 +1102,7 @@
     run.glyphCount = 1;
     run.glyphAdvances = &advance;
     run.fontFace = fTypeface->fDWriteFontFace.get();
-    run.fontEmSize = SkScalarToFloat(fRec.fTextSize);
+    run.fontEmSize = SkScalarToFloat(fTextSizeRender);
     run.bidiLevel = 0;
     run.glyphIndices = &index;
     run.isSideways = FALSE;
@@ -966,7 +1180,7 @@
     uint16_t glyphId = glyph.getGlyphID();
     //TODO: convert to<->from DIUs? This would make a difference if hinting.
     //It may not be needed, it appears that DirectWrite only hints at em size.
-    HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fRec.fTextSize),
+    HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fTextSizeRender),
                                        &glyphId,
                                        NULL, //advances
                                        NULL, //offsets
@@ -976,9 +1190,7 @@
                                        geometryToPath.get()),
          "Could not create glyph outline.");
 
-    SkMatrix mat;
-    fRec.getMatrixFrom2x2(&mat);
-    path->transform(mat);
+    path->transform(fSkXform);
 }
 
 void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
@@ -1146,28 +1358,6 @@
     return stream.get() ? SkFontStream::GetTableTags(stream, ttcIndex, tags) : 0;
 }
 
-class AutoDWriteTable {
-public:
-    AutoDWriteTable(IDWriteFontFace* fontFace, UINT32 beTag) : fFontFace(fontFace), fExists(FALSE) {
-        // Any errors are ignored, user must check fExists anyway.
-        fontFace->TryGetFontTable(beTag,
-            reinterpret_cast<const void **>(&fData), &fSize, &fLock, &fExists);
-    }
-    ~AutoDWriteTable() {
-        if (fExists) {
-            fFontFace->ReleaseFontTable(fLock);
-        }
-    }
-
-    const uint8_t* fData;
-    UINT32 fSize;
-    BOOL fExists;
-private:
-    // Borrowed reference, the user must ensure the fontFace stays alive.
-    IDWriteFontFace* fFontFace;
-    void* fLock;
-};
-
 size_t DWriteFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
                                           size_t length, void* data) const
 {
@@ -1260,7 +1450,6 @@
 
     unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
                                   SkScalerContext::kForceAutohinting_Flag |
-                                  SkScalerContext::kEmbeddedBitmapText_Flag |
                                   SkScalerContext::kEmbolden_Flag |
                                   SkScalerContext::kLCD_BGROrder_Flag |
                                   SkScalerContext::kLCD_Vertical_Flag;
@@ -1362,14 +1551,6 @@
     return true;
 }
 
-template<typename T> class AutoTDWriteTable : public AutoDWriteTable {
-public:
-    static const UINT32 tag = DWRITE_MAKE_OPENTYPE_TAG(T::TAG0, T::TAG1, T::TAG2, T::TAG3);
-    AutoTDWriteTable(IDWriteFontFace* fontFace) : AutoDWriteTable(fontFace, tag) { }
-
-    const T* operator->() const { return reinterpret_cast<const T*>(fData); }
-};
-
 SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics(
         SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
         const uint32_t* glyphIDs,
diff --git a/src/sfnt/SkOTTable_EBDT.h b/src/sfnt/SkOTTable_EBDT.h
new file mode 100644
index 0000000..89d7a3a
--- /dev/null
+++ b/src/sfnt/SkOTTable_EBDT.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkOTTable_EBDT_DEFINED
+#define SkOTTable_EBDT_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+#include "SkOTTable_head.h"
+#include "SkOTTable_loca.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableEmbeddedBitmapData {
+    static const SK_OT_CHAR TAG0 = 'E';
+    static const SK_OT_CHAR TAG1 = 'B';
+    static const SK_OT_CHAR TAG2 = 'D';
+    static const SK_OT_CHAR TAG3 = 'T';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableEmbeddedBitmapData>::value;
+
+    SK_OT_Fixed version;
+    static const SK_OT_Fixed version_initial = SkTEndian_SwapBE32(0x00020000);
+
+    struct BigGlyphMetrics {
+        SK_OT_BYTE height;
+        SK_OT_BYTE width;
+        SK_OT_CHAR horiBearingX;
+        SK_OT_CHAR horiBearingY;
+        SK_OT_BYTE horiAdvance;
+        SK_OT_CHAR vertBearingX;
+        SK_OT_CHAR vertBearingY;
+        SK_OT_BYTE vertAdvance;
+    };
+
+    struct SmallGlyphMetrics {
+        SK_OT_BYTE height;
+        SK_OT_BYTE width;
+        SK_OT_CHAR bearingX;
+        SK_OT_CHAR bearingY;
+        SK_OT_BYTE advance;
+    };
+
+    // Small metrics, byte-aligned data.
+    struct Format1 {
+        SmallGlyphMetrics smallGlyphMetrics;
+        //SK_OT_BYTE[] byteAlignedBitmap;
+    };
+
+    // Small metrics, bit-aligned data.
+    struct Format2 {
+        SmallGlyphMetrics smallGlyphMetrics;
+        //SK_OT_BYTE[] bitAlignedBitmap;
+    };
+
+    // Format 3 is not used.
+
+    // EBLC metrics (IndexSubTable::header::indexFormat 2 or 5), compressed data.
+    // Only used on Mac.
+    struct Format4 {
+        SK_OT_ULONG whiteTreeOffset;
+        SK_OT_ULONG blackTreeOffset;
+        SK_OT_ULONG glyphDataOffset;
+    };
+
+    // EBLC metrics (IndexSubTable::header::indexFormat 2 or 5), bit-aligned data.
+    struct Format5 {
+        //SK_OT_BYTE[] bitAlignedBitmap;
+    };
+
+    // Big metrics, byte-aligned data.
+    struct Format6 {
+        BigGlyphMetrics bigGlyphMetrics;
+        //SK_OT_BYTE[] byteAlignedBitmap;
+    };
+
+    // Big metrics, bit-aligned data.
+    struct Format7 {
+        BigGlyphMetrics bigGlyphMetrics;
+        //SK_OT_BYTE[] bitAlignedBitmap;
+    };
+
+    struct EBDTComponent {
+        SK_OT_USHORT glyphCode; // Component glyph code
+        SK_OT_CHAR xOffset; // Position of component left
+        SK_OT_CHAR yOffset; // Position of component top
+    };
+
+    struct Format8 {
+        SmallGlyphMetrics smallMetrics; // Metrics information for the glyph
+        SK_OT_BYTE pad; // Pad to short boundary
+        SK_OT_USHORT numComponents; // Number of components
+        //EBDTComponent componentArray[numComponents]; // Glyph code, offset array
+    };
+
+    struct Format9 {
+        BigGlyphMetrics bigMetrics; // Metrics information for the glyph
+        SK_OT_USHORT numComponents; // Number of components
+        //EBDTComponent componentArray[numComponents]; // Glyph code, offset array
+    };
+};
+
+#pragma pack(pop)
+
+#endif
diff --git a/src/sfnt/SkOTTable_EBLC.h b/src/sfnt/SkOTTable_EBLC.h
new file mode 100644
index 0000000..845418d
--- /dev/null
+++ b/src/sfnt/SkOTTable_EBLC.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkOTTable_EBLC_DEFINED
+#define SkOTTable_EBLC_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTable_EBDT.h"
+#include "SkOTTableTypes.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableEmbeddedBitmapLocation {
+    static const SK_OT_CHAR TAG0 = 'E';
+    static const SK_OT_CHAR TAG1 = 'B';
+    static const SK_OT_CHAR TAG2 = 'L';
+    static const SK_OT_CHAR TAG3 = 'C';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableEmbeddedBitmapLocation>::value;
+
+    SK_OT_Fixed version;
+    static const SK_OT_Fixed version_initial = SkTEndian_SwapBE32(0x00020000);
+
+    SK_OT_ULONG numSizes;
+
+    struct SbitLineMetrics {
+        SK_OT_CHAR ascender;
+        SK_OT_CHAR descender;
+        SK_OT_BYTE widthMax;
+        SK_OT_CHAR caretSlopeNumerator;
+        SK_OT_CHAR caretSlopeDenominator;
+        SK_OT_CHAR caretOffset;
+        SK_OT_CHAR minOriginSB;
+        SK_OT_CHAR minAdvanceSB;
+        SK_OT_CHAR maxBeforeBL;
+        SK_OT_CHAR minAfterBL;
+        SK_OT_CHAR pad1;
+        SK_OT_CHAR pad2;
+    };
+
+    struct BitmapSizeTable {
+        SK_OT_ULONG indexSubTableArrayOffset; //offset to indexSubtableArray from beginning of EBLC.
+        SK_OT_ULONG indexTablesSize; //number of bytes in corresponding index subtables and array
+        SK_OT_ULONG numberOfIndexSubTables; //an index subtable for each range or format change
+        SK_OT_ULONG colorRef; //not used; set to 0.
+        SbitLineMetrics hori; //line metrics for text rendered horizontally
+        SbitLineMetrics vert; //line metrics for text rendered vertically
+        SK_OT_USHORT startGlyphIndex; //lowest glyph index for this size
+        SK_OT_USHORT endGlyphIndex; //highest glyph index for this size
+        SK_OT_BYTE ppemX; //horizontal pixels per Em
+        SK_OT_BYTE ppemY; //vertical pixels per Em
+        struct BitDepth {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((BW, 1))
+                ((Gray4, 2))
+                ((Gray16, 4))
+                ((Gray256, 8))
+                SK_SEQ_END,
+            SK_SEQ_END)
+            SK_OT_BYTE value;
+        } bitDepth; //the Microsoft rasterizer v.1.7 or greater supports
+        union Flags {
+            struct Field {
+                //0-7
+                SK_OT_BYTE_BITFIELD(
+                    Horizontal, // Horizontal small glyph metrics
+                    Vertical,  // Vertical small glyph metrics
+                    Reserved02,
+                    Reserved03,
+                    Reserved04,
+                    Reserved05,
+                    Reserved06,
+                    Reserved07)
+            } field;
+            struct Raw {
+                static const SK_OT_CHAR Horizontal = 1u << 0;
+                static const SK_OT_CHAR Vertical = 1u << 1;
+                SK_OT_CHAR value;
+            } raw;
+        } flags;
+    }; //bitmapSizeTable[numSizes];
+
+    struct IndexSubTableArray {
+        SK_OT_USHORT firstGlyphIndex; //first glyph code of this range
+        SK_OT_USHORT lastGlyphIndex; //last glyph code of this range (inclusive)
+        SK_OT_ULONG additionalOffsetToIndexSubtable; //add to BitmapSizeTable::indexSubTableArrayOffset to get offset from beginning of 'EBLC'
+    }; //indexSubTableArray[BitmapSizeTable::numberOfIndexSubTables];
+
+    struct IndexSubHeader {
+        SK_OT_USHORT indexFormat; //format of this indexSubTable
+        SK_OT_USHORT imageFormat; //format of 'EBDT' image data
+        SK_OT_ULONG imageDataOffset; //offset to image data in 'EBDT' table
+    };
+
+    // Variable metrics glyphs with 4 byte offsets
+    struct IndexSubTable1 {
+        IndexSubHeader header;
+        //SK_OT_ULONG offsetArray[lastGlyphIndex - firstGlyphIndex + 1 + 1]; //last element points to one past end of last glyph
+        //glyphData = offsetArray[glyphIndex - firstGlyphIndex] + imageDataOffset
+    };
+
+    // All Glyphs have identical metrics
+    struct IndexSubTable2 {
+        IndexSubHeader header;
+        SK_OT_ULONG imageSize; // all glyphs are of the same size
+        SkOTTableEmbeddedBitmapData::BigGlyphMetrics bigMetrics; // all glyphs have the same metrics; glyph data may be compressed, byte-aligned, or bit-aligned
+    };
+
+    // Variable metrics glyphs with 2 byte offsets
+    struct IndexSubTable3 {
+        IndexSubHeader header;
+        //SK_OT_USHORT offsetArray[lastGlyphIndex - firstGlyphIndex + 1 + 1]; //last element points to one past end of last glyph, may have extra element to force even number of elements
+        //glyphData = offsetArray[glyphIndex - firstGlyphIndex] + imageDataOffset
+    };
+
+    // Variable metrics glyphs with sparse glyph codes
+    struct IndexSubTable4 {
+        IndexSubHeader header;
+        SK_OT_ULONG numGlyphs;
+        struct CodeOffsetPair {
+            SK_OT_USHORT glyphCode;
+            SK_OT_USHORT offset; //location in EBDT
+        }; //glyphArray[numGlyphs+1]
+    };
+
+    // Constant metrics glyphs with sparse glyph codes
+    struct IndexSubTable5 {
+        IndexSubHeader header;
+        SK_OT_ULONG imageSize; //all glyphs have the same data size
+        SkOTTableEmbeddedBitmapData::BigGlyphMetrics bigMetrics; //all glyphs have the same metrics
+        SK_OT_ULONG numGlyphs;
+        //SK_OT_USHORT glyphCodeArray[numGlyphs] //must have even number of entries (set pad to 0)
+    };
+
+    union IndexSubTable {
+        IndexSubHeader header;
+        IndexSubTable1 format1;
+        IndexSubTable2 format2;
+        IndexSubTable3 format3;
+        IndexSubTable4 format4;
+        IndexSubTable5 format5;
+    };
+
+};
+
+#pragma pack(pop)
+
+#endif
diff --git a/src/sfnt/SkOTTable_EBSC.h b/src/sfnt/SkOTTable_EBSC.h
new file mode 100644
index 0000000..316c45d
--- /dev/null
+++ b/src/sfnt/SkOTTable_EBSC.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkOTTable_EBSC_DEFINED
+#define SkOTTable_EBSC_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTable_EBLC.h"
+#include "SkOTTableTypes.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableEmbeddedBitmapScaling {
+    static const SK_OT_CHAR TAG0 = 'E';
+    static const SK_OT_CHAR TAG1 = 'S';
+    static const SK_OT_CHAR TAG2 = 'B';
+    static const SK_OT_CHAR TAG3 = 'C';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableEmbeddedBitmapScaling>::value;
+
+    SK_OT_Fixed version;
+    static const SK_OT_Fixed version_initial = SkTEndian_SwapBE32(0x00020000);
+
+    SK_OT_ULONG numSizes;
+
+    struct BitmapScaleTable {
+        SkOTTableEmbeddedBitmapLocation::SbitLineMetrics hori;
+        SkOTTableEmbeddedBitmapLocation::SbitLineMetrics vert;
+        SK_OT_BYTE ppemX; //target horizontal pixels per EM
+        SK_OT_BYTE ppemY; //target vertical pixels per EM
+        SK_OT_BYTE substitutePpemX; //use bitmaps of this size
+        SK_OT_BYTE substitutePpemY; //use bitmaps of this size
+    }; //bitmapScaleTable[numSizes];
+};
+
+#pragma pack(pop)
+
+#endif