Index: src/pdf/SkPDFDocument.cpp |
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp |
index 8280551f8aabdcb5bfd283a1fd0613d514178b11..b71697163847d42f8035523cad94da8152a5415e 100644 |
--- a/src/pdf/SkPDFDocument.cpp |
+++ b/src/pdf/SkPDFDocument.cpp |
@@ -16,20 +16,9 @@ |
#include "SkStream.h" |
#include "SkTSet.h" |
-static void addResourcesToCatalog(bool firstPage, |
- SkTSet<SkPDFObject*>* resourceSet, |
- SkPDFCatalog* catalog) { |
- for (int i = 0; i < resourceSet->count(); i++) { |
- catalog->addObject((*resourceSet)[i], firstPage); |
- } |
-} |
- |
-static void perform_font_subsetting(SkPDFCatalog* catalog, |
- const SkTDArray<SkPDFPage*>& pages, |
- SkTDArray<SkPDFObject*>* substitutes) { |
+static void perform_font_subsetting(const SkTDArray<SkPDFPage*>& pages, |
+ SkPDFCatalog* catalog) { |
SkASSERT(catalog); |
- SkASSERT(substitutes); |
- |
SkPDFGlyphSetMap usage; |
for (int i = 0; i < pages.count(); ++i) { |
usage.merge(pages[i]->getFontGlyphUsage()); |
@@ -37,49 +26,45 @@ static void perform_font_subsetting(SkPDFCatalog* catalog, |
SkPDFGlyphSetMap::F2BIter iterator(usage); |
const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); |
while (entry) { |
- SkPDFFont* subsetFont = |
- entry->fFont->getFontSubset(entry->fGlyphSet); |
+ SkAutoTUnref<SkPDFFont> subsetFont( |
+ entry->fFont->getFontSubset(entry->fGlyphSet)); |
if (subsetFont) { |
catalog->setSubstitute(entry->fFont, subsetFont); |
- substitutes->push(subsetFont); // Transfer ownership to substitutes |
} |
entry = iterator.next(); |
} |
} |
-SkPDFDocument::SkPDFDocument() |
- : fXRefFileOffset(0), |
- fTrailerDict(NULL) { |
- fCatalog.reset(SkNEW(SkPDFCatalog)); |
- fDocCatalog = SkNEW_ARGS(SkPDFDict, ("Catalog")); |
- fCatalog->addObject(fDocCatalog, true); |
- fFirstPageResources = NULL; |
- fOtherPageResources = NULL; |
-} |
+SkPDFDocument::SkPDFDocument() {} |
SkPDFDocument::~SkPDFDocument() { |
fPages.safeUnrefAll(); |
+} |
- // The page tree has both child and parent pointers, so it creates a |
- // reference cycle. We must clear that cycle to properly reclaim memory. |
- for (int i = 0; i < fPageTree.count(); i++) { |
- fPageTree[i]->clear(); |
- } |
- fPageTree.safeUnrefAll(); |
- |
- if (fFirstPageResources) { |
- fFirstPageResources->safeUnrefAll(); |
- } |
- if (fOtherPageResources) { |
- fOtherPageResources->safeUnrefAll(); |
- } |
+static void emit_pdf_header(SkWStream* stream) { |
+ stream->writeText("%PDF-1.4\n%"); |
+ // The PDF spec recommends including a comment with four bytes, all |
+ // with their high bits set. This is "Skia" with the high bits set. |
+ stream->write32(0xD3EBE9E1); |
+ stream->writeText("\n"); |
+} |
- fSubstitutes.safeUnrefAll(); |
+static void emit_pdf_footer(SkWStream* stream, |
+ SkPDFCatalog* catalog, |
+ SkPDFObject* docCatalog, |
+ int64_t objCount, |
+ int32_t xRefFileOffset) { |
+ SkPDFDict trailerDict; |
+ // TODO(vandebo): Linearized format will take a Prev entry too. |
+ // TODO(vandebo): PDF/A requires an ID entry. |
+ trailerDict.insertInt("Size", int(objCount)); |
+ trailerDict.insert("Root", new SkPDFObjRef(docCatalog))->unref(); |
- fDocCatalog->unref(); |
- SkSafeUnref(fTrailerDict); |
- SkDELETE(fFirstPageResources); |
- SkDELETE(fOtherPageResources); |
+ stream->writeText("trailer\n"); |
+ trailerDict.emitObject(stream, catalog); |
+ stream->writeText("\nstartxref\n"); |
+ stream->writeBigDecAsText(xRefFileOffset); |
+ stream->writeText("\n%%EOF"); |
} |
bool SkPDFDocument::emitPDF(SkWStream* stream) { |
@@ -92,154 +77,95 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) { |
} |
} |
- fFirstPageResources = SkNEW(SkTSet<SkPDFObject*>); |
- fOtherPageResources = SkNEW(SkTSet<SkPDFObject*>); |
+ SkPDFCatalog catalog; |
+ SkTDArray<SkPDFDict*> pageTree; |
+ SkAutoTUnref<SkPDFDict> docCatalog(SkNEW(SkPDFDict)); |
+ SkTDArray<SkPDFObject*> substitutes; |
+ SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); |
- // We haven't emitted the document before if fPageTree is empty. |
- if (fPageTree.isEmpty()) { |
- SkPDFDict* pageTreeRoot; |
- SkPDFPage::GeneratePageTree(fPages, fCatalog.get(), &fPageTree, |
- &pageTreeRoot); |
- fDocCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); |
+ SkPDFDict* pageTreeRoot; |
+ SkPDFPage::GeneratePageTree(fPages, &pageTree, &pageTreeRoot); |
+ docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); |
- /* TODO(vandebo): output intent |
- SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); |
- outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); |
- outputIntent->insert("OutputConditionIdentifier", |
- new SkPDFString("sRGB"))->unref(); |
- SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray; |
- intentArray->append(outputIntent.get()); |
- fDocCatalog->insert("OutputIntent", intentArray.get()); |
- */ |
+ /* TODO(vandebo): output intent |
+ SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); |
+ outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); |
+ outputIntent->insert("OutputConditionIdentifier", |
+ new SkPDFString("sRGB"))->unref(); |
+ SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray; |
+ intentArray->append(outputIntent.get()); |
+ fDocCatalog->insert("OutputIntent", intentArray.get()); |
+ */ |
- SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); |
- |
- bool firstPage = true; |
- /* The references returned in newResources are transfered to |
- * fFirstPageResources or fOtherPageResources depending on firstPage and |
- * knownResources doesn't have a reference but just relies on the other |
- * two sets to maintain a reference. |
- */ |
- SkTSet<SkPDFObject*> knownResources; |
- |
- // mergeInto returns the number of duplicates. |
- // If there are duplicates, there is a bug and we mess ref counting. |
- SkDEBUGCODE(int duplicates =) knownResources.mergeInto(*fFirstPageResources); |
- SkASSERT(duplicates == 0); |
- |
- for (int i = 0; i < fPages.count(); i++) { |
- if (i == 1) { |
- firstPage = false; |
- SkDEBUGCODE(duplicates =) knownResources.mergeInto(*fOtherPageResources); |
- } |
- SkTSet<SkPDFObject*> newResources; |
- fPages[i]->finalizePage( |
- fCatalog.get(), firstPage, knownResources, &newResources); |
- addResourcesToCatalog(firstPage, &newResources, fCatalog.get()); |
- if (firstPage) { |
- SkDEBUGCODE(duplicates =) fFirstPageResources->mergeInto(newResources); |
- } else { |
- SkDEBUGCODE(duplicates =) fOtherPageResources->mergeInto(newResources); |
- } |
- SkASSERT(duplicates == 0); |
- |
- SkDEBUGCODE(duplicates =) knownResources.mergeInto(newResources); |
- SkASSERT(duplicates == 0); |
- |
- fPages[i]->appendDestinations(dests); |
- } |
+ SkTSet<SkPDFObject*> knownResources; |
+ for (int i = 0; i < fPages.count(); i++) { |
+ SkTSet<SkPDFObject*> newResources; |
+ fPages[i]->finalizePage(); |
+ fPages[i]->appendDestinations(dests); |
+ } |
- if (dests->size() > 0) { |
- SkPDFDict* raw_dests = dests.get(); |
- fFirstPageResources->add(dests.detach()); // Transfer ownership. |
- fCatalog->addObject(raw_dests, true /* onFirstPage */); |
- fDocCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (raw_dests)))->unref(); |
- } |
+ // Build font subsetting info before proceeding. |
+ perform_font_subsetting(fPages, &catalog); |
- // Build font subsetting info before proceeding. |
- perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes); |
+ if (dests->size() > 0) { |
+ docCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (dests.get()))) |
+ ->unref(); |
} |
SkTSet<SkPDFObject*> resourceSet; |
- if (resourceSet.add(fDocCatalog)) { |
- fDocCatalog->addResources(&resourceSet, fCatalog); |
+ if (resourceSet.add(docCatalog)) { |
+ docCatalog->addResources(&resourceSet, &catalog); |
} |
- off_t baseOffset = SkToOffT(stream->bytesWritten()); |
- emitHeader(stream); |
+ for (int i = 0; i < resourceSet.count(); ++i) { |
+ catalog.addObject(resourceSet[i]); |
+ } |
+ |
+ size_t baseOffset = stream->bytesWritten(); |
+ emit_pdf_header(stream); |
+ |
+ SkTDArray<int32_t> objectOffsets; |
+ |
for (int i = 0; i < resourceSet.count(); ++i) { |
SkPDFObject* object = resourceSet[i]; |
- fCatalog->setFileOffset(object, |
- SkToOffT(stream->bytesWritten()) - baseOffset); |
- SkASSERT(object == fCatalog->getSubstituteObject(object)); |
- stream->writeDecAsText(fCatalog->getObjectNumber(object)); |
+ int32_t offset = SkToS32(stream->bytesWritten() - baseOffset); |
+ objectOffsets.push(offset); |
+ int32_t objectNumber = catalog.getObjectNumber(object); |
+ SkASSERT(object == catalog.getSubstituteObject(object)); |
+ stream->writeDecAsText(objectNumber); |
stream->writeText(" 0 obj\n"); // Generation number is always 0. |
- object->emitObject(stream, fCatalog); |
+ object->emitObject(stream, &catalog); |
stream->writeText("\nendobj\n"); |
} |
- fXRefFileOffset = SkToOffT(stream->bytesWritten()) - baseOffset; |
- int64_t objCount = fCatalog->emitXrefTable(stream, fPages.count() > 1); |
- emitFooter(stream, objCount); |
- return true; |
-} |
+ int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); |
-// TODO(halcanary): remove this method, since it is unused. |
-bool SkPDFDocument::setPage(int pageNumber, SkPDFDevice* pdfDevice) { |
- if (!fPageTree.isEmpty()) { |
- return false; |
+ stream->writeText("xref\n0 "); |
+ stream->writeDecAsText(objectOffsets.count() + 1); |
+ stream->writeText("\n0000000000 65535 f \n"); |
+ for (int i = 0; i < objectOffsets.count(); i++) { |
+ SkASSERT(objectOffsets[i] > 0); |
+ stream->writeBigDecAsText(objectOffsets[i], 10); |
+ stream->writeText(" 00000 n \n"); |
} |
+ int64_t objCount = objectOffsets.count() + 1; |
- pageNumber--; |
- SkASSERT(pageNumber >= 0); |
+ emit_pdf_footer(stream, &catalog, docCatalog.get(), objCount, |
+ xRefFileOffset); |
- if (pageNumber >= fPages.count()) { |
- int oldSize = fPages.count(); |
- fPages.setCount(pageNumber + 1); |
- for (int i = oldSize; i <= pageNumber; i++) { |
- fPages[i] = NULL; |
- } |
+ // The page tree has both child and parent pointers, so it creates a |
+ // reference cycle. We must clear that cycle to properly reclaim memory. |
+ for (int i = 0; i < pageTree.count(); i++) { |
+ pageTree[i]->clear(); |
} |
- |
- SkPDFPage* page = new SkPDFPage(pdfDevice); |
- SkSafeUnref(fPages[pageNumber]); |
- fPages[pageNumber] = page; // Reference from new passed to fPages. |
+ pageTree.safeUnrefAll(); |
return true; |
} |
bool SkPDFDocument::appendPage(SkPDFDevice* pdfDevice) { |
- if (!fPageTree.isEmpty()) { |
- return false; |
- } |
- |
- SkPDFPage* page = new SkPDFPage(pdfDevice); |
- fPages.push(page); // Reference from new passed to fPages. |
+ fPages.push( |
+ new SkPDFPage(pdfDevice)); // Reference from new passed to fPages. |
return true; |
} |
-// Deprecated. |
-// TODO(halcanary): remove |
-void SkPDFDocument::getCountOfFontTypes( |
- int counts[SkAdvancedTypefaceMetrics::kOther_Font + 2]) const { |
- sk_bzero(counts, sizeof(int) * |
- (SkAdvancedTypefaceMetrics::kOther_Font + 2)); |
- SkTDArray<SkFontID> seenFonts; |
- int notEmbeddable = 0; |
- |
- for (int pageNumber = 0; pageNumber < fPages.count(); pageNumber++) { |
- const SkTDArray<SkPDFFont*>& fontResources = |
- fPages[pageNumber]->getFontResources(); |
- for (int font = 0; font < fontResources.count(); font++) { |
- SkFontID fontID = fontResources[font]->typeface()->uniqueID(); |
- if (seenFonts.find(fontID) == -1) { |
- counts[fontResources[font]->getType()]++; |
- seenFonts.push(fontID); |
- if (!fontResources[font]->canEmbed()) { |
- notEmbeddable++; |
- } |
- } |
- } |
- } |
- counts[SkAdvancedTypefaceMetrics::kOther_Font + 1] = notEmbeddable; |
-} |
// TODO(halcanary): expose notEmbeddableCount in SkDocument |
void SkPDFDocument::getCountOfFontTypes( |
@@ -278,34 +204,3 @@ void SkPDFDocument::getCountOfFontTypes( |
} |
} |
-void SkPDFDocument::emitHeader(SkWStream* stream) { |
- stream->writeText("%PDF-1.4\n%"); |
- // The PDF spec recommends including a comment with four bytes, all |
- // with their high bits set. This is "Skia" with the high bits set. |
- stream->write32(0xD3EBE9E1); |
- stream->writeText("\n"); |
-} |
- |
-//TODO(halcanary): remove this function |
-size_t SkPDFDocument::headerSize() { |
- SkDynamicMemoryWStream buffer; |
- emitHeader(&buffer); |
- return buffer.getOffset(); |
-} |
- |
-void SkPDFDocument::emitFooter(SkWStream* stream, int64_t objCount) { |
- if (NULL == fTrailerDict) { |
- fTrailerDict = SkNEW(SkPDFDict); |
- |
- // TODO(vandebo): Linearized format will take a Prev entry too. |
- // TODO(vandebo): PDF/A requires an ID entry. |
- fTrailerDict->insertInt("Size", int(objCount)); |
- fTrailerDict->insert("Root", new SkPDFObjRef(fDocCatalog))->unref(); |
- } |
- |
- stream->writeText("trailer\n"); |
- fTrailerDict->emitObject(stream, fCatalog.get()); |
- stream->writeText("\nstartxref\n"); |
- stream->writeBigDecAsText(fXRefFileOffset); |
- stream->writeText("\n%%EOF"); |
-} |