SkPDF: in-place font subsetting

Motivation: gross code simplification, also no bitset lookups at draw time.

SkPDFFont owns its glyph useage bitset.

SkPDFSubstituteMap goes away.

SkPDFObject interface is simplified.

SkPDFDocument tracks font usage (as hash set), not glyph usage.

SkPDFFont gets a simpler constructor.

SkPDFFont has first and last glyph set in constructor, not adjusted later.

SkPDFFont implementations are simplified.

SkPDFGlyphSet is replaced with simple SkBitSet.

SkPDFFont sizes its SkBitSets based on glyph count.

SkPDFGlyphSetMap goes away.

SkBitSet is now non-copyable.

SkBitSet now how utility methods to match old SkPDFGlyphSet.

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2253283004

CQ_INCLUDE_TRYBOTS=master.client.skia:Test-Win-MSVC-GCE-CPU-AVX2-x86_64-Release-GDI-Trybot,Test-Win-MSVC-GCE-CPU-AVX2-x86_64-Debug-GDI-Trybot

Review-Url: https://codereview.chromium.org/2253283004
diff --git a/bench/PDFBench.cpp b/bench/PDFBench.cpp
index 5b33e8c..1010f74 100644
--- a/bench/PDFBench.cpp
+++ b/bench/PDFBench.cpp
@@ -30,14 +30,13 @@
 static void test_pdf_object_serialization(const sk_sp<SkPDFObject> object) {
     // SkDebugWStream wStream;
     NullWStream wStream;
-    SkPDFSubstituteMap substitutes;
     SkPDFObjNumMap objNumMap;
-    objNumMap.addObjectRecursively(object.get(), substitutes);
+    objNumMap.addObjectRecursively(object.get());
     for (int i = 0; i < objNumMap.objects().count(); ++i) {
         SkPDFObject* object = objNumMap.objects()[i].get();
         wStream.writeDecAsText(i + 1);
         wStream.writeText(" 0 obj\n");
-        object->emitObject(&wStream, objNumMap, substitutes);
+        object->emitObject(&wStream, objNumMap);
         wStream.writeText("\nendobj\n");
     }
 }
diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp
index 772745c..2d789d0 100644
--- a/src/pdf/SkPDFBitmap.cpp
+++ b/src/pdf/SkPDFBitmap.cpp
@@ -346,8 +346,7 @@
                                const SkImage* image,
                                bool alpha,
                                const sk_sp<SkPDFObject>& smask,
-                               const SkPDFObjNumMap& objNumMap,
-                               const SkPDFSubstituteMap& substitutes) {
+                               const SkPDFObjNumMap& objNumMap) {
     SkBitmap bitmap;
     image_get_ro_pixels(image, &bitmap);      // TODO(halcanary): test
     SkAutoLockPixels autoLockPixels(bitmap);  // with malformed images.
@@ -385,7 +384,7 @@
     pdfDict.insertInt("BitsPerComponent", 8);
     pdfDict.insertName("Filter", "FlateDecode");
     pdfDict.insertInt("Length", asset->getLength());
-    pdfDict.emitObject(stream, objNumMap, substitutes);
+    pdfDict.emitObject(stream, objNumMap);
 
     pdf_stream_begin(stream);
     stream->writeStream(asset.get(), asset->getLength());
@@ -400,10 +399,9 @@
 public:
     PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); }
     void emitObject(SkWStream*  stream,
-                    const SkPDFObjNumMap& objNumMap,
-                    const SkPDFSubstituteMap& subs) const override {
+                    const SkPDFObjNumMap& objNumMap) const override {
         SkASSERT(fImage);
-        emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap, subs);
+        emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap);
     }
     void drop() override { fImage = nullptr; }
 
@@ -419,19 +417,12 @@
 class PDFDefaultBitmap final : public SkPDFObject {
 public:
     void emitObject(SkWStream* stream,
-                    const SkPDFObjNumMap& objNumMap,
-                    const SkPDFSubstituteMap& subs) const override {
+                    const SkPDFObjNumMap& objNumMap) const override {
         SkASSERT(fImage);
-        emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap, subs);
+        emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap);
     }
-    void addResources(SkPDFObjNumMap* catalog,
-                      const SkPDFSubstituteMap& subs) const override {
-        SkASSERT(fImage);
-        if (fSMask.get()) {
-            SkPDFObject* obj = subs.getSubstitute(fSMask.get());
-            SkASSERT(obj);
-            catalog->addObjectRecursively(obj, subs);
-        }
+    void addResources(SkPDFObjNumMap* catalog) const override {
+        catalog->addObjectRecursively(fSMask.get());
     }
     void drop() override { fImage = nullptr; fSMask = nullptr; }
     PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask)
@@ -458,15 +449,12 @@
     bool fIsYUV;
     PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
         : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) { SkASSERT(data); }
-    void emitObject(SkWStream*,
-                    const SkPDFObjNumMap&,
-                    const SkPDFSubstituteMap&) const override;
+    void emitObject(SkWStream*, const SkPDFObjNumMap&) const override;
     void drop() override { fData = nullptr; }
 };
 
 void PDFJpegBitmap::emitObject(SkWStream* stream,
-                               const SkPDFObjNumMap& objNumMap,
-                               const SkPDFSubstituteMap& substituteMap) const {
+                               const SkPDFObjNumMap& objNumMap) const {
     SkASSERT(fData);
     SkPDFDict pdfDict("XObject");
     pdfDict.insertName("Subtype", "Image");
@@ -481,7 +469,7 @@
     pdfDict.insertName("Filter", "DCTDecode");
     pdfDict.insertInt("ColorTransform", 0);
     pdfDict.insertInt("Length", SkToInt(fData->size()));
-    pdfDict.emitObject(stream, objNumMap, substituteMap);
+    pdfDict.emitObject(stream, objNumMap);
     pdf_stream_begin(stream);
     stream->write(fData->data(), fData->size());
     pdf_stream_end(stream);
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 290c1c4..edf1728 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -1153,7 +1153,6 @@
                                     font->multiByteGlyphs(),
                                     defaultPositioning,
                                     offset);
-    SkPDFGlyphSetMap* fontGlyphUsage = fDocument->getGlyphUsage();
     const SkGlyphID* const glyphsEnd = glyphs + glyphCount;
 
     while (glyphs < glyphsEnd) {
@@ -1184,7 +1183,7 @@
                 return;
             }
         }
-        fontGlyphUsage->noteGlyphUsage(font, glyphs, stretch);
+        font->noteGlyphUsage(glyphs, stretch);
         if (defaultPositioning) {
             (void)font->glyphsToPDFFontEncoding(glyphs, SkToInt(glyphsEnd - glyphs));
             while (stretch-- > 0) {
@@ -1318,10 +1317,6 @@
             &fonts);
 }
 
-const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const {
-    return fFontResources;
-}
-
 sk_sp<SkPDFArray> SkPDFDevice::copyMediaBox() const {
     auto mediaBox = sk_make_sp<SkPDFArray>();
     mediaBox->reserve(4);
@@ -1948,9 +1943,9 @@
     }
     int resourceIndex = fFontResources.find(newFont.get());
     if (resourceIndex < 0) {
+        fDocument->registerFont(newFont.get());
         resourceIndex = fFontResources.count();
-        fFontResources.push(newFont.get());
-        newFont.get()->ref();
+        fFontResources.push(newFont.release());
     }
     return resourceIndex;
 }
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 364cc61..4a86028 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -126,10 +126,6 @@
     /** Create the resource dictionary for this device. */
     sk_sp<SkPDFDict> makeResourceDict() const;
 
