In SkPDFDocument::emitPDF(), stop pre-calculating file offsets.

* Add Streamer utility class which measures the current
  pdf offset by calling SkWStream::bytesWritten(). Calls
  SkPDFCatalog::setFileOffset() and SkPDFObject::emit() at
  the same time to guarantee that everything works out.

* SkPDFCatalog::setFileOffset() no longer calculates the
  object's size.

* SkPDFCatalog::setSubstituteResourcesOffsets() removed.

* SkPDFCatalog::emitSubstituteResources() removed and
  getSubstituteList() made public in its place.

* Remove SkPDFPage::getPageSize and SkPDFPage::emitPage.
  Replace with SkPDFPage::getContentStream().

* SkPDFObject::getOutputSize no longer virtual, only used in
  unit tests.  All SkPDFObject subclasses getOutputSize()
  overrides removed.

* SkPDFObject::getIndirectOutputSize removed.

* PDFPrimitivesTest updated for new functions.

Review URL: https://codereview.chromium.org/846023003
This commit is contained in:
halcanary 2015-01-13 07:12:57 -08:00 committed by Commit bot
parent 7e783786d8
commit f361b71439
12 changed files with 72 additions and 199 deletions

View File

@ -38,13 +38,11 @@ SkPDFObject* SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) {
return obj;
}
size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, off_t offset) {
void SkPDFCatalog::setFileOffset(SkPDFObject* obj, off_t offset) {
int objIndex = assignObjNum(obj) - 1;
SkASSERT(fCatalog[objIndex].fObjNumAssigned);
SkASSERT(fCatalog[objIndex].fFileOffset == 0);
fCatalog[objIndex].fFileOffset = offset;
return getSubstituteObject(obj)->getOutputSize(this, true);
}
void SkPDFCatalog::emitObjectNumber(SkWStream* stream, SkPDFObject* obj) {
@ -192,23 +190,6 @@ SkPDFObject* SkPDFCatalog::getSubstituteObject(SkPDFObject* object) {
return object;
}
off_t SkPDFCatalog::setSubstituteResourcesOffsets(off_t fileOffset,
bool firstPage) {
SkTSet<SkPDFObject*>* targetSet = getSubstituteList(firstPage);
off_t offsetSum = fileOffset;
for (int i = 0; i < targetSet->count(); ++i) {
offsetSum += SkToOffT(setFileOffset((*targetSet)[i], offsetSum));
}
return offsetSum - fileOffset;
}
void SkPDFCatalog::emitSubstituteResources(SkWStream *stream, bool firstPage) {
SkTSet<SkPDFObject*>* targetSet = getSubstituteList(firstPage);
for (int i = 0; i < targetSet->count(); ++i) {
(*targetSet)[i]->emit(stream, this, true);
}
}
SkTSet<SkPDFObject*>* SkPDFCatalog::getSubstituteList(bool firstPage) {
return firstPage ? &fSubstituteResourcesFirstPage :
&fSubstituteResourcesRemaining;

View File

@ -37,12 +37,11 @@ public:
SkPDFObject* addObject(SkPDFObject* obj, bool onFirstPage);
/** Inform the catalog of the object's position in the final stream.
* The object should already have been added to the catalog. Returns
* the object's size.
* The object should already have been added to the catalog.
* @param obj The object to add.
* @param offset The byte offset in the output stream of this object.
*/
size_t setFileOffset(SkPDFObject* obj, off_t offset);
void setFileOffset(SkPDFObject* obj, off_t offset);
/** Output the object number for the passed object.
* @param obj The object of interest.
@ -77,16 +76,9 @@ public:
*/
SkPDFObject* getSubstituteObject(SkPDFObject* object);
/** Set file offsets for the resources of substitute objects.
* @param fileOffset Accumulated offset of current document.
* @param firstPage Indicate whether this is for the first page only.
* @return Total size of resources of substitute objects.
/** get the resources of substitute objects.
*/
off_t setSubstituteResourcesOffsets(off_t fileOffset, bool firstPage);
/** Emit the resources of substitute objects.
*/
void emitSubstituteResources(SkWStream* stream, bool firstPage);
SkTSet<SkPDFObject*>* getSubstituteList(bool firstPage);
private:
struct Rec {
@ -130,8 +122,6 @@ private:
int findObjectIndex(SkPDFObject* obj) const;
int assignObjNum(SkPDFObject* obj);
SkTSet<SkPDFObject*>* getSubstituteList(bool firstPage);
};
#endif

View File

@ -82,6 +82,29 @@ SkPDFDocument::~SkPDFDocument() {
SkDELETE(fOtherPageResources);
}
namespace {
class Streamer {
public:
Streamer(SkPDFCatalog* cat, SkWStream* out)
: fCat(cat), fOut(out), fBaseOffset(SkToOffT(out->bytesWritten())) {
}
void stream(SkPDFObject* obj) {
fCat->setFileOffset(obj, this->offset());
obj->emit(fOut, fCat, true);
}
off_t offset() {
return SkToOffT(fOut->bytesWritten()) - fBaseOffset;
}
private:
SkPDFCatalog* const fCat;
SkWStream* const fOut;
const off_t fBaseOffset;
};
} // namespace
bool SkPDFDocument::emitPDF(SkWStream* stream) {
if (fPages.isEmpty()) {
return false;
@ -158,46 +181,24 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) {
// Build font subsetting info before proceeding.
perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes);
// Figure out the size of things and inform the catalog of file offsets.
off_t fileOffset = SkToOffT(this->headerSize());
fileOffset += SkToOffT(fCatalog->setFileOffset(fDocCatalog, fileOffset));
fileOffset += SkToOffT(fCatalog->setFileOffset(fPages[0], fileOffset));
fileOffset += fPages[0]->getPageSize(fCatalog.get(), fileOffset);
for (int i = 0; i < fFirstPageResources->count(); i++) {
fileOffset += SkToOffT(fCatalog->setFileOffset((*fFirstPageResources)[i], fileOffset));
}
// Add the size of resources of substitute objects used on page 1.
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 += SkToOffT(fCatalog->setFileOffset(fPageTree[i], fileOffset));
}
for (int i = 1; i < fPages.count(); i++) {
fileOffset += fPages[i]->getPageSize(fCatalog.get(), fileOffset);
}
for (int i = 0; i < fOtherPageResources->count(); i++) {
fileOffset += SkToOffT(fCatalog->setFileOffset((*fOtherPageResources)[i], fileOffset));
}
fileOffset += fCatalog->setSubstituteResourcesOffsets(fileOffset, false);
fXRefFileOffset = fileOffset;
}
Streamer out(fCatalog, stream);
emitHeader(stream);
fDocCatalog->emitIndirectObject(stream, fCatalog.get());
fPages[0]->emitIndirectObject(stream, fCatalog.get());
fPages[0]->emitPage(stream, fCatalog.get());
out.stream(fDocCatalog);
out.stream(fPages[0]);
out.stream(fPages[0]->getContentStream());
for (int i = 0; i < fFirstPageResources->count(); i++) {
(*fFirstPageResources)[i]->emit(stream, fCatalog.get(), true);
out.stream((*fFirstPageResources)[i]);
}
SkTSet<SkPDFObject*>* firstPageSubstituteResources =
fCatalog->getSubstituteList(true);
for (int i = 0; i < firstPageSubstituteResources->count(); ++i) {
out.stream((*firstPageSubstituteResources)[i]);
}
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.
@ -205,18 +206,24 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) {
// }
for (int i = 0; i < fPageTree.count(); i++) {
fPageTree[i]->emitIndirectObject(stream, fCatalog.get());
out.stream(fPageTree[i]);
}
for (int i = 1; i < fPages.count(); i++) {
fPages[i]->emitPage(stream, fCatalog.get());
out.stream(fPages[i]->getContentStream());
}
for (int i = 0; i < fOtherPageResources->count(); i++) {
(*fOtherPageResources)[i]->emit(stream, fCatalog.get(), true);
out.stream((*fOtherPageResources)[i]);
}
fCatalog->emitSubstituteResources(stream, false);
SkTSet<SkPDFObject*>* otherSubstituteResources =
fCatalog->getSubstituteList(false);
for (int i = 0; i < otherSubstituteResources->count(); ++i) {
out.stream((*otherSubstituteResources)[i]);
}
fXRefFileOffset = out.offset();
int64_t objCount = fCatalog->emitXrefTable(stream, fPages.count() > 1);
emitFooter(stream, objCount);
return true;

View File

@ -74,12 +74,6 @@ void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
SkPDFDict::emitObject(stream, catalog);
}
// static
size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
populateDict();
return SkPDFDict::getOutputSize(catalog, indirect);
}
// static
SkTDArray<SkPDFGraphicState::GSCanonicalEntry>& SkPDFGraphicState::CanonicalPaints() {
CanonicalPaintsMutex().assertHeld();

View File

@ -39,10 +39,9 @@ public:
virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
SkTSet<SkPDFObject*>* newResourceObjects);
// Override emitObject and getOutputSize so that we can populate
// the dictionary on demand.
// Override emitObject so that we can populate the dictionary on
// demand.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog);
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
/** Get the graphic state for the passed SkPaint. The reference count of
* the object is incremented and it is the caller's responsibility to

View File

@ -46,17 +46,6 @@ void SkPDFPage::finalizePage(SkPDFCatalog* catalog, bool firstPage,
true);
}
off_t SkPDFPage::getPageSize(SkPDFCatalog* catalog, off_t fileOffset) {
SkASSERT(fContentStream.get() != NULL);
catalog->setFileOffset(fContentStream.get(), fileOffset);
return SkToOffT(fContentStream->getOutputSize(catalog, true));
}
void SkPDFPage::emitPage(SkWStream* stream, SkPDFCatalog* catalog) {
SkASSERT(fContentStream.get() != NULL);
fContentStream->emitIndirectObject(stream, catalog);
}
// static
void SkPDFPage::GeneratePageTree(const SkTDArray<SkPDFPage*>& pages,
SkPDFCatalog* catalog,
@ -156,3 +145,7 @@ const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
void SkPDFPage::appendDestinations(SkPDFDict* dict) {
fDevice->appendDestinations(dict, this);
}
SkPDFObject* SkPDFPage::getContentStream() const {
return fContentStream.get();
}

View File

@ -56,21 +56,6 @@ public:
*/
void appendDestinations(SkPDFDict* dict);
/** Determine the size of the page content and store to the catalog
* the offsets of all nonresource-indirect objects that make up the page
* content. This must be called before emitPage(), but after finalizePage.
* @param catalog The catalog to add the object offsets to.
* @param fileOffset The file offset where the page content will be
* emitted.
*/
off_t getPageSize(SkPDFCatalog* catalog, off_t fileOffset);
/** Output the page content to the passed stream.
* @param stream The writable output stream to send the content to.
* @param catalog The active object catalog.
*/
void emitPage(SkWStream* stream, SkPDFCatalog* catalog);
/** Generate a page tree for the passed vector of pages. New objects are
* added to the catalog. The pageTree vector is populated with all of
* the 'Pages' dictionaries as well as the 'Page' objects. Page trees
@ -97,6 +82,8 @@ public:
*/
const SkPDFGlyphSetMap& getFontGlyphUsage() const;
SkPDFObject* getContentStream() const;
private:
// Multiple pages may reference the content.
SkAutoTUnref<SkPDFDevice> fDevice;

