[PDF] Refactor SkPDFFont to enable font/cmap subsetting.

Patch from Arthur Hsu, original CL: http://codereview.appspot.com/4633050/

Review URL: http://codereview.appspot.com/4811049

git-svn-id: http://skia.googlecode.com/svn/trunk@1943 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/pdf.gyp b/gyp/pdf.gyp
index f0c3937..f0c6b47 100644
--- a/gyp/pdf.gyp
+++ b/gyp/pdf.gyp
@@ -27,11 +27,12 @@
         '../include/pdf/SkPDFTypes.h',
         '../include/pdf/SkPDFUtils.h',
 
-	'../src/pdf/SkBitSet.cpp',
+        '../src/pdf/SkBitSet.cpp',
         '../src/pdf/SkPDFCatalog.cpp',
         '../src/pdf/SkPDFDevice.cpp',
         '../src/pdf/SkPDFDocument.cpp',
         '../src/pdf/SkPDFFont.cpp',
+        '../src/pdf/SkPDFFontImpl.h',
         '../src/pdf/SkPDFFormXObject.cpp',
         '../src/pdf/SkPDFGraphicState.cpp',
         '../src/pdf/SkPDFImage.cpp',
diff --git a/include/pdf/SkBitSet.h b/include/pdf/SkBitSet.h
index 84d52e5..01d9c6c 100755
--- a/include/pdf/SkBitSet.h
+++ b/include/pdf/SkBitSet.h
@@ -40,12 +40,12 @@
 
     /** Test if bit index is set.
      */
-    bool isBitSet(int index);
+    bool isBitSet(int index) const;
 
     /** Or bits from source.  false is returned if this doesn't have the same
      *  bit count as source.
      */
-    bool orBits(SkBitSet& source);
+    bool orBits(const SkBitSet& source);
 
 private:
     SkAutoFree fBitData;
@@ -53,7 +53,7 @@
     size_t fDwordCount;
     size_t fBitCount;
 
-    uint32_t* internalGet(int index) {
+    uint32_t* internalGet(int index) const {
         SkASSERT((size_t)index < fBitCount);
         size_t internalIndex = index / 32;
         SkASSERT(internalIndex < fDwordCount);
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
index 7a3e7bb..1a0807a 100644
--- a/include/pdf/SkPDFDevice.h
+++ b/include/pdf/SkPDFDevice.h
@@ -30,6 +30,7 @@
 class SkPDFDict;
 class SkPDFFont;
 class SkPDFFormXObject;
+class SkPDFGlyphSetMap;
 class SkPDFGraphicState;
 class SkPDFObject;
 class SkPDFShader;
@@ -150,11 +151,18 @@
      *  for calling data->unref() when it is finished.
      */
     SK_API SkData* copyContentToData() const;
-    
+
     SK_API const SkMatrix& initialTransform() const {
         return fInitialTransform;
     }
 
+    /** Returns a SkPDFGlyphSetMap which represents glyph usage of every font
+     *  that shows on this device.
+     */
+    const SkPDFGlyphSetMap& getFontGlyphUsage() const {
+        return *(fFontGlyphUsage.get());
+    }
+
 private:
     // TODO(vandebo) push most of SkPDFDevice's state into a core object in
     // order to get the right access levels without using friend.
@@ -183,17 +191,20 @@
     ContentEntry* getLastContentEntry();
     void setLastContentEntry(ContentEntry* contentEntry);
 
+    // Glyph ids used for each font on this device.
+    SkTScopedPtr<SkPDFGlyphSetMap> fFontGlyphUsage;
+
     SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack,
                 const SkRegion& existingClipRegion);
 
     // override from SkDevice
-    virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config, 
-                                               int width, int height, 
+    virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config,
+                                               int width, int height,
                                                bool isOpaque,
                                                Usage usage);
 
     void init();
-    void cleanUp();
+    void cleanUp(bool clearFontUsage);
     void createFormXObjectFromDevice(SkRefPtr<SkPDFFormXObject>* xobject);
 
     // Clear the passed clip from all existing content entries.
diff --git a/include/pdf/SkPDFDocument.h b/include/pdf/SkPDFDocument.h
index 3f171f5..8b472e1 100644
--- a/include/pdf/SkPDFDocument.h
+++ b/include/pdf/SkPDFDocument.h
@@ -81,6 +81,7 @@
     SkTDArray<SkPDFDict*> fPageTree;
     SkRefPtr<SkPDFDict> fDocCatalog;
     SkTDArray<SkPDFObject*> fPageResources;
+    SkTDArray<SkPDFObject*> fSubstitutes;
     int fSecondPageFirstResourceIndex;
 
     SkRefPtr<SkPDFDict> fTrailerDict;
diff --git a/include/pdf/SkPDFFont.h b/include/pdf/SkPDFFont.h
index 182f27d..00dcb95 100644
--- a/include/pdf/SkPDFFont.h
+++ b/include/pdf/SkPDFFont.h
@@ -18,11 +18,63 @@
 #define SkPDFFont_DEFINED
 
 #include "SkAdvancedTypefaceMetrics.h"
+#include "SkBitSet.h"
 #include "SkPDFTypes.h"
 #include "SkTDArray.h"
 #include "SkThread.h"
+#include "SkTypeface.h"
 
 class SkPaint;
+class SkPDFCatalog;
+class SkPDFFont;
+
+class SkPDFGlyphSet : public SkNoncopyable {
+public:
+    SkPDFGlyphSet();
+
+    void set(const uint16_t* glyphIDs, int numGlyphs);
+    bool has(uint16_t glyphID) const;
+    void merge(const SkPDFGlyphSet& usage);
+
+private:
+    SkBitSet fBitSet;
+};
+
+class SkPDFGlyphSetMap : public SkNoncopyable {
+public:
+    struct FontGlyphSetPair {
+        FontGlyphSetPair(SkPDFFont* font, SkPDFGlyphSet* glyphSet);
+
+        SkPDFFont* fFont;
+        SkPDFGlyphSet* fGlyphSet;
+    };
+
+    SkPDFGlyphSetMap();
+    ~SkPDFGlyphSetMap();
+
+    class F2BIter {
+    public:
+        F2BIter(const SkPDFGlyphSetMap& map);
+        FontGlyphSetPair* next() const;
+        void reset(const SkPDFGlyphSetMap& map);
+
+    private:
+        const SkTDArray<FontGlyphSetPair>* fMap;
+        mutable int fIndex;
+    };
+
+    void merge(const SkPDFGlyphSetMap& usage);
+    void reset();
+
+    void noteGlyphUsage(SkPDFFont* font, const uint16_t* glyphIDs,
+                        int numGlyphs);
+
+private:
+    SkPDFGlyphSet* getGlyphSetForFont(SkPDFFont* font);
+
+    SkTDArray<FontGlyphSetPair> fMap;
+};
+
 
 /** \class SkPDFFont
     A PDF Object class representing a font.  The font may have resources
@@ -45,16 +97,16 @@
     /** Returns the font type represented in this font.  For Type0 fonts,
      *  returns the type of the decendant font.
      */
-    SK_API SkAdvancedTypefaceMetrics::FontType getType();
+    SK_API virtual SkAdvancedTypefaceMetrics::FontType getType();
+
+    /** Returns true if this font encoding supports glyph IDs above 255.
+     */
+    SK_API virtual bool multiByteGlyphs() const = 0;
 
     /** Return true if this font has an encoding for the passed glyph id.
      */
     SK_API bool hasGlyph(uint16_t glyphID);
 
