SkPDF: Factor SkPDFCatalog into SkPDFObjNumMap and SkPDFSubstituteMap

Motivation: Keep separate features separate.  Also, future
linearization work will need to have several objNumMap
objects share a substituteMap.  Also "catalog" has a
specific meaning in PDF.  This catalog did not map to that
catalog.

-   Modify SkPDFObject::emitObject and SkPDFObject::addResources
    interface to requiore SkPDFObjNumMap and SkPDFSubstituteMap.
-   SkPDFObjNumMap const in SkPDFObject::emitObject.
-   Remove SkPDFCatalog.cpp/.h
-   Modify SkDocument_PDF.cpp to use new functions
-   Fold in SkPDFStream::populate
-   Fold in SkPDFBitmap::emitDict
-   Move SkPDFObjNumMap and SkPDFSubstituteMap to SkPDFTypes.h
-   Note (via assert) that SkPDFArray & SkPDFDict don't need to
    check substitutes.
-   Remove extra space from SkPDFDict serialization.
-   SkPDFBitmap SkPDFType0Font SkPDFGraphicState SkPDFStream
    updated to new interface.
-   PDFPrimitivesTest updated for new interface.

BUG=skia:3585

Review URL: https://codereview.chromium.org/1049753002
diff --git a/gyp/pdf.gypi b/gyp/pdf.gypi
index 9de60a6..68c9897 100644
--- a/gyp/pdf.gypi
+++ b/gyp/pdf.gypi
@@ -16,8 +16,6 @@
         '<(skia_src_path)/pdf/SkPDFBitmap.h',
         '<(skia_src_path)/pdf/SkPDFCanon.cpp',
         '<(skia_src_path)/pdf/SkPDFCanon.h',
-        '<(skia_src_path)/pdf/SkPDFCatalog.cpp',
-        '<(skia_src_path)/pdf/SkPDFCatalog.h',
         '<(skia_src_path)/pdf/SkPDFDevice.cpp',
         '<(skia_src_path)/pdf/SkPDFDevice.h',
         '<(skia_src_path)/pdf/SkPDFFont.cpp',
diff --git a/src/doc/SkDocument_PDF.cpp b/src/doc/SkDocument_PDF.cpp
index 3b46fb6..0b62e2c 100644
--- a/src/doc/SkDocument_PDF.cpp
+++ b/src/doc/SkDocument_PDF.cpp
@@ -7,7 +7,6 @@
 
 #include "SkDocument.h"
 #include "SkPDFCanon.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFDevice.h"
 #include "SkPDFFont.h"
 #include "SkPDFResourceDict.h"
@@ -24,7 +23,8 @@
 }
 
 static void emit_pdf_footer(SkWStream* stream,
-                            SkPDFCatalog* catalog,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes,
                             SkPDFObject* docCatalog,
                             int64_t objCount,
                             int32_t xRefFileOffset) {
@@ -35,7 +35,7 @@
     trailerDict.insert("Root", new SkPDFObjRef(docCatalog))->unref();
 
     stream->writeText("trailer\n");
-    trailerDict.emitObject(stream, catalog);
+    trailerDict.emitObject(stream, objNumMap, substitutes);
     stream->writeText("\nstartxref\n");
     stream->writeBigDecAsText(xRefFileOffset);
     stream->writeText("\n%%EOF");
@@ -43,8 +43,8 @@
 
 static void perform_font_subsetting(
         const SkTDArray<const SkPDFDevice*>& pageDevices,
-        SkPDFCatalog* catalog) {
-    SkASSERT(catalog);
+        SkPDFSubstituteMap* substituteMap) {
+    SkASSERT(substituteMap);
 
     SkPDFGlyphSetMap usage;
     for (int i = 0; i < pageDevices.count(); ++i) {
@@ -56,7 +56,7 @@
         SkAutoTUnref<SkPDFFont> subsetFont(
                 entry->fFont->getFontSubset(entry->fGlyphSet));
         if (subsetFont) {
-            catalog->setSubstitute(entry->fFont, subsetFont.get());
+            substituteMap->setSubstitute(entry->fFont, subsetFont.get());
         }
         entry = iterator.next();
     }
@@ -183,7 +183,6 @@
         pageDevices[i]->appendDestinations(dests, page.get());
         pages.push(page.detach());
     }
-    SkPDFCatalog catalog;
 
     SkTDArray<SkPDFDict*> pageTree;
     SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog")));
@@ -209,22 +208,24 @@
     }
 
     // Build font subsetting info before proceeding.
-    perform_font_subsetting(pageDevices, &catalog);
+    SkPDFSubstituteMap substitutes;
+    perform_font_subsetting(pageDevices, &substitutes);
 
