Simplify reference management in SkPDF

Prior to this change, SkPDFObject subclasses were required
to track their resources separately from the document
structure.  (An object has a resource if it depends, via an
indirect reference, on another object).  This led to a lot
of extra code to duplicate effort.  I replace the
getResources() function with the much simpler addResources()
function.  I only define a non-trivial addResources() method
on arrays, dictionaries, and indirect object references.
All other specialized classes simply rely on their parent
class's implementation.

SkPDFObject::addResources() works by recursively walking the
directed graph of object (direct and indirect) references
and adding resources to a set.  It doesn't matter that there
are closed loops in the graph, since we check the set before
walking down a branch.

-  Add SkPDFObject::addResources() virtual function, with
   four implementations
-  Remove SkPDFObject::getResources() virtual function and
   all implementations.
-  Remove SkPDFObject::GetResourcesHelper()
-  Remove SkPDFObject::AddResourceHelper()
-  In SkPDFCatalog::findObjectIndex(), add an object to the
   catalog if it doesn't exist yet.
-  SkPDFCatalog::setSubstitute() no longer sets up resources
-  SkPDFDocument.cpp no longer needs the Streamer object
-  SkPDFDocument.cpp calls fDocCatalog->addResources to build
   the resource list.
-  SkPDFFont::addResource() removed
-  All SkPDF-::fResource sets removed (they are redundant).
-  removed SkPDFImage::addSMask() function
-  SkPDFResourceDict::getReferencedResources() removed.

Motivation: this removes quite a bit of code and makes the
objects slightly slimmer in memory.  Most importantly, this
will lead the way towards removing SkPDFObject's inheritance
from SkRefCnt, which will greatly simplify everything.

Testing: I usually test changes to the PDF backend by
comparing checksums of PDF files rendered from GMs and SKPs
before and after the change.  This change both re-orders and
re-numbers the indirect PDF objects.  I used the qpdf
program to normalize the PDFs and then compared the
normalized outputs from before and after the change; they
matched.

Review URL: https://codereview.chromium.org/870333002
This commit is contained in:
halcanary 2015-02-10 13:32:09 -08:00 committed by Commit bot
parent c8262ccbf9
commit bf799cd228
19 changed files with 89 additions and 399 deletions

View File

