blob: 740fa7dd5e89792840a2d7285f7cf1861f9845d5 [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
halcanaryad5dcd12016-03-31 06:59:00 -070031#define SKPDF_MAGIC "\xD3\xEB\xE9\xE1"
32#ifndef SK_BUILD_FOR_WIN32
33static_assert((SKPDF_MAGIC[0] & 0x7F) == "Skia"[0], "");
34static_assert((SKPDF_MAGIC[1] & 0x7F) == "Skia"[1], "");
35static_assert((SKPDF_MAGIC[2] & 0x7F) == "Skia"[2], "");
36static_assert((SKPDF_MAGIC[3] & 0x7F) == "Skia"[3], "");
37#endif
halcanary50e82e62016-03-21 13:45:05 -070038void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream, const SkPDFMetadata& md) {
39 fBaseOffset = wStream->bytesWritten();
halcanaryad5dcd12016-03-31 06:59:00 -070040 static const char kHeader[] = "%PDF-1.4\n%" SKPDF_MAGIC "\n";
halcanary50e82e62016-03-21 13:45:05 -070041 wStream->write(kHeader, strlen(kHeader));
42 // The PDF spec recommends including a comment with four
43 // bytes, all with their high bits set. "\xD3\xEB\xE9\xE1" is
44 // "Skia" with the high bits set.
45 fInfoDict.reset(md.createDocumentInformationDict());
46 this->addObjectRecursively(fInfoDict);
47 this->serializeObjects(wStream);
48}
halcanaryad5dcd12016-03-31 06:59:00 -070049#undef SKPDF_MAGIC
halcanary50e82e62016-03-21 13:45:05 -070050
51// Serialize all objects in the fObjNumMap that have not yet been serialized;
52void SkPDFObjectSerializer::serializeObjects(SkWStream* wStream) {
53 const SkTArray<sk_sp<SkPDFObject>>& objects = fObjNumMap.objects();
54 while (fNextToBeSerialized < objects.count()) {
55 SkPDFObject* object = objects[fNextToBeSerialized].get();
56 int32_t index = fNextToBeSerialized + 1; // Skip object 0.
57 // "The first entry in the [XREF] table (object number 0) is
58 // always free and has a generation number of 65,535; it is
59 // the head of the linked list of free objects."
60 SkASSERT(fOffsets.count() == fNextToBeSerialized);
61 fOffsets.push(this->offset(wStream));
62 SkASSERT(object == fSubstituteMap.getSubstitute(object));
63 wStream->writeDecAsText(index);
64 wStream->writeText(" 0 obj\n"); // Generation number is always 0.
65 object->emitObject(wStream, fObjNumMap, fSubstituteMap);
66 wStream->writeText("\nendobj\n");
67 object->drop();
68 ++fNextToBeSerialized;
69 }
70}
71
72// Xref table and footer
73void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
74 const sk_sp<SkPDFObject> docCatalog,
75 sk_sp<SkPDFObject> id) {
76 this->serializeObjects(wStream);
77 int32_t xRefFileOffset = this->offset(wStream);
78 // Include the special zeroth object in the count.
79 int32_t objCount = SkToS32(fOffsets.count() + 1);
80 wStream->writeText("xref\n0 ");
81 wStream->writeDecAsText(objCount);
82 wStream->writeText("\n0000000000 65535 f \n");
83 for (int i = 0; i < fOffsets.count(); i++) {
84 wStream->writeBigDecAsText(fOffsets[i], 10);
85 wStream->writeText(" 00000 n \n");
86 }
halcanarya43b4152015-03-25 12:15:04 -070087 SkPDFDict trailerDict;
halcanary50e82e62016-03-21 13:45:05 -070088 trailerDict.insertInt("Size", objCount);
89 SkASSERT(docCatalog);
90 trailerDict.insertObjRef("Root", docCatalog);
91 SkASSERT(fInfoDict);
92 trailerDict.insertObjRef("Info", std::move(fInfoDict));
halcanary34422612015-10-12 10:11:18 -070093 if (id) {
halcanary8103a342016-03-08 15:10:16 -080094 trailerDict.insertObject("ID", std::move(id));
halcanary34422612015-10-12 10:11:18 -070095 }
halcanary50e82e62016-03-21 13:45:05 -070096 wStream->writeText("trailer\n");
97 trailerDict.emitObject(wStream, fObjNumMap, fSubstituteMap);
98 wStream->writeText("\nstartxref\n");
99 wStream->writeBigDecAsText(xRefFileOffset);
100 wStream->writeText("\n%%EOF");
101}
102
103int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) {
104 size_t offset = wStream->bytesWritten();
105 SkASSERT(offset > fBaseOffset);
106 return SkToS32(offset - fBaseOffset);
halcanarya43b4152015-03-25 12:15:04 -0700107}
108
halcanary2f7ebcb2015-03-25 12:45:28 -0700109
halcanary8103a342016-03-08 15:10:16 -0800110// return root node.
halcanarycc77c122016-03-23 06:26:31 -0700111static sk_sp<SkPDFDict> generate_page_tree(SkTArray<sk_sp<SkPDFDict>>* pages) {
halcanary2f7ebcb2015-03-25 12:45:28 -0700112 // PDF wants a tree describing all the pages in the document. We arbitrary
113 // choose 8 (kNodeSize) as the number of allowed children. The internal
114 // nodes have type "Pages" with an array of children, a parent pointer, and
115 // the number of leaves below the node as "Count." The leaves are passed
116 // into the method, have type "Page" and need a parent pointer. This method
117 // builds the tree bottom up, skipping internal nodes that would have only
118 // one child.
119 static const int kNodeSize = 8;
120
halcanary2f7ebcb2015-03-25 12:45:28 -0700121 // curNodes takes a reference to its items, which it passes to pageTree.
halcanarycc77c122016-03-23 06:26:31 -0700122 int totalPageCount = pages->count();
123 SkTArray<sk_sp<SkPDFDict>> curNodes;
124 curNodes.swap(pages);
halcanary2f7ebcb2015-03-25 12:45:28 -0700125
126 // nextRoundNodes passes its references to nodes on to curNodes.
halcanary2f7ebcb2015-03-25 12:45:28 -0700127 int treeCapacity = kNodeSize;
128 do {
halcanarycc77c122016-03-23 06:26:31 -0700129 SkTArray<sk_sp<SkPDFDict>> nextRoundNodes;
halcanary2f7ebcb2015-03-25 12:45:28 -0700130 for (int i = 0; i < curNodes.count(); ) {
131 if (i > 0 && i + 1 == curNodes.count()) {
halcanarycc77c122016-03-23 06:26:31 -0700132 SkASSERT(curNodes[i]);
133 nextRoundNodes.emplace_back(std::move(curNodes[i]));
halcanary2f7ebcb2015-03-25 12:45:28 -0700134 break;
135 }
136
halcanaryece83922016-03-08 08:32:12 -0800137 auto newNode = sk_make_sp<SkPDFDict>("Pages");
138 auto kids = sk_make_sp<SkPDFArray>();
halcanary2f7ebcb2015-03-25 12:45:28 -0700139 kids->reserve(kNodeSize);
140
141 int count = 0;
142 for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
halcanarycc77c122016-03-23 06:26:31 -0700143 SkASSERT(curNodes[i]);
halcanary8103a342016-03-08 15:10:16 -0800144 curNodes[i]->insertObjRef("Parent", newNode);
halcanarycc77c122016-03-23 06:26:31 -0700145 kids->appendObjRef(std::move(curNodes[i]));
halcanary2f7ebcb2015-03-25 12:45:28 -0700146 }
147
148 // treeCapacity is the number of leaf nodes possible for the
149 // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
150 // It is hard to count the number of leaf nodes in the current
151 // subtree. However, by construction, we know that unless it's the
152 // last subtree for the current depth, the leaf count will be
153 // treeCapacity, otherwise it's what ever is left over after
154 // consuming treeCapacity chunks.
155 int pageCount = treeCapacity;
156 if (i == curNodes.count()) {
halcanarycc77c122016-03-23 06:26:31 -0700157 pageCount = ((totalPageCount - 1) % treeCapacity) + 1;
halcanary2f7ebcb2015-03-25 12:45:28 -0700158 }
halcanary72266fd2015-05-05 08:00:24 -0700159 newNode->insertInt("Count", pageCount);
halcanary8103a342016-03-08 15:10:16 -0800160 newNode->insertObject("Kids", std::move(kids));
halcanarycc77c122016-03-23 06:26:31 -0700161 nextRoundNodes.emplace_back(std::move(newNode));
halcanary2f7ebcb2015-03-25 12:45:28 -0700162 }
halcanarycc77c122016-03-23 06:26:31 -0700163 SkDEBUGCODE( for (const auto& n : curNodes) { SkASSERT(!n); } );
halcanary2f7ebcb2015-03-25 12:45:28 -0700164
halcanarycc77c122016-03-23 06:26:31 -0700165 curNodes.swap(&nextRoundNodes);
166 nextRoundNodes.reset();
halcanary2f7ebcb2015-03-25 12:45:28 -0700167 treeCapacity *= kNodeSize;
168 } while (curNodes.count() > 1);
halcanarycc77c122016-03-23 06:26:31 -0700169 return std::move(curNodes[0]);
halcanary2f7ebcb2015-03-25 12:45:28 -0700170}
171
halcanarya43b4152015-03-25 12:15:04 -0700172#if 0
173// TODO(halcanary): expose notEmbeddableCount in SkDocument
174void GetCountOfFontTypes(
175 const SkTDArray<SkPDFDevice*>& pageDevices,
176 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
177 int* notSubsettableCount,
178 int* notEmbeddableCount) {
179 sk_bzero(counts, sizeof(int) *
180 (SkAdvancedTypefaceMetrics::kOther_Font + 1));
181 SkTDArray<SkFontID> seenFonts;
182 int notSubsettable = 0;
183 int notEmbeddable = 0;
184
185 for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) {
186 const SkTDArray<SkPDFFont*>& fontResources =
187 pageDevices[pageNumber]->getFontResources();
188 for (int font = 0; font < fontResources.count(); font++) {
189 SkFontID fontID = fontResources[font]->typeface()->uniqueID();
190 if (seenFonts.find(fontID) == -1) {
191 counts[fontResources[font]->getType()]++;
192 seenFonts.push(fontID);
193 if (!fontResources[font]->canSubset()) {
194 notSubsettable++;
195 }
196 if (!fontResources[font]->canEmbed()) {
197 notEmbeddable++;
198 }
199 }
200 }
201 }
202 if (notSubsettableCount) {
203 *notSubsettableCount = notSubsettable;
204
205 }
206 if (notEmbeddableCount) {
207 *notEmbeddableCount = notEmbeddable;
208 }
209}
210#endif
halcanaryf12a1672015-09-23 12:45:49 -0700211
212template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullptr; }
halcanarya43b4152015-03-25 12:15:04 -0700213////////////////////////////////////////////////////////////////////////////////
reed@google.com99ac02b2013-06-07 20:30:16 +0000214
halcanary50e82e62016-03-21 13:45:05 -0700215SkPDFDocument::SkPDFDocument(SkWStream* stream,
216 void (*doneProc)(SkWStream*, bool),
217 SkScalar rasterDpi,
218 SkPixelSerializer* jpegEncoder)
219 : SkDocument(stream, doneProc)
220 , fRasterDpi(rasterDpi) {
221 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder));
222}
223
224SkPDFDocument::~SkPDFDocument() {
225 // subclasses of SkDocument must call close() in their destructors.
226 this->close();
227}
228
229void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
230 fObjectSerializer.addObjectRecursively(object);
231 fObjectSerializer.serializeObjects(this->getStream());
232}
233
234SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height,
235 const SkRect& trimBox) {
236 SkASSERT(!fCanvas.get()); // endPage() was called before this.
halcanarycc77c122016-03-23 06:26:31 -0700237 if (fPages.empty()) {
halcanary50e82e62016-03-21 13:45:05 -0700238 // if this is the first page if the document.
239 fObjectSerializer.serializeHeader(this->getStream(), fMetadata);
halcanarycc77c122016-03-23 06:26:31 -0700240 fDests = sk_make_sp<SkPDFDict>();
halcanary712fdf72015-12-10 08:59:43 -0800241 }
halcanary50e82e62016-03-21 13:45:05 -0700242 SkISize pageSize = SkISize::Make(
243 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
halcanarycc77c122016-03-23 06:26:31 -0700244 fPageDevice.reset(
halcanary989da4a2016-03-21 14:33:17 -0700245 SkPDFDevice::Create(pageSize, fRasterDpi, this));
halcanarycc77c122016-03-23 06:26:31 -0700246 fCanvas = sk_make_sp<SkPDFCanvas>(fPageDevice);
halcanary50e82e62016-03-21 13:45:05 -0700247 fCanvas->clipRect(trimBox);
248 fCanvas->translate(trimBox.x(), trimBox.y());
249 return fCanvas.get();
250}
skia.committer@gmail.com63193672013-06-08 07:01:13 +0000251
halcanary50e82e62016-03-21 13:45:05 -0700252void SkPDFDocument::onEndPage() {
253 SkASSERT(fCanvas.get());
254 fCanvas->flush();
255 fCanvas.reset(nullptr);
halcanarycc77c122016-03-23 06:26:31 -0700256 SkASSERT(fPageDevice);
257 fGlyphUsage.merge(fPageDevice->getFontGlyphUsage());
258 auto page = sk_make_sp<SkPDFDict>("Page");
259 page->insertObject("Resources", fPageDevice->makeResourceDict());
260 page->insertObject("MediaBox", fPageDevice->copyMediaBox());
261 auto annotations = sk_make_sp<SkPDFArray>();
262 fPageDevice->appendAnnotations(annotations.get());
263 if (annotations->size() > 0) {
264 page->insertObject("Annots", std::move(annotations));
265 }
266 auto contentData = fPageDevice->content();
267 auto contentObject = sk_make_sp<SkPDFStream>(contentData.get());
268 this->serialize(contentObject);
269 page->insertObjRef("Contents", std::move(contentObject));
270 fPageDevice->appendDestinations(fDests.get(), page.get());
271 fPages.emplace_back(std::move(page));
272 fPageDevice.reset(nullptr);
halcanary50e82e62016-03-21 13:45:05 -0700273}
reed@google.com99ac02b2013-06-07 20:30:16 +0000274
halcanary50e82e62016-03-21 13:45:05 -0700275void SkPDFDocument::onAbort() {
276 fCanvas.reset(nullptr);
halcanarycc77c122016-03-23 06:26:31 -0700277 fPages.reset();
halcanary50e82e62016-03-21 13:45:05 -0700278 fCanon.reset();
279 renew(&fObjectSerializer);
280}
reed@google.com99ac02b2013-06-07 20:30:16 +0000281
halcanary50e82e62016-03-21 13:45:05 -0700282void SkPDFDocument::setMetadata(const SkDocument::Attribute info[],
283 int infoCount,
284 const SkTime::DateTime* creationDate,
285 const SkTime::DateTime* modifiedDate) {
286 fMetadata.fInfo.reset(info, infoCount);
287 fMetadata.fCreation.reset(clone(creationDate));
288 fMetadata.fModified.reset(clone(modifiedDate));
289}
reed@google.com99ac02b2013-06-07 20:30:16 +0000290
halcanary50e82e62016-03-21 13:45:05 -0700291bool SkPDFDocument::onClose(SkWStream* stream) {
292 SkASSERT(!fCanvas.get());
halcanarycc77c122016-03-23 06:26:31 -0700293 if (fPages.empty()) {
294 fPages.reset();
halcanary2e3f9d82015-02-27 12:41:03 -0800295 fCanon.reset();
halcanary50e82e62016-03-21 13:45:05 -0700296 renew(&fObjectSerializer);
297 return false;
298 }
halcanary50e82e62016-03-21 13:45:05 -0700299 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
halcanary50e82e62016-03-21 13:45:05 -0700300 sk_sp<SkPDFObject> id, xmp;
301 #ifdef SK_PDF_GENERATE_PDFA
302 SkPDFMetadata::UUID uuid = metadata.uuid();
303 // We use the same UUID for Document ID and Instance ID since this
304 // is the first revision of this document (and Skia does not
305 // support revising existing PDF documents).
306 // If we are not in PDF/A mode, don't use a UUID since testing
307 // works best with reproducible outputs.
308 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid));
309 xmp.reset(metadata.createXMPObject(uuid, uuid));
310 docCatalog->insertObjRef("Metadata", std::move(xmp));
311
312 // sRGB is specified by HTML, CSS, and SVG.
313 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
314 outputIntent->insertName("S", "GTS_PDFA1");
315 outputIntent->insertString("RegistryName", "http://www.color.org");
316 outputIntent->insertString("OutputConditionIdentifier",
317 "sRGB IEC61966-2.1");
318 auto intentArray = sk_make_sp<SkPDFArray>();
319 intentArray->appendObject(std::move(outputIntent));
320 // Don't specify OutputIntents if we are not in PDF/A mode since
321 // no one has ever asked for this feature.
322 docCatalog->insertObject("OutputIntents", std::move(intentArray));
323 #endif
324
halcanarycc77c122016-03-23 06:26:31 -0700325 docCatalog->insertObjRef("Pages", generate_page_tree(&fPages));
halcanary50e82e62016-03-21 13:45:05 -0700326
halcanarycc77c122016-03-23 06:26:31 -0700327 if (fDests->size() > 0) {
328 docCatalog->insertObjRef("Dests", std::move(fDests));
reed@google.com99ac02b2013-06-07 20:30:16 +0000329 }
330
halcanary50e82e62016-03-21 13:45:05 -0700331 // Build font subsetting info before calling addObjectRecursively().
halcanarycc77c122016-03-23 06:26:31 -0700332 for (const auto& entry : fGlyphUsage) {
333 sk_sp<SkPDFFont> subsetFont(
334 entry.fFont->getFontSubset(entry.fGlyphSet));
335 if (subsetFont) {
336 fObjectSerializer.fSubstituteMap.setSubstitute(
337 entry.fFont, subsetFont.get());
338 }
339 }
halcanaryf12a1672015-09-23 12:45:49 -0700340
halcanary50e82e62016-03-21 13:45:05 -0700341 fObjectSerializer.addObjectRecursively(docCatalog);
342 fObjectSerializer.serializeObjects(this->getStream());
343 fObjectSerializer.serializeFooter(
344 this->getStream(), docCatalog, std::move(id));
halcanarycc77c122016-03-23 06:26:31 -0700345
346 SkASSERT(fPages.count() == 0);
halcanary50e82e62016-03-21 13:45:05 -0700347 fCanon.reset();
348 renew(&fObjectSerializer);
349 return true;
350}
351
reed@google.com99ac02b2013-06-07 20:30:16 +0000352///////////////////////////////////////////////////////////////////////////////
353
halcanary23f4d4d2016-03-12 05:59:39 -0800354sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream,
355 void (*proc)(SkWStream*, bool),
356 SkScalar dpi,
357 SkPixelSerializer* jpeg) {
358 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, jpeg) : nullptr;
359}
360
halcanary8c92dc12015-02-19 18:50:05 -0800361SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
halcanary23f4d4d2016-03-12 05:59:39 -0800362 return SkPDFMakeDocument(stream, nullptr, dpi, nullptr).release();
halcanary712fdf72015-12-10 08:59:43 -0800363}
364
365SkDocument* SkDocument::CreatePDF(SkWStream* stream,
366 SkScalar dpi,
367 SkPixelSerializer* jpegEncoder) {
halcanary23f4d4d2016-03-12 05:59:39 -0800368 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release();
reed@google.com99ac02b2013-06-07 20:30:16 +0000369}
370
halcanary8c92dc12015-02-19 18:50:05 -0800371SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
halcanary385fe4d2015-08-26 13:07:48 -0700372 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
halcanaryb8fb9932016-03-28 07:58:30 -0700373 std::unique_ptr<SkFILEWStream> stream(new SkFILEWStream(path));
halcanary23f4d4d2016-03-12 05:59:39 -0800374 return stream->isValid()
mtklein18300a32016-03-16 13:53:35 -0700375 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).release()
halcanary23f4d4d2016-03-12 05:59:39 -0800376 : nullptr;
reed@google.com99ac02b2013-06-07 20:30:16 +0000377}