-    if (catalog.addObject(docCatalog.get())) {
-        docCatalog->addResources(&catalog);
+    SkPDFObjNumMap objNumMap;
+    if (objNumMap.addObject(docCatalog.get())) {
+        docCatalog->addResources(&objNumMap, substitutes);
     }
     size_t baseOffset = SkToOffT(stream->bytesWritten());
     emit_pdf_header(stream);
     SkTDArray<int32_t> offsets;
-    for (int i = 0; i < catalog.objects().count(); ++i) {
-        SkPDFObject* object = catalog.objects()[i];
+    for (int i = 0; i < objNumMap.objects().count(); ++i) {
+        SkPDFObject* object = objNumMap.objects()[i];
         offsets.push(SkToS32(stream->bytesWritten() - baseOffset));
-        SkASSERT(object == catalog.getSubstituteObject(object));
-        SkASSERT(catalog.getObjectNumber(object) == i + 1);
+        SkASSERT(object == substitutes.getSubstitute(object));
+        SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
         stream->writeDecAsText(i + 1);
         stream->writeText(" 0 obj\n");  // Generation number is always 0.
-        object->emitObject(stream, &catalog);
+        object->emitObject(stream, objNumMap, substitutes);
         stream->writeText("\nendobj\n");
     }
     int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);
@@ -240,7 +241,7 @@
         stream->writeBigDecAsText(offsets[i], 10);
         stream->writeText(" 00000 n \n");
     }
-    emit_pdf_footer(stream, &catalog, docCatalog.get(), objCount,
+    emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount,
                     xRefFileOffset);
 
     // The page tree has both child and parent pointers, so it creates a
diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp
index e4580fc..2a9deb6 100644
--- a/src/pdf/SkPDFBitmap.cpp
+++ b/src/pdf/SkPDFBitmap.cpp
@@ -9,7 +9,6 @@
 #include "SkFlate.h"
 #include "SkPDFBitmap.h"
 #include "SkPDFCanon.h"
-#include "SkPDFCatalog.h"
 #include "SkStream.h"
 #include "SkUnPreMultiply.h"
 
@@ -244,14 +243,17 @@
 public:
     PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
     ~PDFAlphaBitmap() {}
-    void emitObject(SkWStream*, SkPDFCatalog*) override;
+    void emitObject(SkWStream*,
+                    const SkPDFObjNumMap&,
+                    const SkPDFSubstituteMap&) override;
 
 private:
     const SkBitmap fBitmap;
-    void emitDict(SkWStream*, SkPDFCatalog*, size_t) const;
 };
 
-void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void PDFAlphaBitmap::emitObject(SkWStream* stream,
+                                const SkPDFObjNumMap& objNumMap,
+                                const SkPDFSubstituteMap& substitutes) {
     SkAutoLockPixels autoLockPixels(fBitmap);
     SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
              fBitmap.getColorTable());
@@ -263,15 +265,6 @@
     deflateWStream.finalize();  // call before detachAsStream().
     SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
 
-    this->emitDict(stream, catalog, asset->getLength());
-    pdf_stream_begin(stream);
-    stream->writeStream(asset.get(), asset->getLength());
-    pdf_stream_end(stream);
-}
-
-void PDFAlphaBitmap::emitDict(SkWStream* stream,
-                              SkPDFCatalog* catalog,
-                              size_t length) const {
     SkPDFDict pdfDict("XObject");
     pdfDict.insertName("Subtype", "Image");
     pdfDict.insertInt("Width", fBitmap.width());
@@ -279,39 +272,28 @@
     pdfDict.insertName("ColorSpace", "DeviceGray");
     pdfDict.insertInt("BitsPerComponent", 8);
     pdfDict.insertName("Filter", "FlateDecode");
-    pdfDict.insertInt("Length", length);
-    pdfDict.emitObject(stream, catalog);
+    pdfDict.insertInt("Length", asset->getLength());
+    pdfDict.emitObject(stream, objNumMap, substitutes);
+
+    pdf_stream_begin(stream);
+    stream->writeStream(asset.get(), asset->getLength());
+    pdf_stream_end(stream);
 }
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void SkPDFBitmap::addResources(SkPDFCatalog* catalog) const {
+void SkPDFBitmap::addResources(SkPDFObjNumMap* catalog,
+                               const SkPDFSubstituteMap& substitutes) const {
     if (fSMask.get()) {
-        if (catalog->addObject(fSMask.get())) {
-            fSMask->addResources(catalog);
+        SkPDFObject* obj = substitutes.getSubstitute(fSMask.get());
+        SkASSERT(obj);
+        if (catalog->addObject(obj)) {
+            obj->addResources(catalog, substitutes);
         }
     }
 }
 
-void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    SkAutoLockPixels autoLockPixels(fBitmap);
-    SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
-             fBitmap.getColorTable());
-
-    // Write to a temporary buffer to get the compressed length.
-    SkDynamicMemoryWStream buffer;
-    SkDeflateWStream deflateWStream(&buffer);
-    bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
-    deflateWStream.finalize();  // call before detachAsStream().
-    SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
-
-    this->emitDict(stream, catalog, asset->getLength());
-    pdf_stream_begin(stream);
-    stream->writeStream(asset.get(), asset->getLength());
-    pdf_stream_end(stream);
-}
-
 static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
     SkPDFArray* result = SkNEW(SkPDFArray);
     result->reserve(4);
@@ -342,9 +324,20 @@
     return result;
 }
 
