Index: src/doc/SkDocument_PDF.cpp |
diff --git a/src/doc/SkDocument_PDF.cpp b/src/doc/SkDocument_PDF.cpp |
index 979cd00e0db5aa249d73158b2f31114992c524eb..ae0386ab1a389272f531eba51c5e29765c60f619 100644 |
--- a/src/doc/SkDocument_PDF.cpp |
+++ b/src/doc/SkDocument_PDF.cpp |
@@ -10,7 +10,8 @@ |
#include "SkPDFCatalog.h" |
#include "SkPDFDevice.h" |
#include "SkPDFFont.h" |
-#include "SkPDFPage.h" |
+#include "SkPDFResourceDict.h" |
+#include "SkPDFStream.h" |
#include "SkPDFTypes.h" |
#include "SkStream.h" |
@@ -40,13 +41,14 @@ static void emit_pdf_footer(SkWStream* stream, |
stream->writeText("\n%%EOF"); |
} |
-static void perform_font_subsetting(SkPDFCatalog* catalog, |
- const SkTDArray<SkPDFPage*>& pages) { |
+static void perform_font_subsetting( |
+ const SkTDArray<const SkPDFDevice*>& pageDevices, |
+ SkPDFCatalog* catalog) { |
SkASSERT(catalog); |
SkPDFGlyphSetMap usage; |
- for (int i = 0; i < pages.count(); ++i) { |
- usage.merge(pages[i]->getFontGlyphUsage()); |
+ for (int i = 0; i < pageDevices.count(); ++i) { |
+ usage.merge(pageDevices[i]->getFontGlyphUsage()); |
} |
SkPDFGlyphSetMap::F2BIter iterator(usage); |
const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); |
@@ -60,23 +62,125 @@ static void perform_font_subsetting(SkPDFCatalog* catalog, |
} |
} |
+static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) { |
+ SkAutoTUnref<SkPDFDict> page(SkNEW_ARGS(SkPDFDict, ("Page"))); |
+ SkAutoTUnref<SkPDFResourceDict> deviceResourceDict( |
+ pageDevice->createResourceDict()); |
+ page->insert("Resources", deviceResourceDict.get()); |
+ |
+ SkAutoTUnref<SkPDFArray> mediaBox(pageDevice->copyMediaBox()); |
+ page->insert("MediaBox", mediaBox.get()); |
+ |
+ SkPDFArray* annots = pageDevice->getAnnotations(); |
+ if (annots && annots->size() > 0) { |
+ page->insert("Annots", annots); |
+ } |
+ |
+ SkAutoTDelete<SkStreamAsset> content(pageDevice->content()); |
+ SkAutoTUnref<SkPDFStream> contentStream( |
+ SkNEW_ARGS(SkPDFStream, (content.get()))); |
+ page->insert("Contents", new SkPDFObjRef(contentStream.get()))->unref(); |
+ return page.detach(); |
+} |
+ |
+static void generate_page_tree(const SkTDArray<SkPDFDict*>& pages, |
+ SkTDArray<SkPDFDict*>* pageTree, |
+ SkPDFDict** rootNode) { |
+ // PDF wants a tree describing all the pages in the document. We arbitrary |
+ // choose 8 (kNodeSize) as the number of allowed children. The internal |
+ // nodes have type "Pages" with an array of children, a parent pointer, and |
+ // the number of leaves below the node as "Count." The leaves are passed |
+ // into the method, have type "Page" and need a parent pointer. This method |
+ // builds the tree bottom up, skipping internal nodes that would have only |
+ // one child. |
+ static const int kNodeSize = 8; |
+ |
+ SkAutoTUnref<SkPDFName> kidsName(new SkPDFName("Kids")); |
+ SkAutoTUnref<SkPDFName> countName(new SkPDFName("Count")); |
+ SkAutoTUnref<SkPDFName> parentName(new SkPDFName("Parent")); |
+ |
+ // curNodes takes a reference to its items, which it passes to pageTree. |
+ SkTDArray<SkPDFDict*> curNodes; |
+ curNodes.setReserve(pages.count()); |
+ for (int i = 0; i < pages.count(); i++) { |
+ SkSafeRef(pages[i]); |
+ curNodes.push(pages[i]); |
+ } |
+ |
+ // nextRoundNodes passes its references to nodes on to curNodes. |
+ SkTDArray<SkPDFDict*> nextRoundNodes; |
+ nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize); |
+ |
+ int treeCapacity = kNodeSize; |
+ do { |
+ for (int i = 0; i < curNodes.count(); ) { |
+ if (i > 0 && i + 1 == curNodes.count()) { |
+ nextRoundNodes.push(curNodes[i]); |
+ break; |
+ } |
+ |
+ SkPDFDict* newNode = new SkPDFDict("Pages"); |
+ SkAutoTUnref<SkPDFObjRef> newNodeRef(new SkPDFObjRef(newNode)); |
+ |
+ SkAutoTUnref<SkPDFArray> kids(new SkPDFArray); |
+ kids->reserve(kNodeSize); |
+ |
+ int count = 0; |
+ for (; i < curNodes.count() && count < kNodeSize; i++, count++) { |
+ curNodes[i]->insert(parentName.get(), newNodeRef.get()); |
+ kids->append(new SkPDFObjRef(curNodes[i]))->unref(); |
+ |
+ // TODO(vandebo): put the objects in strict access order. |
+ // Probably doesn't matter because they are so small. |
+ if (curNodes[i] != pages[0]) { |
+ pageTree->push(curNodes[i]); // Transfer reference. |
+ } else { |
+ SkSafeUnref(curNodes[i]); |
+ } |
+ } |
+ |
+ // treeCapacity is the number of leaf nodes possible for the |
+ // current set of subtrees being generated. (i.e. 8, 64, 512, ...). |
+ // It is hard to count the number of leaf nodes in the current |
+ // subtree. However, by construction, we know that unless it's the |
+ // last subtree for the current depth, the leaf count will be |
+ // treeCapacity, otherwise it's what ever is left over after |
+ // consuming treeCapacity chunks. |
+ int pageCount = treeCapacity; |
+ if (i == curNodes.count()) { |
+ pageCount = ((pages.count() - 1) % treeCapacity) + 1; |
+ } |
+ newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref(); |
+ newNode->insert(kidsName.get(), kids.get()); |
+ nextRoundNodes.push(newNode); // Transfer reference. |
+ } |
+ |
+ curNodes = nextRoundNodes; |
+ nextRoundNodes.rewind(); |
+ treeCapacity *= kNodeSize; |
+ } while (curNodes.count() > 1); |
+ |
+ pageTree->push(curNodes[0]); // Transfer reference. |
+ if (rootNode) { |
+ *rootNode = curNodes[0]; |
+ } |
+} |
+ |
static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, |
SkWStream* stream) { |
if (pageDevices.isEmpty()) { |
return false; |
} |
- SkTDArray<SkPDFPage*> pages; |
+ SkTDArray<SkPDFDict*> pages; |
SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); |
for (int i = 0; i < pageDevices.count(); i++) { |
SkASSERT(pageDevices[i]); |
SkASSERT(i == 0 || |
pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); |
- // Reference from new passed to pages. |
- SkAutoTUnref<SkPDFPage> page(SkNEW_ARGS(SkPDFPage, (pageDevices[i]))); |
- page->finalizePage(); |
- page->appendDestinations(dests); |
+ SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i])); |
+ pageDevices[i]->appendDestinations(dests, page.get()); |
pages.push(page.detach()); |
} |
SkPDFCatalog catalog; |
@@ -85,7 +189,7 @@ static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, |
SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog"))); |
SkPDFDict* pageTreeRoot; |
- SkPDFPage::GeneratePageTree(pages, &pageTree, &pageTreeRoot); |
+ generate_page_tree(pages, &pageTree, &pageTreeRoot); |
docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); |
@@ -105,7 +209,7 @@ static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, |
} |
// Build font subsetting info before proceeding. |
- perform_font_subsetting(&catalog, pages); |
+ perform_font_subsetting(pageDevices, &catalog); |
SkTSet<SkPDFObject*> resourceSet; |
if (resourceSet.add(docCatalog.get())) { |