[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
This commit is contained in:
parent
f6c3ebdeb1
commit
421d6443fb
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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() {
|
||||
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();
|
||||
}
|
||||
|
||||
bool isValid() { return fResources.count() > 0; }
|
||||
|
||||
void getResources(SkTDArray<SkPDFObject*>* resourceList) {
|
||||
GetResourcesHelper(&fResources, resourceList);
|
||||
}
|
||||
|
||||
private:
|
||||
static SkPDFObject* RangeObject();
|
||||
|
||||
SkTDArray<SkPDFObject*> fResources;
|
||||
SkAutoTDelete<const SkPDFShader::State> fState;
|
||||
|
||||
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(this, fState.get());
|
||||
ShaderCanonicalEntry entry(shader, NULL);
|
||||
int index = CanonicalShaders().find(entry);
|
||||
if (fContent.get()) {
|
||||
SkASSERT(index >= 0);
|
||||
CanonicalShaders().removeShuffle(index);
|
||||
}
|
||||
fResources.unrefAll();
|
||||
}
|
||||
|
||||
void SkPDFShader::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
|
||||
bool indirect) {
|
||||
if (indirect)
|
||||
return emitIndirectObject(stream, catalog);
|
||||
|
||||
fContent->emitObject(stream, catalog, indirect);
|
||||
}
|
||||
|
||||
size_t SkPDFShader::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
|
||||
if (indirect)
|
||||
return getIndirectOutputSize(catalog);
|
||||
|
||||
return fContent->getOutputSize(catalog, indirect);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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 ||
|
||||
|
@ -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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
insertInt("Length", fLength);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user