@ -33,7 +33,7 @@ SkPDFObject* SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) {
fFirstPageCount++;
}
struct Rec newEntry(obj, onFirstPage);
Rec newEntry(obj, onFirstPage);
fCatalog.append(1, &newEntry);
return obj;
}
@ -45,18 +45,11 @@ void SkPDFCatalog::setFileOffset(SkPDFObject* obj, off_t offset) {
fCatalog[objIndex].fFileOffset = offset;
}
void SkPDFCatalog::emitObjectNumber(SkWStream* stream, SkPDFObject* obj) {
stream->writeDecAsText(assignObjNum(obj));
stream->writeText(" 0"); // Generation number is always 0.
int32_t SkPDFCatalog::getObjectNumber(SkPDFObject* obj) {
return (int32_t)assignObjNum(obj);
}
size_t SkPDFCatalog::getObjectNumberSize(SkPDFObject* obj) {
SkDynamicMemoryWStream buffer;
emitObjectNumber(&buffer, obj);
return buffer.getOffset();
}
int SkPDFCatalog::findObjectIndex(SkPDFObject* obj) const {
int SkPDFCatalog::findObjectIndex(SkPDFObject* obj) {
for (int i = 0; i < fCatalog.count(); i++) {
if (fCatalog[i].fObject == obj) {
return i;
@ -68,7 +61,9 @@ int SkPDFCatalog::findObjectIndex(SkPDFObject* obj) const {
return findObjectIndex(fSubstituteMap[i].fOriginal);
}
}
return -1;
Rec newEntry(obj, false);
fCatalog.append(1, &newEntry);
return fCatalog.count() - 1;
}
int SkPDFCatalog::assignObjNum(SkPDFObject* obj) {
@ -150,35 +145,8 @@ void SkPDFCatalog::setSubstitute(SkPDFObject* original,
}
}
#endif
// Check if the original is on first page.
bool onFirstPage = false;
for (int i = 0; i < fCatalog.count(); ++i) {
if (fCatalog[i].fObject == original) {
onFirstPage = fCatalog[i].fOnFirstPage;
break;
}
#if defined(SK_DEBUG)
if (i == fCatalog.count() - 1) {
SkASSERT(false); // original not in catalog
return;
}
#endif
}
SubstituteMapping newMapping(original, substitute);
fSubstituteMap.append(1, &newMapping);
// Add resource objects of substitute object to catalog.
SkTSet<SkPDFObject*>* targetSet = getSubstituteList(onFirstPage);
SkTSet<SkPDFObject*> newResourceObjects;
newMapping.fSubstitute->getResources(*targetSet, &newResourceObjects);
for (int i = 0; i < newResourceObjects.count(); ++i) {
addObject(newResourceObjects[i], onFirstPage);
}
// mergeInto returns the number of duplicates.
// If there are duplicates, there is a bug and we mess ref counting.
SkDEBUGCODE(int duplicates =) targetSet->mergeInto(newResourceObjects);
SkASSERT(duplicates == 0);
}
SkPDFObject* SkPDFCatalog::getSubstituteObject(SkPDFObject* object) {
@ -190,7 +158,3 @@ SkPDFObject* SkPDFCatalog::getSubstituteObject(SkPDFObject* object) {
return object;
}
SkTSet<SkPDFObject*>* SkPDFCatalog::getSubstituteList(bool firstPage) {
return firstPage ? &fSubstituteResourcesFirstPage :
&fSubstituteResourcesRemaining;
}

View File

@ -43,17 +43,10 @@ public:
*/
void setFileOffset(SkPDFObject* obj, off_t offset);
/** Output the object number for the passed object.
/** Get the object number for the passed object.
* @param obj The object of interest.
* @param stream The writable output stream to send the output to.
*/
void emitObjectNumber(SkWStream* stream, SkPDFObject* obj);
/** Return the number of bytes that would be emitted for the passed
* object's object number.
* @param obj The object of interest
*/
size_t getObjectNumberSize(SkPDFObject* obj);
int32_t getObjectNumber(SkPDFObject* obj);
/** Return the document flags in effect for this catalog/document.
*/
@ -76,10 +69,6 @@ public:
*/
SkPDFObject* getSubstituteObject(SkPDFObject* object);
/** get the resources of substitute objects.
*/
SkTSet<SkPDFObject*>* getSubstituteList(bool firstPage);
private:
struct Rec {
Rec(SkPDFObject* object, bool onFirstPage)
@ -103,7 +92,7 @@ private:
};
// TODO(vandebo): Make this a hash if it's a performance problem.
SkTDArray<struct Rec> fCatalog;
SkTDArray<Rec> fCatalog;
// TODO(arthurhsu): Make this a hash if it's a performance problem.
SkTDArray<SubstituteMapping> fSubstituteMap;
@ -119,7 +108,7 @@ private:
SkPDFDocument::Flags fDocumentFlags;
int findObjectIndex(SkPDFObject* obj) const;
int findObjectIndex(SkPDFObject* obj);
int assignObjNum(SkPDFObject* obj);
};

View File

@ -82,33 +82,6 @@ SkPDFDocument::~SkPDFDocument() {
SkDELETE(fOtherPageResources);
}
namespace {
class Streamer {
public:
Streamer(SkPDFCatalog* cat, SkWStream* out)
: fCat(cat), fOut(out), fBaseOffset(SkToOffT(out->bytesWritten())) {
}
void stream(SkPDFObject* object) {
fCat->setFileOffset(object, this->offset());
SkPDFObject* realObject = fCat->getSubstituteObject(object);
fCat->emitObjectNumber(fOut, realObject);
fOut->writeText(" obj\n");
realObject->emitObject(fOut, fCat);
fOut->writeText("\nendobj\n");
}
off_t offset() {
return SkToOffT(fOut->bytesWritten()) - fBaseOffset;
}
private:
SkPDFCatalog* const fCat;
SkWStream* const fOut;
const off_t fBaseOffset;
};
} // namespace
bool SkPDFDocument::emitPDF(SkWStream* stream) {
if (fPages.isEmpty()) {
return false;
@ -187,52 +160,29 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) {
perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes);
}
Streamer out(fCatalog, stream);
SkTSet<SkPDFObject*> resourceSet;
if (resourceSet.add(fDocCatalog)) {
fDocCatalog->addResources(&resourceSet, fCatalog);
}
off_t baseOffset = SkToOffT(stream->bytesWritten());
emitHeader(stream);
out.stream(fDocCatalog);
out.stream(fPages[0]);
out.stream(fPages[0]->getContentStream());
for (int i = 0; i < fFirstPageResources->count(); i++) {
out.stream((*fFirstPageResources)[i]);
for (int i = 0; i < resourceSet.count(); ++i) {
SkPDFObject* object = resourceSet[i];
fCatalog->setFileOffset(object,
SkToOffT(stream->bytesWritten()) - baseOffset);
SkASSERT(object == fCatalog->getSubstituteObject(object));
stream->writeDecAsText(fCatalog->getObjectNumber(object));
stream->writeText(" 0 obj\n"); // Generation number is always 0.
object->emitObject(stream, fCatalog);
stream->writeText("\nendobj\n");
}
SkTSet<SkPDFObject*>* firstPageSubstituteResources =
fCatalog->getSubstituteList(true);
for (int i = 0; i < firstPageSubstituteResources->count(); ++i) {
out.stream((*firstPageSubstituteResources)[i]);
}
// TODO(vandebo): Support linearized format
// if (fPages.size() > 1) {
// // TODO(vandebo): Save the file offset for the first page xref table.
// fCatalog->emitXrefTable(stream, true);
// }
for (int i = 0; i < fPageTree.count(); i++) {
out.stream(fPageTree[i]);
}
for (int i = 1; i < fPages.count(); i++) {
out.stream(fPages[i]->getContentStream());
}
for (int i = 0; i < fOtherPageResources->count(); i++) {
out.stream((*fOtherPageResources)[i]);
}
SkTSet<SkPDFObject*>* otherSubstituteResources =
fCatalog->getSubstituteList(false);
for (int i = 0; i < otherSubstituteResources->count(); ++i) {
out.stream((*otherSubstituteResources)[i]);
}
fXRefFileOffset = out.offset();
fXRefFileOffset = SkToOffT(stream->bytesWritten()) - baseOffset;
int64_t objCount = fCatalog->emitXrefTable(stream, fPages.count() > 1);
emitFooter(stream, objCount);
return true;
}
// TODO(halcanary): remove this method, since it is unused.
bool SkPDFDocument::setPage(int pageNumber, SkPDFDevice* pdfDevice) {
if (!fPageTree.isEmpty()) {
return false;
@ -266,6 +216,7 @@ bool SkPDFDocument::appendPage(SkPDFDevice* pdfDevice) {
}
// Deprecated.
// TODO(halcanary): remove
void SkPDFDocument::getCountOfFontTypes(
int counts[SkAdvancedTypefaceMetrics::kOther_Font + 2]) const {
sk_bzero(counts, sizeof(int) *
@ -290,6 +241,7 @@ void SkPDFDocument::getCountOfFontTypes(
counts[SkAdvancedTypefaceMetrics::kOther_Font + 1] = notEmbeddable;
}
// TODO(halcanary): expose notEmbeddableCount in SkDocument
void SkPDFDocument::getCountOfFontTypes(
int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
int* notSubsettableCount,
@ -334,6 +286,7 @@ void SkPDFDocument::emitHeader(SkWStream* stream) {
stream->writeText("\n");
}
//TODO(halcanary): remove this function
size_t SkPDFDocument::headerSize() {
SkDynamicMemoryWStream buffer;
emitHeader(&buffer);

View File

@ -746,12 +746,6 @@ SkPDFFont::~SkPDFFont() {
SkAutoMutexAcquire lock(SkPDFCanon::GetFontMutex());
SkPDFCanon::GetCanon().removeFont(this);
}
fResources.unrefAll();
}
void SkPDFFont::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects) {
GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
}
SkTypeface* SkPDFFont::typeface() {
@ -940,12 +934,6 @@ void SkPDFFont::setLastGlyphID(uint16_t glyphID) {
fLastGlyphID = glyphID;
}
void SkPDFFont::addResource(SkPDFObject* object) {
SkASSERT(object != NULL);
fResources.push(object);
object->ref();
}
SkPDFDict* SkPDFFont::getFontDescriptor() {
return fDescriptor.get();
}
@ -1000,7 +988,6 @@ void SkPDFFont::populateToUnicodeTable(const SkPDFGlyphSet* subset) {
generate_tounicode_cmap(fFontInfo->fGlyphToUnicode, subset,
multiByteGlyphs(), firstGlyphID(),
lastGlyphID()));
addResource(pdfCmap.get());
insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref();
}
@ -1042,7 +1029,6 @@ bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset) {
SkAutoTUnref<SkPDFCIDFont> newCIDFont(
new SkPDFCIDFont(fontInfo(), typeface(), subset));
addResource(newCIDFont.get());
SkAutoTUnref<SkPDFArray> descendantFonts(new SkPDFArray());
descendantFonts->append(new SkPDFObjRef(newCIDFont.get()))->unref();
insert("DescendantFonts", descendantFonts.get());
@ -1069,7 +1055,6 @@ bool SkPDFCIDFont::addFontDescriptor(int16_t defaultWidth,
const SkTDArray<uint32_t>* subset) {
SkAutoTUnref<SkPDFDict> descriptor(new SkPDFDict("FontDescriptor"));
setFontDescriptor(descriptor.get());
addResource(descriptor.get());
insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
if (!addCommonFontDescriptorEntries(defaultWidth)) {
return false;
@ -1098,7 +1083,6 @@ bool SkPDFCIDFont::addFontDescriptor(int16_t defaultWidth,
}
SkASSERT(fontSize);
SkASSERT(fontStream.get());
addResource(fontStream.get());
fontStream->insertInt("Length1", fontSize);
descriptor->insert("FontFile2",
@ -1111,7 +1095,6 @@ bool SkPDFCIDFont::addFontDescriptor(int16_t defaultWidth,
SkAutoTDelete<SkStream> fontData(typeface()->openStream(&ttcIndex));
SkAutoTUnref<SkPDFStream> fontStream(
new SkPDFStream(fontData.get()));
addResource(fontStream.get());
if (getType() == SkAdvancedTypefaceMetrics::kCFF_Font) {
fontStream->insertName("Subtype", "Type1C");
@ -1226,7 +1209,6 @@ SkPDFType1Font::~SkPDFType1Font() {}
bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) {
if (getFontDescriptor() != NULL) {
SkPDFDict* descriptor = getFontDescriptor();
addResource(descriptor);
insert("FontDescriptor", new SkPDFObjRef(descriptor))->unref();
return true;
}
@ -1246,7 +1228,6 @@ bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) {
}
if (canEmbed()) {
SkAutoTUnref<SkPDFStream> fontStream(new SkPDFStream(fontData.get()));
addResource(fontStream.get());
fontStream->insertInt("Length1", header);
fontStream->insertInt("Length2", data);
fontStream->insertInt("Length3", trailer);
@ -1254,7 +1235,6 @@ bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) {
new SkPDFObjRef(fontStream.get()))->unref();
}
addResource(descriptor.get());
insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
return addCommonFontDescriptorEntries(defaultWidth);
@ -1410,7 +1390,6 @@ bool SkPDFType3Font::populate(uint16_t glyphID) {
SkAutoTUnref<SkPDFStream> glyphDescription(
new SkPDFStream(glyphStream.get()));
addResource(glyphDescription.get());
charProcs->insert(characterName.c_str(),
new SkPDFObjRef(glyphDescription.get()))->unref();
}

View File

@ -82,9 +82,6 @@ class SkPDFFont : public SkPDFDict {
public:
virtual ~SkPDFFont();
virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects);
/** Returns the typeface represented by this class. Returns NULL for the
* default typeface.
*/
@ -162,9 +159,6 @@ protected:
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);
@ -196,7 +190,6 @@ private:
uint16_t fFirstGlyphID;
uint16_t fLastGlyphID;
SkAutoTUnref<const SkAdvancedTypefaceMetrics> fFontInfo;
SkTDArray<SkPDFObject*> fResources;
SkAutoTUnref<SkPDFDict> fDescriptor;
SkAdvancedTypefaceMetrics::FontType fFontType;

View File

@ -21,9 +21,7 @@ SkPDFFormXObject::SkPDFFormXObject(SkPDFDevice* device) {
// We don't want to keep around device because we'd have two copies
// of content, so reference or copy everything we need (content and
// resources).
SkTSet<SkPDFObject*> emptySet;
SkPDFResourceDict* resourceDict = device->getResourceDict();
resourceDict->getReferencedResources(emptySet, &fResources, false);
SkAutoTDelete<SkStream> content(device->content());
setData(content.get());
@ -50,9 +48,6 @@ SkPDFFormXObject::SkPDFFormXObject(SkPDFDevice* device) {
*/
SkPDFFormXObject::SkPDFFormXObject(SkStream* content, SkRect bbox,
SkPDFResourceDict* resourceDict) {
SkTSet<SkPDFObject*> emptySet;
resourceDict->getReferencedResources(emptySet, &fResources, false);
setData(content);
SkAutoTUnref<SkPDFArray> bboxArray(SkPDFUtils::RectToArray(bbox));
@ -82,14 +77,4 @@ void SkPDFFormXObject::init(const char* colorSpace,
insert("Group", group.get());
}
SkPDFFormXObject::~SkPDFFormXObject() {
fResources.unrefAll();
}
void SkPDFFormXObject::getResources(
const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects) {
GetResourcesHelper(&fResources.toArray(),
knownResourceObjects,
newResourceObjects);
}
SkPDFFormXObject::~SkPDFFormXObject() {}

View File

@ -47,15 +47,9 @@ public:
SkPDFResourceDict* resourceDict);
virtual ~SkPDFFormXObject();
// The SkPDFObject interface.
virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects);
private:
void init(const char* colorSpace,
SkPDFDict* resourceDict, SkPDFArray* bbox);
SkTSet<SkPDFObject*> fResources;
};
#endif

View File

@ -117,13 +117,6 @@ SkPDFGraphicState::~SkPDFGraphicState() {
if (!fSMask) {
SkPDFCanon::GetCanon().removeGraphicState(this);
}
fResources.unrefAll();
}
void SkPDFGraphicState::getResources(
const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects) {
GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
}
void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
@ -173,11 +166,6 @@ template <typename T> void unref(T* ptr) { ptr->unref(); }
SK_DECLARE_STATIC_LAZY_PTR(SkPDFObject, invertFunction,
create_invert_function, unref<SkPDFObject>);
// static
SkPDFObject* SkPDFGraphicState::GetInvertFunction() {
return invertFunction.get();
}
// static
SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
SkPDFFormXObject* sMask, bool invert, SkPDFSMaskMode sMaskMode) {
@ -196,14 +184,9 @@ SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
result->fSMask = true;
result->insertName("Type", "ExtGState");
result->insert("SMask", sMaskDict.get());
result->fResources.push(sMask);
sMask->ref();
if (invert) {
SkPDFObject* invertFunction = GetInvertFunction();
result->fResources.push(invertFunction);
invertFunction->ref();
sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
sMaskDict->insert("TR", new SkPDFObjRef(invertFunction.get()))->unref();
}
return result;

View File

@ -36,9 +36,6 @@ public:
virtual ~SkPDFGraphicState();
virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects);
// Override emitObject so that we can populate the dictionary on
// demand.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog);
@ -78,7 +75,6 @@ public:
private:
const SkPaint fPaint;
SkTDArray<SkPDFObject*> fResources;
bool fPopulated;
bool fSMask;
@ -87,8 +83,6 @@ private:
void populateDict();
static SkPDFObject* GetInvertFunction();
typedef SkPDFDict INHERITED;
};

View File

@ -504,27 +504,12 @@ SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
SkAutoTUnref<SkPDFImage> mask(
SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap,
true, srcRect, NULL)));
image->addSMask(mask);
image->insert("SMask", new SkPDFObjRef(mask))->unref();
}
return image;
}
SkPDFImage::~SkPDFImage() {
fResources.unrefAll();
}
SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) {
fResources.push(mask);
mask->ref();
insert("SMask", new SkPDFObjRef(mask))->unref();
return mask;
}
void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects) {
GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
}
SkPDFImage::~SkPDFImage() {}
SkPDFImage::SkPDFImage(SkStream* stream,
const SkBitmap& bitmap,
@ -673,8 +658,6 @@ class PDFJPEGImage : public SkPDFObject {
public:
PDFJPEGImage(SkData* data, int width, int height)
: fData(SkRef(data)), fWidth(width), fHeight(height) {}
virtual void getResources(const SkTSet<SkPDFObject*>&,
SkTSet<SkPDFObject*>*) SK_OVERRIDE {}
virtual void emitObject(
SkWStream* stream,
SkPDFCatalog* catalog, bool indirect) SK_OVERRIDE {

View File

@ -54,20 +54,10 @@ public:
virtual ~SkPDFImage();
/** Add a Soft Mask (alpha or shape channel) to the image. Refs mask.
* @param mask A gray scale image representing the mask.
* @return The mask argument is returned.
*/
SkPDFImage* addSMask(SkPDFImage* mask);
bool isEmpty() {
return fSrcRect.isEmpty();
}
// The SkPDFObject interface.
virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects);
private:
SkBitmap fBitmap;
bool fIsAlpha;
@ -75,8 +65,6 @@ private:
SkPicture::EncodeBitmap fEncoder;
bool fStreamValid;
SkTDArray<SkPDFObject*> fResources;
/** Create a PDF image XObject. Entries for the image properties are
* automatically added to the stream dictionary.
* @param stream The image stream. May be NULL. Otherwise, this

View File

@ -24,9 +24,8 @@ SkPDFPage::~SkPDFPage() {}
void SkPDFPage::finalizePage(SkPDFCatalog* catalog, bool firstPage,
const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects) {
SkPDFResourceDict* resourceDict = fDevice->getResourceDict();
if (fContentStream.get() == NULL) {
insert("Resources", resourceDict);
this->insert("Resources", fDevice->getResourceDict());
SkSafeUnref(this->insert("MediaBox", fDevice->copyMediaBox()));
if (!SkToBool(catalog->getDocumentFlags() &
SkPDFDocument::kNoLinks_Flags)) {
@ -41,9 +40,6 @@ void SkPDFPage::finalizePage(SkPDFCatalog* catalog, bool firstPage,
insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref();
}
catalog->addObject(fContentStream.get(), firstPage);
resourceDict->getReferencedResources(knownResourceObjects,
newResourceObjects,
true);
}
// static

View File

@ -77,27 +77,6 @@ SkPDFObject* SkPDFResourceDict::insertResourceAsReference(
return value;
}
void SkPDFResourceDict::getReferencedResources(
const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects,
bool recursive) const {
// TODO: reserve not correct if we need to recursively explore.
newResourceObjects->setReserve(newResourceObjects->count() +
fResources.count());
for (int i = 0; i < fResources.count(); i++) {
if (!knownResourceObjects.contains(fResources[i]) &&
!newResourceObjects->contains(fResources[i])) {
newResourceObjects->add(fResources[i]);
fResources[i]->ref();
if (recursive) {
fResources[i]->getResources(knownResourceObjects,
newResourceObjects);
}
}
}
}
SkString SkPDFResourceDict::getResourceName(
SkPDFResourceType type, int key) {
SkString keyString;

View File

@ -54,20 +54,6 @@ public:
SkPDFObject* insertResourceAsReference(SkPDFResourceType type, int key,
SkPDFObject* value);
/**
* Gets resources inserted into this dictionary as a reference.
*
* @param knownResourceObjects Set containing currently known resources.
* Resources in the dict and this set will not be added to the output.
* @param newResourceObjects Output set to which non-preexisting resources
* will be added.
* @param recursive Whether or not to add resources of resources.
*/
void getReferencedResources(
const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects,
bool recursive) const;
/**
* Returns the name for the resource that will be generated by the resource
* dict.

View File

@ -510,11 +510,6 @@ private:
SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
: SkPDFDict("Pattern"), fShaderState(state) {}
void SkPDFFunctionShader::getResources(const SkTSet<SkPDFObject*>& known,
SkTSet<SkPDFObject*>* newr) {
GetResourcesHelper(&fResources, known, newr);
}
SkPDFFunctionShader::~SkPDFFunctionShader() {
SkAutoMutexAcquire lock(SkPDFCanon::GetShaderMutex());
SkPDFCanon::GetCanon().removeFunctionShader(this);
@ -540,11 +535,6 @@ SkPDFAlphaFunctionShader::~SkPDFAlphaFunctionShader() {
SkPDFCanon::GetCanon().removeAlphaShader(this);
}
void SkPDFAlphaFunctionShader::getResources(const SkTSet<SkPDFObject*>& known,
SkTSet<SkPDFObject*>* newr) {
fResourceDict->getReferencedResources(known, newr, true);
}
////////////////////////////////////////////////////////////////////////////////
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state)
@ -561,11 +551,6 @@ SkPDFImageShader::~SkPDFImageShader() {
fResources.unrefAll();
}
void SkPDFImageShader::getResources(const SkTSet<SkPDFObject*>& known,
SkTSet<SkPDFObject*>* newr) {
GetResourcesHelper(&fResources.toArray(), known, newr);
}
////////////////////////////////////////////////////////////////////////////////
static SkPDFObject* get_pdf_shader_by_state(
@ -1129,10 +1114,6 @@ SkPDFImageShader* SkPDFImageShader::Create(
SkNEW_ARGS(SkPDFImageShader, (autoState->detach()));
imageShader->setData(content.get());
SkPDFResourceDict* resourceDict = pattern.getResourceDict();
resourceDict->getReferencedResources(imageShader->fResources,
&imageShader->fResources, false);
populate_tiling_pattern_dict(imageShader, patternBBox,
pattern.getResourceDict(), finalMatrix);

View File

@ -55,8 +55,6 @@ public:
static SkPDFFunctionShader* Create(SkAutoTDelete<SkPDFShader::State>*);
virtual ~SkPDFFunctionShader();
bool equals(const SkPDFShader::State&) const;
void getResources(const SkTSet<SkPDFObject*>&,
SkTSet<SkPDFObject*>*) SK_OVERRIDE;
private:
SkAutoTDelete<const SkPDFShader::State> fShaderState;
@ -74,8 +72,6 @@ class SkPDFAlphaFunctionShader : public SkPDFStream {
public:
static SkPDFAlphaFunctionShader* Create(SkAutoTDelete<SkPDFShader::State>*);
virtual ~SkPDFAlphaFunctionShader();
void getResources(const SkTSet<SkPDFObject*>&,
SkTSet<SkPDFObject*>*) SK_OVERRIDE;
bool equals(const SkPDFShader::State&) const;
private:
@ -89,8 +85,6 @@ class SkPDFImageShader : public SkPDFStream {
public:
static SkPDFImageShader* Create(SkAutoTDelete<SkPDFShader::State>*);
virtual ~SkPDFImageShader();
void getResources(const SkTSet<SkPDFObject*>&,
SkTSet<SkPDFObject*>*) SK_OVERRIDE;
bool equals(const SkPDFShader::State&) const;
private:

View File

@ -17,36 +17,6 @@
#define SNPRINTF snprintf
#endif
///////////////////////////////////////////////////////////////////////////////
void SkPDFObject::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects) {}
void SkPDFObject::AddResourceHelper(SkPDFObject* resource,
SkTDArray<SkPDFObject*>* list) {
list->push(resource);
resource->ref();
}
void SkPDFObject::GetResourcesHelper(
const SkTDArray<SkPDFObject*>* resources,
const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects) {
if (resources->count()) {
newResourceObjects->setReserve(
newResourceObjects->count() + resources->count());
for (int i = 0; i < resources->count(); i++) {
if (!knownResourceObjects.contains((*resources)[i]) &&
!newResourceObjects->contains((*resources)[i])) {
newResourceObjects->add((*resources)[i]);
(*resources)[i]->ref();
(*resources)[i]->getResources(knownResourceObjects,
newResourceObjects);
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {
@ -56,8 +26,17 @@ SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {
SkPDFObjRef::~SkPDFObjRef() {}
void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
catalog->emitObjectNumber(stream, fObj.get());
stream->writeText(" R");
stream->writeDecAsText(catalog->getObjectNumber(fObj.get()));
stream->writeText(" 0 R"); // Generation number is always 0.
}
void SkPDFObjRef::addResources(SkTSet<SkPDFObject*>* resourceSet,
SkPDFCatalog* catalog) const {
SkPDFObject* obj = catalog->getSubstituteObject(fObj);
SkASSERT(obj);
if (resourceSet->add(obj)) {
obj->addResources(resourceSet, catalog);
}
}
////////////////////////////////////////////////////////////////////////////////
@ -277,6 +256,14 @@ void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
stream->writeText("]");
}
void SkPDFArray::addResources(SkTSet<SkPDFObject*>* resourceSet,
SkPDFCatalog* catalog) const {
for (int i = 0; i < fValue.count(); i++) {
catalog->getSubstituteObject(fValue[i])
->addResources(resourceSet, catalog);
}
}
void SkPDFArray::reserve(int length) {
SkASSERT(length <= kMaxLen);
fValue.setReserve(length);
@ -347,6 +334,17 @@ void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
stream->writeText(">>");
}
void SkPDFDict::addResources(SkTSet<SkPDFObject*>* resourceSet,
SkPDFCatalog* catalog) const {
for (int i = 0; i < fValue.count(); i++) {
SkASSERT(fValue[i].key);
SkASSERT(fValue[i].value);
fValue[i].key->addResources(resourceSet, catalog);
catalog->getSubstituteObject(fValue[i].value)
->addResources(resourceSet, catalog);
}
}
SkPDFObject* SkPDFDict::append(SkPDFName* key, SkPDFObject* value) {
SkASSERT(key);
SkASSERT(value);

View File

@ -21,6 +21,8 @@
class SkPDFCatalog;
class SkWStream;
class SkPDFObject;
/** \class SkPDFObject
A PDF Object is the base class for primitive elements in a PDF file. A
@ -36,41 +38,17 @@ public:
* @param catalog The object catalog to use.
* @param stream The writable output stream to send the output to.
*/
// TODO(halcanary): make this method const
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) = 0;
/** For non-primitive objects (i.e. objects defined outside this file),
* this method will add to newResourceObjects any objects that this method
* depends on, but not already in knownResourceObjects. This operates
* recursively so if this object depends on another object and that object
* depends on two more, all three objects will be added.
/**
* Adds all transitive dependencies of this object to resourceSet.
*
* @param knownResourceObjects The set of resources to be ignored.
* @param newResourceObjects The set to append dependant resources to.
* @param catalog Implementations should respect the catalog's
* object substitution map.
*/
virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects);
/** Static helper function to add a resource to a list. The list takes
* a reference.
* @param resource The resource to add.
* @param list The list to add the resource to.
*/
static void AddResourceHelper(SkPDFObject* resource,
SkTDArray<SkPDFObject*>* list);
/** Static helper function to copy and reference the resources (and all
* their subresources) into a new list.
* @param resources The resource list.
* @param newResourceObjects All the resource objects (recursively) used on
* the page are added to this array. This gives
* the caller a chance to deduplicate resources
* across pages.
* @param knownResourceObjects The set of resources to be ignored.
*/
static void GetResourcesHelper(
const SkTDArray<SkPDFObject*>* resources,
const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects);
virtual void addResources(SkTSet<SkPDFObject*>* resourceSet,
SkPDFCatalog* catalog) const {}
private:
typedef SkRefCnt INHERITED;
@ -92,6 +70,7 @@ public:
// The SkPDFObject interface.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
virtual void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE;
private:
SkAutoTUnref<SkPDFObject> fObj;
@ -255,6 +234,7 @@ public:
// The SkPDFObject interface.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
virtual void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE;
/** The size of the array.
*/
@ -326,6 +306,7 @@ public:
// The SkPDFObject interface.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
virtual void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE;
/** The size of the dictionary.
*/