-    /** Returns true if this font encoding supports glyph IDs above 255.
-     */
-    SK_API bool multiByteGlyphs();
-
     /** Convert (in place) the input glyph IDs into the font encoding.  If the
      *  font has more glyphs than can be encoded (like a type 1 font with more
      *  than 255 glyphs) this method only converts up to the first out of range
@@ -76,24 +128,52 @@
     SK_API static SkPDFFont* GetFontResource(SkTypeface* typeface,
                                              uint16_t glyphID);
 
+    /** Subset the font based on usage set. Returns a SkPDFFont instance with
+     *  subset.
+     *  @param usage  Glyph subset requested.
+     *  @return       NULL if font does not support subsetting, a new instance
+     *                of SkPDFFont otherwise.
+     */
+    SK_API virtual SkPDFFont* getFontSubset(const SkPDFGlyphSet* usage);
+
+protected:
+    // Common constructor to handle common members.
+    SkPDFFont(SkAdvancedTypefaceMetrics* fontInfo, SkTypeface* typeface,
+              uint16_t glyphID, bool descendantFont);
+
+    // Accessors for subclass.
+    SkAdvancedTypefaceMetrics* fontInfo();
+    uint16_t firstGlyphID() const;
+    uint16_t lastGlyphID() const;
+    void setLastGlyphID(uint16_t glyphID);
+
+    // Add object to resource list.
+    void addResource(SkPDFObject* object);
+
+    // Accessors for FontDescriptor associated with this object.
+    SkPDFDict* getFontDescriptor();
+    void setFontDescriptor(SkPDFDict* descriptor);
+
+    // Add common entries to FontDescriptor.
+    bool addCommonFontDescriptorEntries(int16_t defaultWidth);
+
+    /** Set fFirstGlyphID and fLastGlyphID to span at most 255 glyphs,
+     *  including the passed glyphID.
+     */
+    void adjustGlyphRangeForSingleByteEncoding(int16_t glyphID);
+
+    // Generate ToUnicode table according to glyph usage subset.
+    // If subset is NULL, all available glyph ids will be used.
+    void populateToUnicodeTable(const SkPDFGlyphSet* subset);
+
+    // Create instances of derived types based on fontInfo.
+    static SkPDFFont* Create(SkAdvancedTypefaceMetrics* fontInfo,
+                             SkTypeface* typeface, uint16_t glyphID,
+                             SkPDFDict* fontDescriptor);
+
+    static bool Find(uint32_t fontID, uint16_t glyphID, int* index);
+
 private:
-    SkRefPtr<SkTypeface> fTypeface;
-    SkAdvancedTypefaceMetrics::FontType fType;
-#ifdef SK_DEBUG
-    bool fDescendant;
-#endif
-    bool fMultiByteGlyphs;
-
-    // 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.
-    uint16_t fFirstGlyphID;
-    uint16_t fLastGlyphID;
-    // The font info is only kept around after construction for large
-    // Type1 (non CID) fonts that need multiple "fonts" to access all glyphs.
-    SkRefPtr<SkAdvancedTypefaceMetrics> fFontInfo;
-    SkTDArray<SkPDFObject*> fResources;
-    SkRefPtr<SkPDFDict> fDescriptor;
-
     class FontRec {
     public:
         SkPDFFont* fFont;
@@ -105,47 +185,23 @@
         FontRec(SkPDFFont* font, uint32_t fontID, uint16_t fGlyphID);
     };
 
+    SkRefPtr<SkTypeface> fTypeface;
+
+    // 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.
+    uint16_t fFirstGlyphID;
+    uint16_t fLastGlyphID;
+    // The font info is only kept around after construction for large
+    // Type1 (non CID) fonts that need multiple "fonts" to access all glyphs.
+    SkRefPtr<SkAdvancedTypefaceMetrics> fFontInfo;
+    SkTDArray<SkPDFObject*> fResources;
+    SkRefPtr<SkPDFDict> fDescriptor;
+
+    SkAdvancedTypefaceMetrics::FontType fFontType;
+
     // This should be made a hash table if performance is a problem.
     static SkTDArray<FontRec>& CanonicalFonts();
     static SkMutex& CanonicalFontsMutex();
-
-    /** Construct a new font dictionary and support objects.
-     *  @param fontInfo       Information about the to create.
-     *  @param typeface       The typeface for the font.
-     *  @param glyphID        The glyph ID the caller is interested in. This
-     *                        is important only for Type1 fonts, which have
-     *                        more than 255 glyphs.
-     *  @param descendantFont If this is the descendant (true) or root
-     *                        (Type 0 font - false) font dictionary.  Only True
-     *                        Type and CID encoded fonts will use a true value.
-     *  @param fontDescriptor If the font descriptor has already have generated
-     *                        for this font, pass it in here, otherwise pass
-     *                        NULL.
-     */
-    SkPDFFont(class SkAdvancedTypefaceMetrics* fontInfo, SkTypeface* typeface,
-              uint16_t glyphID, bool descendantFont, SkPDFDict* fontDescriptor);
-
-    void populateType0Font();
-    void populateCIDFont();
-    bool populateType1Font(int16_t glyphID);
-
-    /** Populate the PDF font dictionary as Type3 font which includes glyph
-     *  descriptions with instructions for painting the glyphs. This function
-     *  doesn't use any fields from SkAdvancedTypefaceMetrics (fFontInfo). Font
-     *  information including glyph paths are queried from the platform
-     *  dependent SkGlyphCache.
-    */
-    void populateType3Font(int16_t glyphID);
-    bool addFontDescriptor(int16_t defaultWidth);
-    void populateToUnicodeTable();
-    void addWidthInfoFromRange(int16_t defaultWidth,
-        const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry);
-    /** Set fFirstGlyphID and fLastGlyphID to span at most 255 glyphs,
-     *  including the passed glyphID.
-     */
-    void adjustGlyphRangeForSingleByteEncoding(int16_t glyphID);
-
-    static bool Find(uint32_t fontID, uint16_t glyphID, int* index);
 };
 
 #endif
diff --git a/include/pdf/SkPDFPage.h b/include/pdf/SkPDFPage.h
index a397874..7a9505b 100644
--- a/include/pdf/SkPDFPage.h
+++ b/include/pdf/SkPDFPage.h
@@ -90,6 +90,11 @@
      */
     SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
 
+    /** Returns a SkPDFGlyphSetMap which represents glyph usage of every font
+     *  that shows on this page.
+     */
+    const SkPDFGlyphSetMap& getFontGlyphUsage() const;
+
 private:
     // Multiple pages may reference the content.
     SkRefPtr<SkPDFDevice> fDevice;
diff --git a/src/pdf/SkBitSet.cpp b/src/pdf/SkBitSet.cpp
index f19dd69..c7af832 100755
--- a/src/pdf/SkBitSet.cpp
+++ b/src/pdf/SkBitSet.cpp
@@ -73,12 +73,12 @@
     }
 }
 
-bool SkBitSet::isBitSet(int index) {
+bool SkBitSet::isBitSet(int index) const {
     uint32_t mask = 1 << (index % 32);
     return (*internalGet(index) & mask);
 }
 
-bool SkBitSet::orBits(SkBitSet& source) {
+bool SkBitSet::orBits(const SkBitSet& source) {
     if (fBitCount != source.fBitCount) {
         return false;
     }
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 619d55d..e2dfba6 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -424,8 +424,8 @@
     }
 }
 
-SkDevice* SkPDFDevice::onCreateCompatibleDevice(SkBitmap::Config config, 
-                                                int width, int height, 
+SkDevice* SkPDFDevice::onCreateCompatibleDevice(SkBitmap::Config config,
+                                                int width, int height,
                                                 bool isOpaque,
                                                 Usage usage) {
     SkMatrix initialTransform;
@@ -544,7 +544,7 @@
 }
 
 SkPDFDevice::~SkPDFDevice() {
-    this->cleanUp();
+    this->cleanUp(true);
 }
 
 void SkPDFDevice::init() {
@@ -553,18 +553,24 @@
     fLastContentEntry = NULL;
     fMarginContentEntries.reset();
     fLastMarginContentEntry = NULL;
-    fDrawingArea = kContent_DrawingArea; 
+    fDrawingArea = kContent_DrawingArea;
+    if (fFontGlyphUsage == NULL) {
+        fFontGlyphUsage.reset(new SkPDFGlyphSetMap());
+    }
 }
 
-void SkPDFDevice::cleanUp() {
+void SkPDFDevice::cleanUp(bool clearFontUsage) {
     fGraphicStateResources.unrefAll();
     fXObjectResources.unrefAll();
     fFontResources.unrefAll();
     fShaderResources.unrefAll();
+    if (clearFontUsage) {
+        fFontGlyphUsage->reset();
+    }
 }
 
 void SkPDFDevice::clear(SkColor color) {
-    this->cleanUp();
+    this->cleanUp(true);
     this->init();
 
     SkPaint paint;
@@ -825,6 +831,8 @@
         size_t availableGlyphs =
             font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
                                           numGlyphs - consumedGlyphCount);
+        fFontGlyphUsage->noteGlyphUsage(font, glyphIDs + consumedGlyphCount,
+                                        availableGlyphs);
         SkString encodedString =
             SkPDFString::FormatString(glyphIDs + consumedGlyphCount,
                                       availableGlyphs, font->multiByteGlyphs());
@@ -893,6 +901,7 @@
             i--;
             continue;
         }
+        fFontGlyphUsage->noteGlyphUsage(font, &encodedValue, 1);
         SkScalar x = pos[i * scalarsPerPos];
         SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
         align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
@@ -952,6 +961,9 @@
     fXObjectResources.push(xobject);  // Transfer reference.
     SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
                                 &content.entry()->fContent);
+
+    // Merge glyph sets from the drawn device.
+    fFontGlyphUsage->merge(pdfDevice->getFontGlyphUsage());
 }
 
 ContentEntry* SkPDFDevice::getLastContentEntry() {
@@ -1142,7 +1154,7 @@
         SkRect r = SkRect::MakeWH(this->width(), this->height());
         emit_clip(NULL, &r, &data);
     }