-void SkPDFBitmap::emitDict(SkWStream* stream,
-                           SkPDFCatalog* catalog,
-                           size_t length) const {
+void SkPDFBitmap::emitObject(SkWStream* stream,
+                             const SkPDFObjNumMap& objNumMap,
+                             const SkPDFSubstituteMap& substitutes) {
+    SkAutoLockPixels autoLockPixels(fBitmap);
+    SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
+             fBitmap.getColorTable());
+
+    // Write to a temporary buffer to get the compressed length.
+    SkDynamicMemoryWStream buffer;
+    SkDeflateWStream deflateWStream(&buffer);
+    bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
+    deflateWStream.finalize();  // call before detachAsStream().
+    SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
+
     SkPDFDict pdfDict("XObject");
     pdfDict.insertName("Subtype", "Image");
     pdfDict.insertInt("Width", fBitmap.width());
@@ -363,8 +356,12 @@
         pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
     }
     pdfDict.insertName("Filter", "FlateDecode");
-    pdfDict.insertInt("Length", length);
-    pdfDict.emitObject(stream, catalog);
+    pdfDict.insertInt("Length", asset->getLength());
+    pdfDict.emitObject(stream, objNumMap,substitutes);
+
+    pdf_stream_begin(stream);
+    stream->writeStream(asset.get(), asset->getLength());
+    pdf_stream_end(stream);
 }
 
 SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
diff --git a/src/pdf/SkPDFBitmap.h b/src/pdf/SkPDFBitmap.h
index 6816ed6..bf41f63 100644
--- a/src/pdf/SkPDFBitmap.h
+++ b/src/pdf/SkPDFBitmap.h
@@ -27,8 +27,11 @@
     // Returns NULL on unsupported bitmap;
     static SkPDFBitmap* Create(SkPDFCanon*, const SkBitmap&);
     ~SkPDFBitmap();
-    void emitObject(SkWStream*, SkPDFCatalog*) override;
-    void addResources(SkPDFCatalog*) const override;
+    void emitObject(SkWStream*,
+                    const SkPDFObjNumMap& objNumMap,
+                    const SkPDFSubstituteMap& substitutes) override;
+    void addResources(SkPDFObjNumMap*,
+                      const SkPDFSubstituteMap&) const override;
     bool equals(const SkBitmap& other) const {
         return fBitmap.getGenerationID() == other.getGenerationID() &&
                fBitmap.pixelRefOrigin() == other.pixelRefOrigin() &&
@@ -39,7 +42,6 @@
     const SkBitmap fBitmap;
     const SkAutoTUnref<SkPDFObject> fSMask;
     SkPDFBitmap(const SkBitmap&, SkPDFObject*);
-    void emitDict(SkWStream*, SkPDFCatalog*, size_t) const;
 };
 
 #endif  // SkPDFBitmap_DEFINED