View File

@ -58,19 +58,6 @@ void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
stream->writeText("\nendstream");
}
size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
if (indirect) {
return getIndirectOutputSize(catalog);
}
SkAutoMutexAcquire lock(fMutex); // multiple threads could be calling emit
if (!this->populate(catalog)) {
return fSubstitute->getOutputSize(catalog, indirect);
}
return this->INHERITED::getOutputSize(catalog, false) +
strlen(" stream\n\nendstream") + this->dataSize();
}
SkPDFStream::SkPDFStream() : fState(kUnused_State) {}
void SkPDFStream::setData(SkData* data) {

View File

@ -39,10 +39,9 @@ public:
virtual ~SkPDFStream();
// The SkPDFObject interface. These two methods use a mutex to
// The SkPDFObject interface. This two method uses a mutex to
// allow multiple threads to call at the same time.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
protected:
enum State {

View File

@ -45,11 +45,6 @@ void SkPDFObject::emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog) {
stream->writeText("\nendobj\n");
}
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);
@ -86,11 +81,6 @@ void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
stream->writeText(" R");
}
size_t SkPDFObjRef::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
SkASSERT(!indirect);
return catalog->getObjectNumberSize(fObj.get()) + strlen(" R");
}
SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {}
SkPDFInt::~SkPDFInt() {}
@ -109,14 +99,6 @@ void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
}
}
size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
SkASSERT(!indirect);
if (fValue) {
return strlen("true");
}
return strlen("false");
}
SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
SkPDFScalar::~SkPDFScalar() {}
@ -191,12 +173,6 @@ void SkPDFString::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
stream->write(fValue.c_str(), fValue.size());
}
size_t SkPDFString::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
if (indirect)
return getIndirectOutputSize(catalog);
return fValue.size();
}
// static
SkString SkPDFString::FormatString(const char* input, size_t len) {
return DoFormatString(input, len, false, false);
@ -274,11 +250,6 @@ void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
stream->write(fValue.c_str(), fValue.size());
}
size_t SkPDFName::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
SkASSERT(!indirect);
return fValue.size();
}
// static
SkString SkPDFName::FormatName(const SkString& input) {
SkASSERT(input.size() <= kMaxLen);
@ -315,21 +286,6 @@ void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
stream->writeText("]");
}
size_t SkPDFArray::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
if (indirect) {
return getIndirectOutputSize(catalog);
}
size_t result = strlen("[]");
if (fValue.count()) {
result += fValue.count() - 1;
}
for (int i = 0; i < fValue.count(); i++) {
result += fValue[i]->getOutputSize(catalog, false);
}
return result;
}
void SkPDFArray::reserve(int length) {
SkASSERT(length <= kMaxLen);
fValue.setReserve(length);
@ -400,25 +356,6 @@ void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
stream->writeText(">>");
}
size_t SkPDFDict::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
if (indirect) {
return getIndirectOutputSize(catalog);
}
SkAutoMutexAcquire lock(fMutex); // If another thread triggers a
// resize while this thread is in
// the for-loop, we can be left
// with a bad fValue[i] reference.
size_t result = strlen("<<>>") + (fValue.count() * 2);
for (int i = 0; i < fValue.count(); i++) {
SkASSERT(fValue[i].key);
SkASSERT(fValue[i].value);
result += fValue[i].key->getOutputSize(catalog, false);
result += fValue[i].value->getOutputSize(catalog, false);
}
return result;
}
SkPDFObject* SkPDFDict::append(SkPDFName* key, SkPDFObject* value) {
SkASSERT(key);
SkASSERT(value);

View File

@ -31,12 +31,11 @@ public:
SK_DECLARE_INST_COUNT(SkPDFObject)
/** Return the size (number of bytes) of this object in the final output
* file. Compound objects or objects that are computationally intensive
* to output should override this method.
* file. Only used for testing.
* @param catalog The object catalog to use.
* @param indirect If true, output an object identifier with the object.
*/
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
/** For non-primitive objects (i.e. objects defined outside this file),
* this method will add to newResourceObjects any objects that this method
@ -116,7 +115,6 @@ public:
// The SkPDFObject interface.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
private:
SkAutoTUnref<SkPDFObject> fObj;
@ -163,7 +161,6 @@ public:
// The SkPDFObject interface.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
private:
bool fValue;
@ -221,7 +218,6 @@ public:
// The SkPDFObject interface.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
static SkString FormatString(const char* input, size_t len);
static SkString FormatString(const uint16_t* input, size_t len,
@ -256,7 +252,6 @@ public:
// The SkPDFObject interface.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
private:
static const size_t kMaxLen = 127;
@ -283,7 +278,6 @@ public:
// The SkPDFObject interface.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
/** The size of the array.
*/
@ -355,7 +349,6 @@ public:
// The SkPDFObject interface.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
/** The size of the dictionary.
*/

View File

@ -228,12 +228,18 @@ static void TestSubstitute(skiatest::Reporter* reporter) {
SkDynamicMemoryWStream buffer;
proxy->emit(&buffer, &catalog, false);
catalog.emitSubstituteResources(&buffer, false);
SkTSet<SkPDFObject*>* substituteResources =
catalog.getSubstituteList(false);
for (int i = 0; i < substituteResources->count(); ++i) {
(*substituteResources)[i]->emit(&buffer, &catalog, true);
}
char objectResult[] = "2 0 obj\n<</Value 33\n>>\nendobj\n";
REPORTER_ASSERT(
reporter,
catalog.setFileOffset(proxy.get(), 0) == strlen(objectResult));
catalog.setFileOffset(proxy.get(), 0);
size_t outputSize = catalog.getSubstituteObject(proxy.get())
->getOutputSize(&catalog, true);
REPORTER_ASSERT(reporter, outputSize == strlen(objectResult));
char expectedResult[] =
"<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n";