• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkPDFUtils.h"
15 #include "SkStream.h"
16 #include "SkPDFMetadata.h"
17 
18 class SkPDFDict;
19 
emit_pdf_header(SkWStream * stream)20 static void emit_pdf_header(SkWStream* stream) {
21     stream->writeText("%PDF-1.4\n%");
22     // The PDF spec recommends including a comment with four bytes, all
23     // with their high bits set.  This is "Skia" with the high bits set.
24     stream->write32(0xD3EBE9E1);
25     stream->writeText("\n");
26 }
27 
emit_pdf_footer(SkWStream * stream,const SkPDFObjNumMap & objNumMap,const SkPDFSubstituteMap & substitutes,SkPDFObject * docCatalog,int64_t objCount,int32_t xRefFileOffset,SkPDFObject * info,SkPDFObject * id)28 static void emit_pdf_footer(SkWStream* stream,
29                             const SkPDFObjNumMap& objNumMap,
30                             const SkPDFSubstituteMap& substitutes,
31                             SkPDFObject* docCatalog,
32                             int64_t objCount,
33                             int32_t xRefFileOffset,
34                             SkPDFObject* info /* take ownership */,
35                             SkPDFObject* id /* take ownership */) {
36     SkPDFDict trailerDict;
37     // TODO(http://crbug.com/80908): Linearized format will take a
38     //                               Prev entry too.
39     trailerDict.insertInt("Size", int(objCount));
40     trailerDict.insertObjRef("Root", SkRef(docCatalog));
41     SkASSERT(info);
42     trailerDict.insertObjRef("Info", info);
43     if (id) {
44         trailerDict.insertObject("ID", id);
45     }
46     stream->writeText("trailer\n");
47     trailerDict.emitObject(stream, objNumMap, substitutes);
48     stream->writeText("\nstartxref\n");
49     stream->writeBigDecAsText(xRefFileOffset);
50     stream->writeText("\n%%EOF");
51 }
52 
perform_font_subsetting(const SkTDArray<const SkPDFDevice * > & pageDevices,SkPDFSubstituteMap * substituteMap)53 static void perform_font_subsetting(
54         const SkTDArray<const SkPDFDevice*>& pageDevices,
55         SkPDFSubstituteMap* substituteMap) {
56     SkASSERT(substituteMap);
57 
58     SkPDFGlyphSetMap usage;
59     for (int i = 0; i < pageDevices.count(); ++i) {
60         usage.merge(pageDevices[i]->getFontGlyphUsage());
61     }
62     SkPDFGlyphSetMap::F2BIter iterator(usage);
63     const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
64     while (entry) {
65         SkAutoTUnref<SkPDFFont> subsetFont(
66                 entry->fFont->getFontSubset(entry->fGlyphSet));
67         if (subsetFont) {
68             substituteMap->setSubstitute(entry->fFont, subsetFont.get());
69         }
70         entry = iterator.next();
71     }
72 }
73 
create_pdf_page_content(const SkPDFDevice * pageDevice)74 static SkPDFObject* create_pdf_page_content(const SkPDFDevice* pageDevice) {
75     SkAutoTDelete<SkStreamAsset> content(pageDevice->content());
76     return new SkPDFStream(content.get());
77 }
78 
create_pdf_page(const SkPDFDevice * pageDevice)79 static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) {
80     SkAutoTUnref<SkPDFDict> page(new SkPDFDict("Page"));
81     page->insertObject("Resources", pageDevice->createResourceDict());
82     page->insertObject("MediaBox", pageDevice->copyMediaBox());
83     SkAutoTUnref<SkPDFArray> annotations(new SkPDFArray);
84     pageDevice->appendAnnotations(annotations);
85     if (annotations->size() > 0) {
86         page->insertObject("Annots", annotations.detach());
87     }
88     page->insertObjRef("Contents", create_pdf_page_content(pageDevice));
89     return page.detach();
90 }
91 
generate_page_tree(const SkTDArray<SkPDFDict * > & pages,SkTDArray<SkPDFDict * > * pageTree,SkPDFDict ** rootNode)92 static void generate_page_tree(const SkTDArray<SkPDFDict*>& pages,
93                                SkTDArray<SkPDFDict*>* pageTree,
94                                SkPDFDict** rootNode) {
95     // PDF wants a tree describing all the pages in the document.  We arbitrary
96     // choose 8 (kNodeSize) as the number of allowed children.  The internal
97     // nodes have type "Pages" with an array of children, a parent pointer, and
98     // the number of leaves below the node as "Count."  The leaves are passed
99     // into the method, have type "Page" and need a parent pointer. This method
100     // builds the tree bottom up, skipping internal nodes that would have only
101     // one child.
102     static const int kNodeSize = 8;
103 
104     // curNodes takes a reference to its items, which it passes to pageTree.
105     SkTDArray<SkPDFDict*> curNodes;
106     curNodes.setReserve(pages.count());
107     for (int i = 0; i < pages.count(); i++) {
108         SkSafeRef(pages[i]);
109         curNodes.push(pages[i]);
110     }
111 
112     // nextRoundNodes passes its references to nodes on to curNodes.
113     SkTDArray<SkPDFDict*> nextRoundNodes;
114     nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
115 
116     int treeCapacity = kNodeSize;
117     do {
118         for (int i = 0; i < curNodes.count(); ) {
119             if (i > 0 && i + 1 == curNodes.count()) {
120                 nextRoundNodes.push(curNodes[i]);
121                 break;
122             }
123 
124             SkAutoTUnref<SkPDFDict> newNode(new SkPDFDict("Pages"));
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]->insertObjRef("Parent", SkRef(newNode.get()));
131                 kids->appendObjRef(SkRef(curNodes[i]));
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->insertInt("Count", pageCount);
154             newNode->insertObject("Kids", kids.detach());
155             nextRoundNodes.push(newNode.detach());  // 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 
emit_pdf_document(const SkTDArray<const SkPDFDevice * > & pageDevices,const SkPDFMetadata & metadata,SkWStream * stream)169 static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices,
170                               const SkPDFMetadata& metadata,
171                               SkWStream* stream) {
172     if (pageDevices.isEmpty()) {
173         return false;
174     }
175 
176     SkTDArray<SkPDFDict*> pages;
177     SkAutoTUnref<SkPDFDict> dests(new SkPDFDict);
178 
179     for (int i = 0; i < pageDevices.count(); i++) {
180         SkASSERT(pageDevices[i]);
181         SkASSERT(i == 0 ||
182                  pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon());
183         SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i]));
184         pageDevices[i]->appendDestinations(dests, page.get());
185         pages.push(page.detach());
186     }
187 
188     SkAutoTUnref<SkPDFDict> docCatalog(new SkPDFDict("Catalog"));
189 
190     SkAutoTUnref<SkPDFObject> infoDict(
191             metadata.createDocumentInformationDict());
192 
193     SkAutoTUnref<SkPDFObject> id, xmp;
194 #ifdef SK_PDF_GENERATE_PDFA
195     SkPDFMetadata::UUID uuid = metadata.uuid();
196     // We use the same UUID for Document ID and Instance ID since this
197     // is the first revision of this document (and Skia does not
198     // support revising existing PDF documents).
199     // If we are not in PDF/A mode, don't use a UUID since testing
200     // works best with reproducible outputs.
201     id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid));
202     xmp.reset(metadata.createXMPObject(uuid, uuid));
203     docCatalog->insertObjRef("Metadata", xmp.detach());
204 
205     // sRGB is specified by HTML, CSS, and SVG.
206     SkAutoTUnref<SkPDFDict> outputIntent(new SkPDFDict("OutputIntent"));
207     outputIntent->insertName("S", "GTS_PDFA1");
208     outputIntent->insertString("RegistryName", "http://www.color.org");
209     outputIntent->insertString("OutputConditionIdentifier",
210                                "sRGB IEC61966-2.1");
211     SkAutoTUnref<SkPDFArray> intentArray(new SkPDFArray);
212     intentArray->appendObject(outputIntent.detach());
213     // Don't specify OutputIntents if we are not in PDF/A mode since
214     // no one has ever asked for this feature.
215     docCatalog->insertObject("OutputIntents", intentArray.detach());
216 #endif
217 
218     SkTDArray<SkPDFDict*> pageTree;
219     SkPDFDict* pageTreeRoot;
220     generate_page_tree(pages, &pageTree, &pageTreeRoot);
221     docCatalog->insertObjRef("Pages", SkRef(pageTreeRoot));
222 
223     if (dests->size() > 0) {
224         docCatalog->insertObjRef("Dests", dests.detach());
225     }
226 
227     // Build font subsetting info before proceeding.
228     SkPDFSubstituteMap substitutes;
229     perform_font_subsetting(pageDevices, &substitutes);
230 
231     SkPDFObjNumMap objNumMap;
232     objNumMap.addObjectRecursively(infoDict, substitutes);
233     objNumMap.addObjectRecursively(docCatalog.get(), substitutes);
234     size_t baseOffset = stream->bytesWritten();
235     emit_pdf_header(stream);
236     SkTDArray<int32_t> offsets;
237     for (int i = 0; i < objNumMap.objects().count(); ++i) {
238         SkPDFObject* object = objNumMap.objects()[i];
239         size_t offset = stream->bytesWritten();
240         // This assert checks that size(pdf_header) > 0 and that
241         // the output stream correctly reports bytesWritten().
242         SkASSERT(offset > baseOffset);
243         offsets.push(SkToS32(offset - baseOffset));
244         SkASSERT(object == substitutes.getSubstitute(object));
245         SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
246         stream->writeDecAsText(i + 1);
247         stream->writeText(" 0 obj\n");  // Generation number is always 0.
248         object->emitObject(stream, objNumMap, substitutes);
249         stream->writeText("\nendobj\n");
250     }
251     int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);
252 
253     // Include the zeroth object in the count.
254     int32_t objCount = SkToS32(offsets.count() + 1);
255 
256     stream->writeText("xref\n0 ");
257     stream->writeDecAsText(objCount);
258     stream->writeText("\n0000000000 65535 f \n");
259     for (int i = 0; i < offsets.count(); i++) {
260         SkASSERT(offsets[i] > 0);
261         stream->writeBigDecAsText(offsets[i], 10);
262         stream->writeText(" 00000 n \n");
263     }
264     emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount,
265                     xRefFileOffset, infoDict.detach(), id.detach());
266 
267     // The page tree has both child and parent pointers, so it creates a
268     // reference cycle.  We must clear that cycle to properly reclaim memory.
269     for (int i = 0; i < pageTree.count(); i++) {
270         pageTree[i]->clear();
271     }
272     pageTree.safeUnrefAll();
273     pages.unrefAll();
274     return true;
275 }
276 
277 #if 0
278 // TODO(halcanary): expose notEmbeddableCount in SkDocument
279 void GetCountOfFontTypes(
280         const SkTDArray<SkPDFDevice*>& pageDevices,
281         int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
282         int* notSubsettableCount,
283         int* notEmbeddableCount) {
284     sk_bzero(counts, sizeof(int) *
285                      (SkAdvancedTypefaceMetrics::kOther_Font + 1));
286     SkTDArray<SkFontID> seenFonts;
287     int notSubsettable = 0;
288     int notEmbeddable = 0;
289 
290     for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) {
291         const SkTDArray<SkPDFFont*>& fontResources =
292                 pageDevices[pageNumber]->getFontResources();
293         for (int font = 0; font < fontResources.count(); font++) {
294             SkFontID fontID = fontResources[font]->typeface()->uniqueID();
295             if (seenFonts.find(fontID) == -1) {
296                 counts[fontResources[font]->getType()]++;
297                 seenFonts.push(fontID);
298                 if (!fontResources[font]->canSubset()) {
299                     notSubsettable++;
300                 }
301                 if (!fontResources[font]->canEmbed()) {
302                     notEmbeddable++;
303                 }
304             }
305         }
306     }
307     if (notSubsettableCount) {
308         *notSubsettableCount = notSubsettable;
309 
310     }
311     if (notEmbeddableCount) {
312         *notEmbeddableCount = notEmbeddable;
313     }
314 }
315 #endif
316 
clone(const T * o)317 template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullptr; }
318 ////////////////////////////////////////////////////////////////////////////////
319 
320 namespace {
321 class SkDocument_PDF : public SkDocument {
322 public:
SkDocument_PDF(SkWStream * stream,void (* doneProc)(SkWStream *,bool),SkScalar rasterDpi,SkPixelSerializer * jpegEncoder)323     SkDocument_PDF(SkWStream* stream,
324                    void (*doneProc)(SkWStream*, bool),
325                    SkScalar rasterDpi,
326                    SkPixelSerializer* jpegEncoder)
327         : SkDocument(stream, doneProc)
328         , fRasterDpi(rasterDpi) {
329         fCanon.fPixelSerializer.reset(SkSafeRef(jpegEncoder));
330     }
331 
~SkDocument_PDF()332     virtual ~SkDocument_PDF() {
333         // subclasses must call close() in their destructors
334         this->close();
335     }
336 
337 protected:
onBeginPage(SkScalar width,SkScalar height,const SkRect & trimBox)338     SkCanvas* onBeginPage(SkScalar width, SkScalar height,
339                           const SkRect& trimBox) override {
340         SkASSERT(!fCanvas.get());
341 
342         SkISize pageSize = SkISize::Make(
343                 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
344         SkAutoTUnref<SkPDFDevice> device(
345                 SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon));
346         fCanvas.reset(new SkCanvas(device.get()));
347         fPageDevices.push(device.detach());
348         fCanvas->clipRect(trimBox);
349         fCanvas->translate(trimBox.x(), trimBox.y());
350         return fCanvas.get();
351     }
352 
onEndPage()353     void onEndPage() override {
354         SkASSERT(fCanvas.get());
355         fCanvas->flush();
356         fCanvas.reset(nullptr);
357     }
358 
onClose(SkWStream * stream)359     bool onClose(SkWStream* stream) override {
360         SkASSERT(!fCanvas.get());
361 
362         bool success = emit_pdf_document(fPageDevices, fMetadata, stream);
363         fPageDevices.unrefAll();
364         fCanon.reset();
365         return success;
366     }
367 
onAbort()368     void onAbort() override {
369         fPageDevices.unrefAll();
370         fCanon.reset();
371     }
372 
setMetadata(const SkDocument::Attribute info[],int infoCount,const SkTime::DateTime * creationDate,const SkTime::DateTime * modifiedDate)373     void setMetadata(const SkDocument::Attribute info[],
374                      int infoCount,
375                      const SkTime::DateTime* creationDate,
376                      const SkTime::DateTime* modifiedDate) override {
377         fMetadata.fInfo.reset(info, infoCount);
378         fMetadata.fCreation.reset(clone(creationDate));
379         fMetadata.fModified.reset(clone(modifiedDate));
380     }
381 
382 private:
383     SkPDFCanon fCanon;
384     SkTDArray<const SkPDFDevice*> fPageDevices;
385     SkAutoTUnref<SkCanvas> fCanvas;
386     SkScalar fRasterDpi;
387     SkPDFMetadata fMetadata;
388 };
389 }  // namespace
390 ///////////////////////////////////////////////////////////////////////////////
391 
CreatePDF(SkWStream * stream,SkScalar dpi)392 SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
393     return stream ? new SkDocument_PDF(stream, nullptr, dpi, nullptr) : nullptr;
394 }
395 
CreatePDF(SkWStream * stream,SkScalar dpi,SkPixelSerializer * jpegEncoder)396 SkDocument* SkDocument::CreatePDF(SkWStream* stream,
397                                   SkScalar dpi,
398                                   SkPixelSerializer* jpegEncoder) {
399     return stream
400         ? new SkDocument_PDF(stream, nullptr, dpi, jpegEncoder)
401         : nullptr;
402 }
403 
CreatePDF(const char path[],SkScalar dpi)404 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
405     SkFILEWStream* stream = new SkFILEWStream(path);
406     if (!stream->isValid()) {
407         delete stream;
408         return nullptr;
409     }
410     auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
411     return new SkDocument_PDF(stream, delete_wstream, dpi, nullptr);
412 }
413