[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
This commit is contained in:
vandebo@chromium.org 2011-07-25 22:34:12 +00:00
parent d3a094ca34
commit 9859428e71
12 changed files with 902 additions and 473 deletions

View File

@ -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',

View File

@ -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);

View File

@ -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<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.

View File

@ -81,6 +81,7 @@ private:
SkTDArray<SkPDFDict*> fPageTree;
SkRefPtr<SkPDFDict> fDocCatalog;
SkTDArray<SkPDFObject*> fPageResources;
SkTDArray<SkPDFObject*> fSubstitutes;
int fSecondPageFirstResourceIndex;
SkRefPtr<SkPDFDict> fTrailerDict;

View File

@ -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 @@ 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();
/** 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 @@ 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:
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 @@ private:
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

View File

@ -90,6 +90,11 @@ public:
*/
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;

View File

@ -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;
}

View File

@ -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<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();
}

View File

@ -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<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 @@ 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);

1008
src/pdf/SkPDFFont.cpp Executable file → Normal file

File diff suppressed because it is too large Load Diff

90
src/pdf/SkPDFFontImpl.h Executable file
View File

@ -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

View File

@ -141,3 +141,7 @@ void SkPDFPage::GeneratePageTree(const SkTDArray<SkPDFPage*>& pages,
const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
return fDevice->getFontResources();
}
const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
return fDevice->getFontGlyphUsage();
}