[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
diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp
index 790cfae..0ff7b49 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();
+SkPDFStream::SkPDFStream(SkStream* stream)
+    : fState(kUnused_State),
+      fData(stream) {
+}
+
+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;
     }
-    insertInt("Length", fLength);
+    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() {
-}
+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;
 }