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