-    /** Get the fonts used on this device.
-     */
-    const SkTDArray<SkPDFFont*>& getFontResources() const;
-
     /** Add our annotations (link to urls and destinations) to the supplied
      *  array.
      *  @param array Array to add annotations to.
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
index 4f044c8..75c5de9 100644
--- a/src/pdf/SkPDFDocument.cpp
+++ b/src/pdf/SkPDFDocument.cpp
@@ -23,7 +23,7 @@
 }
 
 void SkPDFObjectSerializer::addObjectRecursively(const sk_sp<SkPDFObject>& object) {
-    fObjNumMap.addObjectRecursively(object.get(), fSubstituteMap);
+    fObjNumMap.addObjectRecursively(object.get());
 }
 
 #define SKPDF_MAGIC "\xD3\xEB\xE9\xE1"
@@ -58,10 +58,9 @@
         // the head of the linked list of free objects."
         SkASSERT(fOffsets.count() == fNextToBeSerialized);
         fOffsets.push(this->offset(wStream));
-        SkASSERT(object == fSubstituteMap.getSubstitute(object));
         wStream->writeDecAsText(index);
         wStream->writeText(" 0 obj\n");  // Generation number is always 0.
-        object->emitObject(wStream, fObjNumMap, fSubstituteMap);
+        object->emitObject(wStream, fObjNumMap);
         wStream->writeText("\nendobj\n");
         object->drop();
         ++fNextToBeSerialized;
@@ -93,7 +92,7 @@
         trailerDict.insertObject("ID", std::move(id));
     }
     wStream->writeText("trailer\n");
-    trailerDict.emitObject(wStream, fObjNumMap, fSubstituteMap);
+    trailerDict.emitObject(wStream, fObjNumMap);
     wStream->writeText("\nstartxref\n");
     wStream->writeBigDecAsText(xRefFileOffset);
     wStream->writeText("\n%%EOF");
@@ -246,11 +245,15 @@
 }
 
 void SkPDFDocument::onAbort() {
+    this->reset();
+}
+
+void SkPDFDocument::reset() {
     fCanvas.reset(nullptr);
     fPages.reset();
     fCanon.reset();
     renew(&fObjectSerializer);
-    renew(&fGlyphUsage);
+    fFonts.reset();
 }
 
 #ifdef SK_SUPPORT_LEGACY_DOCUMENT_API
@@ -419,10 +422,7 @@
 bool SkPDFDocument::onClose(SkWStream* stream) {
     SkASSERT(!fCanvas.get());
     if (fPages.empty()) {
-        fPages.reset();
-        fCanon.reset();
-        renew(&fObjectSerializer);
-        renew(&fGlyphUsage);
+        this->reset();
         return false;
     }
     auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
@@ -442,21 +442,12 @@
     }
 
     // Build font subsetting info before calling addObjectRecursively().
-    for (const auto& entry : fGlyphUsage) {
-        sk_sp<SkPDFObject> subsetFont =
-            entry.fFont->getFontSubset(&fCanon, &entry.fGlyphSet);
-        if (subsetFont) {
-            fObjectSerializer.fSubstituteMap.setSubstitute(
-                    entry.fFont, subsetFont.get());
-        }
-    }
-
+    SkPDFCanon* canon = &fCanon;
+    fFonts.foreach([canon](SkPDFFont* p){ p->getFontSubset(canon); });
     fObjectSerializer.addObjectRecursively(docCatalog);
     fObjectSerializer.serializeObjects(this->getStream());
     fObjectSerializer.serializeFooter(this->getStream(), docCatalog, fID);
-    fCanon.reset();
-    renew(&fObjectSerializer);
-    renew(&fGlyphUsage);
+    this->reset();
     return true;
 }
 
diff --git a/src/pdf/SkPDFDocument.h b/src/pdf/SkPDFDocument.h
index 86562fe..5221dd2 100644
--- a/src/pdf/SkPDFDocument.h
+++ b/src/pdf/SkPDFDocument.h
@@ -25,7 +25,6 @@
 // keep similar functionality together.
 struct SkPDFObjectSerializer : SkNoncopyable {
     SkPDFObjNumMap fObjNumMap;
-    SkPDFSubstituteMap fSubstituteMap;
     SkTDArray<int32_t> fOffsets;
     sk_sp<SkPDFObject> fInfoDict;
     size_t fBaseOffset;
@@ -70,18 +69,16 @@
 
        It might go without saying that objects should not be changed
        after calling serialize, since those changes will be too late.
-       The same goes for changes to the SkPDFSubstituteMap that effect
-       the object or its dependencies.
      */
     void serialize(const sk_sp<SkPDFObject>&);
     SkPDFCanon* canon() { return &fCanon; }
-    SkPDFGlyphSetMap* getGlyphUsage() { return &fGlyphUsage; }
+    void registerFont(SkPDFFont* f) { fFonts.add(f); }
 
 private:
     SkPDFObjectSerializer fObjectSerializer;
     SkPDFCanon fCanon;
-    SkPDFGlyphSetMap fGlyphUsage;
     SkTArray<sk_sp<SkPDFDict>> fPages;
+    SkTHashSet<SkPDFFont*> fFonts;
     sk_sp<SkPDFDict> fDests;
     sk_sp<SkPDFDevice> fPageDevice;
     sk_sp<SkCanvas> fCanvas;
@@ -90,6 +87,8 @@
     SkScalar fRasterDpi;
     SkDocument::PDFMetadata fMetadata;
     bool fPDFA;
+
+    void reset();
 };
 
 #endif  // SkPDFDocument_DEFINED
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index 290061a..887f950 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -37,50 +37,27 @@
 // non-symbolic, so always call it symbolic.  (PDF 1.4 spec, section 5.7.1)
 static const int kPdfSymbolic = 4;
 
-class SkPDFType0Font final : public SkPDFFont {
-public:
-    SkPDFType0Font(const SkAdvancedTypefaceMetrics& info,
-                   sk_sp<SkTypeface> typeface,
-                   SkAdvancedTypefaceMetrics::FontType type);
+struct SkPDFType0Font final : public SkPDFFont {
+    SkPDFType0Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&);
     virtual ~SkPDFType0Font();
-    sk_sp<SkPDFObject> getFontSubset(SkPDFCanon*, const SkPDFGlyphSet*) override;
+    void getFontSubset(SkPDFCanon*) override;
 #ifdef SK_DEBUG
-    void emitObject(SkWStream*,
-                    const SkPDFObjNumMap&,
-                    const SkPDFSubstituteMap&) const override;
-#endif
-
-private:
-#ifdef SK_DEBUG
+    void emitObject(SkWStream*, const SkPDFObjNumMap&) const override;
     bool fPopulated;
 #endif
-    bool populate(const SkPDFGlyphSet* subset,
-                  const SkAdvancedTypefaceMetrics& metrics);
     typedef SkPDFDict INHERITED;
 };
 
-class SkPDFType1Font final : public SkPDFFont {
-public:
-    SkPDFType1Font(const SkAdvancedTypefaceMetrics& info,
-                   sk_sp<SkTypeface> typeface,
-                   uint16_t glyphID,
-                   SkPDFCanon* canon);
+struct SkPDFType1Font final : public SkPDFFont {
+    SkPDFType1Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&, SkPDFCanon*);
     virtual ~SkPDFType1Font() {}
+    void getFontSubset(SkPDFCanon*) override {} // TODO(halcanary): implement
 };
 
-class SkPDFType3Font final : public SkPDFFont {
-public:
-    SkPDFType3Font(const SkAdvancedTypefaceMetrics& info,
-                   sk_sp<SkTypeface> typeface,
-                   SkAdvancedTypefaceMetrics::FontType fontType,
-                   uint16_t glyphID);
+struct SkPDFType3Font final : public SkPDFFont {
+    SkPDFType3Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&);
     virtual ~SkPDFType3Font() {}
-    void emitObject(SkWStream*,
-                    const SkPDFObjNumMap&,
-                    const SkPDFSubstituteMap&) const override {
-        SkDEBUGFAIL("should call getFontSubset!");
-    }
-    sk_sp<SkPDFObject> getFontSubset(SkPDFCanon*, const SkPDFGlyphSet*) override;
+    void getFontSubset(SkPDFCanon*) override;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -143,58 +120,6 @@
 }
 }  // namespace
 
