From 421d6443fbd3a913dfa32b6492c4a2969bc6314b Mon Sep 17 00:00:00 2001 From: "vandebo@chromium.org" Date: Wed, 20 Jul 2011 17:39:01 +0000 Subject: [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 --- src/pdf/SkPDFCatalog.cpp | 5 +- src/pdf/SkPDFDevice.cpp | 2 +- src/pdf/SkPDFDocument.cpp | 74 ++++++++++------- src/pdf/SkPDFFont.cpp | 7 +- src/pdf/SkPDFFormXObject.cpp | 32 +------ src/pdf/SkPDFGraphicState.cpp | 7 +- src/pdf/SkPDFImage.cpp | 36 +------- src/pdf/SkPDFShader.cpp | 188 ++++++++++++++++++++++++++---------------- src/pdf/SkPDFStream.cpp | 106 ++++++++++++++++++------ src/pdf/SkPDFTypes.cpp | 44 +++++++++- 10 files changed, 292 insertions(+), 209 deletions(-) (limited to 'src/pdf') 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 pdfShader; + SkRefPtr 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* 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 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* 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* 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* 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* 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 fResources; + SkAutoTDelete fState; -void SkPDFShader::getResources(SkTDArray* 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* resourceList) { + GetResourcesHelper(&fResources, resourceList); } + +private: + SkTDArray fResources; + SkAutoTDelete 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 pdfShader; + SkPDFObject* result; SkAutoMutexAcquire lock(CanonicalShadersMutex()); SkAutoTDelete 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 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* list) { + list->push(resource); + resource->ref(); +} + +void SkPDFObject::GetResourcesHelper(SkTDArray* resources, + SkTDArray* 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; +} -- cgit v1.2.3