View File

@ -75,8 +75,8 @@ static void emit_object(SkPDFObject* object,
bool indirect) {
SkPDFObject* realObject = catalog->getSubstituteObject(object);
if (indirect) {
catalog->emitObjectNumber(stream, realObject);
stream->writeText(" obj\n");
stream->writeDecAsText(catalog->getObjectNumber(object));
stream->writeText(" 0 obj\n"); // Generation number is always 0.
realObject->emitObject(stream, catalog);
stream->writeText("\nendobj\n");
} else {
@ -201,18 +201,10 @@ static void TestCatalog(skiatest::Reporter* reporter) {
catalog.addObject(int2.get(), false);
catalog.addObject(int3.get(), false);
REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int3.get()) == 3);
SkDynamicMemoryWStream buffer;
catalog.emitObjectNumber(&buffer, int1.get());
catalog.emitObjectNumber(&buffer, int2.get());
catalog.emitObjectNumber(&buffer, int3.get());
catalog.emitObjectNumber(&buffer, int1Again.get());
char expectedResult[] = "1 02 03 01 0";
REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
strlen(expectedResult)));
REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1);
REPORTER_ASSERT(reporter, catalog.getObjectNumber(int2.get()) == 2);
REPORTER_ASSERT(reporter, catalog.getObjectNumber(int3.get()) == 3);
REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1Again.get()) == 1);
}
static void TestObjectRef(skiatest::Reporter* reporter) {
@ -223,8 +215,8 @@ static void TestObjectRef(skiatest::Reporter* reporter) {
SkPDFCatalog catalog((SkPDFDocument::Flags)0);
catalog.addObject(int1.get(), false);
catalog.addObject(int2.get(), false);
REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1);
REPORTER_ASSERT(reporter, catalog.getObjectNumber(int2.get()) == 2);
char expectedResult[] = "2 0 R";
SkDynamicMemoryWStream buffer;
@ -237,38 +229,16 @@ static void TestObjectRef(skiatest::Reporter* reporter) {
static void TestSubstitute(skiatest::Reporter* reporter) {
SkAutoTUnref<SkPDFTestDict> proxy(new SkPDFTestDict());
SkAutoTUnref<SkPDFTestDict> stub(new SkPDFTestDict());
SkAutoTUnref<SkPDFInt> int33(new SkPDFInt(33));
SkAutoTUnref<SkPDFDict> stubResource(new SkPDFDict());
SkAutoTUnref<SkPDFInt> int44(new SkPDFInt(44));
stub->insert("Value", int33.get());
stubResource->insert("InnerValue", int44.get());
stub->addResource(stubResource.get());
proxy->insert("Value", new SkPDFInt(33))->unref();
stub->insert("Value", new SkPDFInt(44))->unref();
SkPDFCatalog catalog((SkPDFDocument::Flags)0);
catalog.addObject(proxy.get(), false);
catalog.setSubstitute(proxy.get(), stub.get());
SkDynamicMemoryWStream buffer;
emit_object(proxy, &buffer, &catalog, false);
SkTSet<SkPDFObject*>* substituteResources =
catalog.getSubstituteList(false);
for (int i = 0; i < substituteResources->count(); ++i) {
emit_object((*substituteResources)[i], &buffer, &catalog, true);
}
char objectResult[] = "2 0 obj\n<</Value 33\n>>\nendobj\n";
catalog.setFileOffset(proxy.get(), 0);
size_t outputSize = get_output_size(
catalog.getSubstituteObject(proxy.get()), &catalog, true);
REPORTER_ASSERT(reporter, outputSize == strlen(objectResult));
char expectedResult[] =
"<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n";
REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
buffer.getOffset()));
REPORTER_ASSERT(reporter, stub.get() == catalog.getSubstituteObject(proxy));
REPORTER_ASSERT(reporter, proxy.get() != catalog.getSubstituteObject(stub));
}
// Create a bitmap that would be very eficiently compressed in a ZIP.