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