SkPDF: encode all bitmap images as Type3

Change-Id: Ic99c536ea7da6b6a55dd92395eee26c969fee966
Reviewed-on: https://skia-review.googlesource.com/158343
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Hal Canary <halcanary@google.com>
diff --git a/src/pdf/SkPDFCanon.h b/src/pdf/SkPDFCanon.h
index 38dd358..12ba051 100644
--- a/src/pdf/SkPDFCanon.h
+++ b/src/pdf/SkPDFCanon.h
@@ -52,25 +52,6 @@
     sk_sp<SkPDFStream> fInvertFunction;
     sk_sp<SkPDFDict> fNoSmaskGraphicState;
     sk_sp<SkPDFArray> fRangeObject;
-
-    SK_BEGIN_REQUIRE_DENSE
-    struct BitmapGlyphKey {
-        SkFontID fFontID;      // uint32_t
-        SkScalar fTextSize;    // float32
-        SkScalar fTextScaleX;  // float32
-        SkScalar fTextSkewX;   // float32
-        SkGlyphID fGlyphID;    // uint16_t
-        uint16_t fPadding;
-    };
-    SK_END_REQUIRE_DENSE
-    struct BitmapGlyph {
-        sk_sp<SkImage> fImage;
-        SkIPoint fOffset;
-    };
-    SkTHashMap<BitmapGlyphKey, BitmapGlyph> fBitmapGlyphImages;
 };
 
-inline bool operator==(const SkPDFCanon::BitmapGlyphKey& u, const SkPDFCanon::BitmapGlyphKey& v) {
-    return memcmp(&u, &v, sizeof(SkPDFCanon::BitmapGlyphKey)) == 0;
-}
 #endif  // SkPDFCanon_DEFINED
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 996eb45..13b41dc 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -937,12 +937,10 @@
 public:
     GlyphPositioner(SkDynamicMemoryWStream* content,
                     SkScalar textSkewX,
-                    bool wideChars,
                     SkPoint origin)
         : fContent(content)
         , fCurrentMatrixOrigin(origin)
-        , fTextSkewX(textSkewX)
-        , fWideChars(wideChars) {
+        , fTextSkewX(textSkewX) {
     }
     ~GlyphPositioner() { this->flush(); }
     void flush() {
@@ -951,6 +949,10 @@
             fInText = false;
         }
     }
+    void setWideChars(bool wide) {
+        this->flush();
+        fWideChars = wide;
+    }
     void writeGlyph(SkPoint xy,
                     SkScalar advanceWidth,
                     uint16_t glyph) {
@@ -995,7 +997,7 @@
     SkPoint fCurrentMatrixOrigin;
     SkScalar fXAdvance = 0.0f;
     SkScalar fTextSkewX;
-    bool fWideChars;
+    bool fWideChars = true;
     bool fInText = false;
     bool fInitialized = false;
 };
@@ -1012,12 +1014,6 @@
 };
 }
 
-static bool has_outline_glyph(SkGlyphID gid, SkGlyphCache* cache) {
-    const SkGlyph& glyph = cache->getGlyphIDMetrics(gid);
-    const SkPath* path = cache->findPath(glyph);
-    return (path && !path->isEmpty()) || (glyph.fWidth == 0 && glyph.fHeight == 0);
-}
-
 static SkRect get_glyph_bounds_device_space(SkGlyphID gid, SkGlyphCache* cache,
                                             SkScalar xScale, SkScalar yScale,
                                             SkPoint xy, const SkMatrix& ctm) {
@@ -1036,88 +1032,6 @@
           r.top()  <= p.y() && p.y() <= r.bottom();
 }
 