diff --git a/src/pdf/SkPDFCatalog.cpp b/src/pdf/SkPDFCatalog.cpp
deleted file mode 100644
index 4069cc9..0000000
--- a/src/pdf/SkPDFCatalog.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include "SkPDFCatalog.h"
-
-SkPDFCatalog::SkPDFCatalog() {}
-
-SkPDFCatalog::~SkPDFCatalog() {
-    fSubstituteMap.foreach(
-            [](SkPDFObject*, SkPDFObject** v) { (*v)->unref(); });
-}
-
-bool SkPDFCatalog::addObject(SkPDFObject* obj) {
-    if (fObjectNumbers.find(obj)) {
-        return false;
-    }
-    fObjectNumbers.set(obj, fObjectNumbers.count() + 1);
-    fObjects.push(obj);
-    return true;
-}
-
-int32_t SkPDFCatalog::getObjectNumber(SkPDFObject* obj) const {
-    int32_t* objectNumberFound = fObjectNumbers.find(obj);
-    SkASSERT(objectNumberFound);
-    return *objectNumberFound;
-}
-
-void SkPDFCatalog::setSubstitute(SkPDFObject* original,
-                                 SkPDFObject* substitute) {
-    SkASSERT(original != substitute);
-    SkASSERT(!fSubstituteMap.find(original));
-    fSubstituteMap.set(original, SkRef(substitute));
-}
-
-SkPDFObject* SkPDFCatalog::getSubstituteObject(SkPDFObject* object) const {
-    SkPDFObject** found = fSubstituteMap.find(object);
-    return found ? *found : object;
-}
diff --git a/src/pdf/SkPDFCatalog.h b/src/pdf/SkPDFCatalog.h
deleted file mode 100644
index 3609f3c..0000000
--- a/src/pdf/SkPDFCatalog.h
+++ /dev/null
@@ -1,56 +0,0 @@
-
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkPDFCatalog_DEFINED
-#define SkPDFCatalog_DEFINED
-
-#include "SkPDFTypes.h"
-#include "SkTDArray.h"
-#include "SkTHash.h"
-
-/** \class SkPDFCatalog
-
-    The PDF catalog manages object numbers.  It is used
-    to create the PDF cross reference table.
-*/
-class SkPDFCatalog {
-public:
-    SkPDFCatalog();
-    ~SkPDFCatalog();
-
-    /** Add the passed object to the catalog.
-     *  @param obj         The object to add.
-     *  @return True iff the object was not already added to the catalog.
-     */
-    bool addObject(SkPDFObject* obj);
-
-    /** Get the object number for the passed object.
-     *  @param obj         The object of interest.
-     */
-    int32_t getObjectNumber(SkPDFObject* obj) const;
-
-    /** 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* getSubstituteObject(SkPDFObject* object) const;
-
-    const SkTDArray<SkPDFObject*>& objects() const { return fObjects; }
-
-private:
-    SkTDArray<SkPDFObject*> fObjects;
-    SkTHashMap<SkPDFObject*, int32_t> fObjectNumbers;
-    SkTHashMap<SkPDFObject*, SkPDFObject*> fSubstituteMap;
-};
-
-#endif
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index d36a1d7..1a396c7 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -10,7 +10,6 @@
 #include "SkData.h"
 #include "SkGlyphCache.h"
 #include "SkPaint.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFCanon.h"
 #include "SkPDFDevice.h"
 #include "SkPDFFont.h"
@@ -1016,9 +1015,11 @@
 }
 
 #ifdef SK_DEBUG
-void SkPDFType0Font::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void SkPDFType0Font::emitObject(SkWStream* stream,
+                                const SkPDFObjNumMap& objNumMap,
+                                const SkPDFSubstituteMap& substitutes) {
     SkASSERT(fPopulated);
-    return INHERITED::emitObject(stream, catalog);
+    return INHERITED::emitObject(stream, objNumMap, substitutes);
 }
 #endif
 
diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h
index c286fa4..6f8a923 100644
--- a/src/pdf/SkPDFFont.h
+++ b/src/pdf/SkPDFFont.h
@@ -18,7 +18,7 @@
 
 class SkPaint;
 class SkPDFCanon;
-class SkPDFCatalog;
+class SkPDFObjNumMap;
 class SkPDFFont;
 
 class SkPDFGlyphSet : SkNoncopyable {
diff --git a/src/pdf/SkPDFFontImpl.h b/src/pdf/SkPDFFontImpl.h
index a0689a3..cc7a4a1 100644
--- a/src/pdf/SkPDFFontImpl.h
+++ b/src/pdf/SkPDFFontImpl.h
@@ -18,7 +18,9 @@
     virtual bool multiByteGlyphs() const { return true; }
     virtual SkPDFFont* getFontSubset(const SkPDFGlyphSet* usage);
 #ifdef SK_DEBUG
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog);
+    virtual void emitObject(SkWStream*,
+                            const SkPDFObjNumMap&,
+                            const SkPDFSubstituteMap&);
 #endif
 
 private:
diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp
index 59e81f4..6797eac 100644
--- a/src/pdf/SkPDFFormXObject.cpp
+++ b/src/pdf/SkPDFFormXObject.cpp
@@ -10,7 +10,6 @@
 #include "SkPDFFormXObject.h"
 
 #include "SkMatrix.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFDevice.h"
 #include "SkPDFResourceDict.h"
 #include "SkPDFUtils.h"
diff --git a/src/pdf/SkPDFFormXObject.h b/src/pdf/SkPDFFormXObject.h
index 11fa2d5..337f64b 100644
--- a/src/pdf/SkPDFFormXObject.h
+++ b/src/pdf/SkPDFFormXObject.h
@@ -19,7 +19,7 @@
 
 class SkMatrix;
 class SkPDFDevice;
-class SkPDFCatalog;
+class SkPDFObjNumMap;
 
 /** \class SkPDFFormXObject
 
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index 09545d1..6922b08 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -114,9 +114,11 @@
 
 SkPDFGraphicState::~SkPDFGraphicState() {}
 
-void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void SkPDFGraphicState::emitObject(SkWStream* stream,
+                                   const SkPDFObjNumMap& objNumMap,
+                                   const SkPDFSubstituteMap& substitutes) {
     populateDict();
-    SkPDFDict::emitObject(stream, catalog);
+    SkPDFDict::emitObject(stream, objNumMap, substitutes);
 }
 
 // static
diff --git a/src/pdf/SkPDFGraphicState.h b/src/pdf/SkPDFGraphicState.h
index dc41fc6..85a324b 100644
--- a/src/pdf/SkPDFGraphicState.h
+++ b/src/pdf/SkPDFGraphicState.h
@@ -37,7 +37,9 @@
 
     // Override emitObject so that we can populate the dictionary on
     // demand.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog);
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes);
 
     /** 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/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
index da55340..3032125 100644
--- a/src/pdf/SkPDFShader.cpp
+++ b/src/pdf/SkPDFShader.cpp
@@ -11,7 +11,6 @@
 
 #include "SkData.h"
 #include "SkPDFCanon.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFDevice.h"
 #include "SkPDFFormXObject.h"
 #include "SkPDFGraphicState.h"
diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp
index 939cdd8..5665c59 100644
--- a/src/pdf/SkPDFStream.cpp
+++ b/src/pdf/SkPDFStream.cpp
@@ -9,7 +9,6 @@
 
 #include "SkData.h"
 #include "SkFlate.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFStream.h"
 #include "SkStream.h"
 #include "SkStreamPriv.h"
@@ -24,9 +23,26 @@
 
 SkPDFStream::~SkPDFStream() {}
 
-void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    SkAssertResult(this->populate(catalog));
-    this->INHERITED::emitObject(stream, catalog);
+void SkPDFStream::emitObject(SkWStream* stream,
+                             const SkPDFObjNumMap& objNumMap,
+                             const SkPDFSubstituteMap& substitutes) {
+    if (fState == kUnused_State) {
+        fState = kNoCompression_State;
+        SkDynamicMemoryWStream compressedData;
+
+        SkAssertResult(
+                SkFlate::Deflate(fDataStream.get(), &compressedData));
+        SkAssertResult(fDataStream->rewind());
+        if (compressedData.getOffset() < this->dataSize()) {
+            SkAutoTDelete<SkStream> compressed(
+                    compressedData.detachAsStream());
+            this->setData(compressed.get());
+            insertName("Filter", "FlateDecode");
+        }
+        fState = kCompressed_State;
+        insertInt("Length", this->dataSize());
+    }
+    this->INHERITED::emitObject(stream, objNumMap, substitutes);
     stream->writeText(" stream\n");
     stream->writeStream(fDataStream.get(), fDataStream->getLength());
     SkAssertResult(fDataStream->rewind());
@@ -52,23 +68,3 @@
     SkASSERT(fDataStream->hasLength());
     return fDataStream->getLength();
 }
-
-bool SkPDFStream::populate(SkPDFCatalog* catalog) {
-    if (fState == kUnused_State) {
-        fState = kNoCompression_State;
-        SkDynamicMemoryWStream compressedData;
-
-        SkAssertResult(
-                SkFlate::Deflate(fDataStream.get(), &compressedData));
-        SkAssertResult(fDataStream->rewind());
-        if (compressedData.getOffset() < this->dataSize()) {
-            SkAutoTDelete<SkStream> compressed(
-                    compressedData.detachAsStream());
-            this->setData(compressed.get());
-            insertName("Filter", "FlateDecode");
-        }
-        fState = kCompressed_State;
-        insertInt("Length", this->dataSize());
-    }
-    return true;
-}
diff --git a/src/pdf/SkPDFStream.h b/src/pdf/SkPDFStream.h
index 9a43e3c..b43ead5 100644
--- a/src/pdf/SkPDFStream.h
+++ b/src/pdf/SkPDFStream.h
@@ -15,7 +15,7 @@
 #include "SkStream.h"
 #include "SkTemplates.h"
 
-class SkPDFCatalog;
+class SkPDFObjNumMap;
 
 /** \class SkPDFStream
 
@@ -40,7 +40,9 @@
     virtual ~SkPDFStream();
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) override;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) override;
 
 protected:
     enum State {
@@ -55,10 +57,6 @@
      */
     SkPDFStream();
 
-    // Populate the stream dictionary.  This method returns false if
-    // fSubstitute should be used.
-    virtual bool populate(SkPDFCatalog* catalog);
-
     void setData(SkData* data);
     void setData(SkStream* stream);
 
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index 02c5573..e80d118 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -7,7 +7,6 @@
  */
 
 
-#include "SkPDFCatalog.h"
 #include "SkPDFTypes.h"
 #include "SkStream.h"
 
@@ -25,17 +24,20 @@
 
 SkPDFObjRef::~SkPDFObjRef() {}
 
-void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    SkPDFObject* obj = catalog->getSubstituteObject(fObj);
-    stream->writeDecAsText(catalog->getObjectNumber(obj));
+void SkPDFObjRef::emitObject(SkWStream* stream,
+                             const SkPDFObjNumMap& objNumMap,
+                             const SkPDFSubstituteMap& substitutes) {
+    SkPDFObject* obj = substitutes.getSubstitute(fObj);
+    stream->writeDecAsText(objNumMap.getObjectNumber(obj));
     stream->writeText(" 0 R");  // Generation number is always 0.
 }
 
-void SkPDFObjRef::addResources(SkPDFCatalog* catalog) const {
-    SkPDFObject* obj = catalog->getSubstituteObject(fObj);
+void SkPDFObjRef::addResources(SkPDFObjNumMap* catalog,
+                               const SkPDFSubstituteMap& substitutes) const {
+    SkPDFObject* obj = substitutes.getSubstitute(fObj);
     SkASSERT(obj);
     if (catalog->addObject(obj)) {
-        obj->addResources(catalog);
+        obj->addResources(catalog, substitutes);
     }
 }
 
@@ -44,7 +46,9 @@
 SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {}
 SkPDFInt::~SkPDFInt() {}
 
-void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void SkPDFInt::emitObject(SkWStream* stream,
+                          const SkPDFObjNumMap&,
+                          const SkPDFSubstituteMap&) {
     stream->writeDecAsText(fValue);
 }
 
@@ -53,12 +57,10 @@
 SkPDFBool::SkPDFBool(bool value) : fValue(value) {}
 SkPDFBool::~SkPDFBool() {}
 
-void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    if (fValue) {
-        stream->writeText("true");
-    } else {
-        stream->writeText("false");
-    }
+void SkPDFBool::emitObject(SkWStream* stream,
+                          const SkPDFObjNumMap&,
+                          const SkPDFSubstituteMap&) {
+    stream->writeText(fValue ? "true" : "false");
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -66,8 +68,10 @@
 SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
 SkPDFScalar::~SkPDFScalar() {}
 
-void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    Append(fValue, stream);
+void SkPDFScalar::emitObject(SkWStream* stream,
+                             const SkPDFObjNumMap&,
+                             const SkPDFSubstituteMap&) {
+    SkPDFScalar::Append(fValue, stream);
 }
 
 // static
@@ -135,7 +139,9 @@
 
 SkPDFString::~SkPDFString() {}
 
-void SkPDFString::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void SkPDFString::emitObject(SkWStream* stream,
+                             const SkPDFObjNumMap&,
+                             const SkPDFSubstituteMap&) {
     stream->write(fValue.c_str(), fValue.size());
 }
 
@@ -214,7 +220,9 @@
     return fValue == b.fValue;
 }
 
-void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void SkPDFName::emitObject(SkWStream* stream,
+                             const SkPDFObjNumMap&,
+                             const SkPDFSubstituteMap&) {
     stream->write(fValue.c_str(), fValue.size());
 }
 
@@ -245,10 +253,13 @@
     fValue.unrefAll();
 }
 
-void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void SkPDFArray::emitObject(SkWStream* stream,
+                             const SkPDFObjNumMap& objNumMap,
+                             const SkPDFSubstituteMap& substitutes) {
     stream->writeText("[");
     for (int i = 0; i < fValue.count(); i++) {
-        catalog->getSubstituteObject(fValue[i])->emitObject(stream, catalog);
+        SkASSERT(substitutes.getSubstitute(fValue[i]) == fValue[i]);
+        fValue[i]->emitObject(stream, objNumMap, substitutes);
         if (i + 1 < fValue.count()) {
             stream->writeText(" ");
         }
@@ -256,9 +267,11 @@
     stream->writeText("]");
 }
 
-void SkPDFArray::addResources(SkPDFCatalog* catalog) const {
+void SkPDFArray::addResources(SkPDFObjNumMap* catalog,
+                              const SkPDFSubstituteMap& substitutes) const {
     for (int i = 0; i < fValue.count(); i++) {
-        catalog->getSubstituteObject(fValue[i])->addResources(catalog);
+        SkASSERT(substitutes.getSubstitute(fValue[i]) == fValue[i]);
+        fValue[i]->addResources(catalog, substitutes);
     }
 }
 
@@ -313,26 +326,33 @@
     return fValue.count();
 }
 
-void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void SkPDFDict::emitObject(SkWStream* stream,
+                           const SkPDFObjNumMap& objNumMap,
+                           const SkPDFSubstituteMap& substitutes) {
     stream->writeText("<<");
     for (int i = 0; i < fValue.count(); i++) {
         SkASSERT(fValue[i].key);
         SkASSERT(fValue[i].value);
-        fValue[i].key->emitObject(stream, catalog);
+        SkASSERT(substitutes.getSubstitute(fValue[i].key) == fValue[i].key);
+        SkASSERT(substitutes.getSubstitute(fValue[i].value) == fValue[i].value);
+        fValue[i].key->emitObject(stream, objNumMap, substitutes);
         stream->writeText(" ");
-        catalog->getSubstituteObject(fValue[i].value)
-                ->emitObject(stream, catalog);
-        stream->writeText("\n");
+        fValue[i].value->emitObject(stream, objNumMap, substitutes);
+        if (i + 1 < fValue.count()) {
+            stream->writeText("\n");
+        }
     }
     stream->writeText(">>");
 }
 
-void SkPDFDict::addResources(SkPDFCatalog* catalog) const {
+void SkPDFDict::addResources(SkPDFObjNumMap* catalog,
+                             const SkPDFSubstituteMap& substitutes) const {
     for (int i = 0; i < fValue.count(); i++) {
         SkASSERT(fValue[i].key);
         SkASSERT(fValue[i].value);
-        fValue[i].key->addResources(catalog);
-        catalog->getSubstituteObject(fValue[i].value)->addResources(catalog);
+        fValue[i].key->addResources(catalog, substitutes);
+        SkASSERT(substitutes.getSubstitute(fValue[i].value) == fValue[i].value);
+        fValue[i].value->addResources(catalog, substitutes);
     }
 }
 
@@ -394,3 +414,40 @@
                 Rec(SkRef(other.fValue[i].key), SkRef(other.fValue[i].value));
     }
 }
+
+////////////////////////////////////////////////////////////////////////////////
+
+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;
+    }
+    fObjectNumbers.set(obj, fObjectNumbers.count() + 1);
+    fObjects.push(obj);
+    return true;
+}
+
+int32_t SkPDFObjNumMap::getObjectNumber(SkPDFObject* obj) const {
+    int32_t* objectNumberFound = fObjectNumbers.find(obj);
+    SkASSERT(objectNumberFound);
+    return *objectNumberFound;
+}
+
diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h
index 8eea7f8..0a16bf9 100644
--- a/src/pdf/SkPDFTypes.h
+++ b/src/pdf/SkPDFTypes.h
@@ -10,16 +10,18 @@
 #ifndef SkPDFTypes_DEFINED
 #define SkPDFTypes_DEFINED
 
