blob: 66635a98fb43a02a2ae845acc586f3e0c2a6a52a [file] [log] [blame]
reed@google.com99ac02b2013-06-07 20:30:16 +00001/*
halcanarya43b4152015-03-25 12:15:04 -07002 * Copyright 2011 Google Inc.
reed@google.com99ac02b2013-06-07 20:30:16 +00003 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
halcanarya1f1ee92015-02-20 06:17:26 -08008#include "SkPDFCanon.h"
halcanary66be6262016-03-21 13:01:34 -07009#include "SkPDFCanvas.h"
reed58677362014-10-09 05:30:10 -070010#include "SkPDFDevice.h"
halcanary23f4d4d2016-03-12 05:59:39 -080011#include "SkPDFDocument.h"
halcanarya43b4152015-03-25 12:15:04 -070012#include "SkPDFFont.h"
halcanary2f7ebcb2015-03-25 12:45:28 -070013#include "SkPDFStream.h"
halcanaryf12a1672015-09-23 12:45:49 -070014#include "SkPDFUtils.h"
halcanarya43b4152015-03-25 12:15:04 -070015#include "SkStream.h"
halcanaryf12a1672015-09-23 12:45:49 -070016
halcanary50e82e62016-03-21 13:45:05 -070017
18SkPDFObjectSerializer::SkPDFObjectSerializer() : fBaseOffset(0), fNextToBeSerialized(0) {}
19
20template <class T> static void renew(T* t) { t->~T(); new (t) T; }
21
22void SkPDFObjectSerializer::addObjectRecursively(const sk_sp<SkPDFObject>& object) {
23 fObjNumMap.addObjectRecursively(object.get(), fSubstituteMap);
halcanarya43b4152015-03-25 12:15:04 -070024}
25
halcanary50e82e62016-03-21 13:45:05 -070026void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream, const SkPDFMetadata& md) {
27 fBaseOffset = wStream->bytesWritten();
28 static const char kHeader[] = "%PDF-1.4\n%\xE1\xE9\xEB\xD3\n";
29 wStream->write(kHeader, strlen(kHeader));
30 // The PDF spec recommends including a comment with four
31 // bytes, all with their high bits set. "\xD3\xEB\xE9\xE1" is
32 // "Skia" with the high bits set.
33 fInfoDict.reset(md.createDocumentInformationDict());
34 this->addObjectRecursively(fInfoDict);
35 this->serializeObjects(wStream);
36}
37
38// Serialize all objects in the fObjNumMap that have not yet been serialized;
39void SkPDFObjectSerializer::serializeObjects(SkWStream* wStream) {
40 const SkTArray<sk_sp<SkPDFObject>>& objects = fObjNumMap.objects();
41 while (fNextToBeSerialized < objects.count()) {
42 SkPDFObject* object = objects[fNextToBeSerialized].get();
43 int32_t index = fNextToBeSerialized + 1; // Skip object 0.
44 // "The first entry in the [XREF] table (object number 0) is
45 // always free and has a generation number of 65,535; it is
46 // the head of the linked list of free objects."
47 SkASSERT(fOffsets.count() == fNextToBeSerialized);
48 fOffsets.push(this->offset(wStream));
49 SkASSERT(object == fSubstituteMap.getSubstitute(object));
50 wStream->writeDecAsText(index);
51 wStream->writeText(" 0 obj\n"); // Generation number is always 0.
52 object->emitObject(wStream, fObjNumMap, fSubstituteMap);
53 wStream->writeText("\nendobj\n");
54 object->drop();
55 ++fNextToBeSerialized;
56 }
57}
58
59// Xref table and footer
60void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
61 const sk_sp<SkPDFObject> docCatalog,
62 sk_sp<SkPDFObject> id) {
63 this->serializeObjects(wStream);
64 int32_t xRefFileOffset = this->offset(wStream);
65 // Include the special zeroth object in the count.
66 int32_t objCount = SkToS32(fOffsets.count() + 1);
67 wStream->writeText("xref\n0 ");
68 wStream->writeDecAsText(objCount);
69 wStream->writeText("\n0000000000 65535 f \n");
70 for (int i = 0; i < fOffsets.count(); i++) {
71 wStream->writeBigDecAsText(fOffsets[i], 10);
72 wStream->writeText(" 00000 n \n");
73 }
halcanarya43b4152015-03-25 12:15:04 -070074 SkPDFDict trailerDict;
halcanary50e82e62016-03-21 13:45:05 -070075 trailerDict.insertInt("Size", objCount);
76 SkASSERT(docCatalog);
77 trailerDict.insertObjRef("Root", docCatalog);
78 SkASSERT(fInfoDict);
79 trailerDict.insertObjRef("Info", std::move(fInfoDict));
halcanary34422612015-10-12 10:11:18 -070080 if (id) {
halcanary8103a342016-03-08 15:10:16 -080081 trailerDict.insertObject("ID", std::move(id));
halcanary34422612015-10-12 10:11:18 -070082 }
halcanary50e82e62016-03-21 13:45:05 -070083 wStream->writeText("trailer\n");
84 trailerDict.emitObject(wStream, fObjNumMap, fSubstituteMap);
85 wStream->writeText("\nstartxref\n");
86 wStream->writeBigDecAsText(xRefFileOffset);
87 wStream->writeText("\n%%EOF");
88}
89
90int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) {
91 size_t offset = wStream->bytesWritten();
92 SkASSERT(offset > fBaseOffset);
93 return SkToS32(offset - fBaseOffset);
halcanarya43b4152015-03-25 12:15:04 -070094}
95
halcanary2f7ebcb2015-03-25 12:45:28 -070096static void perform_font_subsetting(
halcanary8103a342016-03-08 15:10:16 -080097 const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices,
halcanary37c46ca2015-03-31 12:30:20 -070098 SkPDFSubstituteMap* substituteMap) {
99 SkASSERT(substituteMap);
halcanarya43b4152015-03-25 12:15:04 -0700100
101 SkPDFGlyphSetMap usage;
halcanary8103a342016-03-08 15:10:16 -0800102 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) {
103 usage.merge(pageDevice->getFontGlyphUsage());
halcanarya43b4152015-03-25 12:15:04 -0700104 }
105 SkPDFGlyphSetMap::F2BIter iterator(usage);
106 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
107 while (entry) {
halcanary48810a02016-03-07 14:57:50 -0800108 sk_sp<SkPDFFont> subsetFont(
halcanarya43b4152015-03-25 12:15:04 -0700109 entry->fFont->getFontSubset(entry->fGlyphSet));
110 if (subsetFont) {
halcanary37c46ca2015-03-31 12:30:20 -0700111 substituteMap->setSubstitute(entry->fFont, subsetFont.get());
halcanarya43b4152015-03-25 12:15:04 -0700112 }
113 entry = iterator.next();
114 }
115}
116
halcanary8103a342016-03-08 15:10:16 -0800117static sk_sp<SkPDFDict> create_pdf_page(const SkPDFDevice* pageDevice) {
halcanaryece83922016-03-08 08:32:12 -0800118 auto page = sk_make_sp<SkPDFDict>("Page");
halcanary8103a342016-03-08 15:10:16 -0800119 page->insertObject("Resources", pageDevice->makeResourceDict());
halcanary72266fd2015-05-05 08:00:24 -0700120 page->insertObject("MediaBox", pageDevice->copyMediaBox());
halcanaryece83922016-03-08 08:32:12 -0800121 auto annotations = sk_make_sp<SkPDFArray>();
halcanaryfcad44b2016-03-06 14:47:10 -0800122 pageDevice->appendAnnotations(annotations.get());
wangxianzhuef6c50a2015-09-17 20:38:02 -0700123 if (annotations->size() > 0) {
halcanary8103a342016-03-08 15:10:16 -0800124 page->insertObject("Annots", std::move(annotations));
halcanary2f7ebcb2015-03-25 12:45:28 -0700125 }
halcanary8103a342016-03-08 15:10:16 -0800126 auto content = pageDevice->content();
127 page->insertObjRef("Contents", sk_make_sp<SkPDFStream>(content.get()));
128 return page;
halcanary2f7ebcb2015-03-25 12:45:28 -0700129}
130
halcanary8103a342016-03-08 15:10:16 -0800131// return root node.
132static sk_sp<SkPDFDict> generate_page_tree(
halcanary50e82e62016-03-21 13:45:05 -0700133 SkTDArray<SkPDFDict*>& pages,
halcanary8103a342016-03-08 15:10:16 -0800134 SkTDArray<SkPDFDict*>* pageTree) {
halcanary2f7ebcb2015-03-25 12:45:28 -0700135 // PDF wants a tree describing all the pages in the document. We arbitrary
136 // choose 8 (kNodeSize) as the number of allowed children. The internal
137 // nodes have type "Pages" with an array of children, a parent pointer, and
138 // the number of leaves below the node as "Count." The leaves are passed
139 // into the method, have type "Page" and need a parent pointer. This method
140 // builds the tree bottom up, skipping internal nodes that would have only
141 // one child.
142 static const int kNodeSize = 8;
143
halcanary2f7ebcb2015-03-25 12:45:28 -0700144 // curNodes takes a reference to its items, which it passes to pageTree.
145 SkTDArray<SkPDFDict*> curNodes;
146 curNodes.setReserve(pages.count());
147 for (int i = 0; i < pages.count(); i++) {
148 SkSafeRef(pages[i]);
149 curNodes.push(pages[i]);
150 }
151
152 // nextRoundNodes passes its references to nodes on to curNodes.
153 SkTDArray<SkPDFDict*> nextRoundNodes;
154 nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
155
156 int treeCapacity = kNodeSize;
157 do {
158 for (int i = 0; i < curNodes.count(); ) {
159 if (i > 0 && i + 1 == curNodes.count()) {
160 nextRoundNodes.push(curNodes[i]);
161 break;
162 }
163
halcanaryece83922016-03-08 08:32:12 -0800164 auto newNode = sk_make_sp<SkPDFDict>("Pages");
165 auto kids = sk_make_sp<SkPDFArray>();
halcanary2f7ebcb2015-03-25 12:45:28 -0700166 kids->reserve(kNodeSize);
167
168 int count = 0;
169 for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
halcanary8103a342016-03-08 15:10:16 -0800170 curNodes[i]->insertObjRef("Parent", newNode);
halcanarye94ea622016-03-09 07:52:09 -0800171 kids->appendObjRef(sk_ref_sp(curNodes[i]));
halcanary2f7ebcb2015-03-25 12:45:28 -0700172
173 // TODO(vandebo): put the objects in strict access order.
174 // Probably doesn't matter because they are so small.
175 if (curNodes[i] != pages[0]) {
176 pageTree->push(curNodes[i]); // Transfer reference.
177 } else {
178 SkSafeUnref(curNodes[i]);
179 }
180 }
181
182 // treeCapacity is the number of leaf nodes possible for the
183 // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
184 // It is hard to count the number of leaf nodes in the current
185 // subtree. However, by construction, we know that unless it's the
186 // last subtree for the current depth, the leaf count will be
187 // treeCapacity, otherwise it's what ever is left over after
188 // consuming treeCapacity chunks.
189 int pageCount = treeCapacity;
190 if (i == curNodes.count()) {
191 pageCount = ((pages.count() - 1) % treeCapacity) + 1;
192 }
halcanary72266fd2015-05-05 08:00:24 -0700193 newNode->insertInt("Count", pageCount);
halcanary8103a342016-03-08 15:10:16 -0800194 newNode->insertObject("Kids", std::move(kids));
halcanaryfcad44b2016-03-06 14:47:10 -0800195 nextRoundNodes.push(newNode.release()); // Transfer reference.
halcanary2f7ebcb2015-03-25 12:45:28 -0700196 }
197
198 curNodes = nextRoundNodes;
199 nextRoundNodes.rewind();
200 treeCapacity *= kNodeSize;
201 } while (curNodes.count() > 1);
202
203 pageTree->push(curNodes[0]); // Transfer reference.
halcanarye94ea622016-03-09 07:52:09 -0800204 return sk_ref_sp(curNodes[0]);
halcanary2f7ebcb2015-03-25 12:45:28 -0700205}
206
halcanarya43b4152015-03-25 12:15:04 -0700207#if 0
208// TODO(halcanary): expose notEmbeddableCount in SkDocument
209void GetCountOfFontTypes(
210 const SkTDArray<SkPDFDevice*>& pageDevices,
211 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
212 int* notSubsettableCount,
213 int* notEmbeddableCount) {
214 sk_bzero(counts, sizeof(int) *
215 (SkAdvancedTypefaceMetrics::kOther_Font + 1));
216 SkTDArray<SkFontID> seenFonts;
217 int notSubsettable = 0;
218 int notEmbeddable = 0;
219
220 for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) {
221 const SkTDArray<SkPDFFont*>& fontResources =
222 pageDevices[pageNumber]->getFontResources();
223 for (int font = 0; font < fontResources.count(); font++) {
224 SkFontID fontID = fontResources[font]->typeface()->uniqueID();
225 if (seenFonts.find(fontID) == -1) {
226 counts[fontResources[font]->getType()]++;
227 seenFonts.push(fontID);
228 if (!fontResources[font]->canSubset()) {
229 notSubsettable++;
230 }
231 if (!fontResources[font]->canEmbed()) {
232 notEmbeddable++;
233 }
234 }
235 }
236 }
237 if (notSubsettableCount) {
238 *notSubsettableCount = notSubsettable;
239
240 }
241 if (notEmbeddableCount) {
242 *notEmbeddableCount = notEmbeddable;
243 }
244}
245#endif
halcanaryf12a1672015-09-23 12:45:49 -0700246
247template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullptr; }
halcanarya43b4152015-03-25 12:15:04 -0700248////////////////////////////////////////////////////////////////////////////////
reed@google.com99ac02b2013-06-07 20:30:16 +0000249
halcanary50e82e62016-03-21 13:45:05 -0700250SkPDFDocument::SkPDFDocument(SkWStream* stream,
251 void (*doneProc)(SkWStream*, bool),
252 SkScalar rasterDpi,
253 SkPixelSerializer* jpegEncoder)
254 : SkDocument(stream, doneProc)
255 , fRasterDpi(rasterDpi) {
256 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder));
257}
258
259SkPDFDocument::~SkPDFDocument() {
260 // subclasses of SkDocument must call close() in their destructors.
261 this->close();
262}
263
264void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
265 fObjectSerializer.addObjectRecursively(object);
266 fObjectSerializer.serializeObjects(this->getStream());
267}
268
269SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height,
270 const SkRect& trimBox) {
271 SkASSERT(!fCanvas.get()); // endPage() was called before this.
272 if (fPageDevices.empty()) {
273 // if this is the first page if the document.
274 fObjectSerializer.serializeHeader(this->getStream(), fMetadata);
halcanary712fdf72015-12-10 08:59:43 -0800275 }
halcanary50e82e62016-03-21 13:45:05 -0700276 SkISize pageSize = SkISize::Make(
277 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
278 sk_sp<SkPDFDevice> device(
halcanary989da4a2016-03-21 14:33:17 -0700279 SkPDFDevice::Create(pageSize, fRasterDpi, this));
halcanary50e82e62016-03-21 13:45:05 -0700280 fCanvas = sk_make_sp<SkPDFCanvas>(device);
281 fPageDevices.push_back(std::move(device));
282 fCanvas->clipRect(trimBox);
283 fCanvas->translate(trimBox.x(), trimBox.y());
284 return fCanvas.get();
285}
skia.committer@gmail.com63193672013-06-08 07:01:13 +0000286
halcanary50e82e62016-03-21 13:45:05 -0700287void SkPDFDocument::onEndPage() {
288 SkASSERT(fCanvas.get());
289 fCanvas->flush();
290 fCanvas.reset(nullptr);
291}
reed@google.com99ac02b2013-06-07 20:30:16 +0000292
halcanary50e82e62016-03-21 13:45:05 -0700293void SkPDFDocument::onAbort() {
294 fCanvas.reset(nullptr);
295 fPageDevices.reset();
296 fCanon.reset();
297 renew(&fObjectSerializer);
298}
reed@google.com99ac02b2013-06-07 20:30:16 +0000299
halcanary50e82e62016-03-21 13:45:05 -0700300void SkPDFDocument::setMetadata(const SkDocument::Attribute info[],
301 int infoCount,
302 const SkTime::DateTime* creationDate,
303 const SkTime::DateTime* modifiedDate) {
304 fMetadata.fInfo.reset(info, infoCount);
305 fMetadata.fCreation.reset(clone(creationDate));
306 fMetadata.fModified.reset(clone(modifiedDate));
307}
reed@google.com99ac02b2013-06-07 20:30:16 +0000308
halcanary50e82e62016-03-21 13:45:05 -0700309bool SkPDFDocument::onClose(SkWStream* stream) {
310 SkASSERT(!fCanvas.get());
311 if (fPageDevices.empty()) {
halcanary8103a342016-03-08 15:10:16 -0800312 fPageDevices.reset();
halcanary2e3f9d82015-02-27 12:41:03 -0800313 fCanon.reset();
halcanary50e82e62016-03-21 13:45:05 -0700314 renew(&fObjectSerializer);
315 return false;
316 }
317 SkTDArray<SkPDFDict*> pages; // TODO: SkTArray<sk_sp<SkPDFDict>>
318 auto dests = sk_make_sp<SkPDFDict>();
319
320 for (const sk_sp<const SkPDFDevice>& pageDevice : fPageDevices) {
321 SkASSERT(pageDevice);
322 SkASSERT(fPageDevices[0]->getCanon() == pageDevice->getCanon());
323 sk_sp<SkPDFDict> page(create_pdf_page(pageDevice.get()));
324 pageDevice->appendDestinations(dests.get(), page.get());
325 pages.push(page.release());
commit-bot@chromium.orgb5a66512013-10-09 21:09:00 +0000326 }
327
halcanary50e82e62016-03-21 13:45:05 -0700328 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
329
330 sk_sp<SkPDFObject> id, xmp;
331 #ifdef SK_PDF_GENERATE_PDFA
332 SkPDFMetadata::UUID uuid = metadata.uuid();
333 // We use the same UUID for Document ID and Instance ID since this
334 // is the first revision of this document (and Skia does not
335 // support revising existing PDF documents).
336 // If we are not in PDF/A mode, don't use a UUID since testing
337 // works best with reproducible outputs.
338 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid));
339 xmp.reset(metadata.createXMPObject(uuid, uuid));
340 docCatalog->insertObjRef("Metadata", std::move(xmp));
341
342 // sRGB is specified by HTML, CSS, and SVG.
343 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
344 outputIntent->insertName("S", "GTS_PDFA1");
345 outputIntent->insertString("RegistryName", "http://www.color.org");
346 outputIntent->insertString("OutputConditionIdentifier",
347 "sRGB IEC61966-2.1");
348 auto intentArray = sk_make_sp<SkPDFArray>();
349 intentArray->appendObject(std::move(outputIntent));
350 // Don't specify OutputIntents if we are not in PDF/A mode since
351 // no one has ever asked for this feature.
352 docCatalog->insertObject("OutputIntents", std::move(intentArray));
353 #endif
354
355 SkTDArray<SkPDFDict*> pageTree;
356 docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree));
357
358 if (dests->size() > 0) {
359 docCatalog->insertObjRef("Dests", std::move(dests));
reed@google.com99ac02b2013-06-07 20:30:16 +0000360 }
361
halcanary50e82e62016-03-21 13:45:05 -0700362 // Build font subsetting info before calling addObjectRecursively().
363 perform_font_subsetting(fPageDevices, &fObjectSerializer.fSubstituteMap);
halcanaryf12a1672015-09-23 12:45:49 -0700364
halcanary50e82e62016-03-21 13:45:05 -0700365 fObjectSerializer.addObjectRecursively(docCatalog);
366 fObjectSerializer.serializeObjects(this->getStream());
367 fObjectSerializer.serializeFooter(
368 this->getStream(), docCatalog, std::move(id));
369 pageTree.unrefAll(); // TODO(halcanary): make this unnecesary by
370 // refactoring generate_page_tree().
371 pages.unrefAll();
372 fPageDevices.reset();
373 fCanon.reset();
374 renew(&fObjectSerializer);
375 return true;
376}
377
reed@google.com99ac02b2013-06-07 20:30:16 +0000378///////////////////////////////////////////////////////////////////////////////
379
halcanary23f4d4d2016-03-12 05:59:39 -0800380sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream,
381 void (*proc)(SkWStream*, bool),
382 SkScalar dpi,
383 SkPixelSerializer* jpeg) {
384 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, jpeg) : nullptr;
385}
386
halcanary8c92dc12015-02-19 18:50:05 -0800387SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
halcanary23f4d4d2016-03-12 05:59:39 -0800388 return SkPDFMakeDocument(stream, nullptr, dpi, nullptr).release();
halcanary712fdf72015-12-10 08:59:43 -0800389}
390
391SkDocument* SkDocument::CreatePDF(SkWStream* stream,
392 SkScalar dpi,
393 SkPixelSerializer* jpegEncoder) {
halcanary23f4d4d2016-03-12 05:59:39 -0800394 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release();
reed@google.com99ac02b2013-06-07 20:30:16 +0000395}
396
halcanary8c92dc12015-02-19 18:50:05 -0800397SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
halcanary385fe4d2015-08-26 13:07:48 -0700398 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
halcanary23f4d4d2016-03-12 05:59:39 -0800399 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path));
400 return stream->isValid()
mtklein18300a32016-03-16 13:53:35 -0700401 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).release()
halcanary23f4d4d2016-03-12 05:59:39 -0800402 : nullptr;
reed@google.com99ac02b2013-06-07 20:30:16 +0000403}