-static sk_sp<SkImage> image_from_mask(const SkMask& mask) {
-    if (!mask.fImage) {
-        return nullptr;
-    }
-    SkIRect bounds = mask.fBounds;
-    SkBitmap bm;
-    switch (mask.fFormat) {
-        case SkMask::kBW_Format:
-            bm.allocPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height()));
-            for (int y = 0; y < bm.height(); ++y) {
-                for (int x8 = 0; x8 < bm.width(); x8 += 8) {
-                    uint8_t v = *mask.getAddr1(x8 + bounds.x(), y + bounds.y());
-                    int e = SkTMin(x8 + 8, bm.width());
-                    for (int x = x8; x < e; ++x) {
-                        *bm.getAddr8(x, y) = (v >> (x & 0x7)) & 0x1 ? 0xFF : 0x00;
-                    }
-                }
-            }
-            bm.setImmutable();
-            return SkImage::MakeFromBitmap(bm);
-        case SkMask::kA8_Format:
-            bm.installPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height()),
-                             mask.fImage, mask.fRowBytes);
-            return SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode);
-        case SkMask::kARGB32_Format:
-            bm.installPixels(SkImageInfo::MakeN32Premul(bounds.width(), bounds.height()),
-                             mask.fImage, mask.fRowBytes);
-            return SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode);
-        case SkMask::k3D_Format:
-            SkASSERT(false);
-            return nullptr;
-        case SkMask::kLCD16_Format:
-            SkASSERT(false);
-            return nullptr;
-        default:
-            SkASSERT(false);
-            return nullptr;
-    }
-}
-
-void draw_missing_glyphs(SkPDFDevice* dev, const SkPaint& paint, SkTypeface* typeface,
-                         const std::vector<PositionedGlyph>& missingGlyphs) {
-    if (missingGlyphs.size() == 0) {
-        return;
-    }
-    // Fall back on images.
-    SkPaint scaledGlyphCachePaint;
-    scaledGlyphCachePaint.setTextSize(paint.getTextSize());
-    scaledGlyphCachePaint.setTextScaleX(paint.getTextScaleX());
-    scaledGlyphCachePaint.setTextSkewX(paint.getTextSkewX());
-    scaledGlyphCachePaint.setTypeface(sk_ref_sp(typeface));
-    auto scaledGlyphCache = SkStrikeCache::FindOrCreateStrikeExclusive(scaledGlyphCachePaint);
-    SkTHashMap<SkPDFCanon::BitmapGlyphKey, SkPDFCanon::BitmapGlyph>* map =
-        &dev->getCanon()->fBitmapGlyphImages;
-    for (PositionedGlyph positionedGlyph : missingGlyphs) {
-        SkPDFCanon::BitmapGlyphKey key = {typeface->uniqueID(),
-                                          paint.getTextSize(),
-                                          paint.getTextScaleX(),
-                                          paint.getTextSkewX(),
-                                          positionedGlyph.fGlyph,
-                                          0};
-        SkImage* img = nullptr;
-        SkIPoint imgOffset = {0, 0};
-        if (SkPDFCanon::BitmapGlyph* ptr = map->find(key)) {
-            img = ptr->fImage.get();
-            imgOffset = ptr->fOffset;
-        } else {
-            (void)scaledGlyphCache->findImage(
-                    scaledGlyphCache->getGlyphIDMetrics(positionedGlyph.fGlyph));
-            SkMask mask;
-            scaledGlyphCache->getGlyphIDMetrics(positionedGlyph.fGlyph).toMask(&mask);
-            imgOffset = {mask.fBounds.x(), mask.fBounds.y()};
-            img = map->set(key, {image_from_mask(mask), imgOffset})->fImage.get();
-        }
-        if (img) {
-            SkPoint pt = positionedGlyph.fPos +
-                         SkPoint{(SkScalar)imgOffset.x(), (SkScalar)imgOffset.y()};
-            dev->drawImage(img, pt.x(), pt.y(), paint);
-        }
-    }
-}
-
 void SkPDFDevice::drawGlyphRunAsPath(const SkGlyphRun& glyphRun, SkPoint offset) {
     const SkPaint& paint = glyphRun.paint();
     SkPath path;
@@ -1150,21 +1064,6 @@
     } else {
         this->internalDrawGlyphRun(tmp, offset);
     }
