1 /*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkDocument.h"
9 #include "SkPDFCanon.h"
10 #include "SkPDFDevice.h"
11 #include "SkPDFFont.h"
12 #include "SkPDFStream.h"
13 #include "SkPDFTypes.h"
14 #include "SkStream.h"
15
emit_pdf_header(SkWStream * stream)16 static void emit_pdf_header(SkWStream* stream) {
17 stream->writeText("%PDF-1.4\n%");
18 // The PDF spec recommends including a comment with four bytes, all
19 // with their high bits set. This is "Skia" with the high bits set.
20 stream->write32(0xD3EBE9E1);
21 stream->writeText("\n");
22 }
23
emit_pdf_footer(SkWStream * stream,const SkPDFObjNumMap & objNumMap,const SkPDFSubstituteMap & substitutes,SkPDFObject * docCatalog,int64_t objCount,int32_t xRefFileOffset)24 static void emit_pdf_footer(SkWStream* stream,
25 const SkPDFObjNumMap& objNumMap,
26 const SkPDFSubstituteMap& substitutes,
27 SkPDFObject* docCatalog,
28 int64_t objCount,
29 int32_t xRefFileOffset) {
30 SkPDFDict trailerDict;
31 // TODO(vandebo): Linearized format will take a Prev entry too.
32 // TODO(vandebo): PDF/A requires an ID entry.
33 trailerDict.insertInt("Size", int(objCount));
34 trailerDict.insertObjRef("Root", SkRef(docCatalog));
35
36 stream->writeText("trailer\n");
37 trailerDict.emitObject(stream, objNumMap, substitutes);
38 stream->writeText("\nstartxref\n");
39 stream->writeBigDecAsText(xRefFileOffset);
40 stream->writeText("\n%%EOF");
41 }
42
perform_font_subsetting(const SkTDArray<const SkPDFDevice * > & pageDevices,SkPDFSubstituteMap * substituteMap)43 static void perform_font_subsetting(
44 const SkTDArray<const SkPDFDevice*>& pageDevices,
45 SkPDFSubstituteMap* substituteMap) {
46 SkASSERT(substituteMap);
47
48 SkPDFGlyphSetMap usage;
49 for (int i = 0; i < pageDevices.count(); ++i) {
50 usage.merge(pageDevices[i]->getFontGlyphUsage());
51 }
52 SkPDFGlyphSetMap::F2BIter iterator(usage);
53 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
54 while (entry) {
55 SkAutoTUnref<SkPDFFont> subsetFont(
56 entry->fFont->getFontSubset(entry->fGlyphSet));
57 if (subsetFont) {
58 substituteMap->setSubstitute(entry->fFont, subsetFont.get());
59 }
60 entry = iterator.next();
61 }
62 }
63
create_pdf_page_content(const SkPDFDevice * pageDevice)64 static SkPDFObject* create_pdf_page_content(const SkPDFDevice* pageDevice) {
65 SkAutoTDelete<SkStreamAsset> content(pageDevice->content());
66 return SkNEW_ARGS(SkPDFStream, (content.get()));
67 }
68
create_pdf_page(const SkPDFDevice * pageDevice)69 static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) {
70 SkAutoTUnref<SkPDFDict> page(SkNEW_ARGS(SkPDFDict, ("Page")));
71 page->insertObject("Resources", pageDevice->createResourceDict());
72 page->insertObject("MediaBox", pageDevice->copyMediaBox());
73 if (SkPDFArray* annots = pageDevice->getAnnotations()) {
74 SkASSERT(annots->size() > 0);
75 page->insertObject("Annots", SkRef(annots));
76 }
77 page->insertObjRef("Contents", create_pdf_page_content(pageDevice));
78 return page.detach();
79 }
80
generate_page_tree(const SkTDArray<SkPDFDict * > & pages,SkTDArray<SkPDFDict * > * pageTree,SkPDFDict ** rootNode)81 static void generate_page_tree(const SkTDArray<SkPDFDict*>& pages,
82 SkTDArray<SkPDFDict*>* pageTree,
83 SkPDFDict** rootNode) {
84 // PDF wants a tree describing all the pages in the document. We arbitrary
85 // choose 8 (kNodeSize) as the number of allowed children. The internal
86 // nodes have type "Pages" with an array of children, a parent pointer, and
87 // the number of leaves below the node as "Count." The leaves are passed
88 // into the method, have type "Page" and need a parent pointer. This method
89 // builds the tree bottom up, skipping internal nodes that would have only
90 // one child.
91 static const int kNodeSize = 8;
92
93 // curNodes takes a reference to its items, which it passes to pageTree.
94 SkTDArray<SkPDFDict*> curNodes;
95 curNodes.setReserve(pages.count());
96 for (int i = 0; i < pages.count(); i++) {
97 SkSafeRef(pages[i]);
98 curNodes.push(pages[i]);
99 }
100
101 // nextRoundNodes passes its references to nodes on to curNodes.
102 SkTDArray<SkPDFDict*> nextRoundNodes;
103 nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
104
105 int treeCapacity = kNodeSize;
106 do {
107 for (int i = 0; i < curNodes.count(); ) {
108 if (i > 0 && i + 1 == curNodes.count()) {
109 nextRoundNodes.push(curNodes[i]);
110 break;
111 }
112
113 SkAutoTUnref<SkPDFDict> newNode(new SkPDFDict("Pages"));
114 SkAutoTUnref<SkPDFArray> kids(new SkPDFArray);
115 kids->reserve(kNodeSize);
116
117 int count = 0;
118 for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
119 curNodes[i]->insertObjRef("Parent", SkRef(newNode.get()));
120 kids->appendObjRef(SkRef(curNodes[i]));
121
122 // TODO(vandebo): put the objects in strict access order.
123 // Probably doesn't matter because they are so small.
124 if (curNodes[i] != pages[0]) {
125 pageTree->push(curNodes[i]); // Transfer reference.
126 } else {
127 SkSafeUnref(curNodes[i]);
128 }
129 }
130
131 // treeCapacity is the number of leaf nodes possible for the
132 // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
133 // It is hard to count the number of leaf nodes in the current
134 // subtree. However, by construction, we know that unless it's the
135 // last subtree for the current depth, the leaf count will be
136 // treeCapacity, otherwise it's what ever is left over after
137 // consuming treeCapacity chunks.
138 int pageCount = treeCapacity;
139 if (i == curNodes.count()) {
140 pageCount = ((pages.count() - 1) % treeCapacity) + 1;
141 }
142 newNode->insertInt("Count", pageCount);
143 newNode->insertObject("Kids", kids.detach());
144 nextRoundNodes.push(newNode.detach()); // Transfer reference.
145 }
146
147 curNodes = nextRoundNodes;
148 nextRoundNodes.rewind();
149 treeCapacity *= kNodeSize;
150 } while (curNodes.count() > 1);
151
152 pageTree->push(curNodes[0]); // Transfer reference.
153 if (rootNode) {
154 *rootNode = curNodes[0];
155 }
156 }
157
emit_pdf_document(const SkTDArray<const SkPDFDevice * > & pageDevices,SkWStream * stream)158 static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices,
159 SkWStream* stream) {
160 if (pageDevices.isEmpty()) {
161 return false;
162 }
163
164 SkTDArray<SkPDFDict*> pages;
165 SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict));
166
167 for (int i = 0; i < pageDevices.count(); i++) {
168 SkASSERT(pageDevices[i]);
169 SkASSERT(i == 0 ||
170 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon());
171 SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i]));
172 pageDevices[i]->appendDestinations(dests, page.get());
173 pages.push(page.detach());
174 }
175
176 SkTDArray<SkPDFDict*> pageTree;
177 SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog")));
178
179 SkPDFDict* pageTreeRoot;
180 generate_page_tree(pages, &pageTree, &pageTreeRoot);
181 docCatalog->insertObjRef("Pages", SkRef(pageTreeRoot));
182
183 if (dests->size() > 0) {
184 docCatalog->insertObjRef("Dests", dests.detach());
185 }
186
187 /* TODO(vandebo): output intent
188 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
189 outputIntent->insertName("S", "GTS_PDFA1");
190 outputIntent->insertString("OutputConditionIdentifier", "sRGB");
191 SkAutoTUnref<SkPDFArray> intentArray(new SkPDFArray);
192 intentArray->appendObject(SkRef(outputIntent.get()));
193 docCatalog->insertObject("OutputIntent", intentArray.detach());
194 */
195
196 // Build font subsetting info before proceeding.
197 SkPDFSubstituteMap substitutes;
198 perform_font_subsetting(pageDevices, &substitutes);
199
200 SkPDFObjNumMap objNumMap;
201 if (objNumMap.addObject(docCatalog.get())) {
202 docCatalog->addResources(&objNumMap, substitutes);
203 }
204 size_t baseOffset = SkToOffT(stream->bytesWritten());
205 emit_pdf_header(stream);
206 SkTDArray<int32_t> offsets;
207 for (int i = 0; i < objNumMap.objects().count(); ++i) {
208 SkPDFObject* object = objNumMap.objects()[i];
209 offsets.push(SkToS32(stream->bytesWritten() - baseOffset));
210 SkASSERT(object == substitutes.getSubstitute(object));
211 SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
212 stream->writeDecAsText(i + 1);
213 stream->writeText(" 0 obj\n"); // Generation number is always 0.
214 object->emitObject(stream, objNumMap, substitutes);
215 stream->writeText("\nendobj\n");
216 }
217 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);
218
219 // Include the zeroth object in the count.
220 int32_t objCount = SkToS32(offsets.count() + 1);
221
222 stream->writeText("xref\n0 ");
223 stream->writeDecAsText(objCount);
224 stream->writeText("\n0000000000 65535 f \n");
225 for (int i = 0; i < offsets.count(); i++) {
226 SkASSERT(offsets[i] > 0);
227 stream->writeBigDecAsText(offsets[i], 10);
228 stream->writeText(" 00000 n \n");
229 }
230 emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount,
231 xRefFileOffset);
232
233 // The page tree has both child and parent pointers, so it creates a
234 // reference cycle. We must clear that cycle to properly reclaim memory.
235 for (int i = 0; i < pageTree.count(); i++) {
236 pageTree[i]->clear();
237 }
238 pageTree.safeUnrefAll();
239 pages.unrefAll();
240 return true;
241 }
242
243 #if 0
244 // TODO(halcanary): expose notEmbeddableCount in SkDocument
245 void GetCountOfFontTypes(
246 const SkTDArray<SkPDFDevice*>& pageDevices,
247 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
248 int* notSubsettableCount,
249 int* notEmbeddableCount) {
250 sk_bzero(counts, sizeof(int) *
251 (SkAdvancedTypefaceMetrics::kOther_Font + 1));
252 SkTDArray<SkFontID> seenFonts;
253 int notSubsettable = 0;
254 int notEmbeddable = 0;
255
256 for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) {
257 const SkTDArray<SkPDFFont*>& fontResources =
258 pageDevices[pageNumber]->getFontResources();
259 for (int font = 0; font < fontResources.count(); font++) {
260 SkFontID fontID = fontResources[font]->typeface()->uniqueID();
261 if (seenFonts.find(fontID) == -1) {
262 counts[fontResources[font]->getType()]++;
263 seenFonts.push(fontID);
264 if (!fontResources[font]->canSubset()) {
265 notSubsettable++;
266 }
267 if (!fontResources[font]->canEmbed()) {
268 notEmbeddable++;
269 }
270 }
271 }
272 }
273 if (notSubsettableCount) {
274 *notSubsettableCount = notSubsettable;
275
276 }
277 if (notEmbeddableCount) {
278 *notEmbeddableCount = notEmbeddable;
279 }
280 }
281 #endif
282 ////////////////////////////////////////////////////////////////////////////////
283
284 namespace {
285 class SkDocument_PDF : public SkDocument {
286 public:
SkDocument_PDF(SkWStream * stream,void (* doneProc)(SkWStream *,bool),SkScalar rasterDpi)287 SkDocument_PDF(SkWStream* stream,
288 void (*doneProc)(SkWStream*, bool),
289 SkScalar rasterDpi)
290 : SkDocument(stream, doneProc)
291 , fRasterDpi(rasterDpi) {}
292
~SkDocument_PDF()293 virtual ~SkDocument_PDF() {
294 // subclasses must call close() in their destructors
295 this->close();
296 }
297
298 protected:
onBeginPage(SkScalar width,SkScalar height,const SkRect & trimBox)299 SkCanvas* onBeginPage(SkScalar width, SkScalar height,
300 const SkRect& trimBox) override {
301 SkASSERT(!fCanvas.get());
302
303 SkISize pageSize = SkISize::Make(
304 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
305 SkAutoTUnref<SkPDFDevice> device(
306 SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon));
307 fCanvas.reset(SkNEW_ARGS(SkCanvas, (device.get())));
308 fPageDevices.push(device.detach());
309 fCanvas->clipRect(trimBox);
310 fCanvas->translate(trimBox.x(), trimBox.y());
311 return fCanvas.get();
312 }
313
onEndPage()314 void onEndPage() override {
315 SkASSERT(fCanvas.get());
316 fCanvas->flush();
317 fCanvas.reset(NULL);
318 }
319
onClose(SkWStream * stream)320 bool onClose(SkWStream* stream) override {
321 SkASSERT(!fCanvas.get());
322
323 bool success = emit_pdf_document(fPageDevices, stream);
324 fPageDevices.unrefAll();
325 fCanon.reset();
326 return success;
327 }
328
onAbort()329 void onAbort() override {
330 fPageDevices.unrefAll();
331 fCanon.reset();
332 }
333
334 private:
335 SkPDFCanon fCanon;
336 SkTDArray<const SkPDFDevice*> fPageDevices;
337 SkAutoTUnref<SkCanvas> fCanvas;
338 SkScalar fRasterDpi;
339 };
340 } // namespace
341 ///////////////////////////////////////////////////////////////////////////////
342
CreatePDF(SkWStream * stream,SkScalar dpi)343 SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
344 return stream ? SkNEW_ARGS(SkDocument_PDF, (stream, NULL, dpi)) : NULL;
345 }
346
CreatePDF(const char path[],SkScalar dpi)347 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
348 SkFILEWStream* stream = SkNEW_ARGS(SkFILEWStream, (path));
349 if (!stream->isValid()) {
350 SkDELETE(stream);
351 return NULL;
352 }
353 auto delete_wstream = [](SkWStream* stream, bool) { SkDELETE(stream); };
354 return SkNEW_ARGS(SkDocument_PDF, (stream, delete_wstream, dpi));
355 }
356