-
-///////////////////////////////////////////////////////////////////////////////
-// class SkPDFGlyphSet
-///////////////////////////////////////////////////////////////////////////////
-
-SkPDFGlyphSet::SkPDFGlyphSet() : fBitSet(SK_MaxU16 + 1) {
-}
-
-void SkPDFGlyphSet::set(const uint16_t* glyphIDs, int numGlyphs) {
-    for (int i = 0; i < numGlyphs; ++i) {
-        fBitSet.setBit(glyphIDs[i], true);
-    }
-}
-
-bool SkPDFGlyphSet::has(uint16_t glyphID) const {
-    return fBitSet.isBitSet(glyphID);
-}
-
-void SkPDFGlyphSet::exportTo(SkTDArray<unsigned int>* glyphIDs) const {
-    fBitSet.exportTo(glyphIDs);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// class SkPDFGlyphSetMap
-///////////////////////////////////////////////////////////////////////////////
-
-SkPDFGlyphSetMap::SkPDFGlyphSetMap() {}
-
-SkPDFGlyphSetMap::~SkPDFGlyphSetMap() {
-    fMap.reset();
-}
-
-void SkPDFGlyphSetMap::noteGlyphUsage(SkPDFFont* font, const uint16_t* glyphIDs,
-                                      int numGlyphs) {
-    SkPDFGlyphSet* subset = getGlyphSetForFont(font);
-    if (subset) {
-        subset->set(glyphIDs, numGlyphs);
-    }
-}
-
-SkPDFGlyphSet* SkPDFGlyphSetMap::getGlyphSetForFont(SkPDFFont* font) {
-    int index = fMap.count();
-    for (int i = 0; i < index; ++i) {
-        if (fMap[i].fFont == font) {
-            return &fMap[i].fGlyphSet;
-        }
-    }
-    FontGlyphSetPair& pair = fMap.push_back();
-    pair.fFont = font;
-    return &pair.fGlyphSet;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // class SkPDFFont
 ///////////////////////////////////////////////////////////////////////////////
@@ -322,47 +247,46 @@
         return nullptr;
     }
 
+    SkGlyphID firstNonZeroGlyph;
+    SkGlyphID lastGlyph;
+    if (multibyte) {
+        firstNonZeroGlyph = 1;
+        lastGlyph = SkToU16(glyphCount - 1);
+    } else {
+        firstNonZeroGlyph = firstGlyph;
+        lastGlyph = SkToU16(SkTMin<int>(glyphCount - 1,
+                                        254 + (int)firstGlyph));
+    }
+    SkPDFFont::Info info = {std::move(typeface), firstNonZeroGlyph, lastGlyph, type};
     sk_sp<SkPDFFont> font;
     switch (type) {
         case SkAdvancedTypefaceMetrics::kType1CID_Font:
         case SkAdvancedTypefaceMetrics::kTrueType_Font:
             SkASSERT(multibyte);
-            font = sk_make_sp<SkPDFType0Font>(metrics,
-                                              std::move(typeface),
-                                              type);
+            font = sk_make_sp<SkPDFType0Font>(std::move(info), metrics);
             break;
         case SkAdvancedTypefaceMetrics::kType1_Font:
             SkASSERT(!multibyte);
-            font = sk_make_sp<SkPDFType1Font>(metrics,
-                                              std::move(typeface),
-                                              glyphID,
-                                              canon);
+            font = sk_make_sp<SkPDFType1Font>(std::move(info), metrics, canon);
             break;
         default:
             SkASSERT(!multibyte);
             // Type3 is our fallback font.
-            font = sk_make_sp<SkPDFType3Font>(metrics,
-                                              std::move(typeface),
-                                              type,
-                                              glyphID);
+            font = sk_make_sp<SkPDFType3Font>(std::move(info), metrics);
             break;
     }
     canon->fFontMap.set(fontID, SkRef(font.get()));
     return font.release();  // TODO(halcanary) return sk_sp<SkPDFFont>.
 }
 
-sk_sp<SkPDFObject> SkPDFFont::getFontSubset(SkPDFCanon*, const SkPDFGlyphSet*) {
-    return nullptr;  // Default: no support.
-}
-
-SkPDFFont::SkPDFFont(sk_sp<SkTypeface> typeface,
-                     SkAdvancedTypefaceMetrics::FontType fontType)
+SkPDFFont::SkPDFFont(SkPDFFont::Info info)
     : SkPDFDict("Font")
-    , fTypeface(std::move(typeface))
-    , fFirstGlyphID(1)
-    , fFontType(fontType) {
+    , fTypeface(std::move(info.fTypeface))
+    , fGlyphUsage(info.fLastGlyphID + 1)  // TODO(halcanary): Adjust mapping?
+    , fFirstGlyphID(info.fFirstGlyphID)
+    , fLastGlyphID(info.fLastGlyphID)
+    , fFontType(info.fFontType) {
     SkASSERT(fTypeface);
-    fLastGlyphID = SkToU16(fTypeface->countGlyphs() - 1);
 }
 
 static void  add_common_font_descriptor_entries(SkPDFDict* descriptor,
@@ -388,50 +312,25 @@
     }
 }
 