-
-    if (!tmp.paint().getTypeface()) {
-        return;
-    }
-    std::vector<PositionedGlyph> missingGlyphs;
-    int emSize;
-    auto glyphCache = SkPDFFont::MakeVectorCache(tmp.paint().getTypeface(), &emSize);
-    for (size_t i = 0; i < glyphRun.shuntGlyphsIDs().size(); ++i) {
-        SkGlyphID gid = glyphRun.shuntGlyphsIDs()[i];
-        if (!has_outline_glyph(gid, glyphCache.get())) {
-            SkPoint xy = glyphRun.positions()[i];
-            missingGlyphs.push_back({xy + offset, gid});
-        }
-    }
-    draw_missing_glyphs(this, paint, tmp.paint().getTypeface(), missingGlyphs);
 }
 
 void SkPDFDevice::internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offset) {
@@ -1217,7 +1116,6 @@
 
     SkASSERT(paint.getTextAlign() == SkPaint::kLeft_Align);
     SkRect clipStackBounds = this->cs().bounds(this->bounds());
-    std::vector<PositionedGlyph> missingGlyphs;
     {
         ScopedContentEntry content(this, paint, true);
         if (!content.entry()) {
@@ -1243,15 +1141,11 @@
 
         const SkGlyphID maxGlyphID = SkToU16(typeface->countGlyphs() - 1);
 
-        bool multiByteGlyphs = SkPDFFont::IsMultiByte(SkPDFFont::FontType(*metrics));
         if (clusterator.reversedChars()) {
             out->writeText("/ReversedChars BMC\n");
         }
         SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n"); } );
-        GlyphPositioner glyphPositioner(out,
-                                        paint.getTextSkewX(),
-                                        multiByteGlyphs,
-                                        offset);
+        GlyphPositioner glyphPositioner(out, paint.getTextSkewX(), offset);
         SkPDFFont* font = nullptr;
 
         while (SkClusterator::Cluster c = clusterator.next()) {
@@ -1300,7 +1194,8 @@
                 if (!font || !font->hasGlyph(gid)) {
                     // Not yet specified font or need to switch font.
                     sk_sp<SkPDFFont> newFont =
-                            SkPDFFont::GetFontResource(fDocument->canon(), typeface, gid);
+                            SkPDFFont::GetFontResource(
+                                    fDocument->canon(), glyphCache.get(), typeface, gid);
                     SkASSERT(newFont);  // All preconditions for SkPDFFont::GetFontResource are met.
                     if (!newFont) {
                         return;
@@ -1310,13 +1205,12 @@
                     int fontIndex = find_or_add(&fFontResources, std::move(newFont));
 
                     glyphPositioner.flush();
-
+                    glyphPositioner.setWideChars(font->multiByteGlyphs());
                     SkPDFWriteResourceName(out, SkPDFResourceType::kFont, fontIndex);
                     out->writeText(" ");
                     SkPDFUtils::AppendScalar(textSize, out);
                     out->writeText(" Tf\n");
 
-                    SkASSERT(font->multiByteGlyphs() == multiByteGlyphs);
                 }
                 SkPoint xy = glyphRun.positions()[index];
                 // Do a glyph-by-glyph bounds-reject if positions are absolute.
@@ -1332,21 +1226,15 @@
                         continue;  // reject glyphs as out of bounds
                     }
                 }
-                if (!has_outline_glyph(gid, glyphCache.get())) {
-                    missingGlyphs.push_back({xy + offset, gid});
-                }
-
                 font->noteGlyphUsage(gid);
 
-                SkGlyphID encodedGlyph = multiByteGlyphs ? gid : font->glyphToPDFFontEncoding(gid);
+                SkGlyphID encodedGlyph = font->multiByteGlyphs()
+                                       ? gid : font->glyphToPDFFontEncoding(gid);
                 SkScalar advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX;
                 glyphPositioner.writeGlyph(xy, advance, encodedGlyph);
             }
         }
     }