+#include "SkPDFTypes.h"
 #include "SkRefCnt.h"
 #include "SkScalar.h"
 #include "SkString.h"
 #include "SkTDArray.h"
+#include "SkTHash.h"
 #include "SkTypes.h"
 
-class SkPDFCatalog;
-class SkWStream;
-
+class SkPDFObjNumMap;
 class SkPDFObject;
+class SkPDFSubstituteMap;
+class SkWStream;
 
 /** \class SkPDFObject
 
@@ -37,14 +39,17 @@
      *  @param stream   The writable output stream to send the output to.
      */
     // TODO(halcanary): make this method const
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) = 0;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) = 0;
 
     /**
      *  Adds all transitive dependencies of this object to the
      *  catalog.  Implementations should respect the catalog's object
      *  substitution map.
      */
-    virtual void addResources(SkPDFCatalog* catalog) const {}
+    virtual void addResources(SkPDFObjNumMap* catalog,
+                              const SkPDFSubstituteMap& substitutes) const {}
 
 private:
     typedef SkRefCnt INHERITED;
@@ -65,8 +70,11 @@
     virtual ~SkPDFObjRef();
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) override;
-    virtual void addResources(SkPDFCatalog*) const override;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) override;
+    virtual void addResources(SkPDFObjNumMap*,
+                              const SkPDFSubstituteMap&) const override;
 
 private:
     SkAutoTUnref<SkPDFObject> fObj;