-    
+
     SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data);
 
     // potentially we could cache this SkData, and only rebuild it if we
@@ -1154,7 +1166,10 @@
         SkRefPtr<SkPDFFormXObject>* xobject) {
     *xobject = new SkPDFFormXObject(this);
     (*xobject)->unref();  // SkRefPtr and new both took a reference.
-    cleanUp();  // Reset this device to have no content.
+    // We always draw the form xobjects that we create back into the device, so
+    // we simply preserve the font usage instead of pulling it out and merging
+    // it back in later.
+    cleanUp(false);  // Reset this device to have no content.
     init();
 }
 
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
index 71c3784..38f4f32 100644
--- a/src/pdf/SkPDFDocument.cpp
+++ b/src/pdf/SkPDFDocument.cpp
@@ -18,6 +18,7 @@
 #include "SkPDFDevice.h"
 #include "SkPDFDocument.h"
 #include "SkPDFPage.h"
+#include "SkPDFFont.h"
 #include "SkStream.h"
 
 // Add the resources, starting at firstIndex to the catalog, removing any dupes.
@@ -37,6 +38,29 @@
     }
 }
 
+static void perform_font_subsetting(SkPDFCatalog* catalog,
+                                    const SkTDArray<SkPDFPage*>& pages,
+                                    SkTDArray<SkPDFObject*>* substitutes) {
+    SkASSERT(catalog);
+    SkASSERT(substitutes);
+
+    SkPDFGlyphSetMap usage;
+    for (int i = 0; i < pages.count(); ++i) {
+        usage.merge(pages[i]->getFontGlyphUsage());
+    }
+    SkPDFGlyphSetMap::F2BIter iterator(usage);
+    SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
+    while (entry) {
+        SkPDFFont* subsetFont =
+            entry->fFont->getFontSubset(entry->fGlyphSet);
+        if (subsetFont) {
+            catalog->setSubstitute(entry->fFont, subsetFont);
+            substitutes->push(subsetFont);  // Transfer ownership to substitutes
+        }
+        entry = iterator.next();
+    }
+}
+
 SkPDFDocument::SkPDFDocument(Flags flags)
         : fXRefFileOffset(0),
           fSecondPageFirstResourceIndex(0) {
@@ -55,6 +79,7 @@
         fPageTree[i]->clear();
     fPageTree.safeUnrefAll();
     fPageResources.safeUnrefAll();
+    fSubstitutes.safeUnrefAll();
 }
 
 bool SkPDFDocument::emitPDF(SkWStream* stream) {
@@ -98,6 +123,9 @@
             }
         }
 
+        // Build font subsetting info before proceeding.
+        perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes);
+
         // Figure out the size of things and inform the catalog of file offsets.
         off_t fileOffset = headerSize();
         fileOffset += fCatalog->setFileOffset(fDocCatalog.get(), fileOffset);
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
old mode 100755
new mode 100644
index be16c76..f50580a
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -20,8 +20,10 @@
 #include "SkFontHost.h"
 #include "SkGlyphCache.h"
 #include "SkPaint.h"
+#include "SkPDFCatalog.h"
 #include "SkPDFDevice.h"
 #include "SkPDFFont.h"
+#include "SkPDFFontImpl.h"
 #include "SkPDFStream.h"
 #include "SkPDFTypes.h"
 #include "SkPDFUtils.h"