-    if (paint.getColor() != SK_ColorTRANSPARENT) {
-        draw_missing_glyphs(this, srcPaint, typeface, missingGlyphs);
-    }
 }
 
 void SkPDFDevice::drawGlyphRunList(const SkGlyphRunList& glyphRunList) {
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index 6844a54..6d4c830 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -9,13 +9,16 @@
 
 #include "SkData.h"
 #include "SkGlyphCache.h"
+#include "SkImagePriv.h"
 #include "SkMacros.h"
 #include "SkMakeUnique.h"
+#include "SkPDFBitmap.h"
 #include "SkPDFCanon.h"
 #include "SkPDFConvertType1FontStream.h"
 #include "SkPDFDevice.h"
 #include "SkPDFMakeCIDGlyphWidthsArray.h"
 #include "SkPDFMakeToUnicodeCmap.h"
+#include "SkPDFResourceDict.h"
 #include "SkPDFUtils.h"
 #include "SkPaint.h"
 #include "SkRefCnt.h"
@@ -205,7 +208,14 @@
     return gid != 0 ? gid - (gid - 1) % 255 : 1;
 }
 
+static bool has_outline_glyph(SkGlyphID gid, SkGlyphCache* cache) {
+    const SkGlyph& glyph = cache->getGlyphIDMetrics(gid);
+    const SkPath* path = cache->findPath(glyph);
+    return (path && !path->isEmpty()) || (glyph.fWidth == 0 && glyph.fHeight == 0);
+}
+
 sk_sp<SkPDFFont> SkPDFFont::GetFontResource(SkPDFCanon* canon,
+                                            SkGlyphCache* cache,
                                             SkTypeface* face,
                                             SkGlyphID glyphID) {
     SkASSERT(canon);
@@ -215,6 +225,9 @@
                             // GetMetrics only returns null to signify a bad typeface.
     const SkAdvancedTypefaceMetrics& metrics = *fontMetrics;
     SkAdvancedTypefaceMetrics::FontType type = SkPDFFont::FontType(metrics);
+    if (!has_outline_glyph(glyphID, cache)) {
+        type = SkAdvancedTypefaceMetrics::kOther_Font;
+    }
     bool multibyte = SkPDFFont::IsMultiByte(type);
     SkGlyphID subsetCode = multibyte ? 0 : first_nonzero_glyph_for_single_byte_encoding(glyphID);
     uint64_t fontID = (static_cast<uint64_t>(SkTypeface::UniqueID(face)) << 16) | subsetCode;
@@ -636,6 +649,51 @@
 };
 }
 