@@ -89,7 +97,9 @@
     virtual ~SkPDFInt();
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) override;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) override;
 
 private:
     int32_t fValue;
@@ -112,7 +122,9 @@
     virtual ~SkPDFBool();
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) override;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) override;
 
 private:
     bool fValue;
@@ -137,7 +149,9 @@
     static void Append(SkScalar value, SkWStream* stream);
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) override;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) override;
 
 private:
     SkScalar fValue;
@@ -169,7 +183,9 @@
     virtual ~SkPDFString();
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) override;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) override;
 
     static SkString FormatString(const char* input, size_t len);
     static SkString FormatString(const uint16_t* input, size_t len,
@@ -203,7 +219,9 @@
     bool operator==(const SkPDFName& b) const;
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) override;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) override;
 
 private:
     static const size_t kMaxLen = 127;
@@ -229,8 +247,11 @@
     virtual ~SkPDFArray();
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) override;
-    virtual void addResources(SkPDFCatalog*) const override;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) override;
+    virtual void addResources(SkPDFObjNumMap*,
+                              const SkPDFSubstituteMap&) const override;
 
     /** The size of the array.
      */
@@ -301,8 +322,11 @@
     virtual ~SkPDFDict();
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) override;
-    virtual void addResources(SkPDFCatalog*) const override;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) override;
+    virtual void addResources(SkPDFObjNumMap*,
+                              const SkPDFSubstituteMap&) const override;
 
     /** The size of the dictionary.
      */
