From 9859428e71c6041928e6dd741ae3284017e78e81 Mon Sep 17 00:00:00 2001 From: "vandebo@chromium.org" Date: Mon, 25 Jul 2011 22:34:12 +0000 Subject: [PATCH] [PDF] Refactor SkPDFFont to enable font/cmap subsetting. Patch from Arthur Hsu, original CL: http://codereview.appspot.com/4633050/ Committed: http://code.google.com/p/skia/source/detail?r=1943 Reverted: http://code.google.com/p/skia/source/detail?r=1944 Review URL: http://codereview.appspot.com/4811049 git-svn-id: http://skia.googlecode.com/svn/trunk@1956 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gyp/pdf.gyp | 3 +- include/pdf/SkBitSet.h | 6 +- include/pdf/SkPDFDevice.h | 19 +- include/pdf/SkPDFDocument.h | 1 + include/pdf/SkPDFFont.h | 172 ++++++---- include/pdf/SkPDFPage.h | 5 + src/pdf/SkBitSet.cpp | 4 +- src/pdf/SkPDFDevice.cpp | 31 +- src/pdf/SkPDFDocument.cpp | 28 ++ src/pdf/SkPDFFont.cpp | 776 ++++++++++++++++++++++++++++---------------- src/pdf/SkPDFFontImpl.h | 90 +++++ src/pdf/SkPDFPage.cpp | 4 + 12 files changed, 784 insertions(+), 355 deletions(-) mode change 100755 => 100644 src/pdf/SkPDFFont.cpp create mode 100755 src/pdf/SkPDFFontImpl.h 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 @@ public: /** 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 @@ private: 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 SkPDFDevice; class SkPDFDict; class SkPDFFont; class SkPDFFormXObject; +class SkPDFGlyphSetMap; class SkPDFGraphicState; class SkPDFObject; class SkPDFShader; @@ -150,11 +151,18 @@ public: * 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 @@ private: ContentEntry* getLastContentEntry(); void setLastContentEntry(ContentEntry* contentEntry); + // Glyph ids used for each font on this device. + SkTScopedPtr 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* 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 @@ private: SkTDArray fPageTree; SkRefPtr fDocCatalog; SkTDArray fPageResources; + SkTDArray fSubstitutes; int fSecondPageFirstResourceIndex; SkRefPtr 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* 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 fMap; +}; + /** \class SkPDFFont A PDF Object class representing a font. The font may have resources @@ -45,15 +97,15 @@ public: /** 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(); - /** Return true if this font has an encoding for the passed glyph id. + /** Returns true if this font encoding supports glyph IDs above 255. */ - SK_API bool hasGlyph(uint16_t glyphID); + SK_API virtual bool multiByteGlyphs() const = 0; - /** Returns true if this font encoding supports glyph IDs above 255. + /** Return true if this font has an encoding for the passed glyph id. */ - SK_API bool multiByteGlyphs(); + SK_API bool hasGlyph(uint16_t glyphID); /** 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 @@ -76,13 +128,64 @@ public: 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: + class FontRec { + public: + SkPDFFont* fFont; + uint32_t fFontID; + uint16_t fGlyphID; + + // A fGlyphID of 0 with no fFont always matches. + bool operator==(const FontRec& b) const; + FontRec(SkPDFFont* font, uint32_t fontID, uint16_t fGlyphID); + }; + SkRefPtr 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. @@ -94,58 +197,11 @@ private: SkTDArray fResources; SkRefPtr fDescriptor; - class FontRec { - public: - SkPDFFont* fFont; - uint32_t fFontID; - uint16_t fGlyphID; - - // A fGlyphID of 0 with no fFont always matches. - bool operator==(const FontRec& b) const; - FontRec(SkPDFFont* font, uint32_t fontID, uint16_t fGlyphID); - }; + SkAdvancedTypefaceMetrics::FontType fFontType; // This should be made a hash table if performance is a problem. static SkTDArray& 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 @@ public: */ SK_API const SkTDArray& 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 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 @@ void SkBitSet::setBit(int index, bool value) { } } -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 @@ void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) { } } -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(const SkISize& layerSize, } SkPDFDevice::~SkPDFDevice() { - this->cleanUp(); + this->cleanUp(true); } void SkPDFDevice::init() { @@ -553,18 +553,24 @@ void SkPDFDevice::init() { 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 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, 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 @@ void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, 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 @@ void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y, 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 @@ SkData* SkPDFDevice::copyContentToData() const { 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 @@ void SkPDFDevice::createFormXObjectFromDevice( SkRefPtr* 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 @@ void addResourcesToCatalog(int firstIndex, bool firstPage, } } +static void perform_font_subsetting(SkPDFCatalog* catalog, + const SkTDArray& pages, + SkTDArray* 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 @@ SkPDFDocument::~SkPDFDocument() { fPageTree[i]->clear(); fPageTree.safeUnrefAll(); fPageResources.safeUnrefAll(); + fSubstitutes.safeUnrefAll(); } bool SkPDFDocument::emitPDF(SkWStream* stream) { @@ -98,6 +123,9 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) { } } + // 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 @@ int8_t hexToBin(uint8_t c) { 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 @@ static void append_cmap_footer(SkDynamicMemoryWStream* cmap) { // Generate table according to PDF spec 1.4 and Adobe Technote 5014. static void append_cmap_bfchar_sections( const SkTDArray& 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 void append_cmap_bfchar_sections( } } +static SkPDFStream* generate_tounicode_cmap( + const SkTDArray& glyphUnicode, + const SkPDFGlyphSet* subset) { + SkDynamicMemoryWStream cmap; + append_tounicode_header(&cmap); + append_cmap_bfchar_sections(glyphUnicode, subset, &cmap); + append_cmap_footer(&cmap); + SkRefPtr 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 @@ SkPDFFont::~SkPDFFont() { 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 @@ SkTypeface* SkPDFFont::typeface() { } 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 @@ size_t SkPDFFont::glyphsToPDFFontEncoding(uint16_t* glyphIDs, 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 fontInfo; - SkPDFDict* fontDescriptor = NULL; - if (index >= 0) { - SkPDFFont* relatedFont = CanonicalFonts()[index].fFont; + SkRefPtr 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 @@ SkPDFFont* SkPDFFont::GetFontResource(SkTypeface* typeface, uint16_t glyphID) { info, SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo); info = SkTBitOr( 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::CanonicalFonts() { // This initialization is only thread safe with gcc. @@ -536,96 +642,270 @@ bool SkPDFFont::Find(uint32_t fontID, uint16_t glyphID, int* index) { 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); + } + + SkASSERT(type == SkAdvancedTypefaceMetrics::kCFF_Font || + type == SkAdvancedTypefaceMetrics::kOther_Font || + type == SkAdvancedTypefaceMetrics::kNotEmbeddable_Font); + + return new SkPDFType3Font(info, typeface, glyphID, relatedFontDescriptor); +} + +SkAdvancedTypefaceMetrics* SkPDFFont::fontInfo() { + return fFontInfo.get(); +} + +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; + } + + 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; +} - if (fType == SkAdvancedTypefaceMetrics::kType1_Font && - populateType1Font(glyphID)) { +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 pdfCmap = + generate_tounicode_cmap(fFontInfo->fGlyphToUnicode, subset); + addResource(pdfCmap.get()); // Pass reference from new. + insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref(); +} + +/////////////////////////////////////////////////////////////////////////////// +// class SkPDFType0Font +/////////////////////////////////////////////////////////////////////////////// - SkASSERT(fType == SkAdvancedTypefaceMetrics::kType1_Font || - fType == SkAdvancedTypefaceMetrics::kCFF_Font || - fType == SkAdvancedTypefaceMetrics::kOther_Font || - fType == SkAdvancedTypefaceMetrics::kNotEmbeddable_Font); - populateType3Font(glyphID); +SkPDFType0Font::SkPDFType0Font(SkAdvancedTypefaceMetrics* info, + SkTypeface* typeface) + : SkPDFFont(info, typeface, 0, false) { + SkDEBUGCODE(fPopulated = false); } -void SkPDFFont::populateType0Font() { - fMultiByteGlyphs = true; +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", fFontInfo->fFontName); + insertName("BaseFont", fontInfo()->fFontName); insertName("Encoding", "Identity-H"); + // Pass ref new created to fResources. + SkPDFCIDFont* newCIDFont = + new SkPDFCIDFont(fontInfo(), typeface(), subset); + addResource(newCIDFont); SkRefPtr 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(); + descendantFonts->append(new SkPDFObjRef(newCIDFont))->unref(); insert("DescendantFonts", descendantFonts.get()); - populateToUnicodeTable(); + populateToUnicodeTable(subset); + + SkDEBUGCODE(fPopulated = true); + return true; } -void SkPDFFont::populateToUnicodeTable() { - if (fFontInfo.get() == NULL || - fFontInfo->fGlyphToUnicode.begin() == NULL) { - return; +/////////////////////////////////////////////////////////////////////////////// +// 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 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 fontData = + SkFontHost::OpenStream(SkTypeface::UniqueID(typeface())); + fontData->unref(); // SkRefPtr and OpenStream both took a ref. + SkRefPtr 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 fontData = + SkFontHost::OpenStream(SkTypeface::UniqueID(typeface())); + fontData->unref(); // SkRefPtr and OpenStream both took a ref. + SkRefPtr 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); } - SkDynamicMemoryWStream cmap; - append_tounicode_header(&cmap); - append_cmap_bfchar_sections(fFontInfo->fGlyphToUnicode, &cmap); - append_cmap_footer(&cmap); - SkRefPtr cmapStream = new SkMemoryStream(); - cmapStream->unref(); // SkRefPtr and new took a reference. - cmapStream->setData(cmap.copyToData())->unref(); - SkRefPtr pdfCmap = new SkPDFStream(cmapStream.get()); - fResources.push(pdfCmap.get()); // Pass reference from new. - insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref(); + addResource(descriptor.get()); + descriptor->ref(); + + insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref(); + return addCommonFontDescriptorEntries(defaultWidth); } -void SkPDFFont::populateCIDFont() { - fMultiByteGlyphs = true; - insertName("BaseFont", fFontInfo->fFontName); +bool SkPDFCIDFont::populate(const SkPDFGlyphSet* subset) { + insertName("BaseFont", fontInfo()->fFontName); - if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kType1CID_Font) { + 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 @@ void SkPDFFont::populateCIDFont() { 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 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 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 @@ void SkPDFFont::populateCIDFont() { 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 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 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 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 @@ bool SkPDFFont::populateType1Font(int16_t glyphID) { return false; insertName("Subtype", "Type1"); - insertName("BaseFont", fFontInfo->fFontName); + insertName("BaseFont", fontInfo()->fFontName); addWidthInfoFromRange(defaultWidth, widthRangeEntry); @@ -718,26 +1054,67 @@ bool SkPDFFont::populateType1Font(int16_t glyphID) { 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 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 @@ void SkPDFFont::populateType3Font(int16_t glyphID) { SkRefPtr 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 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 @@ void SkPDFFont::populateType3Font(int16_t glyphID) { SkRefPtr 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 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 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 fontData = - SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get())); - fontData->unref(); // SkRefPtr and OpenStream both took a ref. - SkRefPtr 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 fontData = - SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get())); - fontData->unref(); // SkRefPtr and OpenStream both took a ref. - SkRefPtr 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 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 @@ void SkPDFPage::GeneratePageTree(const SkTDArray& pages, const SkTDArray& SkPDFPage::getFontResources() const { return fDevice->getFontResources(); } + +const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const { + return fDevice->getFontGlyphUsage(); +} -- 2.7.4