[PDF] Make stream compression optional on a per device basis.
authorvandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 20 Jul 2011 17:39:01 +0000 (17:39 +0000)
committervandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 20 Jul 2011 17:39:01 +0000 (17:39 +0000)
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

20 files changed:
include/pdf/SkPDFCatalog.h
include/pdf/SkPDFDevice.h
include/pdf/SkPDFDocument.h
include/pdf/SkPDFFormXObject.h
include/pdf/SkPDFImage.h
include/pdf/SkPDFShader.h
include/pdf/SkPDFStream.h
include/pdf/SkPDFTypes.h
src/core/SkFlate.cpp
src/pdf/SkPDFCatalog.cpp
src/pdf/SkPDFDevice.cpp
src/pdf/SkPDFDocument.cpp
src/pdf/SkPDFFont.cpp
src/pdf/SkPDFFormXObject.cpp
src/pdf/SkPDFGraphicState.cpp
src/pdf/SkPDFImage.cpp
src/pdf/SkPDFShader.cpp
src/pdf/SkPDFStream.cpp
src/pdf/SkPDFTypes.cpp
tests/PDFPrimitivesTest.cpp

index 4f8f8c1ecefe4640bfefd1d46d9e7081998dd189..3a644e6a5a6cda0d7b70647fec3ecacde6b06bc5 100644 (file)
@@ -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);
index 1677546ef336dc39964846d83d5fb1440007ba86..4c526d2291bc6215a410e339dd5e8e8f7d4457ba 100644 (file)
@@ -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;
index 5fb7c954059ce3181a1b6b49a32c1c3653c0b432..1a5d8354e94c7bf0211f503a86850f8c0bac40f7 100644 (file)
 #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;
index 41719f082036f1063e372eef6b366cb9e30047e1..1e0ad4604a7827c2fbf1c3821337cc80a8b9ecd5 100644 (file)
@@ -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;
 };
 
index 945ff9e541dc3a7f28d25d39568f306a806c8fe3..a08cdb5a7dff0f4dc55ce00efa2af64491107b2a 100644 (file)
@@ -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
index 2974c88e7f03088627eb38bb5c8e065f71db96af..350569c99367bc9f924249fcf3a4ba2874b13514 100644 (file)
@@ -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
index 739177f56d37b9657827a8340bc8c0ed0ae77ab4..263a408d72e502e770ccc80a90cb53887402999c 100644 (file)
@@ -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
index c834bc2dfe7e50ce4f8302cbefd0bd3feab780f1..6c5a410074123fc0ae9ed3cd9de1a71fb410fc9d 100644 (file)
@@ -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;
 };
 
index 3bae8a9334319136171a160055adf37eff4ce115..a5a7b2209fc16282bc1ce5cb9a8f406bde85fb76 100644 (file)
@@ -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 {
index 025c86d82fd99e7ac1c833cb29176596b68ca555..420ce4051136d26ad956a34c94b10dcb7c6a2d41 100644 (file)
 #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() {
index 7833362b6c85c91d6d631fe3c1a66d75addfa133..caba8226b48e8bbc5f429674c3cf494361464218 100644 (file)
@@ -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) {
index 1cfe64df12fadab028988d9027899f93266ac183..d60512e7acaddd10938552514d629b9714d469bf 100644 (file)
@@ -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");
index 4602c68e1c02cbae35b5308e142f968a751086e3..be16c769360b8e02bb10a69bcc387c4ec4122ea7 100755 (executable)
@@ -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() {
index 40a5564847bd43d9f96d43e73bd63987128b8f04..a8a3290e2220b46b264cd89309004b138324046b 100644 (file)
@@ -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);
 }
index 59b2817518ea5e265f1ea2d2fb03209e77aa065a..0e6e23049ca7a022e2a7e6d81931805942aabef2 100644 (file)
@@ -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,
index be69f7f757a0709e1158b2222e03aaa19982f4de..ebbcd11a97c0b0d90da65d4fb382ea980a476b50 100644 (file)
@@ -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);
-}
index 95fdb612f06e5bbd0d6264e1b70c558f00c12988..ee225173b70fa7dfab2ef20bfc3a4b56a63b473e 100644 (file)
@@ -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 ||
index 790cfaec3d027584b63ec4594888d12ee21812bc..0ff7b49ff087e021f8e62d8149bfc54176998549 100644 (file)
 #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;
 }
index 810d8601fad0bd154928f9e49c6098945bfe5793..a026d6927be7278c946f2ed2683e2efbceeccd44 100644 (file)
@@ -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;
+}
index d4d1eea958e7638033057b3c93beff220e293d63..b3f510528a30f967bee9ce11302f64bf703adbf2 100644 (file)
 
 #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);