@@ -387,4 +411,53 @@
     typedef SkPDFObject INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
+/** \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;
+
+private:
+    SkTHashMap<SkPDFObject*, SkPDFObject*> fSubstituteMap;
+};
+
+/** \class SkPDFObjNumMap
+
+    The PDF Object Number Map manages object numbers.  It is used to
+    create the PDF cross reference table.
+*/
+class SkPDFObjNumMap : SkNoncopyable {
+public:
+    /** Add the passed object to the catalog.
+     *  @param obj         The object to add.
+     *  @return True iff the object was not already added to the catalog.
+     */
+    bool addObject(SkPDFObject* obj);
+
+    /** Get the object number for the passed object.
+     *  @param obj         The object of interest.
+     */
+    int32_t getObjectNumber(SkPDFObject* obj) const;
+
+    const SkTDArray<SkPDFObject*>& objects() const { return fObjects; }
+
+private:
+    SkTDArray<SkPDFObject*> fObjects;
+    SkTHashMap<SkPDFObject*, int32_t> fObjectNumbers;
+};
+
 #endif
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index e361554..a98aacb 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -13,7 +13,6 @@
 #include "SkImageEncoder.h"
 #include "SkMatrix.h"
 #include "SkPDFCanon.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFDevice.h"
 #include "SkPDFStream.h"
 #include "SkPDFTypes.h"
