diff options
author | vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2011-07-20 17:39:01 +0000 |
---|---|---|
committer | vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2011-07-20 17:39:01 +0000 |
commit | 421d6443fbd3a913dfa32b6492c4a2969bc6314b (patch) | |
tree | 5c02a30275d78adf764e0c91437bada0c6dcbd33 | |
parent | f6c3ebdeb135dcdb9af225bd7af77f1fe1f92787 (diff) |
[PDF] Make stream compression optional on a per device basis.
There are a lot of small pieces to make this change work:
- SkPDFDocument (and SkPDFCatalog) take flags to disable compression (and font embedding - not implemented yet, can disable font subsetting for now).
- SkPDFStream now defers compression until the size/emit step.
- Classes that *had* a stream (because they didn't know the stream size at construction time) now *are* streams to make the substitution work correctly.
- The SkPDFShader implementation got pulled apart into two classes, one that is a SkPDFDict, and one that is a SkPDFStream (making the common ancestor SkPDFObject).
- Added helper methods in SkPDFObject for children that have simple resource lists.
- Added an iterator to SkPDFDict so that a substitute SkPDFStream can get a copy of the stream dictionary.
- Change SkPDFDocument to have a pointer to an SkPDFCatalog to remove a new circular header reference.
Review URL: http://codereview.appspot.com/4700045
git-svn-id: http://skia.googlecode.com/svn/trunk@1911 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | include/pdf/SkPDFCatalog.h | 9 | ||||
-rw-r--r-- | include/pdf/SkPDFDevice.h | 2 | ||||
-rw-r--r-- | include/pdf/SkPDFDocument.h | 13 | ||||
-rw-r--r-- | include/pdf/SkPDFFormXObject.h | 20 | ||||
-rw-r--r-- | include/pdf/SkPDFImage.h | 20 | ||||
-rw-r--r-- | include/pdf/SkPDFShader.h | 58 | ||||
-rw-r--r-- | include/pdf/SkPDFStream.h | 36 | ||||
-rw-r--r-- | include/pdf/SkPDFTypes.h | 34 | ||||
-rw-r--r-- | src/core/SkFlate.cpp | 4 | ||||
-rw-r--r-- | src/pdf/SkPDFCatalog.cpp | 5 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 2 | ||||
-rw-r--r-- | src/pdf/SkPDFDocument.cpp | 74 | ||||
-rwxr-xr-x | src/pdf/SkPDFFont.cpp | 7 | ||||
-rw-r--r-- | src/pdf/SkPDFFormXObject.cpp | 32 | ||||
-rw-r--r-- | src/pdf/SkPDFGraphicState.cpp | 7 | ||||
-rw-r--r-- | src/pdf/SkPDFImage.cpp | 36 | ||||
-rw-r--r-- | src/pdf/SkPDFShader.cpp | 188 | ||||
-rw-r--r-- | src/pdf/SkPDFStream.cpp | 106 | ||||
-rw-r--r-- | src/pdf/SkPDFTypes.cpp | 44 | ||||
-rw-r--r-- | tests/PDFPrimitivesTest.cpp | 141 |
20 files changed, 483 insertions, 355 deletions
diff --git a/include/pdf/SkPDFCatalog.h b/include/pdf/SkPDFCatalog.h index 4f8f8c1ece..3a644e6a5a 100644 --- a/include/pdf/SkPDFCatalog.h +++ b/include/pdf/SkPDFCatalog.h @@ -19,6 +19,7 @@ #include <sys/types.h> +#include "SkPDFDocument.h" #include "SkPDFTypes.h" #include "SkRefCnt.h" #include "SkTDArray.h" @@ -32,7 +33,7 @@ class SK_API SkPDFCatalog { public: /** Create a PDF catalog. */ - SkPDFCatalog(); + explicit SkPDFCatalog(SkPDFDocument::Flags flags); ~SkPDFCatalog(); /** Add the passed object to the catalog. Refs obj. @@ -62,6 +63,10 @@ public: */ size_t getObjectNumberSize(SkPDFObject* obj); + /** Return the document flags in effect for this catalog/document. + */ + SkPDFDocument::Flags getDocumentFlags() const { return fDocumentFlags; } + /** Output the cross reference table for objects in the catalog. * Returns the total number of objects. * @param stream The writable output stream to send the output to. @@ -127,6 +132,8 @@ private: // Next object number to assign on the first page. uint32_t fNextFirstPageObjNum; + SkPDFDocument::Flags fDocumentFlags; + int findObjectIndex(SkPDFObject* obj) const; int assignObjNum(SkPDFObject* obj); diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h index 1677546ef3..4c526d2291 100644 --- a/include/pdf/SkPDFDevice.h +++ b/include/pdf/SkPDFDevice.h @@ -170,7 +170,7 @@ private: SkTDArray<SkPDFGraphicState*> fGraphicStateResources; SkTDArray<SkPDFObject*> fXObjectResources; SkTDArray<SkPDFFont*> fFontResources; - SkTDArray<SkPDFShader*> fShaderResources; + SkTDArray<SkPDFObject*> fShaderResources; SkTScopedPtr<ContentEntry> fContentEntries; ContentEntry* fLastContentEntry; diff --git a/include/pdf/SkPDFDocument.h b/include/pdf/SkPDFDocument.h index 5fb7c95405..1a5d8354e9 100644 --- a/include/pdf/SkPDFDocument.h +++ b/include/pdf/SkPDFDocument.h @@ -17,11 +17,12 @@ #ifndef SkPDFDocument_DEFINED #define SkPDFDocument_DEFINED -#include "SkPDFCatalog.h" #include "SkPDFTypes.h" #include "SkRefCnt.h" #include "SkTDArray.h" +#include "SkTScopedPtr.h" +class SkPDFCatalog; class SkPDFDevice; class SkPDFPage; class SkWSteam; @@ -32,9 +33,15 @@ class SkWSteam; */ class SkPDFDocument { public: + enum Flags { + kNoCompression_Flag = 0x01, //!< mask disable stream compression. + kNoEmbedding_Flag = 0x02, //!< mask do not embed fonts. + + kDraftMode_Flags = 0x03, + }; /** Create a PDF document. */ - SK_API SkPDFDocument(); + explicit SK_API SkPDFDocument(Flags flags = (Flags)0); SK_API ~SkPDFDocument(); /** Output the PDF to the passed stream. It is an error to call this (it @@ -67,7 +74,7 @@ public: SK_API const SkTDArray<SkPDFPage*>& getPages(); private: - SkPDFCatalog fCatalog; + SkTScopedPtr<SkPDFCatalog> fCatalog; int64_t fXRefFileOffset; SkTDArray<SkPDFPage*> fPages; diff --git a/include/pdf/SkPDFFormXObject.h b/include/pdf/SkPDFFormXObject.h index 41719f0820..1e0ad4604a 100644 --- a/include/pdf/SkPDFFormXObject.h +++ b/include/pdf/SkPDFFormXObject.h @@ -36,7 +36,7 @@ class SkPDFCatalog; // The caller could keep track of the form XObjects it creates and // canonicalize them, but the Skia API doesn't provide enough context to // automatically do it (trivially). -class SkPDFFormXObject : public SkPDFObject { +class SkPDFFormXObject : public SkPDFStream { public: /** Create a PDF form XObject. Entries for the dictionary entries are * automatically added. @@ -46,27 +46,9 @@ public: virtual ~SkPDFFormXObject(); // The SkPDFObject interface. - virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, - bool indirect); - virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); virtual void getResources(SkTDArray<SkPDFObject*>* resourceList); - /** Add the value to the stream dictionary with the given key. Refs value. - * @param key The key for this dictionary entry. - * @param value The value for this dictionary entry. - * @return The value argument is returned. - */ - SkPDFObject* insert(SkPDFName* key, SkPDFObject* value); - - /** Add the value to the stream dictionary with the given key. Refs value. - * @param key The text of the key for this dictionary entry. - * @param value The value for this dictionary entry. - * @return The value argument is returned. - */ - SkPDFObject* insert(const char key[], SkPDFObject* value); - private: - SkRefPtr<SkPDFStream> fStream; SkTDArray<SkPDFObject*> fResources; }; diff --git a/include/pdf/SkPDFImage.h b/include/pdf/SkPDFImage.h index 945ff9e541..a08cdb5a7d 100644 --- a/include/pdf/SkPDFImage.h +++ b/include/pdf/SkPDFImage.h @@ -34,7 +34,7 @@ struct SkIRect; // We could play the same trick here as is done in SkPDFGraphicState, storing // a copy of the Bitmap object (not the pixels), the pixel generation number, // and settings used from the paint to canonicalize image objects. -class SkPDFImage : public SkPDFObject { +class SkPDFImage : public SkPDFStream { public: /** Create a new Image XObject to represent the passed bitmap. * @param bitmap The image to encode. @@ -56,13 +56,9 @@ public: SkPDFImage* addSMask(SkPDFImage* mask); // The SkPDFObject interface. - virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, - bool indirect); - virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); virtual void getResources(SkTDArray<SkPDFObject*>* resourceList); private: - SkRefPtr<SkPDFStream> fStream; SkTDArray<SkPDFObject*> fResources; /** Create a PDF image XObject. Entries for the image properties are @@ -76,20 +72,6 @@ private: */ SkPDFImage(SkStream* imageData, const SkBitmap& bitmap, const SkIRect& srcRect, bool alpha, const SkPaint& paint); - - /** Add the value to the stream dictionary with the given key. Refs value. - * @param key The key for this dictionary entry. - * @param value The value for this dictionary entry. - * @return The value argument is returned. - */ - SkPDFObject* insert(SkPDFName* key, SkPDFObject* value); - - /** Add the value to the stream dictionary with the given key. Refs value. - * @param key The text of the key for this dictionary entry. - * @param value The value for this dictionary entry. - * @return The value argument is returned. - */ - SkPDFObject* insert(const char key[], SkPDFObject* value); }; #endif diff --git a/include/pdf/SkPDFShader.h b/include/pdf/SkPDFShader.h index 2974c88e7f..350569c993 100644 --- a/include/pdf/SkPDFShader.h +++ b/include/pdf/SkPDFShader.h @@ -32,16 +32,8 @@ class SkPDFCatalog; pattern color space is selected. */ -class SkPDFShader : public SkPDFObject { +class SkPDFShader { public: - virtual ~SkPDFShader(); - - // The SkPDFObject interface. - virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, - bool indirect); - virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); - virtual void getResources(SkTDArray<SkPDFObject*>* resourceList); - /** Get the PDF shader for the passed SkShader. If the SkShader is * invalid in some way, returns NULL. The reference count of * the object is incremented and it is the caller's responsibility to @@ -54,57 +46,27 @@ public: * @param surfceBBox The bounding box of the drawing surface (with matrix * already applied). */ - static SkPDFShader* GetPDFShader(const SkShader& shader, + static SkPDFObject* GetPDFShader(const SkShader& shader, const SkMatrix& matrix, const SkIRect& surfaceBBox); -private: - class State { - public: - SkShader::GradientType fType; - SkShader::GradientInfo fInfo; - SkAutoFree fColorData; - SkMatrix fCanvasTransform; - SkMatrix fShaderTransform; - SkIRect fBBox; - - SkBitmap fImage; - uint32_t fPixelGeneration; - SkShader::TileMode fImageTileModes[2]; - - explicit State(const SkShader& shader, const SkMatrix& canvasTransform, - const SkIRect& bbox); - bool operator==(const State& b) const; - }; - - SkRefPtr<SkPDFDict> fContent; - SkTDArray<SkPDFObject*> fResources; - SkAutoTDelete<const State> fState; +protected: + class State; class ShaderCanonicalEntry { public: - SkPDFShader* fPDFShader; - const State* fState; + ShaderCanonicalEntry(SkPDFObject* pdfShader, const State* state); + bool operator==(const ShaderCanonicalEntry& b) const; - bool operator==(const ShaderCanonicalEntry& b) const { - return fPDFShader == b.fPDFShader || *fState == *b.fState; - } - ShaderCanonicalEntry(SkPDFShader* pdfShader, const State* state) - : fPDFShader(pdfShader), - fState(state) { - } + SkPDFObject* fPDFShader; + const State* fState; }; // This should be made a hash table if performance is a problem. static SkTDArray<ShaderCanonicalEntry>& CanonicalShaders(); static SkMutex& CanonicalShadersMutex(); + static void RemoveShader(SkPDFObject* shader); - static SkPDFObject* RangeObject(); - - SkPDFShader(State* state); - - void doFunctionShader(); - void doImageShader(); - SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain); + SkPDFShader(); }; #endif diff --git a/include/pdf/SkPDFStream.h b/include/pdf/SkPDFStream.h index 739177f56d..263a408d72 100644 --- a/include/pdf/SkPDFStream.h +++ b/include/pdf/SkPDFStream.h @@ -34,9 +34,15 @@ public: /** Create a PDF stream. A Length entry is automatically added to the * stream dictionary. The stream may be retained (stream->ref() may be * called) so its contents must not be changed after calling this. - * @param stream The data part of the stream. + * @param data The data part of the stream. */ + explicit SkPDFStream(SkData* data); + /** Deprecated constructor. */ explicit SkPDFStream(SkStream* stream); + /** Create a PDF stream with the same content and dictionary entries + * as the passed one. + */ + explicit SkPDFStream(const SkPDFStream& pdfStream); virtual ~SkPDFStream(); // The SkPDFObject interface. @@ -44,13 +50,33 @@ public: bool indirect); virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); +protected: + /* Create a PDF stream with no data. The setData method must be called to + * set the data. + */ + SkPDFStream(); + + void setData(SkStream* stream); + private: - size_t fLength; - // Only one of the two streams will be valid. - SkRefPtr<SkStream> fPlainData; - SkDynamicMemoryWStream fCompressedData; + enum State { + kUnused_State, //!< The stream hasn't been requested yet. + kNoCompression_State, //!< The stream's been requested in an + // uncompressed form. + kCompressed_State, //!< The stream's already been compressed. + }; + // Indicates what form (or if) the stream has been requested. + State fState; + + // TODO(vandebo) Use SkData (after removing deprecated constructor). + SkRefPtr<SkStream> fData; + SkRefPtr<SkPDFStream> fSubstitute; typedef SkPDFDict INHERITED; + + // Populate the stream dictionary. This method returns false if + // fSubstitute should be used. + bool populate(SkPDFCatalog* catalog); }; #endif diff --git a/include/pdf/SkPDFTypes.h b/include/pdf/SkPDFTypes.h index c834bc2dfe..6c5a410074 100644 --- a/include/pdf/SkPDFTypes.h +++ b/include/pdf/SkPDFTypes.h @@ -73,6 +73,22 @@ public: */ size_t getIndirectOutputSize(SkPDFCatalog* catalog); + /** 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 result The list to add to. + */ + static void GetResourcesHelper(SkTDArray<SkPDFObject*>* resources, + SkTDArray<SkPDFObject*>* result); + protected: /** Subclasses must implement this method to print the object to the * PDF file. @@ -219,6 +235,8 @@ public: explicit SkPDFName(const SkString& name); virtual ~SkPDFName(); + bool operator==(const SkPDFName& b) const; + // The SkPDFObject interface. virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, bool indirect); @@ -367,13 +385,25 @@ public: void clear(); private: - static const int kMaxLen = 4095; - struct Rec { SkPDFName* key; SkPDFObject* value; }; +public: + class Iter { + public: + explicit Iter(const SkPDFDict& dict); + SkPDFName* next(SkPDFObject** value); + + private: + Rec* fIter; + Rec* fStop; + }; + +private: + static const int kMaxLen = 4095; + SkTDArray<struct Rec> fValue; }; diff --git a/src/core/SkFlate.cpp b/src/core/SkFlate.cpp index 3bae8a9334..a5a7b2209f 100644 --- a/src/core/SkFlate.cpp +++ b/src/core/SkFlate.cpp @@ -26,11 +26,7 @@ bool SkFlate::Inflate(SkStream*, SkWStream*) { return false; } // static bool SkFlate::HaveFlate() { -#ifdef SK_DEBUG - return false; -#else return true; -#endif } namespace { diff --git a/src/pdf/SkPDFCatalog.cpp b/src/pdf/SkPDFCatalog.cpp index 025c86d82f..420ce40511 100644 --- a/src/pdf/SkPDFCatalog.cpp +++ b/src/pdf/SkPDFCatalog.cpp @@ -19,10 +19,11 @@ #include "SkStream.h" #include "SkTypes.h" -SkPDFCatalog::SkPDFCatalog() +SkPDFCatalog::SkPDFCatalog(SkPDFDocument::Flags flags) : fFirstPageCount(0), fNextObjNum(1), - fNextFirstPageObjNum(0) { + fNextFirstPageObjNum(0), + fDocumentFlags(flags) { } SkPDFCatalog::~SkPDFCatalog() { diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 7833362b6c..caba8226b4 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -1394,7 +1394,7 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint( entry->fClipRegion = clipRegion; // PDF treats a shader as a color, so we only set one or the other. - SkRefPtr<SkPDFShader> pdfShader; + SkRefPtr<SkPDFObject> pdfShader; const SkShader* shader = paint.getShader(); SkColor color = paint.getColor(); if (shader) { diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp index 1cfe64df12..d60512e7ac 100644 --- a/src/pdf/SkPDFDocument.cpp +++ b/src/pdf/SkPDFDocument.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "SkPDFCatalog.h" #include "SkPDFDevice.h" #include "SkPDFDocument.h" #include "SkPDFPage.h" @@ -36,12 +37,13 @@ void addResourcesToCatalog(int firstIndex, bool firstPage, } } -SkPDFDocument::SkPDFDocument() +SkPDFDocument::SkPDFDocument(Flags flags) : fXRefFileOffset(0), fSecondPageFirstResourceIndex(0) { + fCatalog.reset(new SkPDFCatalog(flags)); fDocCatalog = new SkPDFDict("Catalog"); fDocCatalog->unref(); // SkRefPtr and new both took a reference. - fCatalog.addObject(fDocCatalog.get(), true); + fCatalog->addObject(fDocCatalog.get(), true); } SkPDFDocument::~SkPDFDocument() { @@ -68,7 +70,7 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) { // We haven't emitted the document before if fPageTree is empty. if (fPageTree.count() == 0) { SkPDFDict* pageTreeRoot; - SkPDFPage::GeneratePageTree(fPages, &fCatalog, &fPageTree, + SkPDFPage::GeneratePageTree(fPages, fCatalog.get(), &fPageTree, &pageTreeRoot); fDocCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); @@ -87,9 +89,9 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) { bool firstPage = true; for (int i = 0; i < fPages.count(); i++) { int resourceCount = fPageResources.count(); - fPages[i]->finalizePage(&fCatalog, firstPage, &fPageResources); + fPages[i]->finalizePage(fCatalog.get(), firstPage, &fPageResources); addResourcesToCatalog(resourceCount, firstPage, &fPageResources, - &fCatalog); + fCatalog.get()); if (i == 0) { firstPage = false; fSecondPageFirstResourceIndex = fPageResources.count(); @@ -98,57 +100,67 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) { // Figure out the size of things and inform the catalog of file offsets. off_t fileOffset = headerSize(); - fileOffset += fCatalog.setFileOffset(fDocCatalog.get(), fileOffset); - fileOffset += fCatalog.setFileOffset(fPages[0], fileOffset); - fileOffset += fPages[0]->getPageSize(&fCatalog, fileOffset); - for (int i = 0; i < fSecondPageFirstResourceIndex; i++) - fileOffset += fCatalog.setFileOffset(fPageResources[i], fileOffset); + fileOffset += fCatalog->setFileOffset(fDocCatalog.get(), fileOffset); + fileOffset += fCatalog->setFileOffset(fPages[0], fileOffset); + fileOffset += fPages[0]->getPageSize(fCatalog.get(), fileOffset); + for (int i = 0; i < fSecondPageFirstResourceIndex; i++) { + fileOffset += fCatalog->setFileOffset(fPageResources[i], + fileOffset); + } // Add the size of resources of substitute objects used on page 1. - fileOffset += fCatalog.setSubstituteResourcesOffsets(fileOffset, true); + fileOffset += fCatalog->setSubstituteResourcesOffsets(fileOffset, true); if (fPages.count() > 1) { // TODO(vandebo) For linearized format, save the start of the // first page xref table and calculate the size. } for (int i = 0; i < fPageTree.count(); i++) - fileOffset += fCatalog.setFileOffset(fPageTree[i], fileOffset); + fileOffset += fCatalog->setFileOffset(fPageTree[i], fileOffset); for (int i = 1; i < fPages.count(); i++) - fileOffset += fPages[i]->getPageSize(&fCatalog, fileOffset); + fileOffset += fPages[i]->getPageSize(fCatalog.get(), fileOffset); for (int i = fSecondPageFirstResourceIndex; i < fPageResources.count(); i++) - fileOffset += fCatalog.setFileOffset(fPageResources[i], fileOffset); + fileOffset += fCatalog->setFileOffset(fPageResources[i], + fileOffset); - fileOffset += fCatalog.setSubstituteResourcesOffsets(fileOffset, false); + fileOffset += fCatalog->setSubstituteResourcesOffsets(fileOffset, + false); fXRefFileOffset = fileOffset; } emitHeader(stream); - fDocCatalog->emitObject(stream, &fCatalog, true); - fPages[0]->emitObject(stream, &fCatalog, true); - fPages[0]->emitPage(stream, &fCatalog); - for (int i = 0; i < fSecondPageFirstResourceIndex; i++) - fPageResources[i]->emit(stream, &fCatalog, true); - fCatalog.emitSubstituteResources(stream, true); + fDocCatalog->emitObject(stream, fCatalog.get(), true); + fPages[0]->emitObject(stream, fCatalog.get(), true); + fPages[0]->emitPage(stream, fCatalog.get()); + for (int i = 0; i < fSecondPageFirstResourceIndex; i++) { + fPageResources[i]->emit(stream, fCatalog.get(), true); + } + fCatalog->emitSubstituteResources(stream, true); // 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); + // fCatalog->emitXrefTable(stream, true); //} - for (int i = 0; i < fPageTree.count(); i++) - fPageTree[i]->emitObject(stream, &fCatalog, true); + for (int i = 0; i < fPageTree.count(); i++) { + fPageTree[i]->emitObject(stream, fCatalog.get(), true); + } - for (int i = 1; i < fPages.count(); i++) - fPages[i]->emitPage(stream, &fCatalog); + for (int i = 1; i < fPages.count(); i++) { + fPages[i]->emitPage(stream, fCatalog.get()); + } - for (int i = fSecondPageFirstResourceIndex; i < fPageResources.count(); i++) - fPageResources[i]->emit(stream, &fCatalog, true); + for (int i = fSecondPageFirstResourceIndex; + i < fPageResources.count(); + i++) { + fPageResources[i]->emit(stream, fCatalog.get(), true); + } - fCatalog.emitSubstituteResources(stream, false); - int64_t objCount = fCatalog.emitXrefTable(stream, fPages.count() > 1); + fCatalog->emitSubstituteResources(stream, false); + int64_t objCount = fCatalog->emitXrefTable(stream, fPages.count() > 1); emitFooter(stream, objCount); return true; } @@ -217,7 +229,7 @@ void SkPDFDocument::emitFooter(SkWStream* stream, int64_t objCount) { } stream->writeText("trailer\n"); - fTrailerDict->emitObject(stream, &fCatalog, false); + fTrailerDict->emitObject(stream, fCatalog.get(), false); stream->writeText("\nstartxref\n"); stream->writeBigDecAsText(fXRefFileOffset); stream->writeText("\n%%EOF"); diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp index 4602c68e1c..be16c76936 100755 --- a/src/pdf/SkPDFFont.cpp +++ b/src/pdf/SkPDFFont.cpp @@ -435,12 +435,7 @@ SkPDFFont::~SkPDFFont() { } void SkPDFFont::getResources(SkTDArray<SkPDFObject*>* resourceList) { - resourceList->setReserve(resourceList->count() + fResources.count()); - for (int i = 0; i < fResources.count(); i++) { - resourceList->push(fResources[i]); - fResources[i]->ref(); - fResources[i]->getResources(resourceList); - } + GetResourcesHelper(&fResources, resourceList); } SkTypeface* SkPDFFont::typeface() { diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp index 40a5564847..a8a3290e22 100644 --- a/src/pdf/SkPDFFormXObject.cpp +++ b/src/pdf/SkPDFFormXObject.cpp @@ -31,8 +31,7 @@ SkPDFFormXObject::SkPDFFormXObject(SkPDFDevice* device) { SkRefPtr<SkStream> content = device->content(); content->unref(); // SkRefPtr and content() both took a reference. - fStream = new SkPDFStream(content.get()); - fStream->unref(); // SkRefPtr and new both took a reference. + setData(content.get()); insert("Type", new SkPDFName("XObject"))->unref(); insert("Subtype", new SkPDFName("Form"))->unref(); @@ -62,33 +61,6 @@ SkPDFFormXObject::~SkPDFFormXObject() { fResources.unrefAll(); } -void SkPDFFormXObject::emitObject(SkWStream* stream, SkPDFCatalog* catalog, - bool indirect) { - if (indirect) - return emitIndirectObject(stream, catalog); - - fStream->emitObject(stream, catalog, indirect); -} - -size_t SkPDFFormXObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) { - if (indirect) - return getIndirectOutputSize(catalog); - - return fStream->getOutputSize(catalog, indirect); -} - void SkPDFFormXObject::getResources(SkTDArray<SkPDFObject*>* resourceList) { - resourceList->setReserve(resourceList->count() + fResources.count()); - for (int i = 0; i < fResources.count(); i++) { - resourceList->push(fResources[i]); - fResources[i]->ref(); - } -} - -SkPDFObject* SkPDFFormXObject::insert(SkPDFName* key, SkPDFObject* value) { - return fStream->insert(key, value); -} - -SkPDFObject* SkPDFFormXObject::insert(const char key[], SkPDFObject* value) { - return fStream->insert(key, value); + GetResourcesHelper(&fResources, resourceList); } diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp index 59b2817518..0e6e23049c 100644 --- a/src/pdf/SkPDFGraphicState.cpp +++ b/src/pdf/SkPDFGraphicState.cpp @@ -67,12 +67,7 @@ SkPDFGraphicState::~SkPDFGraphicState() { } void SkPDFGraphicState::getResources(SkTDArray<SkPDFObject*>* resourceList) { - resourceList->setReserve(resourceList->count() + fResources.count()); - for (int i = 0; i < fResources.count(); i++) { - resourceList->push(fResources[i]); - fResources[i]->ref(); - fResources[i]->getResources(resourceList); - } + GetResourcesHelper(&fResources, resourceList); } void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog, diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp index be69f7f757..ebbcd11a97 100644 --- a/src/pdf/SkPDFImage.cpp +++ b/src/pdf/SkPDFImage.cpp @@ -281,38 +281,14 @@ SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { return mask; } -void SkPDFImage::emitObject(SkWStream* stream, SkPDFCatalog* catalog, - bool indirect) { - if (indirect) - return emitIndirectObject(stream, catalog); - - fStream->emitObject(stream, catalog, indirect); -} - -size_t SkPDFImage::getOutputSize(SkPDFCatalog* catalog, bool indirect) { - if (indirect) - return getIndirectOutputSize(catalog); - - return fStream->getOutputSize(catalog, indirect); -} - void SkPDFImage::getResources(SkTDArray<SkPDFObject*>* resourceList) { - if (fResources.count()) { - resourceList->setReserve(resourceList->count() + fResources.count()); - for (int i = 0; i < fResources.count(); i++) { - resourceList->push(fResources[i]); - fResources[i]->ref(); - fResources[i]->getResources(resourceList); - } - } + GetResourcesHelper(&fResources, resourceList); } SkPDFImage::SkPDFImage(SkStream* imageData, const SkBitmap& bitmap, const SkIRect& srcRect, bool doingAlpha, const SkPaint& paint) { - fStream = new SkPDFStream(imageData); - fStream->unref(); // SkRefPtr and new both took a reference. - + this->setData(imageData); SkBitmap::Config config = bitmap.getConfig(); bool alphaOnly = (config == SkBitmap::kA1_Config || config == SkBitmap::kA8_Config); @@ -372,11 +348,3 @@ SkPDFImage::SkPDFImage(SkStream* imageData, const SkBitmap& bitmap, insert("Decode", decodeValue.get()); } } - -SkPDFObject* SkPDFImage::insert(SkPDFName* key, SkPDFObject* value) { - return fStream->insert(key, value); -} - -SkPDFObject* SkPDFImage::insert(const char key[], SkPDFObject* value) { - return fStream->insert(key, value); -} diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp index 95fdb612f0..ee225173b7 100644 --- a/src/pdf/SkPDFShader.cpp +++ b/src/pdf/SkPDFShader.cpp @@ -17,6 +17,7 @@ #include "SkPDFShader.h" #include "SkCanvas.h" +#include "SkData.h" #include "SkPDFCatalog.h" #include "SkPDFDevice.h" #include "SkPDFTypes.h" @@ -286,65 +287,107 @@ static SkString sweepCode(const SkShader::GradientInfo& info) { return function; } -SkPDFShader::~SkPDFShader() { - SkAutoMutexAcquire lock(CanonicalShadersMutex()); - ShaderCanonicalEntry entry(this, fState.get()); - int index = CanonicalShaders().find(entry); - if (fContent.get()) { - SkASSERT(index >= 0); - CanonicalShaders().removeShuffle(index); +class SkPDFShader::State { +public: + SkShader::GradientType fType; + SkShader::GradientInfo fInfo; + SkAutoFree fColorData; + SkMatrix fCanvasTransform; + SkMatrix fShaderTransform; + SkIRect fBBox; + + SkBitmap fImage; + uint32_t fPixelGeneration; + SkShader::TileMode fImageTileModes[2]; + + explicit State(const SkShader& shader, const SkMatrix& canvasTransform, + const SkIRect& bbox); + bool operator==(const State& b) const; +}; + +class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader { +public: + SkPDFFunctionShader(SkPDFShader::State* state); + ~SkPDFFunctionShader() { + if (isValid()) { + RemoveShader(this); + } + fResources.unrefAll(); } - fResources.unrefAll(); -} -void SkPDFShader::emitObject(SkWStream* stream, SkPDFCatalog* catalog, - bool indirect) { - if (indirect) - return emitIndirectObject(stream, catalog); + bool isValid() { return fResources.count() > 0; } - fContent->emitObject(stream, catalog, indirect); -} + void getResources(SkTDArray<SkPDFObject*>* resourceList) { + GetResourcesHelper(&fResources, resourceList); + } -size_t SkPDFShader::getOutputSize(SkPDFCatalog* catalog, bool indirect) { - if (indirect) - return getIndirectOutputSize(catalog); +private: + static SkPDFObject* RangeObject(); - return fContent->getOutputSize(catalog, indirect); -} + SkTDArray<SkPDFObject*> fResources; + SkAutoTDelete<const SkPDFShader::State> fState; -void SkPDFShader::getResources(SkTDArray<SkPDFObject*>* resourceList) { - resourceList->setReserve(resourceList->count() + fResources.count()); - for (int i = 0; i < fResources.count(); i++) { - resourceList->push(fResources[i]); - fResources[i]->ref(); + SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain); +}; + +class SkPDFImageShader : public SkPDFStream, public SkPDFShader { +public: + SkPDFImageShader(SkPDFShader::State* state); + ~SkPDFImageShader() { + RemoveShader(this); + fResources.unrefAll(); + } + + void getResources(SkTDArray<SkPDFObject*>* resourceList) { + GetResourcesHelper(&fResources, resourceList); } + +private: + SkTDArray<SkPDFObject*> fResources; + SkAutoTDelete<const SkPDFShader::State> fState; +}; + +SkPDFShader::SkPDFShader() {} + +// static +void SkPDFShader::RemoveShader(SkPDFObject* shader) { + SkAutoMutexAcquire lock(CanonicalShadersMutex()); + ShaderCanonicalEntry entry(shader, NULL); + int index = CanonicalShaders().find(entry); + SkASSERT(index >= 0); + CanonicalShaders().removeShuffle(index); } // static -SkPDFShader* SkPDFShader::GetPDFShader(const SkShader& shader, +SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, const SkMatrix& matrix, const SkIRect& surfaceBBox) { - SkRefPtr<SkPDFShader> pdfShader; + SkPDFObject* result; SkAutoMutexAcquire lock(CanonicalShadersMutex()); SkAutoTDelete<State> shaderState(new State(shader, matrix, surfaceBBox)); ShaderCanonicalEntry entry(NULL, shaderState.get()); int index = CanonicalShaders().find(entry); if (index >= 0) { - SkPDFShader* result = CanonicalShaders()[index].fPDFShader; + result = CanonicalShaders()[index].fPDFShader; result->ref(); return result; } // The PDFShader takes ownership of the shaderSate. - pdfShader = new SkPDFShader(shaderState.detach()); - // Check for a valid shader. - if (pdfShader->fContent.get() == NULL) { - pdfShader->unref(); - return NULL; + if (shaderState.get()->fType == SkShader::kNone_GradientType) { + result = new SkPDFImageShader(shaderState.detach()); + } else { + SkPDFFunctionShader* functionShader = + new SkPDFFunctionShader(shaderState.detach()); + if (!functionShader->isValid()) { + delete functionShader; + return NULL; + } + result = functionShader; } - entry.fPDFShader = pdfShader.get(); + entry.fPDFShader = result; CanonicalShaders().push(entry); - return pdfShader.get(); // return the reference that came from new. + return result; // return the reference that came from new. } // static @@ -362,7 +405,7 @@ SkMutex& SkPDFShader::CanonicalShadersMutex() { } // static -SkPDFObject* SkPDFShader::RangeObject() { +SkPDFObject* SkPDFFunctionShader::RangeObject() { // This initialization is only thread safe with gcc. static SkPDFArray* range = NULL; // This method is only used with CanonicalShadersMutex, so it's safe to @@ -380,15 +423,9 @@ SkPDFObject* SkPDFShader::RangeObject() { return range; } -SkPDFShader::SkPDFShader(State* state) : fState(state) { - if (fState.get()->fType == SkShader::kNone_GradientType) { - doImageShader(); - } else { - doFunctionShader(); - } -} - -void SkPDFShader::doFunctionShader() { +SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) + : SkPDFDict("Pattern"), + fState(state) { SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL; SkPoint transformPoints[2]; @@ -407,9 +444,8 @@ void SkPDFShader::doFunctionShader() { codeFunction = &radialCode; break; case SkShader::kRadial2_GradientType: { - // Bail out if the radii are the same. Not setting fContent will - // cause the higher level code to detect the resulting object - // as invalid. + // Bail out if the radii are the same. Empty fResources signals + // an error and isValid will return false. if (info->fRadius[0] == info->fRadius[1]) { return; } @@ -479,15 +515,12 @@ void SkPDFShader::doFunctionShader() { pdfShader->insert("Domain", domain.get()); pdfShader->insert("Function", new SkPDFObjRef(function.get()))->unref(); - fContent = new SkPDFDict("Pattern"); - fContent->unref(); // SkRefPtr and new both took a reference. - fContent->insertInt("PatternType", 2); - fContent->insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); - fContent->insert("Shading", pdfShader.get()); + insertInt("PatternType", 2); + insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); + insert("Shading", pdfShader.get()); } -// SkShader* shader, SkMatrix matrix, const SkRect& surfaceBBox -void SkPDFShader::doImageShader() { +SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { fState.get()->fImage.lockPixels(); SkMatrix finalMatrix = fState.get()->fCanvasTransform; @@ -666,34 +699,43 @@ void SkPDFShader::doImageShader() { content->unref(); // SkRefPtr and content() both took a reference. pattern.getResources(&fResources); - fContent = new SkPDFStream(content.get()); - fContent->unref(); // SkRefPtr and new both took a reference. - fContent->insertName("Type", "Pattern"); - fContent->insertInt("PatternType", 1); - fContent->insertInt("PaintType", 1); - fContent->insertInt("TilingType", 1); - fContent->insert("BBox", patternBBoxArray.get()); - fContent->insertScalar("XStep", patternBBox.width()); - fContent->insertScalar("YStep", patternBBox.height()); - fContent->insert("Resources", pattern.getResourceDict().get()); - fContent->insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); + setData(content.get()); + insertName("Type", "Pattern"); + insertInt("PatternType", 1); + insertInt("PaintType", 1); + insertInt("TilingType", 1); + insert("BBox", patternBBoxArray.get()); + insertScalar("XStep", patternBBox.width()); + insertScalar("YStep", patternBBox.height()); + insert("Resources", pattern.getResourceDict().get()); + insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); fState.get()->fImage.unlockPixels(); } -SkPDFStream* SkPDFShader::makePSFunction(const SkString& psCode, - SkPDFArray* domain) { - SkRefPtr<SkMemoryStream> funcStream = - new SkMemoryStream(psCode.c_str(), psCode.size(), true); - funcStream->unref(); // SkRefPtr and new both took a reference. - - SkPDFStream* result = new SkPDFStream(funcStream.get()); +SkPDFStream* SkPDFFunctionShader::makePSFunction(const SkString& psCode, + SkPDFArray* domain) { + SkAutoDataUnref funcData(SkData::NewWithCopy(psCode.c_str(), + psCode.size())); + SkPDFStream* result = new SkPDFStream(funcData.get()); result->insertInt("FunctionType", 4); result->insert("Domain", domain); result->insert("Range", RangeObject()); return result; } +SkPDFShader::ShaderCanonicalEntry::ShaderCanonicalEntry(SkPDFObject* pdfShader, + const State* state) + : fPDFShader(pdfShader), + fState(state) { +} + +bool SkPDFShader::ShaderCanonicalEntry::operator==( + const ShaderCanonicalEntry& b) const { + return fPDFShader == b.fPDFShader || + (fState != NULL && b.fState != NULL && *fState == *b.fState); +} + bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const { if (fType != b.fType || fCanvasTransform != b.fCanvasTransform || diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp index 790cfaec3d..0ff7b49ff0 100644 --- a/src/pdf/SkPDFStream.cpp +++ b/src/pdf/SkPDFStream.cpp @@ -20,45 +20,105 @@ #include "SkPDFStream.h" #include "SkStream.h" -SkPDFStream::SkPDFStream(SkStream* stream) { - if (SkFlate::HaveFlate()) - SkAssertResult(SkFlate::Deflate(stream, &fCompressedData)); +static bool skip_compression(SkPDFCatalog* catalog) { + return catalog->getDocumentFlags() & SkPDFDocument::kNoCompression_Flag; +} - if (SkFlate::HaveFlate() && - fCompressedData.getOffset() < stream->getLength()) { - fLength = fCompressedData.getOffset(); - insert("Filter", new SkPDFName("FlateDecode"))->unref(); - } else { - fCompressedData.reset(); - fPlainData = stream; - fLength = fPlainData->getLength(); - } - insertInt("Length", fLength); +SkPDFStream::SkPDFStream(SkStream* stream) + : fState(kUnused_State), + fData(stream) { } -SkPDFStream::~SkPDFStream() { +SkPDFStream::SkPDFStream(SkData* data) : fState(kUnused_State) { + SkMemoryStream* stream = new SkMemoryStream; + stream->setData(data); + fData = stream; + fData->unref(); // SkRefPtr and new both took a reference. +} + +SkPDFStream::SkPDFStream(const SkPDFStream& pdfStream) + : SkPDFDict(), + fState(kUnused_State), + fData(pdfStream.fData) { + bool removeLength = true; + // Don't uncompress an already compressed stream, but we could. + if (pdfStream.fState == kCompressed_State) { + fState = kCompressed_State; + removeLength = false; + } + SkPDFDict::Iter dict(pdfStream); + SkPDFName* key; + SkPDFObject* value; + SkPDFName lengthName("Length"); + for (key = dict.next(&value); key != NULL; key = dict.next(&value)) { + if (removeLength && *key == lengthName) { + continue; + } + this->insert(key, value); + } } +SkPDFStream::~SkPDFStream() {} + void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog, bool indirect) { - if (indirect) + if (indirect) { return emitIndirectObject(stream, catalog); + } + if (!this->populate(catalog)) { + return fSubstitute->emitObject(stream, catalog, indirect); + } this->INHERITED::emitObject(stream, catalog, false); stream->writeText(" stream\n"); - if (fPlainData.get()) { - stream->write(fPlainData->getMemoryBase(), fLength); - } else { - SkAutoDataUnref data(fCompressedData.copyToData()); - stream->write(data.data(), fLength); - } + stream->write(fData->getMemoryBase(), fData->getLength()); stream->writeText("\nendstream"); } size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) { - if (indirect) + if (indirect) { return getIndirectOutputSize(catalog); + } + if (!this->populate(catalog)) { + return fSubstitute->getOutputSize(catalog, indirect); + } return this->INHERITED::getOutputSize(catalog, false) + - strlen(" stream\n\nendstream") + fLength; + strlen(" stream\n\nendstream") + fData->getLength(); +} + +SkPDFStream::SkPDFStream() : fState(kUnused_State) {} + +void SkPDFStream::setData(SkStream* stream) { + fData = stream; +} + +bool SkPDFStream::populate(SkPDFCatalog* catalog) { + if (fState == kUnused_State) { + if (!skip_compression(catalog) && SkFlate::HaveFlate()) { + SkDynamicMemoryWStream compressedData; + + SkAssertResult(SkFlate::Deflate(fData.get(), &compressedData)); + if (compressedData.getOffset() < fData->getLength()) { + SkMemoryStream* stream = new SkMemoryStream; + stream->setData(compressedData.copyToData()); + fData = stream; + fData->unref(); // SkRefPtr and new both took a reference. + insertName("Filter", "FlateDecode"); + } + fState = kCompressed_State; + } else { + fState = kNoCompression_State; + } + insertInt("Length", fData->getLength()); + } else if (fState == kNoCompression_State && !skip_compression(catalog) && + SkFlate::HaveFlate()) { + if (!fSubstitute.get()) { + fSubstitute = new SkPDFStream(*this); + fSubstitute->unref(); // SkRefPtr and new both took a reference. + catalog->setSubstitute(this, fSubstitute.get()); + } + return false; + } + return true; } diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp index 810d8601fa..a026d6927b 100644 --- a/src/pdf/SkPDFTypes.cpp +++ b/src/pdf/SkPDFTypes.cpp @@ -48,14 +48,32 @@ void SkPDFObject::emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog) { stream->writeText("\nendobj\n"); } -SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {} -SkPDFObjRef::~SkPDFObjRef() {} - size_t SkPDFObject::getIndirectOutputSize(SkPDFCatalog* catalog) { return catalog->getObjectNumberSize(this) + strlen(" obj\n") + this->getOutputSize(catalog, false) + strlen("\nendobj\n"); } +void SkPDFObject::AddResourceHelper(SkPDFObject* resource, + SkTDArray<SkPDFObject*>* list) { + list->push(resource); + resource->ref(); +} + +void SkPDFObject::GetResourcesHelper(SkTDArray<SkPDFObject*>* resources, + SkTDArray<SkPDFObject*>* result) { + if (resources->count()) { + result->setReserve(result->count() + resources->count()); + for (int i = 0; i < resources->count(); i++) { + result->push((*resources)[i]); + (*resources)[i]->ref(); + (*resources)[i]->getResources(result); + } + } +} + +SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {} +SkPDFObjRef::~SkPDFObjRef() {} + void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog, bool indirect) { SkASSERT(!indirect); @@ -258,6 +276,10 @@ SkPDFName::SkPDFName(const char name[]) : fValue(FormatName(SkString(name))) {} SkPDFName::SkPDFName(const SkString& name) : fValue(FormatName(name)) {} SkPDFName::~SkPDFName() {} +bool SkPDFName::operator==(const SkPDFName& b) const { + return fValue == b.fValue; +} + void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog, bool indirect) { SkASSERT(!indirect); @@ -433,3 +455,19 @@ void SkPDFDict::clear() { } fValue.reset(); } + +SkPDFDict::Iter::Iter(const SkPDFDict& dict) + : fIter(dict.fValue.begin()), + fStop(dict.fValue.end()) { +} + +SkPDFName* SkPDFDict::Iter::next(SkPDFObject** value) { + if (fIter != fStop) { + Rec* cur = fIter; + fIter++; + *value = cur->value; + return cur->key; + } + *value = NULL; + return NULL; +} diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp index d4d1eea958..b3f510528a 100644 --- a/tests/PDFPrimitivesTest.cpp +++ b/tests/PDFPrimitivesTest.cpp @@ -18,11 +18,13 @@ #include "Test.h" #include "SkData.h" +#include "SkFlate.h" #include "SkPDFCatalog.h" #include "SkPDFStream.h" #include "SkPDFTypes.h" #include "SkScalar.h" #include "SkStream.h" +#include "SkTypes.h" class SkPDFTestDict : public SkPDFDict { public: @@ -52,16 +54,20 @@ static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset, } static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj, - const std::string& representation, - bool indirect) { - SkPDFCatalog catalog; + const char* expectedData, size_t expectedSize, + bool indirect, bool compression) { + SkPDFDocument::Flags docFlags = (SkPDFDocument::Flags) 0; + if (!compression) { + docFlags = SkTBitOr(docFlags, SkPDFDocument::kNoCompression_Flag); + } + SkPDFCatalog catalog(docFlags); size_t directSize = obj->getOutputSize(&catalog, false); - REPORTER_ASSERT(reporter, directSize == representation.size()); + REPORTER_ASSERT(reporter, directSize == expectedSize); SkDynamicMemoryWStream buffer; obj->emit(&buffer, &catalog, false); REPORTER_ASSERT(reporter, directSize == buffer.getOffset()); - REPORTER_ASSERT(reporter, stream_equals(buffer, 0, representation.c_str(), + REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData, directSize)); if (indirect) { @@ -81,16 +87,74 @@ static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj, obj->emit(&buffer, &catalog, true); REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset()); REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen)); - REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, - representation.c_str(), + REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData, directSize)); REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize, footer, footerLen)); } } +static void SimpleCheckObjectOutput(skiatest::Reporter* reporter, + SkPDFObject* obj, + const std::string& expectedResult) { + CheckObjectOutput(reporter, obj, expectedResult.c_str(), + expectedResult.length(), true, false); +} + +static void TestPDFStream(skiatest::Reporter* reporter) { + char streamBytes[] = "Test\nFoo\tBar"; + SkRefPtr<SkMemoryStream> streamData = new SkMemoryStream( + streamBytes, strlen(streamBytes), true); + streamData->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get()); + stream->unref(); // SkRefPtr and new both took a reference. + SimpleCheckObjectOutput( + reporter, stream.get(), + "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream"); + stream->insert("Attribute", new SkPDFInt(42))->unref(); + SimpleCheckObjectOutput(reporter, stream.get(), + "<</Length 12\n/Attribute 42\n>> stream\n" + "Test\nFoo\tBar\nendstream"); + + if (SkFlate::HaveFlate()) { + char streamBytes2[] = "This is a longer string, so that compression " + "can do something with it. With shorter strings, " + "the short circuit logic cuts in and we end up " + "with an uncompressed string."; + SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2, + strlen(streamBytes2))); + SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData2.get()); + stream->unref(); // SkRefPtr and new both took a reference. + + SkDynamicMemoryWStream compressedByteStream; + SkFlate::Deflate(streamData2.get(), &compressedByteStream); + SkAutoDataUnref compressedData(compressedByteStream.copyToData()); + + // Check first without compression. + SkDynamicMemoryWStream expectedResult1; + expectedResult1.writeText("<</Length 167\n>> stream\n"); + expectedResult1.writeText(streamBytes2); + expectedResult1.writeText("\nendstream"); + SkAutoDataUnref expectedResultData1(expectedResult1.copyToData()); + CheckObjectOutput(reporter, stream.get(), + (const char*) expectedResultData1.data(), + expectedResultData1.size(), true, false); + + // Then again with compression. + SkDynamicMemoryWStream expectedResult2; + expectedResult2.writeText("<</Filter /FlateDecode\n/Length 116\n" + ">> stream\n"); + expectedResult2.write(compressedData.data(), compressedData.size()); + expectedResult2.writeText("\nendstream"); + SkAutoDataUnref expectedResultData2(expectedResult2.copyToData()); + CheckObjectOutput(reporter, stream.get(), + (const char*) expectedResultData2.data(), + expectedResultData2.size(), true, true); + } +} + static void TestCatalog(skiatest::Reporter* reporter) { - SkPDFCatalog catalog; + SkPDFCatalog catalog((SkPDFDocument::Flags)0); SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1); int1->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2); @@ -125,7 +189,7 @@ static void TestObjectRef(skiatest::Reporter* reporter) { SkRefPtr<SkPDFObjRef> int2ref = new SkPDFObjRef(int2.get()); int2ref->unref(); // SkRefPtr and new both took a reference. - SkPDFCatalog catalog; + SkPDFCatalog catalog((SkPDFDocument::Flags)0); catalog.addObject(int1.get(), false); catalog.addObject(int2.get(), false); REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3); @@ -155,7 +219,7 @@ static void TestSubstitute(skiatest::Reporter* reporter) { stubResource->insert("InnerValue", int44.get()); stub->addResource(stubResource.get()); - SkPDFCatalog catalog; + SkPDFCatalog catalog((SkPDFDocument::Flags)0); catalog.addObject(proxy.get(), false); catalog.setSubstitute(proxy.get(), stub.get()); @@ -173,90 +237,79 @@ static void TestSubstitute(skiatest::Reporter* reporter) { static void TestPDFPrimitives(skiatest::Reporter* reporter) { SkRefPtr<SkPDFInt> int42 = new SkPDFInt(42); int42->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, int42.get(), "42", true); + SimpleCheckObjectOutput(reporter, int42.get(), "42"); SkRefPtr<SkPDFScalar> realHalf = new SkPDFScalar(SK_ScalarHalf); realHalf->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, realHalf.get(), "0.5", true); + SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5"); #if defined(SK_SCALAR_IS_FLOAT) SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75); bigScalar->unref(); // SkRefPtr and new both took a reference. #if !defined(SK_ALLOW_LARGE_PDF_SCALARS) - CheckObjectOutput(reporter, bigScalar.get(), "111000", true); + SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000"); #else - CheckObjectOutput(reporter, bigScalar.get(), "110999.75", true); + SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75"); SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1); biggerScalar->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, biggerScalar.get(), "50000000", true); + SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000"); SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536); smallestScalar->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, smallestScalar.get(), "0.00001526", true); + SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526"); #endif #endif SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo"); stringSimple->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, stringSimple.get(), "(test \\) string \\( foo)", - true); + SimpleCheckObjectOutput(reporter, stringSimple.get(), + "(test \\) string \\( foo)"); SkRefPtr<SkPDFString> stringComplex = new SkPDFString("\ttest ) string ( foo"); stringComplex->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, stringComplex.get(), - "<0974657374202920737472696E67202820666F6F>", true); + SimpleCheckObjectOutput(reporter, stringComplex.get(), + "<0974657374202920737472696E67202820666F6F>"); SkRefPtr<SkPDFName> name = new SkPDFName("Test name\twith#tab"); name->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, name.get(), "/Test#20name#09with#23tab", false); + const char expectedResult[] = "/Test#20name#09with#23tab"; + CheckObjectOutput(reporter, name.get(), expectedResult, + strlen(expectedResult), false, false); SkRefPtr<SkPDFArray> array = new SkPDFArray; array->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, array.get(), "[]", true); + SimpleCheckObjectOutput(reporter, array.get(), "[]"); array->append(int42.get()); - CheckObjectOutput(reporter, array.get(), "[42]", true); + SimpleCheckObjectOutput(reporter, array.get(), "[42]"); array->append(realHalf.get()); - CheckObjectOutput(reporter, array.get(), "[42 0.5]", true); + SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]"); SkRefPtr<SkPDFInt> int0 = new SkPDFInt(0); int0->unref(); // SkRefPtr and new both took a reference. array->append(int0.get()); - CheckObjectOutput(reporter, array.get(), "[42 0.5 0]", true); + SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]"); SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1); int1->unref(); // SkRefPtr and new both took a reference. array->setAt(0, int1.get()); - CheckObjectOutput(reporter, array.get(), "[1 0.5 0]", true); + SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]"); SkRefPtr<SkPDFDict> dict = new SkPDFDict; dict->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, dict.get(), "<<>>", true); + SimpleCheckObjectOutput(reporter, dict.get(), "<<>>"); SkRefPtr<SkPDFName> n1 = new SkPDFName("n1"); n1->unref(); // SkRefPtr and new both took a reference. dict->insert(n1.get(), int42.get()); - CheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>", true); + SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>"); SkRefPtr<SkPDFName> n2 = new SkPDFName("n2"); n2->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFName> n3 = new SkPDFName("n3"); n3->unref(); // SkRefPtr and new both took a reference. dict->insert(n2.get(), realHalf.get()); dict->insert(n3.get(), array.get()); - CheckObjectOutput(reporter, dict.get(), - "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>", true); + SimpleCheckObjectOutput(reporter, dict.get(), + "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>"); - char streamBytes[] = "Test\nFoo\tBar"; - SkRefPtr<SkMemoryStream> streamData = new SkMemoryStream( - streamBytes, strlen(streamBytes), true); - streamData->unref(); // SkRefPtr and new both took a reference. - SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get()); - stream->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, stream.get(), - "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream", - true); - stream->insert(n1.get(), int42.get()); - CheckObjectOutput(reporter, stream.get(), - "<</Length 12\n/n1 42\n>> stream\nTest\nFoo\tBar" - "\nendstream", - true); + TestPDFStream(reporter); TestCatalog(reporter); |