+struct ImageAndOffset {
+    sk_sp<SkImage> fImage;
+    SkIPoint fOffset;
+};
+static ImageAndOffset to_image(SkGlyphID gid, SkGlyphCache* cache) {
+    (void)cache->findImage(cache->getGlyphIDMetrics(gid));
+    SkMask mask;
+    cache->getGlyphIDMetrics(gid).toMask(&mask);
+    if (!mask.fImage) {
+        return {nullptr, {0, 0}};
+    }
+    SkIRect bounds = mask.fBounds;
+    SkBitmap bm;
+    switch (mask.fFormat) {
+        case SkMask::kBW_Format:
+            bm.allocPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height()));
+            for (int y = 0; y < bm.height(); ++y) {
+                for (int x8 = 0; x8 < bm.width(); x8 += 8) {
+                    uint8_t v = *mask.getAddr1(x8 + bounds.x(), y + bounds.y());
+                    int e = SkTMin(x8 + 8, bm.width());
+                    for (int x = x8; x < e; ++x) {
+                        *bm.getAddr8(x, y) = (v >> (x & 0x7)) & 0x1 ? 0xFF : 0x00;
+                    }
+                }
+            }
+            bm.setImmutable();
+            return {SkImage::MakeFromBitmap(bm), {bounds.x(), bounds.y()}};
+        case SkMask::kA8_Format:
+            bm.installPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height()),
+                             mask.fImage, mask.fRowBytes);
+            return {SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode),
+                    {bounds.x(), bounds.y()}};
+        case SkMask::kARGB32_Format:
+            bm.installPixels(SkImageInfo::MakeN32Premul(bounds.width(), bounds.height()),
+                             mask.fImage, mask.fRowBytes);
+            return {SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode),
+                    {bounds.x(), bounds.y()}};
+        case SkMask::k3D_Format:
+        case SkMask::kLCD16_Format:
+        default:
+            SkASSERT(false);
+            return {nullptr, {0, 0}};
+    }
+}
+
 static void add_type3_font_info(SkPDFCanon* canon,
                                 SkPDFDict* font,
                                 SkTypeface* typeface,
@@ -704,12 +762,35 @@
                     characterName, sk_make_sp<SkPDFStream>(
                             std::unique_ptr<SkStreamAsset>(content.detachAsStream())));
             } else {
-                if (!emptyStream) {
-                    emptyStream = sk_make_sp<SkPDFStream>(
-                            std::unique_ptr<SkStreamAsset>(
-                                    new SkMemoryStream((size_t)0)));
+                auto pimg = to_image(gID, cache.get());
+                if (!pimg.fImage) {
+                    if (!emptyStream) {
+                        emptyStream = sk_make_sp<SkPDFStream>(
+                                std::unique_ptr<SkStreamAsset>(
+                                        new SkMemoryStream((size_t)0)));
+                    }
+                    charProcs->insertObjRef(characterName, emptyStream);
+                } else {
+                    SkDynamicMemoryWStream content;
+                    SkPDFUtils::AppendScalar(SkFloatToScalar(glyph.fAdvanceX), &content);
+                    content.writeText(" 0 d0\n");
+                    content.writeDecAsText(pimg.fImage->width());
+                    content.writeText(" 0 0 ");
+                    content.writeDecAsText(-pimg.fImage->height());
+                    content.writeText(" ");
+                    content.writeDecAsText(pimg.fOffset.x());
+                    content.writeText(" ");
+                    content.writeDecAsText(pimg.fImage->height() + pimg.fOffset.y());
+                    content.writeText(" cm\n");
+                    content.writeText("/X Do\n");
+                    auto proc = sk_make_sp<SkPDFStream>(content.detachAsStream());
+                    auto d0 = sk_make_sp<SkPDFDict>();
+                    d0->insertObjRef("X", SkPDFCreateBitmapObject(std::move(pimg.fImage)));
+                    auto d1 = sk_make_sp<SkPDFDict>();
+                    d1->insertObject("XObject", std::move(d0));
+                    proc->dict()->insertObject("Resources", std::move(d1));
+                    charProcs->insertObjRef(characterName, std::move(proc));
                 }
-                charProcs->insertObjRef(characterName, emptyStream);
             }
         }
         encDiffs->appendName(characterName.c_str());
diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h
index 3305213..5efcdaf 100644
--- a/src/pdf/SkPDFFont.h
+++ b/src/pdf/SkPDFFont.h
@@ -11,13 +11,11 @@
 
 #include "SkAdvancedTypefaceMetrics.h"
 #include "SkBitSet.h"
-#include "SkStrikeCache.h"
+#include "SkPDFCanon.h"
 #include "SkPDFTypes.h"
+#include "SkStrikeCache.h"
 #include "SkTypeface.h"
 
-class SkPDFCanon;
-class SkPDFFont;
-
 /** \class SkPDFFont
     A PDF Object class representing a font.  The font may have resources
     attached to it in order to embed the font.  SkPDFFonts are canonicalized
@@ -84,6 +82,7 @@
      *  @param glyphID   Specify which section of a large font is of interest.
      */
     static sk_sp<SkPDFFont> GetFontResource(SkPDFCanon* canon,
+                                            SkGlyphCache* cache,
                                             SkTypeface* typeface,
                                             SkGlyphID glyphID);