@@ -36,36 +35,39 @@
 
 static void emit_object(SkPDFObject* object,
                         SkWStream* stream,
-                        SkPDFCatalog* catalog,
+                        const SkPDFObjNumMap& objNumMap,
+                        const SkPDFSubstituteMap& substitutes,
                         bool indirect) {
-    SkPDFObject* realObject = catalog->getSubstituteObject(object);
+    SkPDFObject* realObject = substitutes.getSubstitute(object);
     if (indirect) {
-        stream->writeDecAsText(catalog->getObjectNumber(object));
+        stream->writeDecAsText(objNumMap.getObjectNumber(realObject));
         stream->writeText(" 0 obj\n");  // Generation number is always 0.
-        realObject->emitObject(stream, catalog);
+        realObject->emitObject(stream, objNumMap, substitutes);
         stream->writeText("\nendobj\n");
     } else {
-        realObject->emitObject(stream, catalog);
+        realObject->emitObject(stream, objNumMap, substitutes);
     }
 }
 
 static size_t get_output_size(SkPDFObject* object,
-                              SkPDFCatalog* catalog,
+                              const SkPDFObjNumMap& objNumMap,
+                              const SkPDFSubstituteMap& substitutes,
                               bool indirect) {
     SkDynamicMemoryWStream buffer;
-    emit_object(object, &buffer, catalog, indirect);
+    emit_object(object, &buffer, objNumMap, substitutes, indirect);
     return buffer.getOffset();
 }
 
 static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
                               const char* expectedData, size_t expectedSize,
                               bool indirect) {
-    SkPDFCatalog catalog;
-    size_t directSize = get_output_size(obj, &catalog, false);
+    SkPDFSubstituteMap substituteMap;
+    SkPDFObjNumMap catalog;
+    size_t directSize = get_output_size(obj, catalog, substituteMap, false);
     REPORTER_ASSERT(reporter, directSize == expectedSize);
 
     SkDynamicMemoryWStream buffer;
-    emit_object(obj, &buffer, &catalog, false);
+    emit_object(obj, &buffer, catalog, substituteMap, false);
     REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData,
                                             directSize));
@@ -79,12 +81,13 @@
 
         catalog.addObject(obj);
 
-        size_t indirectSize = get_output_size(obj, &catalog, true);
+        size_t indirectSize =
+                get_output_size(obj, catalog, substituteMap, true);
         REPORTER_ASSERT(reporter,
                         indirectSize == directSize + headerLen + footerLen);
 
         buffer.reset();
-        emit_object(obj, &buffer, &catalog, true);
+        emit_object(obj, &buffer, catalog, substituteMap, true);
         REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
         REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen));
         REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData,
@@ -108,10 +111,10 @@
     SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get()));
     SimpleCheckObjectOutput(
         reporter, stream.get(),
-        "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
+        "<</Length 12>> stream\nTest\nFoo\tBar\nendstream");
     stream->insert("Attribute", new SkPDFInt(42))->unref();
     SimpleCheckObjectOutput(reporter, stream.get(),
-                            "<</Length 12\n/Attribute 42\n>> stream\n"
+                            "<</Length 12\n/Attribute 42>> stream\n"
                                 "Test\nFoo\tBar\nendstream");
 
     {
@@ -128,8 +131,7 @@
         SkAutoDataUnref compressedData(compressedByteStream.copyToData());
 
         SkDynamicMemoryWStream expected;
-        expected.writeText("<</Filter /FlateDecode\n/Length 116\n"
-                                 ">> stream\n");
+        expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n");
         expected.write(compressedData->data(), compressedData->size());
         expected.writeText("\nendstream");
         SkAutoDataUnref expectedResultData2(expected.copyToData());
@@ -140,7 +142,8 @@
 }
 
 static void TestCatalog(skiatest::Reporter* reporter) {
-    SkPDFCatalog catalog;
+    SkPDFSubstituteMap substituteMap;
+    SkPDFObjNumMap catalog;
     SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
     SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
     SkAutoTUnref<SkPDFInt> int3(new SkPDFInt(3));
@@ -162,7 +165,8 @@
     SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
     SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get()));
 
-    SkPDFCatalog catalog;
+    SkPDFSubstituteMap substituteMap;
+    SkPDFObjNumMap catalog;
     catalog.addObject(int1.get());
     catalog.addObject(int2.get());
     REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1);
@@ -170,7 +174,7 @@
 
     char expectedResult[] = "2 0 R";
     SkDynamicMemoryWStream buffer;
-    int2ref->emitObject(&buffer, &catalog);
+    int2ref->emitObject(&buffer, catalog, substituteMap);
     REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
                                             buffer.getOffset()));
@@ -183,12 +187,13 @@
     proxy->insert("Value", new SkPDFInt(33))->unref();
     stub->insert("Value", new SkPDFInt(44))->unref();
 
-    SkPDFCatalog catalog;
+    SkPDFSubstituteMap substituteMap;
+    substituteMap.setSubstitute(proxy.get(), stub.get());
+    SkPDFObjNumMap catalog;
     catalog.addObject(proxy.get());
-    catalog.setSubstitute(proxy.get(), stub.get());
 
-    REPORTER_ASSERT(reporter, stub.get() == catalog.getSubstituteObject(proxy));
-    REPORTER_ASSERT(reporter, proxy.get() != catalog.getSubstituteObject(stub));
+    REPORTER_ASSERT(reporter, stub.get() == substituteMap.getSubstitute(proxy));
+    REPORTER_ASSERT(reporter, proxy.get() != substituteMap.getSubstitute(stub));
 }
 
 // This test used to assert without the fix submitted for
@@ -272,13 +277,13 @@
     SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
     SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1"));
     dict->insert(n1.get(), int42.get());
-    SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
+    SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42>>");
     SkAutoTUnref<SkPDFName> n2(new SkPDFName("n2"));
     SkAutoTUnref<SkPDFName> n3(new SkPDFName("n3"));
     dict->insert(n2.get(), realHalf.get());
     dict->insert(n3.get(), array.get());
     SimpleCheckObjectOutput(reporter, dict.get(),
-                            "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
+                            "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]>>");
 
     TestPDFStream(reporter);