OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkDocument.h" | 8 #include "SkDocument.h" |
9 #include "SkPDFCanon.h" | 9 #include "SkPDFCanon.h" |
10 #include "SkPDFCatalog.h" | 10 #include "SkPDFCatalog.h" |
11 #include "SkPDFDevice.h" | 11 #include "SkPDFDevice.h" |
12 #include "SkPDFFont.h" | 12 #include "SkPDFFont.h" |
13 #include "SkPDFPage.h" | 13 #include "SkPDFResourceDict.h" |
| 14 #include "SkPDFStream.h" |
14 #include "SkPDFTypes.h" | 15 #include "SkPDFTypes.h" |
15 #include "SkStream.h" | 16 #include "SkStream.h" |
16 | 17 |
17 static void emit_pdf_header(SkWStream* stream) { | 18 static void emit_pdf_header(SkWStream* stream) { |
18 stream->writeText("%PDF-1.4\n%"); | 19 stream->writeText("%PDF-1.4\n%"); |
19 // The PDF spec recommends including a comment with four bytes, all | 20 // The PDF spec recommends including a comment with four bytes, all |
20 // with their high bits set. This is "Skia" with the high bits set. | 21 // with their high bits set. This is "Skia" with the high bits set. |
21 stream->write32(0xD3EBE9E1); | 22 stream->write32(0xD3EBE9E1); |
22 stream->writeText("\n"); | 23 stream->writeText("\n"); |
23 } | 24 } |
24 | 25 |
25 static void emit_pdf_footer(SkWStream* stream, | 26 static void emit_pdf_footer(SkWStream* stream, |
26 SkPDFCatalog* catalog, | 27 SkPDFCatalog* catalog, |
27 SkPDFObject* docCatalog, | 28 SkPDFObject* docCatalog, |
28 int64_t objCount, | 29 int64_t objCount, |
29 int32_t xRefFileOffset) { | 30 int32_t xRefFileOffset) { |
30 SkPDFDict trailerDict; | 31 SkPDFDict trailerDict; |
31 // TODO(vandebo): Linearized format will take a Prev entry too. | 32 // TODO(vandebo): Linearized format will take a Prev entry too. |
32 // TODO(vandebo): PDF/A requires an ID entry. | 33 // TODO(vandebo): PDF/A requires an ID entry. |
33 trailerDict.insertInt("Size", int(objCount)); | 34 trailerDict.insertInt("Size", int(objCount)); |
34 trailerDict.insert("Root", new SkPDFObjRef(docCatalog))->unref(); | 35 trailerDict.insert("Root", new SkPDFObjRef(docCatalog))->unref(); |
35 | 36 |
36 stream->writeText("trailer\n"); | 37 stream->writeText("trailer\n"); |
37 trailerDict.emitObject(stream, catalog); | 38 trailerDict.emitObject(stream, catalog); |
38 stream->writeText("\nstartxref\n"); | 39 stream->writeText("\nstartxref\n"); |
39 stream->writeBigDecAsText(xRefFileOffset); | 40 stream->writeBigDecAsText(xRefFileOffset); |
40 stream->writeText("\n%%EOF"); | 41 stream->writeText("\n%%EOF"); |
41 } | 42 } |
42 | 43 |
43 static void perform_font_subsetting(SkPDFCatalog* catalog, | 44 static void perform_font_subsetting( |
44 const SkTDArray<SkPDFPage*>& pages) { | 45 const SkTDArray<const SkPDFDevice*>& pageDevices, |
| 46 SkPDFCatalog* catalog) { |
45 SkASSERT(catalog); | 47 SkASSERT(catalog); |
46 | 48 |
47 SkPDFGlyphSetMap usage; | 49 SkPDFGlyphSetMap usage; |
48 for (int i = 0; i < pages.count(); ++i) { | 50 for (int i = 0; i < pageDevices.count(); ++i) { |
49 usage.merge(pages[i]->getFontGlyphUsage()); | 51 usage.merge(pageDevices[i]->getFontGlyphUsage()); |
50 } | 52 } |
51 SkPDFGlyphSetMap::F2BIter iterator(usage); | 53 SkPDFGlyphSetMap::F2BIter iterator(usage); |
52 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); | 54 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); |
53 while (entry) { | 55 while (entry) { |
54 SkAutoTUnref<SkPDFFont> subsetFont( | 56 SkAutoTUnref<SkPDFFont> subsetFont( |
55 entry->fFont->getFontSubset(entry->fGlyphSet)); | 57 entry->fFont->getFontSubset(entry->fGlyphSet)); |
56 if (subsetFont) { | 58 if (subsetFont) { |
57 catalog->setSubstitute(entry->fFont, subsetFont.get()); | 59 catalog->setSubstitute(entry->fFont, subsetFont.get()); |
58 } | 60 } |
59 entry = iterator.next(); | 61 entry = iterator.next(); |
60 } | 62 } |
61 } | 63 } |
62 | 64 |
| 65 static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) { |
| 66 SkAutoTUnref<SkPDFDict> page(SkNEW_ARGS(SkPDFDict, ("Page"))); |
| 67 SkAutoTUnref<SkPDFResourceDict> deviceResourceDict( |
| 68 pageDevice->createResourceDict()); |
| 69 page->insert("Resources", deviceResourceDict.get()); |
| 70 |
| 71 SkAutoTUnref<SkPDFArray> mediaBox(pageDevice->copyMediaBox()); |
| 72 page->insert("MediaBox", mediaBox.get()); |
| 73 |
| 74 SkPDFArray* annots = pageDevice->getAnnotations(); |
| 75 if (annots && annots->size() > 0) { |
| 76 page->insert("Annots", annots); |
| 77 } |
| 78 |
| 79 SkAutoTDelete<SkStreamAsset> content(pageDevice->content()); |
| 80 SkAutoTUnref<SkPDFStream> contentStream( |
| 81 SkNEW_ARGS(SkPDFStream, (content.get()))); |
| 82 page->insert("Contents", new SkPDFObjRef(contentStream.get()))->unref(); |
| 83 return page.detach(); |
| 84 } |
| 85 |
| 86 static void generate_page_tree(const SkTDArray<SkPDFDict*>& pages, |
| 87 SkTDArray<SkPDFDict*>* pageTree, |
| 88 SkPDFDict** rootNode) { |
| 89 // PDF wants a tree describing all the pages in the document. We arbitrary |
| 90 // choose 8 (kNodeSize) as the number of allowed children. The internal |
| 91 // nodes have type "Pages" with an array of children, a parent pointer, and |
| 92 // the number of leaves below the node as "Count." The leaves are passed |
| 93 // into the method, have type "Page" and need a parent pointer. This method |
| 94 // builds the tree bottom up, skipping internal nodes that would have only |
| 95 // one child. |
| 96 static const int kNodeSize = 8; |
| 97 |
| 98 SkAutoTUnref<SkPDFName> kidsName(new SkPDFName("Kids")); |
| 99 SkAutoTUnref<SkPDFName> countName(new SkPDFName("Count")); |
| 100 SkAutoTUnref<SkPDFName> parentName(new SkPDFName("Parent")); |
| 101 |
| 102 // curNodes takes a reference to its items, which it passes to pageTree. |
| 103 SkTDArray<SkPDFDict*> curNodes; |
| 104 curNodes.setReserve(pages.count()); |
| 105 for (int i = 0; i < pages.count(); i++) { |
| 106 SkSafeRef(pages[i]); |
| 107 curNodes.push(pages[i]); |
| 108 } |
| 109 |
| 110 // nextRoundNodes passes its references to nodes on to curNodes. |
| 111 SkTDArray<SkPDFDict*> nextRoundNodes; |
| 112 nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize); |
| 113 |
| 114 int treeCapacity = kNodeSize; |
| 115 do { |
| 116 for (int i = 0; i < curNodes.count(); ) { |
| 117 if (i > 0 && i + 1 == curNodes.count()) { |
| 118 nextRoundNodes.push(curNodes[i]); |
| 119 break; |
| 120 } |
| 121 |
| 122 SkPDFDict* newNode = new SkPDFDict("Pages"); |
| 123 SkAutoTUnref<SkPDFObjRef> newNodeRef(new SkPDFObjRef(newNode)); |
| 124 |
| 125 SkAutoTUnref<SkPDFArray> kids(new SkPDFArray); |
| 126 kids->reserve(kNodeSize); |
| 127 |
| 128 int count = 0; |
| 129 for (; i < curNodes.count() && count < kNodeSize; i++, count++) { |
| 130 curNodes[i]->insert(parentName.get(), newNodeRef.get()); |
| 131 kids->append(new SkPDFObjRef(curNodes[i]))->unref(); |
| 132 |
| 133 // TODO(vandebo): put the objects in strict access order. |
| 134 // Probably doesn't matter because they are so small. |
| 135 if (curNodes[i] != pages[0]) { |
| 136 pageTree->push(curNodes[i]); // Transfer reference. |
| 137 } else { |
| 138 SkSafeUnref(curNodes[i]); |
| 139 } |
| 140 } |
| 141 |
| 142 // treeCapacity is the number of leaf nodes possible for the |
| 143 // current set of subtrees being generated. (i.e. 8, 64, 512, ...). |
| 144 // It is hard to count the number of leaf nodes in the current |
| 145 // subtree. However, by construction, we know that unless it's the |
| 146 // last subtree for the current depth, the leaf count will be |
| 147 // treeCapacity, otherwise it's what ever is left over after |
| 148 // consuming treeCapacity chunks. |
| 149 int pageCount = treeCapacity; |
| 150 if (i == curNodes.count()) { |
| 151 pageCount = ((pages.count() - 1) % treeCapacity) + 1; |
| 152 } |
| 153 newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref(); |
| 154 newNode->insert(kidsName.get(), kids.get()); |
| 155 nextRoundNodes.push(newNode); // Transfer reference. |
| 156 } |
| 157 |
| 158 curNodes = nextRoundNodes; |
| 159 nextRoundNodes.rewind(); |
| 160 treeCapacity *= kNodeSize; |
| 161 } while (curNodes.count() > 1); |
| 162 |
| 163 pageTree->push(curNodes[0]); // Transfer reference. |
| 164 if (rootNode) { |
| 165 *rootNode = curNodes[0]; |
| 166 } |
| 167 } |
| 168 |
63 static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, | 169 static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, |
64 SkWStream* stream) { | 170 SkWStream* stream) { |
65 if (pageDevices.isEmpty()) { | 171 if (pageDevices.isEmpty()) { |
66 return false; | 172 return false; |
67 } | 173 } |
68 | 174 |
69 SkTDArray<SkPDFPage*> pages; | 175 SkTDArray<SkPDFDict*> pages; |
70 SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); | 176 SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); |
71 | 177 |
72 for (int i = 0; i < pageDevices.count(); i++) { | 178 for (int i = 0; i < pageDevices.count(); i++) { |
73 SkASSERT(pageDevices[i]); | 179 SkASSERT(pageDevices[i]); |
74 SkASSERT(i == 0 || | 180 SkASSERT(i == 0 || |
75 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); | 181 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); |
76 // Reference from new passed to pages. | 182 SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i])); |
77 SkAutoTUnref<SkPDFPage> page(SkNEW_ARGS(SkPDFPage, (pageDevices[i]))); | 183 pageDevices[i]->appendDestinations(dests, page.get()); |
78 page->finalizePage(); | |
79 page->appendDestinations(dests); | |
80 pages.push(page.detach()); | 184 pages.push(page.detach()); |
81 } | 185 } |
82 SkPDFCatalog catalog; | 186 SkPDFCatalog catalog; |
83 | 187 |
84 SkTDArray<SkPDFDict*> pageTree; | 188 SkTDArray<SkPDFDict*> pageTree; |
85 SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog"))); | 189 SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog"))); |
86 | 190 |
87 SkPDFDict* pageTreeRoot; | 191 SkPDFDict* pageTreeRoot; |
88 SkPDFPage::GeneratePageTree(pages, &pageTree, &pageTreeRoot); | 192 generate_page_tree(pages, &pageTree, &pageTreeRoot); |
89 | 193 |
90 docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); | 194 docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); |
91 | 195 |
92 /* TODO(vandebo): output intent | 196 /* TODO(vandebo): output intent |
93 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); | 197 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); |
94 outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); | 198 outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); |
95 outputIntent->insert("OutputConditionIdentifier", | 199 outputIntent->insert("OutputConditionIdentifier", |
96 new SkPDFString("sRGB"))->unref(); | 200 new SkPDFString("sRGB"))->unref(); |
97 SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray; | 201 SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray; |
98 intentArray->append(outputIntent.get()); | 202 intentArray->append(outputIntent.get()); |
99 docCatalog->insert("OutputIntent", intentArray.get()); | 203 docCatalog->insert("OutputIntent", intentArray.get()); |
100 */ | 204 */ |
101 | 205 |
102 if (dests->size() > 0) { | 206 if (dests->size() > 0) { |
103 docCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (dests.get()))) | 207 docCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (dests.get()))) |
104 ->unref(); | 208 ->unref(); |
105 } | 209 } |
106 | 210 |
107 // Build font subsetting info before proceeding. | 211 // Build font subsetting info before proceeding. |
108 perform_font_subsetting(&catalog, pages); | 212 perform_font_subsetting(pageDevices, &catalog); |
109 | 213 |
110 SkTSet<SkPDFObject*> resourceSet; | 214 SkTSet<SkPDFObject*> resourceSet; |
111 if (resourceSet.add(docCatalog.get())) { | 215 if (resourceSet.add(docCatalog.get())) { |
112 docCatalog->addResources(&resourceSet, &catalog); | 216 docCatalog->addResources(&resourceSet, &catalog); |
113 } | 217 } |
114 for (int i = 0; i < resourceSet.count(); ++i) { | 218 for (int i = 0; i < resourceSet.count(); ++i) { |
115 SkAssertResult(catalog.addObject(resourceSet[i])); | 219 SkAssertResult(catalog.addObject(resourceSet[i])); |
116 } | 220 } |
117 | 221 |
118 size_t baseOffset = SkToOffT(stream->bytesWritten()); | 222 size_t baseOffset = SkToOffT(stream->bytesWritten()); |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 | 363 |
260 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { | 364 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { |
261 SkFILEWStream* stream = SkNEW_ARGS(SkFILEWStream, (path)); | 365 SkFILEWStream* stream = SkNEW_ARGS(SkFILEWStream, (path)); |
262 if (!stream->isValid()) { | 366 if (!stream->isValid()) { |
263 SkDELETE(stream); | 367 SkDELETE(stream); |
264 return NULL; | 368 return NULL; |
265 } | 369 } |
266 auto delete_wstream = [](SkWStream* stream, bool) { SkDELETE(stream); }; | 370 auto delete_wstream = [](SkWStream* stream, bool) { SkDELETE(stream); }; |
267 return SkNEW_ARGS(SkDocument_PDF, (stream, delete_wstream, dpi)); | 371 return SkNEW_ARGS(SkDocument_PDF, (stream, delete_wstream, dpi)); |
268 } | 372 } |
OLD | NEW |