blob: 32fffde676fc87db0be9d9ae0a4be1b82c75a4dc [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 -070017SkPDFObjectSerializer::SkPDFObjectSerializer() : fBaseOffset(0), fNextToBeSerialized(0) {}
18
19template <class T> static void renew(T* t) { t->~T(); new (t) T; }
20
halcanarya50151d2016-03-25 11:57:49 -070021SkPDFObjectSerializer::~SkPDFObjectSerializer() {
22 for (int i = 0; i < fObjNumMap.objects().count(); ++i) {
23 fObjNumMap.objects()[i]->drop();
24 }
25}
26
halcanary50e82e62016-03-21 13:45:05 -070027void SkPDFObjectSerializer::addObjectRecursively(const sk_sp<SkPDFObject>& object) {
28 fObjNumMap.addObjectRecursively(object.get(), fSubstituteMap);
halcanarya43b4152015-03-25 12:15:04 -070029}
30
halcanary50e82e62016-03-21 13:45:05 -070031void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream, const SkPDFMetadata& md) {
32 fBaseOffset = wStream->bytesWritten();
33 static const char kHeader[] = "%PDF-1.4\n%\xE1\xE9\xEB\xD3\n";
34 wStream->write(kHeader, strlen(kHeader));
35 // The PDF spec recommends including a comment with four
36 // bytes, all with their high bits set. "\xD3\xEB\xE9\xE1" is
37 // "Skia" with the high bits set.
38 fInfoDict.reset(md.createDocumentInformationDict());
39 this->addObjectRecursively(fInfoDict);
40 this->serializeObjects(wStream);
41}
42
43// Serialize all objects in the fObjNumMap that have not yet been serialized;
44void SkPDFObjectSerializer::serializeObjects(SkWStream* wStream) {
45 const SkTArray<sk_sp<SkPDFObject>>& objects = fObjNumMap.objects();
46 while (fNextToBeSerialized < objects.count()) {
47 SkPDFObject* object = objects[fNextToBeSerialized].get();
48 int32_t index = fNextToBeSerialized + 1; // Skip object 0.
49 // "The first entry in the [XREF] table (object number 0) is
50 // always free and has a generation number of 65,535; it is
51 // the head of the linked list of free objects."
52 SkASSERT(fOffsets.count() == fNextToBeSerialized);
53 fOffsets.push(this->offset(wStream));
54 SkASSERT(object == fSubstituteMap.getSubstitute(object));
55 wStream->writeDecAsText(index);
56 wStream->writeText(" 0 obj\n"); // Generation number is always 0.
57 object->emitObject(wStream, fObjNumMap, fSubstituteMap);
58 wStream->writeText("\nendobj\n");
59 object->drop();
60 ++fNextToBeSerialized;
61 }
62}
63
64// Xref table and footer
65void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
66 const sk_sp<SkPDFObject> docCatalog,
67 sk_sp<SkPDFObject> id) {
68 this->serializeObjects(wStream);
69 int32_t xRefFileOffset = this->offset(wStream);
70 // Include the special zeroth object in the count.
71 int32_t objCount = SkToS32(fOffsets.count() + 1);
72 wStream->writeText("xref\n0 ");
73 wStream->writeDecAsText(objCount);
74 wStream->writeText("\n0000000000 65535 f \n");
75 for (int i = 0; i < fOffsets.count(); i++) {
76 wStream->writeBigDecAsText(fOffsets[i], 10);
77 wStream->writeText(" 00000 n \n");
78 }
halcanarya43b4152015-03-25 12:15:04 -070079 SkPDFDict trailerDict;
halcanary50e82e62016-03-21 13:45:05 -070080 trailerDict.insertInt("Size", objCount);
81 SkASSERT(docCatalog);
82 trailerDict.insertObjRef("Root", docCatalog);
83 SkASSERT(fInfoDict);
84 trailerDict.insertObjRef("Info", std::move(fInfoDict));
halcanary34422612015-10-12 10:11:18 -070085 if (id) {
halcanary8103a342016-03-08 15:10:16 -080086 trailerDict.insertObject("ID", std::move(id));
halcanary34422612015-10-12 10:11:18 -070087 }
halcanary50e82e62016-03-21 13:45:05 -070088 wStream->writeText("trailer\n");
89 trailerDict.emitObject(wStream, fObjNumMap, fSubstituteMap);
90 wStream->writeText("\nstartxref\n");
91 wStream->writeBigDecAsText(xRefFileOffset);
92 wStream->writeText("\n%%EOF");
93}
94
95int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) {
96 size_t offset = wStream->bytesWritten();
97 SkASSERT(offset > fBaseOffset);
98 return SkToS32(offset - fBaseOffset);
halcanarya43b4152015-03-25 12:15:04 -070099}
100
halcanary2f7ebcb2015-03-25 12:45:28 -0700101
halcanary8103a342016-03-08 15:10:16 -0800102// return root node.
halcanarycc77c122016-03-23 06:26:31 -0700103static sk_sp<SkPDFDict> generate_page_tree(SkTArray<sk_sp<SkPDFDict>>* pages) {
halcanary2f7ebcb2015-03-25 12:45:28 -0700104 // PDF wants a tree describing all the pages in the document. We arbitrary
105 // choose 8 (kNodeSize) as the number of allowed children. The internal
106 // nodes have type "Pages" with an array of children, a parent pointer, and
107 // the number of leaves below the node as "Count." The leaves are passed
108 // into the method, have type "Page" and need a parent pointer. This method
109 // builds the tree bottom up, skipping internal nodes that would have only
110 // one child.
111 static const int kNodeSize = 8;
112
halcanary2f7ebcb2015-03-25 12:45:28 -0700113 // curNodes takes a reference to its items, which it passes to pageTree.
halcanarycc77c122016-03-23 06:26:31 -0700114 int totalPageCount = pages->count();
115 SkTArray<sk_sp<SkPDFDict>> curNodes;
116 curNodes.swap(pages);
halcanary2f7ebcb2015-03-25 12:45:28 -0700117
118 // nextRoundNodes passes its references to nodes on to curNodes.
halcanary2f7ebcb2015-03-25 12:45:28 -0700119 int treeCapacity = kNodeSize;
120 do {
halcanarycc77c122016-03-23 06:26:31 -0700121 SkTArray<sk_sp<SkPDFDict>> nextRoundNodes;
halcanary2f7ebcb2015-03-25 12:45:28 -0700122 for (int i = 0; i < curNodes.count(); ) {
123 if (i > 0 && i + 1 == curNodes.count()) {
halcanarycc77c122016-03-23 06:26:31 -0700124 SkASSERT(curNodes[i]);
125 nextRoundNodes.emplace_back(std::move(curNodes[i]));
halcanary2f7ebcb2015-03-25 12:45:28 -0700126 break;
127 }
128
halcanaryece83922016-03-08 08:32:12 -0800129 auto newNode = sk_make_sp<SkPDFDict>("Pages");
130 auto kids = sk_make_sp<SkPDFArray>();
halcanary2f7ebcb2015-03-25 12:45:28 -0700131 kids->reserve(kNodeSize);
132
133 int count = 0;
134 for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
halcanarycc77c122016-03-23 06:26:31 -0700135 SkASSERT(curNodes[i]);
halcanary8103a342016-03-08 15:10:16 -0800136 curNodes[i]->insertObjRef("Parent", newNode);
halcanarycc77c122016-03-23 06:26:31 -0700137 kids->appendObjRef(std::move(curNodes[i]));
halcanary2f7ebcb2015-03-25 12:45:28 -0700138 }
139
140 // treeCapacity is the number of leaf nodes possible for the
141 // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
142 // It is hard to count the number of leaf nodes in the current
143 // subtree. However, by construction, we know that unless it's the
144 // last subtree for the current depth, the leaf count will be
145 // treeCapacity, otherwise it's what ever is left over after
146 // consuming treeCapacity chunks.
147 int pageCount = treeCapacity;
148 if (i == curNodes.count()) {
halcanarycc77c122016-03-23 06:26:31 -0700149 pageCount = ((totalPageCount - 1) % treeCapacity) + 1;
halcanary2f7ebcb2015-03-25 12:45:28 -0700150 }
halcanary72266fd2015-05-05 08:00:24 -0700151 newNode->insertInt("Count", pageCount);
halcanary8103a342016-03-08 15:10:16 -0800152 newNode->insertObject("Kids", std::move(kids));
halcanarycc77c122016-03-23 06:26:31 -0700153 nextRoundNodes.emplace_back(std::move(newNode));
halcanary2f7ebcb2015-03-25 12:45:28 -0700154 }
halcanarycc77c122016-03-23 06:26:31 -0700155 SkDEBUGCODE( for (const auto& n : curNodes) { SkASSERT(!n); } );
halcanary2f7ebcb2015-03-25 12:45:28 -0700156
halcanarycc77c122016-03-23 06:26:31 -0700157 curNodes.swap(&nextRoundNodes);
158 nextRoundNodes.reset();
halcanary2f7ebcb2015-03-25 12:45:28 -0700159 treeCapacity *= kNodeSize;
160 } while (curNodes.count() > 1);
halcanarycc77c122016-03-23 06:26:31 -0700161 return std::move(curNodes[0]);
halcanary2f7ebcb2015-03-25 12:45:28 -0700162}
163
halcanarya43b4152015-03-25 12:15:04 -0700164#if 0
165// TODO(halcanary): expose notEmbeddableCount in SkDocument
166void GetCountOfFontTypes(
167 const SkTDArray<SkPDFDevice*>& pageDevices,
168 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
169 int* notSubsettableCount,
170 int* notEmbeddableCount) {
171 sk_bzero(counts, sizeof(int) *
172 (SkAdvancedTypefaceMetrics::kOther_Font + 1));
173 SkTDArray<SkFontID> seenFonts;
174 int notSubsettable = 0;
175 int notEmbeddable = 0;
176
177 for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) {
178 const SkTDArray<SkPDFFont*>& fontResources =
179 pageDevices[pageNumber]->getFontResources();
180 for (int font = 0; font < fontResources.count(); font++) {
181 SkFontID fontID = fontResources[font]->typeface()->uniqueID();
182 if (seenFonts.find(fontID) == -1) {
183 counts[fontResources[font]->getType()]++;
184 seenFonts.push(fontID);
185 if (!fontResources[font]->canSubset()) {
186 notSubsettable++;
187 }
188 if (!fontResources[font]->canEmbed()) {
189 notEmbeddable++;
190 }
191 }
192 }
193 }
194 if (notSubsettableCount) {
195 *notSubsettableCount = notSubsettable;
196
197 }
198 if (notEmbeddableCount) {
199 *notEmbeddableCount = notEmbeddable;
200 }
201}
202#endif
halcanaryf12a1672015-09-23 12:45:49 -0700203
204template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullptr; }
halcanarya43b4152015-03-25 12:15:04 -0700205////////////////////////////////////////////////////////////////////////////////
reed@google.com99ac02b2013-06-07 20:30:16 +0000206
halcanary50e82e62016-03-21 13:45:05 -0700207SkPDFDocument::SkPDFDocument(SkWStream* stream,
208 void (*doneProc)(SkWStream*, bool),
209 SkScalar rasterDpi,
210 SkPixelSerializer* jpegEncoder)
211 : SkDocument(stream, doneProc)
212 , fRasterDpi(rasterDpi) {
213 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder));
214}
215
216SkPDFDocument::~SkPDFDocument() {
217 // subclasses of SkDocument must call close() in their destructors.
218 this->close();
219}
220
221void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
222 fObjectSerializer.addObjectRecursively(object);
223 fObjectSerializer.serializeObjects(this->getStream());
224}
225
226SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height,
227 const SkRect& trimBox) {
228 SkASSERT(!fCanvas.get()); // endPage() was called before this.
halcanarycc77c122016-03-23 06:26:31 -0700229 if (fPages.empty()) {
halcanary50e82e62016-03-21 13:45:05 -0700230 // if this is the first page if the document.
231 fObjectSerializer.serializeHeader(this->getStream(), fMetadata);
halcanarycc77c122016-03-23 06:26:31 -0700232 fDests = sk_make_sp<SkPDFDict>();
halcanary712fdf72015-12-10 08:59:43 -0800233 }
halcanary50e82e62016-03-21 13:45:05 -0700234 SkISize pageSize = SkISize::Make(
235 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
halcanarycc77c122016-03-23 06:26:31 -0700236 fPageDevice.reset(
halcanary989da4a2016-03-21 14:33:17 -0700237 SkPDFDevice::Create(pageSize, fRasterDpi, this));
halcanarycc77c122016-03-23 06:26:31 -0700238 fCanvas = sk_make_sp<SkPDFCanvas>(fPageDevice);
halcanary50e82e62016-03-21 13:45:05 -0700239 fCanvas->clipRect(trimBox);
240 fCanvas->translate(trimBox.x(), trimBox.y());
241 return fCanvas.get();
242}
skia.committer@gmail.com63193672013-06-08 07:01:13 +0000243
halcanary50e82e62016-03-21 13:45:05 -0700244void SkPDFDocument::onEndPage() {
245 SkASSERT(fCanvas.get());
246 fCanvas->flush();
247 fCanvas.reset(nullptr);
halcanarycc77c122016-03-23 06:26:31 -0700248 SkASSERT(fPageDevice);
249 fGlyphUsage.merge(fPageDevice->getFontGlyphUsage());
250 auto page = sk_make_sp<SkPDFDict>("Page");
251 page->insertObject("Resources", fPageDevice->makeResourceDict());
252 page->insertObject("MediaBox", fPageDevice->copyMediaBox());
253 auto annotations = sk_make_sp<SkPDFArray>();
254 fPageDevice->appendAnnotations(annotations.get());
255 if (annotations->size() > 0) {
256 page->insertObject("Annots", std::move(annotations));
257 }
258 auto contentData = fPageDevice->content();
259 auto contentObject = sk_make_sp<SkPDFStream>(contentData.get());
260 this->serialize(contentObject);
261 page->insertObjRef("Contents", std::move(contentObject));
262 fPageDevice->appendDestinations(fDests.get(), page.get());
263 fPages.emplace_back(std::move(page));
264 fPageDevice.reset(nullptr);
halcanary50e82e62016-03-21 13:45:05 -0700265}
reed@google.com99ac02b2013-06-07 20:30:16 +0000266
halcanary50e82e62016-03-21 13:45:05 -0700267void SkPDFDocument::onAbort() {
268 fCanvas.reset(nullptr);
halcanarycc77c122016-03-23 06:26:31 -0700269 fPages.reset();
halcanary50e82e62016-03-21 13:45:05 -0700270 fCanon.reset();
271 renew(&fObjectSerializer);
272}
reed@google.com99ac02b2013-06-07 20:30:16 +0000273
halcanary50e82e62016-03-21 13:45:05 -0700274void SkPDFDocument::setMetadata(const SkDocument::Attribute info[],
275 int infoCount,
276 const SkTime::DateTime* creationDate,
277 const SkTime::DateTime* modifiedDate) {
278 fMetadata.fInfo.reset(info, infoCount);
279 fMetadata.fCreation.reset(clone(creationDate));
280 fMetadata.fModified.reset(clone(modifiedDate));
281}
reed@google.com99ac02b2013-06-07 20:30:16 +0000282
halcanary50e82e62016-03-21 13:45:05 -0700283bool SkPDFDocument::onClose(SkWStream* stream) {
284 SkASSERT(!fCanvas.get());
halcanarycc77c122016-03-23 06:26:31 -0700285 if (fPages.empty()) {
286 fPages.reset();
halcanary2e3f9d82015-02-27 12:41:03 -0800287 fCanon.reset();
halcanary50e82e62016-03-21 13:45:05 -0700288 renew(&fObjectSerializer);
289 return false;
290 }
halcanary50e82e62016-03-21 13:45:05 -0700291 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
halcanary50e82e62016-03-21 13:45:05 -0700292 sk_sp<SkPDFObject> id, xmp;
293 #ifdef SK_PDF_GENERATE_PDFA
294 SkPDFMetadata::UUID uuid = metadata.uuid();
295 // We use the same UUID for Document ID and Instance ID since this
296 // is the first revision of this document (and Skia does not
297 // support revising existing PDF documents).
298 // If we are not in PDF/A mode, don't use a UUID since testing
299 // works best with reproducible outputs.
300 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid));
301 xmp.reset(metadata.createXMPObject(uuid, uuid));
302 docCatalog->insertObjRef("Metadata", std::move(xmp));
303
304 // sRGB is specified by HTML, CSS, and SVG.
305 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
306 outputIntent->insertName("S", "GTS_PDFA1");
307 outputIntent->insertString("RegistryName", "http://www.color.org");
308 outputIntent->insertString("OutputConditionIdentifier",
309 "sRGB IEC61966-2.1");
310 auto intentArray = sk_make_sp<SkPDFArray>();
311 intentArray->appendObject(std::move(outputIntent));
312 // Don't specify OutputIntents if we are not in PDF/A mode since
313 // no one has ever asked for this feature.
314 docCatalog->insertObject("OutputIntents", std::move(intentArray));
315 #endif
316
halcanarycc77c122016-03-23 06:26:31 -0700317 docCatalog->insertObjRef("Pages", generate_page_tree(&fPages));
halcanary50e82e62016-03-21 13:45:05 -0700318
halcanarycc77c122016-03-23 06:26:31 -0700319 if (fDests->size() > 0) {
320 docCatalog->insertObjRef("Dests", std::move(fDests));
reed@google.com99ac02b2013-06-07 20:30:16 +0000321 }
322
halcanary50e82e62016-03-21 13:45:05 -0700323 // Build font subsetting info before calling addObjectRecursively().
halcanarycc77c122016-03-23 06:26:31 -0700324 for (const auto& entry : fGlyphUsage) {
325 sk_sp<SkPDFFont> subsetFont(
326 entry.fFont->getFontSubset(entry.fGlyphSet));
327 if (subsetFont) {
328 fObjectSerializer.fSubstituteMap.setSubstitute(
329 entry.fFont, subsetFont.get());
330 }
331 }
halcanaryf12a1672015-09-23 12:45:49 -0700332
halcanary50e82e62016-03-21 13:45:05 -0700333 fObjectSerializer.addObjectRecursively(docCatalog);
334 fObjectSerializer.serializeObjects(this->getStream());
335 fObjectSerializer.serializeFooter(
336 this->getStream(), docCatalog, std::move(id));
halcanarycc77c122016-03-23 06:26:31 -0700337
338 SkASSERT(fPages.count() == 0);
halcanary50e82e62016-03-21 13:45:05 -0700339 fCanon.reset();
340 renew(&fObjectSerializer);
341 return true;
342}
343
reed@google.com99ac02b2013-06-07 20:30:16 +0000344///////////////////////////////////////////////////////////////////////////////
345
halcanary23f4d4d2016-03-12 05:59:39 -0800346sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream,
347 void (*proc)(SkWStream*, bool),
348 SkScalar dpi,
349 SkPixelSerializer* jpeg) {
350 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, jpeg) : nullptr;
351}
352
halcanary8c92dc12015-02-19 18:50:05 -0800353SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
halcanary23f4d4d2016-03-12 05:59:39 -0800354 return SkPDFMakeDocument(stream, nullptr, dpi, nullptr).release();
halcanary712fdf72015-12-10 08:59:43 -0800355}
356
357SkDocument* SkDocument::CreatePDF(SkWStream* stream,
358 SkScalar dpi,
359 SkPixelSerializer* jpegEncoder) {
halcanary23f4d4d2016-03-12 05:59:39 -0800360 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release();
reed@google.com99ac02b2013-06-07 20:30:16 +0000361}
362
halcanary8c92dc12015-02-19 18:50:05 -0800363SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
halcanary385fe4d2015-08-26 13:07:48 -0700364 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
halcanaryb8fb9932016-03-28 07:58:30 -0700365 std::unique_ptr<SkFILEWStream> stream(new SkFILEWStream(path));
halcanary23f4d4d2016-03-12 05:59:39 -0800366 return stream->isValid()
mtklein18300a32016-03-16 13:53:35 -0700367 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).release()
halcanary23f4d4d2016-03-12 05:59:39 -0800368 : nullptr;
reed@google.com99ac02b2013-06-07 20:30:16 +0000369}