blob: 95857c18469805a66102a03b76c5a270f0134461 [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
halcanary99e22fb2016-05-26 08:31:06 -07008#include "SkColorSpace_Base.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"
halcanary2f7ebcb2015-03-25 12:45:28 -070012#include "SkPDFStream.h"
halcanaryf12a1672015-09-23 12:45:49 -070013#include "SkPDFUtils.h"
halcanarya43b4152015-03-25 12:15:04 -070014#include "SkStream.h"
halcanaryf12a1672015-09-23 12:45:49 -070015
halcanary50e82e62016-03-21 13:45:05 -070016SkPDFObjectSerializer::SkPDFObjectSerializer() : fBaseOffset(0), fNextToBeSerialized(0) {}
17
18template <class T> static void renew(T* t) { t->~T(); new (t) T; }
19
halcanarya50151d2016-03-25 11:57:49 -070020SkPDFObjectSerializer::~SkPDFObjectSerializer() {
21 for (int i = 0; i < fObjNumMap.objects().count(); ++i) {
22 fObjNumMap.objects()[i]->drop();
23 }
24}
25
halcanary50e82e62016-03-21 13:45:05 -070026void SkPDFObjectSerializer::addObjectRecursively(const sk_sp<SkPDFObject>& object) {
27 fObjNumMap.addObjectRecursively(object.get(), fSubstituteMap);
halcanarya43b4152015-03-25 12:15:04 -070028}
29
halcanaryad5dcd12016-03-31 06:59:00 -070030#define SKPDF_MAGIC "\xD3\xEB\xE9\xE1"
31#ifndef SK_BUILD_FOR_WIN32
32static_assert((SKPDF_MAGIC[0] & 0x7F) == "Skia"[0], "");
33static_assert((SKPDF_MAGIC[1] & 0x7F) == "Skia"[1], "");
34static_assert((SKPDF_MAGIC[2] & 0x7F) == "Skia"[2], "");
35static_assert((SKPDF_MAGIC[3] & 0x7F) == "Skia"[3], "");
36#endif
halcanary4b656662016-04-27 07:45:18 -070037void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream,
38 const SkDocument::PDFMetadata& md) {
halcanary50e82e62016-03-21 13:45:05 -070039 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.
halcanary4b656662016-04-27 07:45:18 -070045 fInfoDict = SkPDFMetadata::MakeDocumentInformationDict(md);
halcanary50e82e62016-03-21 13:45:05 -070046 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,
halcanary4b656662016-04-27 07:45:18 -0700218 const SkDocument::PDFMetadata& metadata,
219 sk_sp<SkPixelSerializer> jpegEncoder,
halcanary488165e2016-04-22 06:10:21 -0700220 bool pdfa)
halcanary50e82e62016-03-21 13:45:05 -0700221 : SkDocument(stream, doneProc)
halcanary488165e2016-04-22 06:10:21 -0700222 , fRasterDpi(rasterDpi)
halcanary4b656662016-04-27 07:45:18 -0700223 , fMetadata(metadata)
halcanary488165e2016-04-22 06:10:21 -0700224 , fPDFA(pdfa) {
halcanary4b656662016-04-27 07:45:18 -0700225 fCanon.setPixelSerializer(std::move(jpegEncoder));
halcanary50e82e62016-03-21 13:45:05 -0700226}
227
228SkPDFDocument::~SkPDFDocument() {
229 // subclasses of SkDocument must call close() in their destructors.
230 this->close();
231}
232
233void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
234 fObjectSerializer.addObjectRecursively(object);
235 fObjectSerializer.serializeObjects(this->getStream());
236}
237
238SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height,
239 const SkRect& trimBox) {
240 SkASSERT(!fCanvas.get()); // endPage() was called before this.
halcanarycc77c122016-03-23 06:26:31 -0700241 if (fPages.empty()) {
halcanary50e82e62016-03-21 13:45:05 -0700242 // if this is the first page if the document.
243 fObjectSerializer.serializeHeader(this->getStream(), fMetadata);
halcanarycc77c122016-03-23 06:26:31 -0700244 fDests = sk_make_sp<SkPDFDict>();
halcanary488165e2016-04-22 06:10:21 -0700245 if (fPDFA) {
halcanary4b656662016-04-27 07:45:18 -0700246 SkPDFMetadata::UUID uuid = SkPDFMetadata::CreateUUID(fMetadata);
halcanary8cd4a242016-04-07 08:56:15 -0700247 // We use the same UUID for Document ID and Instance ID since this
248 // is the first revision of this document (and Skia does not
249 // support revising existing PDF documents).
250 // If we are not in PDF/A mode, don't use a UUID since testing
251 // works best with reproducible outputs.
halcanary4b656662016-04-27 07:45:18 -0700252 fID = SkPDFMetadata::MakePdfId(uuid, uuid);
253 fXMP = SkPDFMetadata::MakeXMPObject(fMetadata, uuid, uuid);
halcanary8cd4a242016-04-07 08:56:15 -0700254 fObjectSerializer.addObjectRecursively(fXMP);
255 fObjectSerializer.serializeObjects(this->getStream());
halcanary488165e2016-04-22 06:10:21 -0700256 }
halcanary712fdf72015-12-10 08:59:43 -0800257 }
halcanary50e82e62016-03-21 13:45:05 -0700258 SkISize pageSize = SkISize::Make(
259 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
halcanarycc77c122016-03-23 06:26:31 -0700260 fPageDevice.reset(
halcanary989da4a2016-03-21 14:33:17 -0700261 SkPDFDevice::Create(pageSize, fRasterDpi, this));
halcanarycc77c122016-03-23 06:26:31 -0700262 fCanvas = sk_make_sp<SkPDFCanvas>(fPageDevice);
halcanary50e82e62016-03-21 13:45:05 -0700263 fCanvas->clipRect(trimBox);
264 fCanvas->translate(trimBox.x(), trimBox.y());
265 return fCanvas.get();
266}
skia.committer@gmail.com63193672013-06-08 07:01:13 +0000267
halcanary50e82e62016-03-21 13:45:05 -0700268void SkPDFDocument::onEndPage() {
269 SkASSERT(fCanvas.get());
270 fCanvas->flush();
271 fCanvas.reset(nullptr);
halcanarycc77c122016-03-23 06:26:31 -0700272 SkASSERT(fPageDevice);
halcanarycc77c122016-03-23 06:26:31 -0700273 auto page = sk_make_sp<SkPDFDict>("Page");
274 page->insertObject("Resources", fPageDevice->makeResourceDict());
275 page->insertObject("MediaBox", fPageDevice->copyMediaBox());
276 auto annotations = sk_make_sp<SkPDFArray>();
277 fPageDevice->appendAnnotations(annotations.get());
278 if (annotations->size() > 0) {
279 page->insertObject("Annots", std::move(annotations));
280 }
halcanaryac0e00d2016-07-27 11:12:23 -0700281 auto contentObject = sk_make_sp<SkPDFStream>(fPageDevice->content());
halcanarycc77c122016-03-23 06:26:31 -0700282 this->serialize(contentObject);
283 page->insertObjRef("Contents", std::move(contentObject));
284 fPageDevice->appendDestinations(fDests.get(), page.get());
285 fPages.emplace_back(std::move(page));
286 fPageDevice.reset(nullptr);
halcanary50e82e62016-03-21 13:45:05 -0700287}
reed@google.com99ac02b2013-06-07 20:30:16 +0000288
halcanary50e82e62016-03-21 13:45:05 -0700289void SkPDFDocument::onAbort() {
290 fCanvas.reset(nullptr);
halcanarycc77c122016-03-23 06:26:31 -0700291 fPages.reset();
halcanary50e82e62016-03-21 13:45:05 -0700292 fCanon.reset();
293 renew(&fObjectSerializer);
halcanary3c35fb32016-06-30 11:55:07 -0700294 renew(&fGlyphUsage);
halcanary50e82e62016-03-21 13:45:05 -0700295}
reed@google.com99ac02b2013-06-07 20:30:16 +0000296
halcanary4b656662016-04-27 07:45:18 -0700297#ifdef SK_SUPPORT_LEGACY_DOCUMENT_API
halcanary50e82e62016-03-21 13:45:05 -0700298void SkPDFDocument::setMetadata(const SkDocument::Attribute info[],
299 int infoCount,
300 const SkTime::DateTime* creationDate,
301 const SkTime::DateTime* modifiedDate) {
halcanary4b656662016-04-27 07:45:18 -0700302 for (int i = 0; i < infoCount; ++i) {
303 const SkDocument::Attribute& kv = info[i];
304 SkPDFMetadata::SetMetadataByKey(kv.fKey, kv.fValue, &fMetadata);
305 }
306 if (creationDate) {
307 fMetadata.fCreation.fEnabled = true;
308 fMetadata.fCreation.fDateTime = *creationDate;
309 }
310 if (modifiedDate) {
311 fMetadata.fModified.fEnabled = true;
312 fMetadata.fModified.fDateTime = *modifiedDate;
313 }
halcanary50e82e62016-03-21 13:45:05 -0700314}
halcanary4b656662016-04-27 07:45:18 -0700315#endif // SK_SUPPORT_LEGACY_DOCUMENT_API
reed@google.com99ac02b2013-06-07 20:30:16 +0000316
halcanary78daeff2016-04-07 12:34:59 -0700317static sk_sp<SkData> SkSrgbIcm() {
halcanary99e22fb2016-05-26 08:31:06 -0700318 auto srgb = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
319 return as_CSB(srgb)->writeToICC();
halcanary78daeff2016-04-07 12:34:59 -0700320}
321
322static sk_sp<SkPDFStream> make_srgb_color_profile() {
halcanaryfe8f0e02016-07-27 14:14:04 -0700323 sk_sp<SkPDFStream> stream = sk_make_sp<SkPDFStream>(SkSrgbIcm());
halcanary78daeff2016-04-07 12:34:59 -0700324 stream->insertInt("N", 3);
325 sk_sp<SkPDFArray> array = sk_make_sp<SkPDFArray>();
326 array->appendScalar(0.0f);
327 array->appendScalar(1.0f);
328 array->appendScalar(0.0f);
329 array->appendScalar(1.0f);
330 array->appendScalar(0.0f);
331 array->appendScalar(1.0f);
332 stream->insertObject("Range", std::move(array));
333 return stream;
334}
halcanary488165e2016-04-22 06:10:21 -0700335
336static sk_sp<SkPDFArray> make_srgb_output_intents() {
337 // sRGB is specified by HTML, CSS, and SVG.
338 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
339 outputIntent->insertName("S", "GTS_PDFA1");
340 outputIntent->insertString("RegistryName", "http://www.color.org");
341 outputIntent->insertString("OutputConditionIdentifier",
342 "Custom");
343 outputIntent->insertString("Info","sRGB IEC61966-2.1");
344 outputIntent->insertObjRef("DestOutputProfile",
345 make_srgb_color_profile());
346 auto intentArray = sk_make_sp<SkPDFArray>();
347 intentArray->appendObject(std::move(outputIntent));
348 return intentArray;
349}
halcanary78daeff2016-04-07 12:34:59 -0700350
halcanary50e82e62016-03-21 13:45:05 -0700351bool SkPDFDocument::onClose(SkWStream* stream) {
352 SkASSERT(!fCanvas.get());
halcanarycc77c122016-03-23 06:26:31 -0700353 if (fPages.empty()) {
354 fPages.reset();
halcanary2e3f9d82015-02-27 12:41:03 -0800355 fCanon.reset();
halcanary50e82e62016-03-21 13:45:05 -0700356 renew(&fObjectSerializer);
halcanary3c35fb32016-06-30 11:55:07 -0700357 renew(&fGlyphUsage);
halcanary50e82e62016-03-21 13:45:05 -0700358 return false;
359 }
halcanary50e82e62016-03-21 13:45:05 -0700360 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
halcanary488165e2016-04-22 06:10:21 -0700361 if (fPDFA) {
halcanary8cd4a242016-04-07 08:56:15 -0700362 SkASSERT(fXMP);
363 docCatalog->insertObjRef("Metadata", fXMP);
halcanary50e82e62016-03-21 13:45:05 -0700364 // Don't specify OutputIntents if we are not in PDF/A mode since
365 // no one has ever asked for this feature.
halcanary488165e2016-04-22 06:10:21 -0700366 docCatalog->insertObject("OutputIntents", make_srgb_output_intents());
367 }
368 SkASSERT(!fPages.empty());
halcanarycc77c122016-03-23 06:26:31 -0700369 docCatalog->insertObjRef("Pages", generate_page_tree(&fPages));
halcanary488165e2016-04-22 06:10:21 -0700370 SkASSERT(fPages.empty());
halcanary50e82e62016-03-21 13:45:05 -0700371
halcanarycc77c122016-03-23 06:26:31 -0700372 if (fDests->size() > 0) {
373 docCatalog->insertObjRef("Dests", std::move(fDests));
reed@google.com99ac02b2013-06-07 20:30:16 +0000374 }
375
halcanary50e82e62016-03-21 13:45:05 -0700376 // Build font subsetting info before calling addObjectRecursively().
halcanarycc77c122016-03-23 06:26:31 -0700377 for (const auto& entry : fGlyphUsage) {
378 sk_sp<SkPDFFont> subsetFont(
halcanary3c35fb32016-06-30 11:55:07 -0700379 entry.fFont->getFontSubset(&entry.fGlyphSet));
halcanarycc77c122016-03-23 06:26:31 -0700380 if (subsetFont) {
381 fObjectSerializer.fSubstituteMap.setSubstitute(
382 entry.fFont, subsetFont.get());
383 }
384 }
halcanaryf12a1672015-09-23 12:45:49 -0700385
halcanary50e82e62016-03-21 13:45:05 -0700386 fObjectSerializer.addObjectRecursively(docCatalog);
387 fObjectSerializer.serializeObjects(this->getStream());
halcanary488165e2016-04-22 06:10:21 -0700388 fObjectSerializer.serializeFooter(this->getStream(), docCatalog, fID);
halcanary50e82e62016-03-21 13:45:05 -0700389 fCanon.reset();
390 renew(&fObjectSerializer);
halcanary3c35fb32016-06-30 11:55:07 -0700391 renew(&fGlyphUsage);
halcanary50e82e62016-03-21 13:45:05 -0700392 return true;
393}
394
reed@google.com99ac02b2013-06-07 20:30:16 +0000395///////////////////////////////////////////////////////////////////////////////
396
halcanary23f4d4d2016-03-12 05:59:39 -0800397sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream,
398 void (*proc)(SkWStream*, bool),
399 SkScalar dpi,
halcanary4b656662016-04-27 07:45:18 -0700400 const SkDocument::PDFMetadata& metadata,
401 sk_sp<SkPixelSerializer> jpeg,
halcanary488165e2016-04-22 06:10:21 -0700402 bool pdfa) {
halcanary4b656662016-04-27 07:45:18 -0700403 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, metadata,
404 std::move(jpeg), pdfa)
halcanary488165e2016-04-22 06:10:21 -0700405 : nullptr;
halcanary23f4d4d2016-03-12 05:59:39 -0800406}
407
halcanary4b656662016-04-27 07:45:18 -0700408sk_sp<SkDocument> SkDocument::MakePDF(const char path[], SkScalar dpi) {
halcanary385fe4d2015-08-26 13:07:48 -0700409 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
halcanaryb8fb9932016-03-28 07:58:30 -0700410 std::unique_ptr<SkFILEWStream> stream(new SkFILEWStream(path));
halcanary23f4d4d2016-03-12 05:59:39 -0800411 return stream->isValid()
halcanary4b656662016-04-27 07:45:18 -0700412 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi,
413 SkDocument::PDFMetadata(), nullptr,
414 false)
415 : nullptr;
416}
417
418sk_sp<SkDocument> SkDocument::MakePDF(SkWStream* stream,
419 SkScalar dpi,
420 const SkDocument::PDFMetadata& metadata,
421 sk_sp<SkPixelSerializer> jpegEncoder,
422 bool pdfa) {
423 return SkPDFMakeDocument(stream, nullptr, dpi, metadata,
424 std::move(jpegEncoder), pdfa);
reed@google.com99ac02b2013-06-07 20:30:16 +0000425}