aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/pdf/SkPDFCatalog.h9
-rw-r--r--include/pdf/SkPDFDevice.h2
-rw-r--r--include/pdf/SkPDFDocument.h13
-rw-r--r--include/pdf/SkPDFFormXObject.h20
-rw-r--r--include/pdf/SkPDFImage.h20
-rw-r--r--include/pdf/SkPDFShader.h58
-rw-r--r--include/pdf/SkPDFStream.h36
-rw-r--r--include/pdf/SkPDFTypes.h34
-rw-r--r--src/core/SkFlate.cpp4
-rw-r--r--src/pdf/SkPDFCatalog.cpp5
-rw-r--r--src/pdf/SkPDFDevice.cpp2
-rw-r--r--src/pdf/SkPDFDocument.cpp74
-rwxr-xr-xsrc/pdf/SkPDFFont.cpp7
-rw-r--r--src/pdf/SkPDFFormXObject.cpp32
-rw-r--r--src/pdf/SkPDFGraphicState.cpp7
-rw-r--r--src/pdf/SkPDFImage.cpp36
-rw-r--r--src/pdf/SkPDFShader.cpp188
-rw-r--r--src/pdf/SkPDFStream.cpp106
-rw-r--r--src/pdf/SkPDFTypes.cpp44
-rw-r--r--tests/PDFPrimitivesTest.cpp141
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);