@@ -34,6 +36,10 @@
 
 namespace {
 
+///////////////////////////////////////////////////////////////////////////////
+// File-Local Functions
+///////////////////////////////////////////////////////////////////////////////
+
 bool parsePFBSection(const uint8_t** src, size_t* len, int sectionType,
                      size_t* size) {
     // PFB sections have a two or six bytes header. 0x80 and a one byte
@@ -134,7 +140,7 @@
 
 SkStream* handleType1Stream(SkStream* srcStream, size_t* headerLen,
                             size_t* dataLen, size_t* trailerLen) {
-    // srcStream may be backed by a file or a unseekable fd, so we may not be 
+    // srcStream may be backed by a file or a unseekable fd, so we may not be
     // able to use skip(), rewind(), or getMemoryBase().  read()ing through
     // the input only once is doable, but very ugly. Furthermore, it'd be nice
     // if the data was NUL terminated so that we can use strstr() to search it.
@@ -385,14 +391,14 @@
 // Generate <bfchar> table according to PDF spec 1.4 and Adobe Technote 5014.
 static void append_cmap_bfchar_sections(
                 const SkTDArray<SkUnichar>& glyphUnicode,
-                SkDynamicMemoryWStream* cmap) {
+                const SkPDFGlyphSet* subset, SkDynamicMemoryWStream* cmap) {
     // PDF spec defines that every bf* list can have at most 100 entries.
     const size_t kMaxEntries = 100;
     uint16_t glyphId[kMaxEntries];
     SkUnichar unicode[kMaxEntries];
     size_t index = 0;
     for (int i = 0; i < glyphUnicode.count(); i++) {
-        if (glyphUnicode[i]) {
+        if (glyphUnicode[i] && (subset == NULL || subset->has(i))) {
             glyphId[index] = i;
             unicode[index] = glyphUnicode[i];
             ++index;
@@ -408,6 +414,112 @@
     }
 }
 
+static SkPDFStream* generate_tounicode_cmap(
+                        const SkTDArray<SkUnichar>& glyphUnicode,
+                        const SkPDFGlyphSet* subset) {
+    SkDynamicMemoryWStream cmap;
+    append_tounicode_header(&cmap);
+    append_cmap_bfchar_sections(glyphUnicode, subset, &cmap);
+    append_cmap_footer(&cmap);
+    SkRefPtr<SkMemoryStream> cmapStream = new SkMemoryStream();
+    cmapStream->unref();  // SkRefPtr and new took a reference.
+    cmapStream->setData(cmap.copyToData());
+    return new SkPDFStream(cmapStream.get());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// 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::merge(const SkPDFGlyphSet& usage) {
+    fBitSet.orBits(usage.fBitSet);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFGlyphSetMap
+///////////////////////////////////////////////////////////////////////////////
+SkPDFGlyphSetMap::FontGlyphSetPair::FontGlyphSetPair(SkPDFFont* font,
+                                                     SkPDFGlyphSet* glyphSet)
+        : fFont(font),
+          fGlyphSet(glyphSet) {
+}
+
+SkPDFGlyphSetMap::F2BIter::F2BIter(const SkPDFGlyphSetMap& map) {
+    reset(map);
+}
+
+SkPDFGlyphSetMap::FontGlyphSetPair* SkPDFGlyphSetMap::F2BIter::next() const {
+    if (fIndex >= fMap->count()) {
+        return NULL;
+    }
+    return &((*fMap)[fIndex++]);
+}
+
+void SkPDFGlyphSetMap::F2BIter::reset(const SkPDFGlyphSetMap& map) {
+    fMap = &(map.fMap);
+    fIndex = 0;
+}
+
+SkPDFGlyphSetMap::SkPDFGlyphSetMap() {
+}
+
+SkPDFGlyphSetMap::~SkPDFGlyphSetMap() {
+    reset();
+}
+
+void SkPDFGlyphSetMap::merge(const SkPDFGlyphSetMap& usage) {
+    for (int i = 0; i < usage.fMap.count(); ++i) {
+        SkPDFGlyphSet* myUsage = getGlyphSetForFont(usage.fMap[i].fFont);
+        myUsage->merge(*(usage.fMap[i].fGlyphSet));
+    }
+}
+
+void SkPDFGlyphSetMap::reset() {
+    for (int i = 0; i < fMap.count(); ++i) {
+        delete fMap[i].fGlyphSet;  // Should not be NULL.
+    }
+    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;
+        }
+    }
+    fMap.append();
+    index = fMap.count() - 1;
+    fMap[index].fFont = font;
+    fMap[index].fGlyphSet = new SkPDFGlyphSet();
+    return fMap[index].fGlyphSet;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFFont
+///////////////////////////////////////////////////////////////////////////////
+
 /* Font subset design: It would be nice to be able to subset fonts
  * (particularly type 3 fonts), but it's a lot of work and not a priority.
  *
@@ -425,11 +537,6 @@
     int index;
     if (Find(SkTypeface::UniqueID(fTypeface.get()), fFirstGlyphID, &index)) {
         CanonicalFonts().removeShuffle(index);
-#ifdef SK_DEBUG
-        SkASSERT(!fDescendant);
-    } else {
-        SkASSERT(fDescendant);
-#endif
     }
     fResources.unrefAll();
 }
@@ -443,21 +550,17 @@
 }
 
 SkAdvancedTypefaceMetrics::FontType SkPDFFont::getType() {
-    return fType;
+    return fFontType;
 }
 
 bool SkPDFFont::hasGlyph(uint16_t id) {
     return (id >= fFirstGlyphID && id <= fLastGlyphID) || id == 0;
 }
 
-bool SkPDFFont::multiByteGlyphs() {
-    return fMultiByteGlyphs;
-}
-
 size_t SkPDFFont::glyphsToPDFFontEncoding(uint16_t* glyphIDs,
                                           size_t numGlyphs) {
     // A font with multibyte glyphs will support all glyph IDs in a single font.
-    if (fMultiByteGlyphs) {
+    if (this->multiByteGlyphs()) {
         return numGlyphs;
     }
 
@@ -478,19 +581,19 @@
 SkPDFFont* SkPDFFont::GetFontResource(SkTypeface* typeface, uint16_t glyphID) {
     SkAutoMutexAcquire lock(CanonicalFontsMutex());
     const uint32_t fontID = SkTypeface::UniqueID(typeface);
-    int index;
-    if (Find(fontID, glyphID, &index)) {
-        CanonicalFonts()[index].fFont->ref();
-        return CanonicalFonts()[index].fFont;
+    int relatedFontIndex;
+    if (Find(fontID, glyphID, &relatedFontIndex)) {
+        CanonicalFonts()[relatedFontIndex].fFont->ref();
+        return CanonicalFonts()[relatedFontIndex].fFont;
     }
 
-    SkRefPtr<SkAdvancedTypefaceMetrics> fontInfo;
-    SkPDFDict* fontDescriptor = NULL;
-    if (index >= 0) {
-        SkPDFFont* relatedFont = CanonicalFonts()[index].fFont;
+    SkRefPtr<SkAdvancedTypefaceMetrics> fontMetrics;
+    SkPDFDict* relatedFontDescriptor = NULL;
+    if (relatedFontIndex >= 0) {
+        SkPDFFont* relatedFont = CanonicalFonts()[relatedFontIndex].fFont;
         SkASSERT(relatedFont->fFontInfo.get());
-        fontInfo = relatedFont->fFontInfo;
-        fontDescriptor = relatedFont->fDescriptor.get();
+        fontMetrics = relatedFont->fontInfo();
+        relatedFontDescriptor = relatedFont->getFontDescriptor();
     } else {
         SkAdvancedTypefaceMetrics::PerGlyphInfo info;
         info = SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo;
@@ -498,18 +601,21 @@
                   info, SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo);
         info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
                   info, SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo);
-        fontInfo = SkFontHost::GetAdvancedTypefaceMetrics(fontID, info);
-        SkSafeUnref(fontInfo.get());  // SkRefPtr and Get both took a reference.
+        fontMetrics = SkFontHost::GetAdvancedTypefaceMetrics(fontID, info);
+        SkSafeUnref(fontMetrics.get());  // SkRefPtr and Get both took a ref.
     }
 
-    SkPDFFont* font = new SkPDFFont(fontInfo.get(), typeface, glyphID, false,
-                                    fontDescriptor);
+    SkPDFFont* font = Create(fontMetrics.get(), typeface, glyphID,
+                             relatedFontDescriptor);
     FontRec newEntry(font, fontID, font->fFirstGlyphID);
-    index = CanonicalFonts().count();
     CanonicalFonts().push(newEntry);
     return font;  // Return the reference new SkPDFFont() created.
 }
 
+SkPDFFont* SkPDFFont::getFontSubset(const SkPDFGlyphSet* usage) {
+    return NULL;  // Default: no support.
+}
+
 // static
 SkTDArray<SkPDFFont::FontRec>& SkPDFFont::CanonicalFonts() {
     // This initialization is only thread safe with gcc.
@@ -536,96 +642,270 @@
     return false;
 }
 
-SkPDFFont::SkPDFFont(class SkAdvancedTypefaceMetrics* fontInfo,
-                     SkTypeface* typeface,
-                     uint16_t glyphID,
-                     bool descendantFont,
-                     SkPDFDict* fontDescriptor)
+SkPDFFont::SkPDFFont(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface,
+                     uint16_t glyphID, bool descendantFont)
         : SkPDFDict("Font"),
           fTypeface(typeface),
-          fType(fontInfo ? fontInfo->fType :
-                           SkAdvancedTypefaceMetrics::kNotEmbeddable_Font),
-#ifdef SK_DEBUG
-          fDescendant(descendantFont),
-#endif
-          fMultiByteGlyphs(false),
           fFirstGlyphID(1),
-          fLastGlyphID(fontInfo ? fontInfo->fLastGlyphID : 0),
-          fFontInfo(fontInfo),
-          fDescriptor(fontDescriptor) {
-    if (fontInfo && fontInfo->fMultiMaster) {
+          fLastGlyphID(info ? info->fLastGlyphID : 0),
+          fFontInfo(info) {
+    if (info == NULL) {
+        fFontType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+    } else if (info->fMultiMaster) {
+        fFontType = SkAdvancedTypefaceMetrics::kOther_Font;
+    } else {
+        fFontType = info->fType;
+    }
+}
+
+// static
+SkPDFFont* SkPDFFont::Create(SkAdvancedTypefaceMetrics* info,
+                             SkTypeface* typeface, uint16_t glyphID,
+                             SkPDFDict* relatedFontDescriptor) {
+    SkAdvancedTypefaceMetrics::FontType type =
+        info ? info->fType : SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+
+    if (info && info->fMultiMaster) {
         NOT_IMPLEMENTED(true, true);
-        fType = SkAdvancedTypefaceMetrics::kOther_Font;
+        return new SkPDFType3Font(info,
+                                  typeface,
+                                  glyphID,
+                                  relatedFontDescriptor);
     }
-    if (fType == SkAdvancedTypefaceMetrics::kType1CID_Font ||
-        fType == SkAdvancedTypefaceMetrics::kTrueType_Font) {
-        if (descendantFont) {
-            populateCIDFont();
-        } else {
-            populateType0Font();
-        }
-        // No need to hold onto the font info for fonts types that
-        // support multibyte glyphs.
-        fFontInfo = NULL;
-        return;
+    if (type == SkAdvancedTypefaceMetrics::kType1CID_Font ||
+        type == SkAdvancedTypefaceMetrics::kTrueType_Font) {
+        SkASSERT(relatedFontDescriptor == NULL);
+        return new SkPDFType0Font(info, typeface);
+    }
+    if (type == SkAdvancedTypefaceMetrics::kType1_Font) {
+        return new SkPDFType1Font(info,
+                                  typeface,
+                                  glyphID,
+                                  relatedFontDescriptor);
     }
 
-    if (fType == SkAdvancedTypefaceMetrics::kType1_Font &&
-        populateType1Font(glyphID)) {
-        return;
-    }
+    SkASSERT(type == SkAdvancedTypefaceMetrics::kCFF_Font ||
+             type == SkAdvancedTypefaceMetrics::kOther_Font ||
+             type == SkAdvancedTypefaceMetrics::kNotEmbeddable_Font);
 
-    SkASSERT(fType == SkAdvancedTypefaceMetrics::kType1_Font ||
-             fType == SkAdvancedTypefaceMetrics::kCFF_Font ||
-             fType == SkAdvancedTypefaceMetrics::kOther_Font ||
-             fType == SkAdvancedTypefaceMetrics::kNotEmbeddable_Font);
-    populateType3Font(glyphID);
+    return new SkPDFType3Font(info, typeface, glyphID, relatedFontDescriptor);
 }
 
-void SkPDFFont::populateType0Font() {
-    fMultiByteGlyphs = true;
-
-    insertName("Subtype", "Type0");
-    insertName("BaseFont", fFontInfo->fFontName);
-    insertName("Encoding", "Identity-H");
-
-    SkRefPtr<SkPDFArray> descendantFonts = new SkPDFArray();
-    descendantFonts->unref();  // SkRefPtr and new took a reference.
-
-    // Pass ref new created to fResources.
-    fResources.push(
-        new SkPDFFont(fFontInfo.get(), fTypeface.get(), 1, true, NULL));
-    descendantFonts->append(new SkPDFObjRef(fResources.top()))->unref();
-    insert("DescendantFonts", descendantFonts.get());
-
-    populateToUnicodeTable();
+SkAdvancedTypefaceMetrics* SkPDFFont::fontInfo() {
+    return fFontInfo.get();
 }
 
-void SkPDFFont::populateToUnicodeTable() {
-    if (fFontInfo.get() == NULL ||
-        fFontInfo->fGlyphToUnicode.begin() == NULL) {
-        return;
+uint16_t SkPDFFont::firstGlyphID() const {
+    return fFirstGlyphID;
+}
+
+uint16_t SkPDFFont::lastGlyphID() const {
+    return fLastGlyphID;
+}
+
+void SkPDFFont::setLastGlyphID(uint16_t glyphID) {
+    fLastGlyphID = glyphID;
+}
+
+void SkPDFFont::addResource(SkPDFObject* object) {
+    SkASSERT(object != NULL);
+    fResources.push(object);
+}
+
+SkPDFDict* SkPDFFont::getFontDescriptor() {
+    return fDescriptor.get();
+}
+
+void SkPDFFont::setFontDescriptor(SkPDFDict* descriptor) {
+    fDescriptor = descriptor;
+}
+
+bool SkPDFFont::addCommonFontDescriptorEntries(int16_t defaultWidth) {
+    if (fDescriptor.get() == NULL) {
+        return false;
     }
 
-    SkDynamicMemoryWStream cmap;
-    append_tounicode_header(&cmap);
-    append_cmap_bfchar_sections(fFontInfo->fGlyphToUnicode, &cmap);
-    append_cmap_footer(&cmap);
-    SkRefPtr<SkMemoryStream> cmapStream = new SkMemoryStream();
-    cmapStream->unref();  // SkRefPtr and new took a reference.
-    cmapStream->setData(cmap.copyToData())->unref();
-    SkRefPtr<SkPDFStream> pdfCmap = new SkPDFStream(cmapStream.get());
-    fResources.push(pdfCmap.get());  // Pass reference from new.
+    const uint16_t emSize = fFontInfo->fEmSize;
+
+    fDescriptor->insertName("FontName", fFontInfo->fFontName);
+    fDescriptor->insertInt("Flags", fFontInfo->fStyle);
+    fDescriptor->insertScalar("Ascent",
+            scaleFromFontUnits(fFontInfo->fAscent, emSize));
+    fDescriptor->insertScalar("Descent",
+            scaleFromFontUnits(fFontInfo->fDescent, emSize));
+    fDescriptor->insertScalar("StemV",
+            scaleFromFontUnits(fFontInfo->fStemV, emSize));
+    fDescriptor->insertScalar("CapHeight",
+            scaleFromFontUnits(fFontInfo->fCapHeight, emSize));
+    fDescriptor->insertInt("ItalicAngle", fFontInfo->fItalicAngle);
+    fDescriptor->insert("FontBBox", makeFontBBox(fFontInfo->fBBox,
+                                                 fFontInfo->fEmSize))->unref();
+
+    if (defaultWidth > 0) {
+        fDescriptor->insertScalar("MissingWidth",
+                scaleFromFontUnits(defaultWidth, emSize));
+    }
+    return true;
+}
+
+void SkPDFFont::adjustGlyphRangeForSingleByteEncoding(int16_t glyphID) {
+    // Single byte glyph encoding supports a max of 255 glyphs.
+    fFirstGlyphID = glyphID - (glyphID - 1) % 255;
+    if (fLastGlyphID > fFirstGlyphID + 255 - 1) {
+        fLastGlyphID = fFirstGlyphID + 255 - 1;
+    }
+}
+
+bool SkPDFFont::FontRec::operator==(const SkPDFFont::FontRec& b) const {
+    if (fFontID != b.fFontID)
+        return false;
+    if (fFont != NULL && b.fFont != NULL) {
+        return fFont->fFirstGlyphID == b.fFont->fFirstGlyphID &&
+            fFont->fLastGlyphID == b.fFont->fLastGlyphID;
+    }
+    if (fGlyphID == 0 || b.fGlyphID == 0)
+        return true;
+
+    if (fFont != NULL) {
+        return fFont->fFirstGlyphID <= b.fGlyphID &&
+            b.fGlyphID <= fFont->fLastGlyphID;
+    } else if (b.fFont != NULL) {
+        return b.fFont->fFirstGlyphID <= fGlyphID &&
+            fGlyphID <= b.fFont->fLastGlyphID;
+    }
+    return fGlyphID == b.fGlyphID;
+}
+
+SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID, uint16_t glyphID)
+    : fFont(font),
+      fFontID(fontID),
+      fGlyphID(glyphID) {
+}
+
+void SkPDFFont::populateToUnicodeTable(const SkPDFGlyphSet* subset) {
+    if (fFontInfo == NULL || fFontInfo->fGlyphToUnicode.begin() == NULL) {
+        return;
+    }
+    SkRefPtr<SkPDFStream> pdfCmap =
+        generate_tounicode_cmap(fFontInfo->fGlyphToUnicode, subset);
+    addResource(pdfCmap.get());  // Pass reference from new.
     insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref();
 }
 
-void SkPDFFont::populateCIDFont() {
-    fMultiByteGlyphs = true;
-    insertName("BaseFont", fFontInfo->fFontName);
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFType0Font
+///////////////////////////////////////////////////////////////////////////////
 
-    if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kType1CID_Font) {
+SkPDFType0Font::SkPDFType0Font(SkAdvancedTypefaceMetrics* info,
+                               SkTypeface* typeface)
+        : SkPDFFont(info, typeface, 0, false) {
+    SkDEBUGCODE(fPopulated = false);
+}
+
+SkPDFType0Font::~SkPDFType0Font() {}
+
+SkPDFFont* SkPDFType0Font::getFontSubset(const SkPDFGlyphSet* subset) {
+    SkPDFType0Font* newSubset = new SkPDFType0Font(fontInfo(), typeface());
+    newSubset->populate(subset);
+    return newSubset;
+}
+
+#ifdef SK_DEBUG
+void SkPDFType0Font::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                                bool indirect) {
+    SkASSERT(fPopulated);
+    return INHERITED::emitObject(stream, catalog, indirect);
+}
+#endif
+
+bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset) {
+    insertName("Subtype", "Type0");
+    insertName("BaseFont", fontInfo()->fFontName);
+    insertName("Encoding", "Identity-H");
+
+    // Pass ref new created to fResources.
+    SkPDFCIDFont* newCIDFont =
+        new SkPDFCIDFont(fontInfo(), typeface(), subset);
+    addResource(newCIDFont);
+    SkRefPtr<SkPDFArray> descendantFonts = new SkPDFArray();
+    descendantFonts->unref();  // SkRefPtr and new took a reference.
+    descendantFonts->append(new SkPDFObjRef(newCIDFont))->unref();
+    insert("DescendantFonts", descendantFonts.get());
+
+    populateToUnicodeTable(subset);
+
+    SkDEBUGCODE(fPopulated = true);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFCIDFont
+///////////////////////////////////////////////////////////////////////////////
+
+SkPDFCIDFont::SkPDFCIDFont(SkAdvancedTypefaceMetrics* info,
+                           SkTypeface* typeface, const SkPDFGlyphSet* subset)
+        : SkPDFFont(info, typeface, 0, true) {
+    populate(subset);
+}
+
+SkPDFCIDFont::~SkPDFCIDFont() {}
+
+bool SkPDFCIDFont::addFontDescriptor(int16_t defaultWidth,
+                                     const SkPDFGlyphSet* subset) {
+    SkRefPtr<SkPDFDict> descriptor = new SkPDFDict("FontDescriptor");
+    descriptor->unref();  // SkRefPtr and new both took a ref.
+    setFontDescriptor(descriptor.get());
+
+    switch (getType()) {
+        case SkAdvancedTypefaceMetrics::kTrueType_Font: {
+            // TODO(arthurhsu): sfntly font subsetting
+            SkRefPtr<SkStream> fontData =
+                SkFontHost::OpenStream(SkTypeface::UniqueID(typeface()));
+            fontData->unref();  // SkRefPtr and OpenStream both took a ref.
+            SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
+            // SkRefPtr and new both ref()'d fontStream, pass one.
+            addResource(fontStream.get());
+
+            fontStream->insertInt("Length1", fontData->getLength());
+            descriptor->insert("FontFile2",
+                                new SkPDFObjRef(fontStream.get()))->unref();
+            break;
+        }
+        case SkAdvancedTypefaceMetrics::kCFF_Font:
+        case SkAdvancedTypefaceMetrics::kType1CID_Font: {
+            SkRefPtr<SkStream> fontData =
+                SkFontHost::OpenStream(SkTypeface::UniqueID(typeface()));
+            fontData->unref();  // SkRefPtr and OpenStream both took a ref.
+            SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
+            // SkRefPtr and new both ref()'d fontStream, pass one.
+            addResource(fontStream.get());
+
+            if (getType() == SkAdvancedTypefaceMetrics::kCFF_Font) {
+                fontStream->insertName("Subtype", "Type1C");
+            } else {
+                fontStream->insertName("Subtype", "CIDFontType0c");
+            }
+            descriptor->insert("FontFile3",
+                                new SkPDFObjRef(fontStream.get()))->unref();
+            break;
+        }
+        default:
+            SkASSERT(false);
+    }
+
+    addResource(descriptor.get());
+    descriptor->ref();
+
+    insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
+    return addCommonFontDescriptorEntries(defaultWidth);
+}
+
+bool SkPDFCIDFont::populate(const SkPDFGlyphSet* subset) {
+    insertName("BaseFont", fontInfo()->fFontName);
+
+    if (getType() == SkAdvancedTypefaceMetrics::kType1CID_Font) {
         insertName("Subtype", "CIDFontType0");
-    } else if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kTrueType_Font) {
+    } else if (getType() == SkAdvancedTypefaceMetrics::kTrueType_Font) {
         insertName("Subtype", "CIDFontType2");
         insertName("CIDToGIDMap", "Identity");
     } else {
@@ -639,29 +919,30 @@
     sysInfo->insertInt("Supplement", 0);
     insert("CIDSystemInfo", sysInfo.get());
 
-    addFontDescriptor(0);
+    addFontDescriptor(0, subset);
 
-    if (fFontInfo->fGlyphWidths.get()) {
+    if (fontInfo()->fGlyphWidths.get()) {
         int16_t defaultWidth = 0;
         SkRefPtr<SkPDFArray> widths =
-            composeAdvanceData(fFontInfo->fGlyphWidths.get(),
-                               fFontInfo->fEmSize, &appendWidth, &defaultWidth);
+            composeAdvanceData(fontInfo()->fGlyphWidths.get(),
+                               fontInfo()->fEmSize, &appendWidth,
+                               &defaultWidth);
         widths->unref();  // SkRefPtr and compose both took a reference.
         if (widths->size())
             insert("W", widths.get());
         if (defaultWidth != 0) {
             insertScalar("DW", scaleFromFontUnits(defaultWidth,
-                                                  fFontInfo->fEmSize));
+                                                  fontInfo()->fEmSize));
         }
     }
-    if (fFontInfo->fVerticalMetrics.get()) {
+    if (fontInfo()->fVerticalMetrics.get()) {
         struct SkAdvancedTypefaceMetrics::VerticalMetric defaultAdvance;
         defaultAdvance.fVerticalAdvance = 0;
         defaultAdvance.fOriginXDisp = 0;
         defaultAdvance.fOriginYDisp = 0;
         SkRefPtr<SkPDFArray> advances =
-            composeAdvanceData(fFontInfo->fVerticalMetrics.get(),
-                               fFontInfo->fEmSize, &appendVerticalAdvance,
+            composeAdvanceData(fontInfo()->fVerticalMetrics.get(),
+                               fontInfo()->fEmSize, &appendVerticalAdvance,
                                &defaultAdvance);
         advances->unref();  // SkRefPtr and compose both took a ref.
         if (advances->size())
@@ -670,22 +951,77 @@
                 defaultAdvance.fOriginXDisp ||
                 defaultAdvance.fOriginYDisp) {
             insert("DW2", appendVerticalAdvance(defaultAdvance,
-                                                fFontInfo->fEmSize,
+                                                fontInfo()->fEmSize,
                                                 new SkPDFArray))->unref();
         }
     }
+
+    return true;
 }
 
-bool SkPDFFont::populateType1Font(int16_t glyphID) {
-    SkASSERT(!fFontInfo->fVerticalMetrics.get());
-    SkASSERT(fFontInfo->fGlyphWidths.get());
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFType1Font
+///////////////////////////////////////////////////////////////////////////////
+
+SkPDFType1Font::SkPDFType1Font(SkAdvancedTypefaceMetrics* info,
+                               SkTypeface* typeface,
+                               uint16_t glyphID,
+                               SkPDFDict* relatedFontDescriptor)
+        : SkPDFFont(info, typeface, glyphID, false) {
+    populate(glyphID);
+}
+
+SkPDFType1Font::~SkPDFType1Font() {}
+
+bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) {
+    SkRefPtr<SkPDFDict> descriptor = getFontDescriptor();
+    if (descriptor.get() != NULL) {
+        addResource(descriptor.get());
+        descriptor->ref();
+        insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
+        return true;
+    }
+
+    descriptor = new SkPDFDict("FontDescriptor");
+    descriptor->unref();  // SkRefPtr and new both took a ref.
+    setFontDescriptor(descriptor.get());
+
+    size_t header SK_INIT_TO_AVOID_WARNING;
+    size_t data SK_INIT_TO_AVOID_WARNING;
+    size_t trailer SK_INIT_TO_AVOID_WARNING;
+    SkRefPtr<SkStream> rawFontData =
+        SkFontHost::OpenStream(SkTypeface::UniqueID(typeface()));
+    rawFontData->unref();  // SkRefPtr and OpenStream both took a ref.
+    SkStream* fontData = handleType1Stream(rawFontData.get(), &header, &data,
+                                           &trailer);
+    if (fontData == NULL) {
+        return false;
+    }
+    SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData);
+    // SkRefPtr and new both ref()'d fontStream, pass one.
+    addResource(fontStream.get());
+    fontStream->insertInt("Length1", header);
+    fontStream->insertInt("Length2", data);
+    fontStream->insertInt("Length3", trailer);
+    descriptor->insert("FontFile", new SkPDFObjRef(fontStream.get()))->unref();
+
+    addResource(descriptor.get());
+    descriptor->ref();
+    insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
+
+    return addCommonFontDescriptorEntries(defaultWidth);
+}
+
+bool SkPDFType1Font::populate(int16_t glyphID) {
+    SkASSERT(!fontInfo()->fVerticalMetrics.get());
+    SkASSERT(fontInfo()->fGlyphWidths.get());
 
     adjustGlyphRangeForSingleByteEncoding(glyphID);
 
     int16_t defaultWidth = 0;
     const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry = NULL;
     const SkAdvancedTypefaceMetrics::WidthRange* widthEntry;
-    for (widthEntry = fFontInfo.get()->fGlyphWidths.get();
+    for (widthEntry = fontInfo()->fGlyphWidths.get();
             widthEntry != NULL;
             widthEntry = widthEntry->fNext.get()) {
         switch (widthEntry->fType) {
@@ -706,7 +1042,7 @@
         return false;
 
     insertName("Subtype", "Type1");
-    insertName("BaseFont", fFontInfo->fFontName);
+    insertName("BaseFont", fontInfo()->fFontName);
 
     addWidthInfoFromRange(defaultWidth, widthRangeEntry);
 
@@ -718,26 +1054,67 @@
     encDiffs->unref();  // SkRefPtr and new both took a reference.
     encoding->insert("Differences", encDiffs.get());
 
-    encDiffs->reserve(fLastGlyphID - fFirstGlyphID + 2);
+    encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2);
     encDiffs->appendInt(1);
-    for (int gID = fFirstGlyphID; gID <= fLastGlyphID; gID++) {
-        encDiffs->appendName(fFontInfo->fGlyphNames->get()[gID].c_str());
+    for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) {
+        encDiffs->appendName(fontInfo()->fGlyphNames->get()[gID].c_str());
     }
 
-    if (fFontInfo->fLastGlyphID <= 255)
-        fFontInfo = NULL;
     return true;
 }
 
-void SkPDFFont::populateType3Font(int16_t glyphID) {
+void SkPDFType1Font::addWidthInfoFromRange(
+        int16_t defaultWidth,
+        const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry) {
+    SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
+    widthArray->unref();  // SkRefPtr and new both took a ref.
+    int firstChar = 0;
+    if (widthRangeEntry) {
+        const uint16_t emSize = fontInfo()->fEmSize;
+        int startIndex = firstGlyphID() - widthRangeEntry->fStartId;
+        int endIndex = startIndex + lastGlyphID() - firstGlyphID() + 1;
+        if (startIndex < 0)
+            startIndex = 0;
+        if (endIndex > widthRangeEntry->fAdvance.count())
+            endIndex = widthRangeEntry->fAdvance.count();
+        if (widthRangeEntry->fStartId == 0) {
+            appendWidth(widthRangeEntry->fAdvance[0], emSize, widthArray.get());
+        } else {
+            firstChar = startIndex + widthRangeEntry->fStartId;
+        }
+        for (int i = startIndex; i < endIndex; i++)
+            appendWidth(widthRangeEntry->fAdvance[i], emSize, widthArray.get());
+    } else {
+        appendWidth(defaultWidth, 1000, widthArray.get());
+    }
+    insertInt("FirstChar", firstChar);
+    insertInt("LastChar", firstChar + widthArray->size() - 1);
+    insert("Widths", widthArray.get());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFType3Font
+///////////////////////////////////////////////////////////////////////////////
+
+SkPDFType3Font::SkPDFType3Font(SkAdvancedTypefaceMetrics* info,
+                               SkTypeface* typeface,
+                               uint16_t glyphID,
+                               SkPDFDict* relatedFontDescriptor)
+        : SkPDFFont(info, typeface, glyphID, false) {
+    populate(glyphID);
+}
+
+SkPDFType3Font::~SkPDFType3Font() {}
+
+bool SkPDFType3Font::populate(int16_t glyphID) {
     SkPaint paint;
-    paint.setTypeface(fTypeface.get());
+    paint.setTypeface(typeface());
     paint.setTextSize(1000);
     SkAutoGlyphCache autoCache(paint, NULL);
     SkGlyphCache* cache = autoCache.getCache();
     // If fLastGlyphID isn't set (because there is not fFontInfo), look it up.
-    if (fLastGlyphID == 0) {
-        fLastGlyphID = cache->getGlyphCount() - 1;
+    if (lastGlyphID() == 0) {
+        setLastGlyphID(cache->getGlyphCount() - 1);
     }
 
     adjustGlyphRangeForSingleByteEncoding(glyphID);
@@ -759,14 +1136,14 @@
     SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
     encDiffs->unref();  // SkRefPtr and new both took a reference.
     encoding->insert("Differences", encDiffs.get());
-    encDiffs->reserve(fLastGlyphID - fFirstGlyphID + 2);
+    encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2);
     encDiffs->appendInt(1);
 
     SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
     widthArray->unref();  // SkRefPtr and new both took a ref.
 
     SkIRect bbox = SkIRect::MakeEmpty();
-    for (int gID = fFirstGlyphID; gID <= fLastGlyphID; gID++) {
+    for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) {
         SkString characterName;
         characterName.printf("gid%d", gID);
         encDiffs->appendName(characterName.c_str());
@@ -793,176 +1170,17 @@
         SkRefPtr<SkPDFStream> glyphDescription =
             new SkPDFStream(glyphStream.get());
         // SkRefPtr and new both ref()'d charProcs, pass one.
-        fResources.push(glyphDescription.get());
+        addResource(glyphDescription.get());
         charProcs->insert(characterName.c_str(),
                           new SkPDFObjRef(glyphDescription.get()))->unref();
     }
 
     insert("FontBBox", makeFontBBox(bbox, 1000))->unref();
-    insertInt("FirstChar", fFirstGlyphID);
-    insertInt("LastChar", fLastGlyphID);
+    insertInt("FirstChar", firstGlyphID());
+    insertInt("LastChar", lastGlyphID());
     insert("Widths", widthArray.get());
     insertName("CIDToGIDMap", "Identity");
 
-    if (fFontInfo && fFontInfo->fLastGlyphID <= 255) {
-        fFontInfo = NULL;
-    }
-    populateToUnicodeTable();
-}
-
-bool SkPDFFont::addFontDescriptor(int16_t defaultWidth) {
-    if (fDescriptor.get() != NULL) {
-        fResources.push(fDescriptor.get());
-        fDescriptor->ref();
-        insert("FontDescriptor", new SkPDFObjRef(fDescriptor.get()))->unref();
-        return true;
-    }
-
-    fDescriptor = new SkPDFDict("FontDescriptor");
-    fDescriptor->unref();  // SkRefPtr and new both took a ref.
-
-    switch (fFontInfo->fType) {
-        case SkAdvancedTypefaceMetrics::kType1_Font: {
-            size_t header SK_INIT_TO_AVOID_WARNING;
-            size_t data SK_INIT_TO_AVOID_WARNING;
-            size_t trailer SK_INIT_TO_AVOID_WARNING;
-            SkRefPtr<SkStream> rawFontData =
-                SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get()));
-            rawFontData->unref();  // SkRefPtr and OpenStream both took a ref.
-            SkStream* fontData = handleType1Stream(rawFontData.get(), &header,
-                                                   &data, &trailer);
-            if (fontData == NULL)
-                return false;
-            SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData);
-            // SkRefPtr and new both ref()'d fontStream, pass one.
-            fResources.push(fontStream.get());
-            fontStream->insertInt("Length1", header);
-            fontStream->insertInt("Length2", data);
-            fontStream->insertInt("Length3", trailer);
-            fDescriptor->insert("FontFile",
-                                new SkPDFObjRef(fontStream.get()))->unref();
-            break;
-        }
-        case SkAdvancedTypefaceMetrics::kTrueType_Font: {
-            SkRefPtr<SkStream> fontData =
-                SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get()));
-            fontData->unref();  // SkRefPtr and OpenStream both took a ref.
-            SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
-            // SkRefPtr and new both ref()'d fontStream, pass one.
-            fResources.push(fontStream.get());
-
-            fontStream->insertInt("Length1", fontData->getLength());
-            fDescriptor->insert("FontFile2",
-                                new SkPDFObjRef(fontStream.get()))->unref();
-            break;
-        }
-        case SkAdvancedTypefaceMetrics::kCFF_Font:
-        case SkAdvancedTypefaceMetrics::kType1CID_Font: {
-            SkRefPtr<SkStream> fontData =
-                SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get()));
-            fontData->unref();  // SkRefPtr and OpenStream both took a ref.
-            SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
-            // SkRefPtr and new both ref()'d fontStream, pass one.
-            fResources.push(fontStream.get());
-
-            if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kCFF_Font) {
-                fontStream->insertName("Subtype", "Type1C");
-            } else {
-                fontStream->insertName("Subtype", "CIDFontType0c");
-            }
-            fDescriptor->insert("FontFile3",
-                                new SkPDFObjRef(fontStream.get()))->unref();
-            break;
-        }
-        default:
-            SkASSERT(false);
-    }
-
-    const uint16_t emSize = fFontInfo->fEmSize;
-    fResources.push(fDescriptor.get());
-    fDescriptor->ref();
-    insert("FontDescriptor", new SkPDFObjRef(fDescriptor.get()))->unref();
-
-    fDescriptor->insertName("FontName", fFontInfo->fFontName);
-    fDescriptor->insertInt("Flags", fFontInfo->fStyle);
-    fDescriptor->insertScalar("Ascent",
-                              scaleFromFontUnits(fFontInfo->fAscent, emSize));
-    fDescriptor->insertScalar("Descent",
-                              scaleFromFontUnits(fFontInfo->fDescent, emSize));
-    fDescriptor->insertScalar("StemV",
-                              scaleFromFontUnits(fFontInfo->fStemV, emSize));
-    fDescriptor->insertScalar("CapHeight",
-                             scaleFromFontUnits(fFontInfo->fCapHeight, emSize));
-    fDescriptor->insertInt("ItalicAngle", fFontInfo->fItalicAngle);
-    fDescriptor->insert("FontBBox", makeFontBBox(fFontInfo->fBBox,
-                                                 fFontInfo->fEmSize))->unref();
-
-    if (defaultWidth > 0) {
-        fDescriptor->insertScalar("MissingWidth",
-                                  scaleFromFontUnits(defaultWidth, emSize));
-    }
+    populateToUnicodeTable(NULL);
     return true;
 }
-void SkPDFFont::addWidthInfoFromRange(
-        int16_t defaultWidth,
-        const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry) {
-    SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
-    widthArray->unref();  // SkRefPtr and new both took a ref.
-    int firstChar = 0;
-    if (widthRangeEntry) {
-        const uint16_t emSize = fFontInfo->fEmSize;
-        int startIndex = fFirstGlyphID - widthRangeEntry->fStartId;
-        int endIndex = startIndex + fLastGlyphID - fFirstGlyphID + 1;
-        if (startIndex < 0)
-            startIndex = 0;
-        if (endIndex > widthRangeEntry->fAdvance.count())
-            endIndex = widthRangeEntry->fAdvance.count();
-        if (widthRangeEntry->fStartId == 0) {
-            appendWidth(widthRangeEntry->fAdvance[0], emSize, widthArray.get());
-        } else {
-            firstChar = startIndex + widthRangeEntry->fStartId;
-        }
-        for (int i = startIndex; i < endIndex; i++)
-            appendWidth(widthRangeEntry->fAdvance[i], emSize, widthArray.get());
-    } else {
-        appendWidth(defaultWidth, 1000, widthArray.get());
-    }
-    insertInt("FirstChar", firstChar);
-    insertInt("LastChar", firstChar + widthArray->size() - 1);
-    insert("Widths", widthArray.get());
-}
-
-void SkPDFFont::adjustGlyphRangeForSingleByteEncoding(int16_t glyphID) {
-    // Single byte glyph encoding supports a max of 255 glyphs.
-    fFirstGlyphID = glyphID - (glyphID - 1) % 255;
-    if (fLastGlyphID > fFirstGlyphID + 255 - 1) {
-        fLastGlyphID = fFirstGlyphID + 255 - 1;
-    }
-}
-
-
-bool SkPDFFont::FontRec::operator==(const SkPDFFont::FontRec& b) const {
-    if (fFontID != b.fFontID)
-        return false;
-    if (fFont != NULL && b.fFont != NULL) {
-        return fFont->fFirstGlyphID == b.fFont->fFirstGlyphID &&
-            fFont->fLastGlyphID == b.fFont->fLastGlyphID;
-    }
-    if (fGlyphID == 0 || b.fGlyphID == 0)
-        return true;
-
-    if (fFont != NULL) {
-        return fFont->fFirstGlyphID <= b.fGlyphID &&
-            b.fGlyphID <= fFont->fLastGlyphID;
-    } else if (b.fFont != NULL) {
-        return b.fFont->fFirstGlyphID <= fGlyphID &&
-            fGlyphID <= b.fFont->fLastGlyphID;
-    }
-    return fGlyphID == b.fGlyphID;
-}
-
-SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID, uint16_t glyphID)
-    : fFont(font),
-      fFontID(fontID),
-      fGlyphID(glyphID) {
-}
diff --git a/src/pdf/SkPDFFontImpl.h b/src/pdf/SkPDFFontImpl.h
new file mode 100755
index 0000000..2220625
--- /dev/null
+++ b/src/pdf/SkPDFFontImpl.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFFontImpl_DEFINED
+#define SkPDFFontImpl_DEFINED
+
+#include "SkPDFFont.h"
+
+class SkPDFType0Font : public SkPDFFont {
+public:
+    virtual ~SkPDFType0Font();
+    virtual bool multiByteGlyphs() const { return true; }
+    SK_API virtual SkPDFFont* getFontSubset(const SkPDFGlyphSet* usage);
+#ifdef SK_DEBUG
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+#endif
+
+private:
+    friend class SkPDFFont;  // to access the constructor
+#ifdef SK_DEBUG
+    bool fPopulated;
+    typedef SkPDFDict INHERITED;
+#endif
+
+    SkPDFType0Font(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface);
+
+    bool populate(const SkPDFGlyphSet* subset);
+};
+
+class SkPDFCIDFont : public SkPDFFont {
+public:
+    virtual ~SkPDFCIDFont();
+    virtual bool multiByteGlyphs() const { return true; }
+
+private:
+    friend class SkPDFType0Font;  // to access the constructor
+
+    SkPDFCIDFont(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface,
+                 const SkPDFGlyphSet* subset);
+
+    bool populate(const SkPDFGlyphSet* subset);
+    bool addFontDescriptor(int16_t defaultWidth, const SkPDFGlyphSet* subset);
+};
+
+class SkPDFType1Font : public SkPDFFont {
+public:
+    virtual ~SkPDFType1Font();
+    virtual bool multiByteGlyphs() const { return false; }
+
+private:
+    friend class SkPDFFont;  // to access the constructor
+
+    SkPDFType1Font(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface,
+                   uint16_t glyphID, SkPDFDict* relatedFontDescriptor);
+
+    bool populate(int16_t glyphID);
+    bool addFontDescriptor(int16_t defaultWidth);
+    void addWidthInfoFromRange(int16_t defaultWidth,
+        const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry);
+};
+
+class SkPDFType3Font : public SkPDFFont {
+public:
+    virtual ~SkPDFType3Font();
+    virtual bool multiByteGlyphs() const { return false; }
+
+private:
+    friend class SkPDFFont;  // to access the constructor
+
+    SkPDFType3Font(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface,
+                   uint16_t glyphID, SkPDFDict* relatedFontDescriptor);
+
+    bool populate(int16_t glyphID);
+};
+
+#endif
diff --git a/src/pdf/SkPDFPage.cpp b/src/pdf/SkPDFPage.cpp
index 09849b0..846df64 100644
--- a/src/pdf/SkPDFPage.cpp
+++ b/src/pdf/SkPDFPage.cpp
@@ -141,3 +141,7 @@
 const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
     return fDevice->getFontResources();
 }
+
+const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
+    return fDevice->getFontGlyphUsage();
+}