-void SkPDFFont::adjustGlyphRangeForSingleByteEncoding(SkGlyphID glyphID) {
-    // Single byte glyph encoding supports a max of 255 glyphs.
-    fFirstGlyphID = first_glyph_for_single_byte_encoding(glyphID);
-    if (fLastGlyphID > fFirstGlyphID + 255 - 1) {
-        fLastGlyphID = fFirstGlyphID + 255 - 1;
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // class SkPDFType0Font
 ///////////////////////////////////////////////////////////////////////////////
 
-SkPDFType0Font::SkPDFType0Font(const SkAdvancedTypefaceMetrics& info,
-                               sk_sp<SkTypeface> typeface,
-                               SkAdvancedTypefaceMetrics::FontType fontType)
-    : SkPDFFont(std::move(typeface), fontType) {
+SkPDFType0Font::SkPDFType0Font(
+        SkPDFFont::Info info,
+        const SkAdvancedTypefaceMetrics& metrics)
+    : SkPDFFont(std::move(info)) {
     SkDEBUGCODE(fPopulated = false);
-    if (!can_subset(info)) {
-        this->populate(nullptr, info);
-    }
 }
 
 SkPDFType0Font::~SkPDFType0Font() {}
 
-sk_sp<SkPDFObject>  SkPDFType0Font::getFontSubset(SkPDFCanon* canon,
-                                                  const SkPDFGlyphSet* subset) {
-    const SkAdvancedTypefaceMetrics* metrics =
-        SkPDFFont::GetMetrics(this->typeface(), canon);
-    SkASSERT(metrics);
-    if (!metrics || !can_subset(*metrics)) {
-        return nullptr;
-    }
-    auto newSubset = sk_make_sp<SkPDFType0Font>(
-            *metrics, this->refTypeface(), this->getType());
-    newSubset->populate(subset, *metrics);
-    return newSubset;
-}
 
 #ifdef SK_DEBUG
 void SkPDFType0Font::emitObject(SkWStream* stream,
-                                const SkPDFObjNumMap& objNumMap,
-                                const SkPDFSubstituteMap& substitutes) const {
+                                const SkPDFObjNumMap& objNumMap) const {
     SkASSERT(fPopulated);
-    return INHERITED::emitObject(stream, objNumMap, substitutes);
+    return INHERITED::emitObject(stream, objNumMap);
 }
 #endif
 
@@ -488,8 +387,12 @@
 }
 #endif  // SK_SFNTLY_SUBSETTER
 
-bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset,
-                              const SkAdvancedTypefaceMetrics& metrics) {
+void SkPDFType0Font::getFontSubset(SkPDFCanon* canon) {
+    const SkAdvancedTypefaceMetrics* metricsPtr =
+        SkPDFFont::GetMetrics(this->typeface(), canon);
+    SkASSERT(metricsPtr);
+    if (!metricsPtr) { return; }
+    const SkAdvancedTypefaceMetrics& metrics = *metricsPtr;
     SkASSERT(can_embed(metrics));
     SkAdvancedTypefaceMetrics::FontType type = this->getType();
     SkTypeface* face = this->typeface();
@@ -504,24 +407,22 @@
             std::unique_ptr<SkStreamAsset> fontAsset(face->openStream(&ttcIndex));
             SkASSERT(fontAsset);
             if (!fontAsset) {
-                return false;
+                return;
             }
             size_t fontSize = fontAsset->getLength();
             SkASSERT(fontSize > 0);
             if (fontSize == 0) {
-                return false;
+                return;
             }
 
             #ifdef SK_SFNTLY_SUBSETTER
-            if (can_subset(metrics) && subset) {
+            if (can_subset(metrics)) {
                 // Generate glyph id array. in format needed by sfntly
                 SkTDArray<uint32_t> glyphIDs;
-                if (subset) {
-                    if (!subset->has(0)) {
-                        glyphIDs.push(0);  // Always include glyph 0.
-                    }
-                    subset->exportTo(&glyphIDs);
+                if (!this->glyphUsage().has(0)) {
+                    glyphIDs.push(0);  // Always include glyph 0.
                 }
+                this->glyphUsage().exportTo(&glyphIDs);
                 sk_sp<SkPDFObject> subsetStream = get_subset_font_stream(
                         std::move(fontAsset), glyphIDs, name.c_str());
                 if (subsetStream) {
@@ -542,7 +443,7 @@
             SkASSERT(fontData);
             SkASSERT(fontData->getLength() > 0);
             if (!fontData || 0 == fontData->getLength()) {
-                return false;
+                return;
             }
             auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontData));
             fontStream->dict()->insertName("Subtype", "CIDFontType0c");
@@ -574,11 +475,10 @@
 
     uint16_t emSize = metrics.fEmSize;
     int16_t defaultWidth = 0;
-    const SkBitSet* bitSet = subset ? &subset->bitSet() : nullptr;
     {
         SkAutoGlyphCache glyphCache = vector_cache(face);
         sk_sp<SkPDFArray> widths = SkPDFMakeCIDGlyphWidthsArray(
-                glyphCache.get(), bitSet, emSize, &defaultWidth);
+                glyphCache.get(), &this->glyphUsage(), emSize, &defaultWidth);
         if (widths && widths->size() > 0) {
             newCIDFont->insertObject("W", std::move(widths));
         }
@@ -598,13 +498,13 @@
     if (metrics.fGlyphToUnicode.count() > 0) {
         this->insertObjRef("ToUnicode",
                            SkPDFMakeToUnicodeCmap(metrics.fGlyphToUnicode,
-                                                  subset,
+                                                  &this->glyphUsage(),
                                                   multiByteGlyphs(),
                                                   firstGlyphID(),
                                                   lastGlyphID()));
     }
     SkDEBUGCODE(fPopulated = true);
-    return true;
+    return;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -679,24 +579,22 @@
     font->insertObject("Encoding", std::move(encoding));
 }
 
-SkPDFType1Font::SkPDFType1Font(const SkAdvancedTypefaceMetrics& info,
-                               sk_sp<SkTypeface> typeface,
-                               uint16_t glyphID,
+SkPDFType1Font::SkPDFType1Font(SkPDFFont::Info info,
+                               const SkAdvancedTypefaceMetrics& metrics,
                                SkPDFCanon* canon)
-    : SkPDFFont(std::move(typeface), SkAdvancedTypefaceMetrics::kType1_Font)
+    : SkPDFFont(std::move(info))
 {
     SkFontID fontID = this->typeface()->uniqueID();
     sk_sp<SkPDFDict> fontDescriptor;
     if (SkPDFDict** ptr = canon->fFontDescriptors.find(fontID)) {
         fontDescriptor = sk_ref_sp(*ptr);
     } else {
-        fontDescriptor = make_type1_font_descriptor(this->typeface(), info);
+        fontDescriptor = make_type1_font_descriptor(this->typeface(), metrics);
         canon->fFontDescriptors.set(fontID, SkRef(fontDescriptor.get()));
     }
     this->insertObjRef("FontDescriptor", std::move(fontDescriptor));
-    this->adjustGlyphRangeForSingleByteEncoding(glyphID);
     // TODO(halcanary): subset this (advances and names).
-    populate_type_1_font(this, info, this->typeface(),
+    populate_type_1_font(this, metrics, this->typeface(),
                          this->firstGlyphID(), this->lastGlyphID());
 }
 
@@ -738,7 +636,7 @@
                                 SkPDFDict* font,
                                 SkTypeface* typeface,
                                 SkScalar emSize,
-                                const SkPDFGlyphSet* subset,
+                                const SkBitSet& subset,
                                 SkGlyphID firstGlyphID,
                                 SkGlyphID lastGlyphID) {
     SkASSERT(lastGlyphID >= firstGlyphID);
@@ -770,7 +668,7 @@
 
     sk_sp<SkPDFStream> emptyStream;
     for (SkGlyphID gID : SingleByteGlyphIdIterator(firstGlyphID, lastGlyphID)) {
-        bool skipGlyph = subset && gID != 0 && !subset->has(gID);
+        bool skipGlyph = gID != 0 && !subset.has(gID);
         SkString characterName;
         SkScalar advance = 0.0f;
         SkIRect glyphBBox;
@@ -827,7 +725,7 @@
     if (metrics /* && metrics->fGlyphToUnicode.count() > 0 */) {
         font->insertObjRef("ToUnicode",
                            SkPDFMakeToUnicodeCmap(metrics->fGlyphToUnicode,
-                                                  subset,
+                                                  &subset,
                                                   false,
                                                   firstGlyphID,
                                                   lastGlyphID));
@@ -837,29 +735,20 @@
     font->insertObject("CharProcs", std::move(charProcs));
 }
 
-SkPDFType3Font::SkPDFType3Font(const SkAdvancedTypefaceMetrics& info,
-                               sk_sp<SkTypeface> typeface,
-                               SkAdvancedTypefaceMetrics::FontType fontType,
-                               uint16_t glyphID)
-    : SkPDFFont(std::move(typeface), fontType) {
-    this->adjustGlyphRangeForSingleByteEncoding(glyphID);
-}
+SkPDFType3Font::SkPDFType3Font(SkPDFFont::Info info,
+                               const SkAdvancedTypefaceMetrics& metrics)
+    : SkPDFFont(std::move(info)) {}
 
-sk_sp<SkPDFObject> SkPDFType3Font::getFontSubset(SkPDFCanon* canon,
-                                                 const SkPDFGlyphSet* usage) {
-    // All fonts are subset before serialization.
-    // TODO(halcanary): all fonts should follow this pattern.
+void SkPDFType3Font::getFontSubset(SkPDFCanon* canon) {
     const SkAdvancedTypefaceMetrics* info =
         SkPDFFont::GetMetrics(this->typeface(), canon);
     SkASSERT(info);
     uint16_t emSize = info->fEmSize > 0 ? info->fEmSize : 1000;
-    auto font = sk_make_sp<SkPDFDict>("Font");
-    add_type3_font_info(canon, font.get(), this->typeface(), (SkScalar)emSize, usage,
+    add_type3_font_info(canon, this, this->typeface(), (SkScalar)emSize,
+                        this->glyphUsage(),
                         this->firstGlyphID(), this->lastGlyphID());
-    return font;
 }
 
-
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SkPDFFont::CanEmbedTypeface(SkTypeface* typeface, SkPDFCanon* canon) {
diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h
index f2f1df4..abf164c 100644
--- a/src/pdf/SkPDFFont.h
+++ b/src/pdf/SkPDFFont.h
@@ -18,48 +18,6 @@
 class SkPDFCanon;
 class SkPDFFont;
 
-class SkPDFGlyphSet : SkNoncopyable {
-public:
-    SkPDFGlyphSet();
-    SkPDFGlyphSet(SkPDFGlyphSet&& o) : fBitSet(std::move(o.fBitSet)) {}
-
-    void set(const uint16_t* glyphIDs, int numGlyphs);
-    bool has(uint16_t glyphID) const;
-    void exportTo(SkTDArray<uint32_t>* glyphIDs) const;
-    const SkBitSet& bitSet() const { return fBitSet; }
-
-private:
-    SkBitSet fBitSet;
-};
-
-class SkPDFGlyphSetMap : SkNoncopyable {
-public:
-    struct FontGlyphSetPair : SkNoncopyable {
-        FontGlyphSetPair() : fFont(nullptr) {}
-        FontGlyphSetPair(FontGlyphSetPair&& o)
-            : fFont(o.fFont)
-            , fGlyphSet(std::move(o.fGlyphSet)) {
-            o.fFont = nullptr;
-        }
-        SkPDFFont* fFont;
-        SkPDFGlyphSet fGlyphSet;
-    };
-
-    SkPDFGlyphSetMap();
-    ~SkPDFGlyphSetMap();
-
-    const FontGlyphSetPair* begin() const { return fMap.begin(); }
-    const FontGlyphSetPair* end() const { return fMap.end(); }
-
-    void noteGlyphUsage(SkPDFFont* font, const uint16_t* glyphIDs,
-                        int numGlyphs);
-
-private:
-    SkPDFGlyphSet* getGlyphSetForFont(SkPDFFont* font);
-
-    SkTArray<FontGlyphSetPair> fMap;
-};
-
 /** \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
@@ -112,6 +70,10 @@
     int glyphsToPDFFontEncodingCount(const SkGlyphID* glyphIDs,
                                      int numGlyphs) const;
 
+    void noteGlyphUsage(const SkGlyphID* glyphs, int count) {
+        fGlyphUsage.setAll(glyphs, count);
+    }
+
     /** Get the font resource for the passed typeface and glyphID. The
      *  reference count of the object is incremented and it is the caller's
      *  responsibility to unreference it when done.  This is needed to
@@ -128,14 +90,10 @@
     static const SkAdvancedTypefaceMetrics* GetMetrics(SkTypeface* typeface,
                                                        SkPDFCanon* canon);
 
-    /** Subset the font based on usage set. Returns a SkPDFFont instance with
-     *  subset.
-     *  @param usage  Glyph subset requested.
-     *  @return       nullptr if font does not support subsetting, a new instance
-     *                of SkPDFFont otherwise.
+    /** Subset the font based on current usage.
+     *  Must be called before emitObject().
      */
-    virtual sk_sp<SkPDFObject> getFontSubset(SkPDFCanon* canon,
-                                             const SkPDFGlyphSet* usage);
+    virtual void getFontSubset(SkPDFCanon*) = 0;
 
     /**
      *  Return false iff the typeface has its NotEmbeddable flag set.
@@ -145,23 +103,24 @@
 
 protected:
     // Common constructor to handle common members.
-    SkPDFFont(sk_sp<SkTypeface> typeface,
-              SkAdvancedTypefaceMetrics::FontType fontType);
+    struct Info {
+        sk_sp<SkTypeface> fTypeface;
+        SkGlyphID fFirstGlyphID;
+        SkGlyphID fLastGlyphID;
+        SkAdvancedTypefaceMetrics::FontType fFontType;
+    };
+    SkPDFFont(Info);
 
     SkGlyphID firstGlyphID() const { return fFirstGlyphID; }
     SkGlyphID lastGlyphID() const { return fLastGlyphID; }
-
+    const SkBitSet& glyphUsage() const { return fGlyphUsage; }
     sk_sp<SkTypeface> refTypeface() const { return fTypeface; }
 
-    /** Set fFirstGlyphID and fLastGlyphID to span at most 255 glyphs,
-     *  including the passed glyphID.
-     */
-    void adjustGlyphRangeForSingleByteEncoding(SkGlyphID glyphID);
-
     void drop() override;
 
 private:
     sk_sp<SkTypeface> fTypeface;
+    SkBitSet fGlyphUsage;
 
     // The glyph IDs accessible with this font.  For Type1 (non CID) fonts,
     // this will be a subset if the font has more than 255 glyphs.
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index 5a603ce..a78c4c5 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -176,8 +176,7 @@
 
 void SkPDFGraphicState::emitObject(
         SkWStream* stream,
-        const SkPDFObjNumMap& objNumMap,
-        const SkPDFSubstituteMap& substitutes) const {
+        const SkPDFObjNumMap& objNumMap) const {
     auto dict = sk_make_sp<SkPDFDict>("ExtGState");
     dict->insertName("Type", "ExtGState");
 
@@ -207,5 +206,5 @@
     dict->insertScalar("ML", fStrokeMiter);
     dict->insertBool("SA", true);  // SA = Auto stroke adjustment.
     dict->insertName("BM", as_blend_mode(xferMode));
-    dict->emitObject(stream, objNumMap, substitutes);
+    dict->emitObject(stream, objNumMap);
 }
diff --git a/src/pdf/SkPDFGraphicState.h b/src/pdf/SkPDFGraphicState.h
index 49723bd..0c2e4a0 100644
--- a/src/pdf/SkPDFGraphicState.h
+++ b/src/pdf/SkPDFGraphicState.h
@@ -31,8 +31,7 @@
     // Override emitObject so that we can populate the dictionary on
     // demand.
     void emitObject(SkWStream* stream,
-                    const SkPDFObjNumMap& objNumMap,
-                    const SkPDFSubstituteMap& substitutes) const override;
+                    const SkPDFObjNumMap& objNumMap) const override;
 
     /** Get the graphic state for the passed SkPaint. The reference count of
      *  the object is incremented and it is the caller's responsibility to
diff --git a/src/pdf/SkPDFMakeToUnicodeCmap.cpp b/src/pdf/SkPDFMakeToUnicodeCmap.cpp
index 6fd8b1c..5186cbb 100644
--- a/src/pdf/SkPDFMakeToUnicodeCmap.cpp
+++ b/src/pdf/SkPDFMakeToUnicodeCmap.cpp
@@ -149,7 +149,7 @@
 // one of them), the possible savings by aggressive optimization is 416KB
 // pre-compressed and does not provide enough motivation for implementation.
 void SkPDFAppendCmapSections(const SkTDArray<SkUnichar>& glyphToUnicode,
-                             const SkPDFGlyphSet* subset,
+                             const SkBitSet* subset,
                              SkDynamicMemoryWStream* cmap,
                              bool multiByteGlyphs,
                              SkGlyphID firstGlyphID,
@@ -212,7 +212,7 @@
 
 sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
         const SkTDArray<SkUnichar>& glyphToUnicode,
-        const SkPDFGlyphSet* subset,
+        const SkBitSet* subset,
         bool multiByteGlyphs,
         SkGlyphID firstGlyphID,
         SkGlyphID lastGlyphID) {
diff --git a/src/pdf/SkPDFMakeToUnicodeCmap.h b/src/pdf/SkPDFMakeToUnicodeCmap.h
index 1bd8930..0c4d1c3 100644
--- a/src/pdf/SkPDFMakeToUnicodeCmap.h
+++ b/src/pdf/SkPDFMakeToUnicodeCmap.h
@@ -13,14 +13,14 @@
 
 sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
         const SkTDArray<SkUnichar>& glyphToUnicode,
-        const SkPDFGlyphSet* subset,
+        const SkBitSet* subset,
         bool multiByteGlyphs,
         SkGlyphID firstGlyphID,
         SkGlyphID lastGlyphID);
 
 // Exposed for unit testing.
 void SkPDFAppendCmapSections(const SkTDArray<SkUnichar>& glyphToUnicode,
-                             const SkPDFGlyphSet* subset,
+                             const SkBitSet* subset,
                              SkDynamicMemoryWStream* cmap,
                              bool multiByteGlyphs,
                              SkGlyphID firstGlyphID,
diff --git a/src/pdf/SkPDFMetadata.cpp b/src/pdf/SkPDFMetadata.cpp
index 2592496..46fe461 100644
--- a/src/pdf/SkPDFMetadata.cpp
+++ b/src/pdf/SkPDFMetadata.cpp
@@ -168,12 +168,11 @@
 public:
     PDFXMLObject(SkString xml) : fXML(std::move(xml)) {}
     void emitObject(SkWStream* stream,
-                    const SkPDFObjNumMap& omap,
-                    const SkPDFSubstituteMap& smap) const override {
+                    const SkPDFObjNumMap& omap) const override {
         SkPDFDict dict("Metadata");
         dict.insertName("Subtype", "XML");
         dict.insertInt("Length", fXML.size());
-        dict.emitObject(stream, omap, smap);
+        dict.emitObject(stream, omap);
         static const char streamBegin[] = " stream\n";
         stream->write(streamBegin, strlen(streamBegin));
         // Do not compress this.  The standard requires that a
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index 6bf6afc..838b5ef 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -113,8 +113,7 @@
 }
 
 void SkPDFUnion::emitObject(SkWStream* stream,
-                            const SkPDFObjNumMap& objNumMap,
-                            const SkPDFSubstituteMap& substitutes) const {
+                            const SkPDFObjNumMap& objNumMap) const {
     switch (fType) {
         case Type::kInt:
             stream->writeDecAsText(fIntValue);
@@ -147,20 +146,18 @@
                                     pun(fSkString)->size());
             return;
         case Type::kObjRef:
-            stream->writeDecAsText(objNumMap.getObjectNumber(
-                    substitutes.getSubstitute(fObject)));
+            stream->writeDecAsText(objNumMap.getObjectNumber(fObject));
             stream->writeText(" 0 R");  // Generation number is always 0.
             return;
         case Type::kObject:
-            fObject->emitObject(stream, objNumMap, substitutes);
+            fObject->emitObject(stream, objNumMap);
             return;
         default:
             SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
     }
 }
 
-void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap,
-                              const SkPDFSubstituteMap& substituteMap) const {
+void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap) const {
     switch (fType) {
         case Type::kInt:
         case Type::kColorComponent:
@@ -171,13 +168,11 @@
         case Type::kNameSkS:
         case Type::kStringSkS:
             return;  // These have no resources.
-        case Type::kObjRef: {
-            SkPDFObject* obj = substituteMap.getSubstitute(fObject);
-            objNumMap->addObjectRecursively(obj, substituteMap);
+        case Type::kObjRef:
+            objNumMap->addObjectRecursively(fObject);
             return;
-        }
         case Type::kObject:
-            fObject->addResources(objNumMap, substituteMap);
+            fObject->addResources(objNumMap);
             return;
         default:
             SkDEBUGFAIL("SkPDFUnion::addResources with bad type");
@@ -253,13 +248,11 @@
 
 #if 0  // Enable if needed.
 void SkPDFAtom::emitObject(SkWStream* stream,
-                           const SkPDFObjNumMap& objNumMap,
-                           const SkPDFSubstituteMap& substitutes) const {
-    fValue.emitObject(stream, objNumMap, substitutes);
+                           const SkPDFObjNumMap& objNumMap) const {
+    fValue.emitObject(stream, objNumMap);
 }
-void SkPDFAtom::addResources(SkPDFObjNumMap* map,
-                             const SkPDFSubstituteMap& substitutes) const {
-    fValue.addResources(map, substitutes);
+void SkPDFAtom::addResources(SkPDFObjNumMap* map) const {
+    fValue.addResources(map);
 }
 #endif  // 0
 
@@ -282,12 +275,11 @@
 }
 
 void SkPDFArray::emitObject(SkWStream* stream,
-                            const SkPDFObjNumMap& objNumMap,
-                            const SkPDFSubstituteMap& substitutes) const {
+                            const SkPDFObjNumMap& objNumMap) const {
     SkASSERT(!fDumped);
     stream->writeText("[");
     for (int i = 0; i < fValues.count(); i++) {
-        fValues[i].emitObject(stream, objNumMap, substitutes);
+        fValues[i].emitObject(stream, objNumMap);
         if (i + 1 < fValues.count()) {
             stream->writeText(" ");
         }
@@ -295,11 +287,10 @@
     stream->writeText("]");
 }
 
-void SkPDFArray::addResources(SkPDFObjNumMap* catalog,
-                              const SkPDFSubstituteMap& substitutes) const {
+void SkPDFArray::addResources(SkPDFObjNumMap* catalog) const {
     SkASSERT(!fDumped);
     for (const SkPDFUnion& value : fValues) {
-        value.addResources(catalog, substitutes);
+        value.addResources(catalog);
     }
 }
 
@@ -364,33 +355,30 @@
 }
 
 void SkPDFDict::emitObject(SkWStream* stream,
-                           const SkPDFObjNumMap& objNumMap,
-                           const SkPDFSubstituteMap& substitutes) const {
+                           const SkPDFObjNumMap& objNumMap) const {
     stream->writeText("<<");
-    this->emitAll(stream, objNumMap, substitutes);
+    this->emitAll(stream, objNumMap);
     stream->writeText(">>");
 }
 
 void SkPDFDict::emitAll(SkWStream* stream,
-                        const SkPDFObjNumMap& objNumMap,
-                        const SkPDFSubstituteMap& substitutes) const {
+                        const SkPDFObjNumMap& objNumMap) const {
     SkASSERT(!fDumped);
     for (int i = 0; i < fRecords.count(); i++) {
-        fRecords[i].fKey.emitObject(stream, objNumMap, substitutes);
+        fRecords[i].fKey.emitObject(stream, objNumMap);
         stream->writeText(" ");
-        fRecords[i].fValue.emitObject(stream, objNumMap, substitutes);
+        fRecords[i].fValue.emitObject(stream, objNumMap);
         if (i + 1 < fRecords.count()) {
             stream->writeText("\n");
         }
     }
 }
 
-void SkPDFDict::addResources(SkPDFObjNumMap* catalog,
-                             const SkPDFSubstituteMap& substitutes) const {
+void SkPDFDict::addResources(SkPDFObjNumMap* catalog) const {
     SkASSERT(!fDumped);
     for (int i = 0; i < fRecords.count(); i++) {
-        fRecords[i].fKey.addResources(catalog, substitutes);
-        fRecords[i].fValue.addResources(catalog, substitutes);
+        fRecords[i].fKey.addResources(catalog);
+        fRecords[i].fValue.addResources(catalog);
     }
 }
 
@@ -463,20 +451,17 @@
 #ifdef SK_PDF_LESS_COMPRESSION
 void SkPDFSharedStream::emitObject(
         SkWStream* stream,
-        const SkPDFObjNumMap& objNumMap,
-        const SkPDFSubstituteMap& substitutes) const {
+        const SkPDFObjNumMap& objNumMap) const {
     SkASSERT(fAsset);
     std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate());
     SkASSERT(dup && dup->hasLength());
     size_t length = dup->getLength();
     stream->writeText("<<");
-    fDict.emitAll(stream, objNumMap, substitutes);
+    fDict.emitAll(stream, objNumMap);
     stream->writeText("\n");
-    SkPDFUnion::Name("Length").emitObject(
-            stream, objNumMap, substitutes);
+    SkPDFUnion::Name("Length").emitObject(stream, objNumMap);
     stream->writeText(" ");
-    SkPDFUnion::Int(length).emitObject(
-            stream, objNumMap, substitutes);
+    SkPDFUnion::Int(length).emitObject(stream, objNumMap);
     stream->writeText("\n>>stream\n");
     SkStreamCopy(stream, dup.get());
     stream->writeText("\nendstream");
@@ -484,8 +469,7 @@
 #else
 void SkPDFSharedStream::emitObject(
         SkWStream* stream,
-        const SkPDFObjNumMap& objNumMap,
-        const SkPDFSubstituteMap& substitutes) const {
+        const SkPDFObjNumMap& objNumMap) const {
     SkASSERT(fAsset);
     SkDynamicMemoryWStream buffer;
     SkDeflateWStream deflateWStream(&buffer);
@@ -496,15 +480,15 @@
     deflateWStream.finalize();
     size_t length = buffer.bytesWritten();
     stream->writeText("<<");
-    fDict.emitAll(stream, objNumMap, substitutes);
+    fDict.emitAll(stream, objNumMap);
     stream->writeText("\n");
-    SkPDFUnion::Name("Length").emitObject(stream, objNumMap, substitutes);
+    SkPDFUnion::Name("Length").emitObject(stream, objNumMap);
     stream->writeText(" ");
-    SkPDFUnion::Int(length).emitObject(stream, objNumMap, substitutes);
+    SkPDFUnion::Int(length).emitObject(stream, objNumMap);
     stream->writeText("\n");
-    SkPDFUnion::Name("Filter").emitObject(stream, objNumMap, substitutes);
+    SkPDFUnion::Name("Filter").emitObject(stream, objNumMap);
     stream->writeText(" ");
-    SkPDFUnion::Name("FlateDecode").emitObject(stream, objNumMap, substitutes);
+    SkPDFUnion::Name("FlateDecode").emitObject(stream, objNumMap);
     stream->writeText(">>");
     stream->writeText(" stream\n");
     buffer.writeToStream(stream);
@@ -513,9 +497,9 @@
 #endif
 
 void SkPDFSharedStream::addResources(
-        SkPDFObjNumMap* catalog, const SkPDFSubstituteMap& substitutes) const {
+        SkPDFObjNumMap* catalog) const {
     SkASSERT(fAsset);
-    fDict.addResources(catalog, substitutes);
+    fDict.addResources(catalog);
 }
 
 
@@ -534,10 +518,9 @@
 
 SkPDFStream::~SkPDFStream() {}
 
-void SkPDFStream::addResources(
-        SkPDFObjNumMap* catalog, const SkPDFSubstituteMap& substitutes) const {
+void SkPDFStream::addResources(SkPDFObjNumMap* catalog) const {
     SkASSERT(fCompressedData);
-    fDict.addResources(catalog, substitutes);
+    fDict.addResources(catalog);
 }
 
 void SkPDFStream::drop() {
@@ -546,10 +529,9 @@
 }
 
 void SkPDFStream::emitObject(SkWStream* stream,
-                             const SkPDFObjNumMap& objNumMap,
-                             const SkPDFSubstituteMap& substitutes) const {
+                             const SkPDFObjNumMap& objNumMap) const {
     SkASSERT(fCompressedData);
-    fDict.emitObject(stream, objNumMap, substitutes);
+    fDict.emitObject(stream, objNumMap);
     // duplicate (a cheap operation) preserves const on fCompressedData.
     std::unique_ptr<SkStreamAsset> dup(fCompressedData->duplicate());
     SkASSERT(dup);
@@ -594,25 +576,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-SkPDFSubstituteMap::~SkPDFSubstituteMap() {
-    fSubstituteMap.foreach(
-            [](SkPDFObject*, SkPDFObject** v) { (*v)->unref(); });
-}
-
-void SkPDFSubstituteMap::setSubstitute(SkPDFObject* original,
-                                       SkPDFObject* substitute) {
-    SkASSERT(original != substitute);
-    SkASSERT(!fSubstituteMap.find(original));
-    fSubstituteMap.set(original, SkRef(substitute));
-}
-
-SkPDFObject* SkPDFSubstituteMap::getSubstitute(SkPDFObject* object) const {
-    SkPDFObject** found = fSubstituteMap.find(object);
-    return found ? *found : object;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 bool SkPDFObjNumMap::addObject(SkPDFObject* obj) {
     if (fObjectNumbers.find(obj)) {
         return false;
@@ -622,10 +585,9 @@
     return true;
 }
 
-void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj,
-                                          const SkPDFSubstituteMap& subs) {
+void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj) {
     if (obj && this->addObject(obj)) {
-        obj->addResources(this, subs);
+        obj->addResources(this);
     }
 }
 
diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h
index cdfef6c..201d62b 100644
--- a/src/pdf/SkPDFTypes.h
+++ b/src/pdf/SkPDFTypes.h
@@ -17,7 +17,6 @@
 class SkData;
 class SkPDFObjNumMap;
 class SkPDFObject;
-class SkPDFSubstituteMap;
 class SkStreamAsset;
 class SkString;
 class SkWStream;
@@ -41,16 +40,14 @@
      *  @param stream   The writable output stream to send the output to.
      */
     virtual void emitObject(SkWStream* stream,
-                            const SkPDFObjNumMap& objNumMap,
-                            const SkPDFSubstituteMap& substitutes) const = 0;
+                            const SkPDFObjNumMap& objNumMap) const = 0;
 
     /**
      *  Adds all transitive dependencies of this object to the
      *  catalog.  Implementations should respect the catalog's object
      *  substitution map.
      */
-    virtual void addResources(SkPDFObjNumMap* catalog,
-                              const SkPDFSubstituteMap& substitutes) const {}
+    virtual void addResources(SkPDFObjNumMap* catalog) const {}
 
     /**
      *  Release all resources associated with this SkPDFObject.  It is
@@ -121,10 +118,8 @@
 
     /** These two non-virtual methods mirror SkPDFObject's
         corresponding virtuals. */
-    void emitObject(SkWStream*,
-                    const SkPDFObjNumMap&,
-                    const SkPDFSubstituteMap&) const;
-    void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const;
+    void emitObject(SkWStream*, const SkPDFObjNumMap&) const;
+    void addResources(SkPDFObjNumMap*) const;
 
     bool isName() const;
 
@@ -171,9 +166,8 @@
 class SkPDFAtom final : public SkPDFObject {
 public:
     void emitObject(SkWStream* stream,
-                    const SkPDFObjNumMap& objNumMap,
-                    const SkPDFSubstituteMap& substitutes) final;
-    void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const final;
+                    const SkPDFObjNumMap& objNumMap) final;
+    void addResources(SkPDFObjNumMap* const final;
     SkPDFAtom(SkPDFUnion&& v) : fValue(std::move(v) {}
 
 private:
@@ -197,10 +191,8 @@
 
     // The SkPDFObject interface.
     void emitObject(SkWStream* stream,
-                    const SkPDFObjNumMap& objNumMap,
-                    const SkPDFSubstituteMap& substitutes) const override;
-    void addResources(SkPDFObjNumMap*,
-                      const SkPDFSubstituteMap&) const override;
+                    const SkPDFObjNumMap& objNumMap) const override;
+    void addResources(SkPDFObjNumMap*) const override;
     void drop() override;
 
     /** The size of the array.
@@ -247,10 +239,8 @@
 
     // The SkPDFObject interface.
     void emitObject(SkWStream* stream,
-                    const SkPDFObjNumMap& objNumMap,
-                    const SkPDFSubstituteMap& substitutes) const override;
-    void addResources(SkPDFObjNumMap*,
-                      const SkPDFSubstituteMap&) const override;
+                    const SkPDFObjNumMap& objNumMap) const override;
+    void addResources(SkPDFObjNumMap*) const override;
     void drop() override;
 
     /** The size of the dictionary.
@@ -282,8 +272,7 @@
     /** Emit the dictionary, without the "<<" and ">>".
      */
     void emitAll(SkWStream* stream,
-                 const SkPDFObjNumMap& objNumMap,
-                 const SkPDFSubstituteMap& substitutes) const;
+                 const SkPDFObjNumMap& objNumMap) const;
 
 private:
     struct Record {
@@ -312,10 +301,8 @@
     ~SkPDFSharedStream();
     SkPDFDict* dict() { return &fDict; }
     void emitObject(SkWStream*,
-                    const SkPDFObjNumMap&,
-                    const SkPDFSubstituteMap&) const override;
-    void addResources(SkPDFObjNumMap*,
-                      const SkPDFSubstituteMap&) const override;
+                    const SkPDFObjNumMap&) const override;
+    void addResources(SkPDFObjNumMap*) const override;
     void drop() override;
 
 private:
@@ -346,9 +333,8 @@
 
     // The SkPDFObject interface.
     void emitObject(SkWStream* stream,
-                    const SkPDFObjNumMap& objNumMap,
-                    const SkPDFSubstituteMap& substitutes) const override;
-    void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const final;
+                    const SkPDFObjNumMap& objNumMap) const override;
+    void addResources(SkPDFObjNumMap*) const final;
     void drop() override;
 
 protected:
@@ -383,9 +369,8 @@
 
     /** Add the passed object to the catalog, as well as all its dependencies.
      *  @param obj   The object to add.  If nullptr, this is a noop.
-     *  @param subs  Will be passed to obj->addResources().
      */
-    void addObjectRecursively(SkPDFObject* obj, const SkPDFSubstituteMap& subs);
+    void addObjectRecursively(SkPDFObject* obj);
 
     /** Get the object number for the passed object.
      *  @param obj         The object of interest.
@@ -401,32 +386,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-/** \class SkPDFSubstituteMap
-
-    The PDF Substitute Map manages substitute objects and owns the
-    substitutes.
-*/
-class SkPDFSubstituteMap : SkNoncopyable {
-public:
-    ~SkPDFSubstituteMap();
-    /** Set substitute object for the passed object.
-        Refs substitute.
-     */
-    void setSubstitute(SkPDFObject* original, SkPDFObject* substitute);
-
-    /** Find and return any substitute object set for the passed object. If
-     *  there is none, return the passed object.
-     */
-    SkPDFObject* getSubstitute(SkPDFObject* object) const;
-
-    SkPDFObject* operator()(SkPDFObject* o) const {
-        return this->getSubstitute(o);
-    }
-
-private:
-    SkTHashMap<SkPDFObject*, SkPDFObject*> fSubstituteMap;
-};
-
 #ifdef SK_PDF_IMAGE_STATS
 extern SkAtomic<int> gDrawImageCalls;
 extern SkAtomic<int> gJpegImageObjects;
diff --git a/src/utils/SkBitSet.cpp b/src/utils/SkBitSet.cpp
index 0a1ecac..4323ffb 100755
--- a/src/utils/SkBitSet.cpp
+++ b/src/utils/SkBitSet.cpp
@@ -16,11 +16,6 @@
     fBitData.set(sk_calloc_throw(fDwordCount * sizeof(uint32_t)));
 }
 
-SkBitSet::SkBitSet(const SkBitSet& source)
-    : fBitData(nullptr), fDwordCount(0), fBitCount(0) {
-    *this = source;
-}
-
 SkBitSet::SkBitSet(SkBitSet&& source)
     : fBitData(source.fBitData.release())
     , fDwordCount(source.fDwordCount)
@@ -29,15 +24,15 @@
     source.fBitCount = 0;
 }
 
-SkBitSet& SkBitSet::operator=(const SkBitSet& rhs) {
-    if (this == &rhs) {
-        return *this;
+SkBitSet& SkBitSet::operator=(SkBitSet&& rhs) {
+    if (this != &rhs) {
+        fBitCount = rhs.fBitCount;
+        fDwordCount = rhs.fDwordCount;
+        fBitData.reset();  // Free old pointer.
+        fBitData.set(rhs.fBitData.release());
+        rhs.fBitCount = 0;
+        rhs.fDwordCount = 0;
     }
-    fBitCount = rhs.fBitCount;
-    fBitData.reset();
-    fDwordCount = rhs.fDwordCount;
-    fBitData.set(sk_malloc_throw(fDwordCount * sizeof(uint32_t)));
-    memcpy(fBitData.get(), rhs.fBitData.get(), fDwordCount * sizeof(uint32_t));
     return *this;
 }
 
diff --git a/src/utils/SkBitSet.h b/src/utils/SkBitSet.h
index 6882752..1e35c91 100644
--- a/src/utils/SkBitSet.h
+++ b/src/utils/SkBitSet.h
@@ -17,10 +17,11 @@
     /** NumberOfBits must be greater than zero.
      */
     explicit SkBitSet(int numberOfBits);
-    explicit SkBitSet(const SkBitSet& source);
-    explicit SkBitSet(SkBitSet&&);
+    SkBitSet(const SkBitSet&) = delete;
+    SkBitSet(SkBitSet&&);
+    SkBitSet& operator=(const SkBitSet&) = delete;
+    SkBitSet& operator=(SkBitSet&& rhs);
 
-    SkBitSet& operator=(const SkBitSet& rhs);
     bool operator==(const SkBitSet& rhs);
     bool operator!=(const SkBitSet& rhs);
 
@@ -39,6 +40,15 @@
             *chunk &= ~mask;
         }
     }
+    void set(int index) { this->setBit(index, true); }
+
+    template<typename T>
+    void setAll(T* array, int len) {
+        static_assert(std::is_integral<T>::value, "T is integral");
+        for (int i = 0; i < len; ++i) {
+            this->set(static_cast<int>(array[i]));
+        }
+    }
 
     /** Test if bit index is set.
      */
@@ -46,6 +56,7 @@
         uint32_t mask = 1 << (index & 31);
         return SkToBool(*this->internalGet(index) & mask);
     }
+    bool has(int index) const { return this->isBitSet(index); }
 
     /** Or bits from source.  false is returned if this doesn't have the same
      *  bit count as source.
@@ -56,6 +67,7 @@
      */
     template<typename T>
     void exportTo(SkTDArray<T>* array) const {
+        static_assert(std::is_integral<T>::value, "T is integral");
         SkASSERT(array);
         uint32_t* data = reinterpret_cast<uint32_t*>(fBitData.get());
         for (unsigned int i = 0; i < fDwordCount; ++i) {
diff --git a/tests/BitSetTest.cpp b/tests/BitSetTest.cpp
index da02376..716f414 100644
--- a/tests/BitSetTest.cpp
+++ b/tests/BitSetTest.cpp
@@ -70,7 +70,4 @@
     set3.setBit(0, true);
     REPORTER_ASSERT(reporter, set2 == set3);
     set3.clearAll();
-    set3 = set2;
-    set2 = set2;
-    REPORTER_ASSERT(reporter, set2 == set3);
 }
diff --git a/tests/PDFGlyphsToUnicodeTest.cpp b/tests/PDFGlyphsToUnicodeTest.cpp
index b8157ca..d83ce66 100644
--- a/tests/PDFGlyphsToUnicodeTest.cpp
+++ b/tests/PDFGlyphsToUnicodeTest.cpp
@@ -5,12 +5,14 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitSet.h"
 #include "SkData.h"
-#include "SkPDFFont.h"
 #include "SkPDFMakeToUnicodeCmap.h"
 #include "SkStream.h"
 #include "Test.h"
 
+static const int kMaximumGlyphCount = SK_MaxU16 + 1;
+
 static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
                           const char* buffer, size_t len) {
     sk_sp<SkData> data(stream.copyToData());
@@ -26,7 +28,7 @@
 DEF_TEST(ToUnicode, reporter) {
     SkTDArray<SkUnichar> glyphToUnicode;
     SkTDArray<uint16_t> glyphsInSubset;
-    SkPDFGlyphSet subset;
+    SkBitSet subset(kMaximumGlyphCount);
 
     glyphToUnicode.push(0);  // 0
     glyphToUnicode.push(0);  // 1
@@ -65,7 +67,7 @@
     glyphToUnicode.push(0x1013);
 
     SkDynamicMemoryWStream buffer;
-    subset.set(glyphsInSubset.begin(), glyphsInSubset.count());
+    subset.setAll(glyphsInSubset.begin(), glyphsInSubset.count());
     SkPDFAppendCmapSections(glyphToUnicode, &subset, &buffer, true, 0, 0xFFFF);
 
     char expectedResult[] =
@@ -136,7 +138,7 @@
 
     glyphToUnicode.reset();
     glyphsInSubset.reset();
-    SkPDFGlyphSet subset2;
+    SkBitSet subset2(kMaximumGlyphCount);
 
     // Test mapping:
     //           I  n  s  t  a  l
@@ -154,7 +156,7 @@
     glyphsInSubset.push(0x57);
 
     SkDynamicMemoryWStream buffer2;
-    subset2.set(glyphsInSubset.begin(), glyphsInSubset.count());
+    subset2.setAll(glyphsInSubset.begin(), glyphsInSubset.count());
     SkPDFAppendCmapSections(glyphToUnicode, &subset2, &buffer2, true, 0, 0xffff);
 
     char expectedResult2[] =
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 554f1ee..1faecb5 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -31,21 +31,14 @@
 
 #define DUMMY_TEXT "DCT compessed stream."
 
-namespace {
-struct Catalog {
-    SkPDFSubstituteMap substitutes;
-    SkPDFObjNumMap numbers;
-};
-}  // namespace
-
 template <typename T>
-static SkString emit_to_string(T& obj, Catalog* catPtr = nullptr) {
-    Catalog catalog;
+static SkString emit_to_string(T& obj, SkPDFObjNumMap* catPtr = nullptr) {
+    SkPDFObjNumMap catalog;
     SkDynamicMemoryWStream buffer;
     if (!catPtr) {
         catPtr = &catalog;
     }
-    obj.emitObject(&buffer, catPtr->numbers, catPtr->substitutes);
+    obj.emitObject(&buffer, *catPtr);
     SkString tmp(buffer.bytesWritten());
     buffer.copyTo(tmp.writable_str());
     return tmp;
@@ -148,9 +141,9 @@
     sk_sp<SkPDFArray> a2(new SkPDFArray);
     a2->appendObjRef(a1);
 
-    Catalog catalog;
-    catalog.numbers.addObject(a1.get());
-    REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(a1.get()) == 1);
+    SkPDFObjNumMap catalog;
+    catalog.addObject(a1.get());
+    REPORTER_ASSERT(reporter, catalog.getObjectNumber(a1.get()) == 1);
 
     SkString result = emit_to_string(*a2, &catalog);
     // If appendObjRef misbehaves, then the result would
@@ -158,22 +151,6 @@
     assert_eq(reporter, result, "[1 0 R]");
 }
 
-static void TestSubstitute(skiatest::Reporter* reporter) {
-    sk_sp<SkPDFDict> proxy(new SkPDFDict());
-    sk_sp<SkPDFDict> stub(new SkPDFDict());
-
-    proxy->insertInt("Value", 33);
-    stub->insertInt("Value", 44);
-
-    SkPDFSubstituteMap substituteMap;
-    substituteMap.setSubstitute(proxy.get(), stub.get());
-    SkPDFObjNumMap catalog;
-    catalog.addObject(proxy.get());
-
-    REPORTER_ASSERT(reporter, stub.get() == substituteMap.getSubstitute(proxy.get()));
-    REPORTER_ASSERT(reporter, proxy.get() != substituteMap.getSubstitute(stub.get()));
-}
-
 // This test used to assert without the fix submitted for
 // http://code.google.com/p/skia/issues/detail?id=1083.
 // SKP files might have invalid glyph ids. This test ensures they are ignored,
@@ -283,9 +260,9 @@
                    "(Another String) [-1]]");
 
     sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
-    Catalog catalog;
-    catalog.numbers.addObject(referencedArray.get());
-    REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(
+    SkPDFObjNumMap catalog;
+    catalog.addObject(referencedArray.get());
+    REPORTER_ASSERT(reporter, catalog.getObjectNumber(
                             referencedArray.get()) == 1);
     array->appendObjRef(std::move(referencedArray));
 
@@ -347,9 +324,9 @@
     assert_emit_eq(reporter, *dict, "<</Type /DType>>");
 
     sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
-    Catalog catalog;
-    catalog.numbers.addObject(referencedArray.get());
-    REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(
+    SkPDFObjNumMap catalog;
+    catalog.addObject(referencedArray.get());
+    REPORTER_ASSERT(reporter, catalog.getObjectNumber(
                             referencedArray.get()) == 1);
     dict->insertObjRef("n1", std::move(referencedArray));
     SkString result = emit_to_string(*dict, &catalog);
@@ -363,7 +340,6 @@
     TestPDFStream(reporter);
     TestObjectNumberMap(reporter);
     TestObjectRef(reporter);
-    TestSubstitute(reporter);
     test_issue1083();
 }