blob: 670908e6d51199f706d26cbddee6458daed6b536 [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"
reed58677362014-10-09 05:30:10 -07009#include "SkPDFDevice.h"
halcanary23f4d4d2016-03-12 05:59:39 -080010#include "SkPDFDocument.h"
halcanarya43b4152015-03-25 12:15:04 -070011#include "SkPDFFont.h"
halcanary23f4d4d2016-03-12 05:59:39 -080012#include "SkPDFMetadata.h"
halcanary2f7ebcb2015-03-25 12:45:28 -070013#include "SkPDFStream.h"
halcanarya43b4152015-03-25 12:15:04 -070014#include "SkPDFTypes.h"
halcanaryf12a1672015-09-23 12:45:49 -070015#include "SkPDFUtils.h"
halcanarya43b4152015-03-25 12:15:04 -070016#include "SkStream.h"
halcanaryf12a1672015-09-23 12:45:49 -070017
halcanarya43b4152015-03-25 12:15:04 -070018static void emit_pdf_header(SkWStream* stream) {
19 stream->writeText("%PDF-1.4\n%");
20 // The PDF spec recommends including a comment with four bytes, all
21 // with their high bits set. This is "Skia" with the high bits set.
22 stream->write32(0xD3EBE9E1);
23 stream->writeText("\n");
24}
25
26static void emit_pdf_footer(SkWStream* stream,
halcanary37c46ca2015-03-31 12:30:20 -070027 const SkPDFObjNumMap& objNumMap,
28 const SkPDFSubstituteMap& substitutes,
halcanarya43b4152015-03-25 12:15:04 -070029 SkPDFObject* docCatalog,
30 int64_t objCount,
halcanaryf12a1672015-09-23 12:45:49 -070031 int32_t xRefFileOffset,
halcanary8103a342016-03-08 15:10:16 -080032 sk_sp<SkPDFObject> info,
33 sk_sp<SkPDFObject> id) {
halcanarya43b4152015-03-25 12:15:04 -070034 SkPDFDict trailerDict;
halcanary34422612015-10-12 10:11:18 -070035 // TODO(http://crbug.com/80908): Linearized format will take a
36 // Prev entry too.
halcanarya43b4152015-03-25 12:15:04 -070037 trailerDict.insertInt("Size", int(objCount));
halcanarye94ea622016-03-09 07:52:09 -080038 trailerDict.insertObjRef("Root", sk_ref_sp(docCatalog));
halcanaryf12a1672015-09-23 12:45:49 -070039 SkASSERT(info);
halcanary8103a342016-03-08 15:10:16 -080040 trailerDict.insertObjRef("Info", std::move(info));
halcanary34422612015-10-12 10:11:18 -070041 if (id) {
halcanary8103a342016-03-08 15:10:16 -080042 trailerDict.insertObject("ID", std::move(id));
halcanary34422612015-10-12 10:11:18 -070043 }
halcanarya43b4152015-03-25 12:15:04 -070044 stream->writeText("trailer\n");
halcanary37c46ca2015-03-31 12:30:20 -070045 trailerDict.emitObject(stream, objNumMap, substitutes);
halcanarya43b4152015-03-25 12:15:04 -070046 stream->writeText("\nstartxref\n");
47 stream->writeBigDecAsText(xRefFileOffset);
48 stream->writeText("\n%%EOF");
49}
50
halcanary2f7ebcb2015-03-25 12:45:28 -070051static void perform_font_subsetting(
halcanary8103a342016-03-08 15:10:16 -080052 const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices,
halcanary37c46ca2015-03-31 12:30:20 -070053 SkPDFSubstituteMap* substituteMap) {
54 SkASSERT(substituteMap);
halcanarya43b4152015-03-25 12:15:04 -070055
56 SkPDFGlyphSetMap usage;
halcanary8103a342016-03-08 15:10:16 -080057 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) {
58 usage.merge(pageDevice->getFontGlyphUsage());
halcanarya43b4152015-03-25 12:15:04 -070059 }
60 SkPDFGlyphSetMap::F2BIter iterator(usage);
61 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
62 while (entry) {
halcanary48810a02016-03-07 14:57:50 -080063 sk_sp<SkPDFFont> subsetFont(
halcanarya43b4152015-03-25 12:15:04 -070064 entry->fFont->getFontSubset(entry->fGlyphSet));
65 if (subsetFont) {
halcanary37c46ca2015-03-31 12:30:20 -070066 substituteMap->setSubstitute(entry->fFont, subsetFont.get());
halcanarya43b4152015-03-25 12:15:04 -070067 }
68 entry = iterator.next();
69 }
70}
71
halcanary8103a342016-03-08 15:10:16 -080072static sk_sp<SkPDFDict> create_pdf_page(const SkPDFDevice* pageDevice) {
halcanaryece83922016-03-08 08:32:12 -080073 auto page = sk_make_sp<SkPDFDict>("Page");
halcanary8103a342016-03-08 15:10:16 -080074 page->insertObject("Resources", pageDevice->makeResourceDict());
halcanary72266fd2015-05-05 08:00:24 -070075 page->insertObject("MediaBox", pageDevice->copyMediaBox());
halcanaryece83922016-03-08 08:32:12 -080076 auto annotations = sk_make_sp<SkPDFArray>();
halcanaryfcad44b2016-03-06 14:47:10 -080077 pageDevice->appendAnnotations(annotations.get());
wangxianzhuef6c50a2015-09-17 20:38:02 -070078 if (annotations->size() > 0) {
halcanary8103a342016-03-08 15:10:16 -080079 page->insertObject("Annots", std::move(annotations));
halcanary2f7ebcb2015-03-25 12:45:28 -070080 }
halcanary8103a342016-03-08 15:10:16 -080081 auto content = pageDevice->content();
82 page->insertObjRef("Contents", sk_make_sp<SkPDFStream>(content.get()));
83 return page;
halcanary2f7ebcb2015-03-25 12:45:28 -070084}
85
halcanary8103a342016-03-08 15:10:16 -080086// return root node.
87static sk_sp<SkPDFDict> generate_page_tree(
88 const SkTDArray<SkPDFDict*>& pages,
89 SkTDArray<SkPDFDict*>* pageTree) {
halcanary2f7ebcb2015-03-25 12:45:28 -070090 // PDF wants a tree describing all the pages in the document. We arbitrary
91 // choose 8 (kNodeSize) as the number of allowed children. The internal
92 // nodes have type "Pages" with an array of children, a parent pointer, and
93 // the number of leaves below the node as "Count." The leaves are passed
94 // into the method, have type "Page" and need a parent pointer. This method
95 // builds the tree bottom up, skipping internal nodes that would have only
96 // one child.
97 static const int kNodeSize = 8;
98
halcanary2f7ebcb2015-03-25 12:45:28 -070099 // curNodes takes a reference to its items, which it passes to pageTree.
100 SkTDArray<SkPDFDict*> curNodes;
101 curNodes.setReserve(pages.count());
102 for (int i = 0; i < pages.count(); i++) {
103 SkSafeRef(pages[i]);
104 curNodes.push(pages[i]);
105 }
106
107 // nextRoundNodes passes its references to nodes on to curNodes.
108 SkTDArray<SkPDFDict*> nextRoundNodes;
109 nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
110
111 int treeCapacity = kNodeSize;
112 do {
113 for (int i = 0; i < curNodes.count(); ) {
114 if (i > 0 && i + 1 == curNodes.count()) {
115 nextRoundNodes.push(curNodes[i]);
116 break;
117 }
118
halcanaryece83922016-03-08 08:32:12 -0800119 auto newNode = sk_make_sp<SkPDFDict>("Pages");
120 auto kids = sk_make_sp<SkPDFArray>();
halcanary2f7ebcb2015-03-25 12:45:28 -0700121 kids->reserve(kNodeSize);
122
123 int count = 0;
124 for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
halcanary8103a342016-03-08 15:10:16 -0800125 curNodes[i]->insertObjRef("Parent", newNode);
halcanarye94ea622016-03-09 07:52:09 -0800126 kids->appendObjRef(sk_ref_sp(curNodes[i]));
halcanary2f7ebcb2015-03-25 12:45:28 -0700127
128 // TODO(vandebo): put the objects in strict access order.
129 // Probably doesn't matter because they are so small.
130 if (curNodes[i] != pages[0]) {
131 pageTree->push(curNodes[i]); // Transfer reference.
132 } else {
133 SkSafeUnref(curNodes[i]);
134 }
135 }
136
137 // treeCapacity is the number of leaf nodes possible for the
138 // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
139 // It is hard to count the number of leaf nodes in the current
140 // subtree. However, by construction, we know that unless it's the
141 // last subtree for the current depth, the leaf count will be
142 // treeCapacity, otherwise it's what ever is left over after
143 // consuming treeCapacity chunks.
144 int pageCount = treeCapacity;
145 if (i == curNodes.count()) {
146 pageCount = ((pages.count() - 1) % treeCapacity) + 1;
147 }
halcanary72266fd2015-05-05 08:00:24 -0700148 newNode->insertInt("Count", pageCount);
halcanary8103a342016-03-08 15:10:16 -0800149 newNode->insertObject("Kids", std::move(kids));
halcanaryfcad44b2016-03-06 14:47:10 -0800150 nextRoundNodes.push(newNode.release()); // Transfer reference.
halcanary2f7ebcb2015-03-25 12:45:28 -0700151 }
152
153 curNodes = nextRoundNodes;
154 nextRoundNodes.rewind();
155 treeCapacity *= kNodeSize;
156 } while (curNodes.count() > 1);
157
158 pageTree->push(curNodes[0]); // Transfer reference.
halcanarye94ea622016-03-09 07:52:09 -0800159 return sk_ref_sp(curNodes[0]);
halcanary2f7ebcb2015-03-25 12:45:28 -0700160}
161
halcanary8103a342016-03-08 15:10:16 -0800162static bool emit_pdf_document(const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices,
halcanary34422612015-10-12 10:11:18 -0700163 const SkPDFMetadata& metadata,
halcanarya43b4152015-03-25 12:15:04 -0700164 SkWStream* stream) {
halcanary8103a342016-03-08 15:10:16 -0800165 if (pageDevices.empty()) {
halcanarya43b4152015-03-25 12:15:04 -0700166 return false;
167 }
168
halcanary8103a342016-03-08 15:10:16 -0800169 SkTDArray<SkPDFDict*> pages; // TODO: SkTArray<sk_sp<SkPDFDict>>
halcanaryece83922016-03-08 08:32:12 -0800170 auto dests = sk_make_sp<SkPDFDict>();
halcanarya43b4152015-03-25 12:15:04 -0700171
halcanary8103a342016-03-08 15:10:16 -0800172 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) {
173 SkASSERT(pageDevice);
174 SkASSERT(pageDevices[0]->getCanon() == pageDevice->getCanon());
175 sk_sp<SkPDFDict> page(create_pdf_page(pageDevice.get()));
176 pageDevice->appendDestinations(dests.get(), page.get());
halcanaryfcad44b2016-03-06 14:47:10 -0800177 pages.push(page.release());
halcanarya43b4152015-03-25 12:15:04 -0700178 }
halcanarya43b4152015-03-25 12:15:04 -0700179
halcanaryece83922016-03-08 08:32:12 -0800180 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
halcanarya43b4152015-03-25 12:15:04 -0700181
halcanary8103a342016-03-08 15:10:16 -0800182 sk_sp<SkPDFObject> infoDict(metadata.createDocumentInformationDict());
halcanary34422612015-10-12 10:11:18 -0700183
halcanary48810a02016-03-07 14:57:50 -0800184 sk_sp<SkPDFObject> id, xmp;
halcanary34422612015-10-12 10:11:18 -0700185#ifdef SK_PDF_GENERATE_PDFA
186 SkPDFMetadata::UUID uuid = metadata.uuid();
187 // We use the same UUID for Document ID and Instance ID since this
188 // is the first revision of this document (and Skia does not
189 // support revising existing PDF documents).
190 // If we are not in PDF/A mode, don't use a UUID since testing
191 // works best with reproducible outputs.
192 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid));
193 xmp.reset(metadata.createXMPObject(uuid, uuid));
halcanary8103a342016-03-08 15:10:16 -0800194 docCatalog->insertObjRef("Metadata", std::move(xmp));
halcanary34422612015-10-12 10:11:18 -0700195
196 // sRGB is specified by HTML, CSS, and SVG.
halcanaryece83922016-03-08 08:32:12 -0800197 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
halcanary34422612015-10-12 10:11:18 -0700198 outputIntent->insertName("S", "GTS_PDFA1");
199 outputIntent->insertString("RegistryName", "http://www.color.org");
200 outputIntent->insertString("OutputConditionIdentifier",
201 "sRGB IEC61966-2.1");
halcanaryece83922016-03-08 08:32:12 -0800202 auto intentArray = sk_make_sp<SkPDFArray>();
halcanary8103a342016-03-08 15:10:16 -0800203 intentArray->appendObject(std::move(outputIntent));
halcanary34422612015-10-12 10:11:18 -0700204 // Don't specify OutputIntents if we are not in PDF/A mode since
205 // no one has ever asked for this feature.
halcanary8103a342016-03-08 15:10:16 -0800206 docCatalog->insertObject("OutputIntents", std::move(intentArray));
halcanary34422612015-10-12 10:11:18 -0700207#endif
208
209 SkTDArray<SkPDFDict*> pageTree;
halcanary8103a342016-03-08 15:10:16 -0800210 docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree));
halcanarya43b4152015-03-25 12:15:04 -0700211
halcanary72266fd2015-05-05 08:00:24 -0700212 if (dests->size() > 0) {
halcanary8103a342016-03-08 15:10:16 -0800213 docCatalog->insertObjRef("Dests", std::move(dests));
halcanary72266fd2015-05-05 08:00:24 -0700214 }
halcanarya43b4152015-03-25 12:15:04 -0700215
halcanarya43b4152015-03-25 12:15:04 -0700216 // Build font subsetting info before proceeding.
halcanary37c46ca2015-03-31 12:30:20 -0700217 SkPDFSubstituteMap substitutes;
218 perform_font_subsetting(pageDevices, &substitutes);
halcanarya43b4152015-03-25 12:15:04 -0700219
halcanary37c46ca2015-03-31 12:30:20 -0700220 SkPDFObjNumMap objNumMap;
halcanaryfcad44b2016-03-06 14:47:10 -0800221 objNumMap.addObjectRecursively(infoDict.get(), substitutes);
halcanary34422612015-10-12 10:11:18 -0700222 objNumMap.addObjectRecursively(docCatalog.get(), substitutes);
halcanary4f2b3312015-08-10 08:49:03 -0700223 size_t baseOffset = stream->bytesWritten();
halcanarya43b4152015-03-25 12:15:04 -0700224 emit_pdf_header(stream);
225 SkTDArray<int32_t> offsets;
halcanary37c46ca2015-03-31 12:30:20 -0700226 for (int i = 0; i < objNumMap.objects().count(); ++i) {
halcanarybae235e2016-03-21 10:05:23 -0700227 SkPDFObject* object = objNumMap.objects()[i].get();
halcanary4f2b3312015-08-10 08:49:03 -0700228 size_t offset = stream->bytesWritten();
229 // This assert checks that size(pdf_header) > 0 and that
230 // the output stream correctly reports bytesWritten().
231 SkASSERT(offset > baseOffset);
232 offsets.push(SkToS32(offset - baseOffset));
halcanary37c46ca2015-03-31 12:30:20 -0700233 SkASSERT(object == substitutes.getSubstitute(object));
234 SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
halcanarya43b4152015-03-25 12:15:04 -0700235 stream->writeDecAsText(i + 1);
236 stream->writeText(" 0 obj\n"); // Generation number is always 0.
halcanary37c46ca2015-03-31 12:30:20 -0700237 object->emitObject(stream, objNumMap, substitutes);
halcanarya43b4152015-03-25 12:15:04 -0700238 stream->writeText("\nendobj\n");
halcanarybae235e2016-03-21 10:05:23 -0700239 object->drop();
halcanarya43b4152015-03-25 12:15:04 -0700240 }
241 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);
242
halcanary41f88f02015-03-26 15:35:18 -0700243 // Include the zeroth object in the count.
halcanarya43b4152015-03-25 12:15:04 -0700244 int32_t objCount = SkToS32(offsets.count() + 1);
245
246 stream->writeText("xref\n0 ");
halcanary41f88f02015-03-26 15:35:18 -0700247 stream->writeDecAsText(objCount);
halcanarya43b4152015-03-25 12:15:04 -0700248 stream->writeText("\n0000000000 65535 f \n");
249 for (int i = 0; i < offsets.count(); i++) {
250 SkASSERT(offsets[i] > 0);
251 stream->writeBigDecAsText(offsets[i], 10);
252 stream->writeText(" 00000 n \n");
253 }
halcanary37c46ca2015-03-31 12:30:20 -0700254 emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount,
halcanary8103a342016-03-08 15:10:16 -0800255 xRefFileOffset, std::move(infoDict), std::move(id));
halcanarya43b4152015-03-25 12:15:04 -0700256
257 // The page tree has both child and parent pointers, so it creates a
258 // reference cycle. We must clear that cycle to properly reclaim memory.
259 for (int i = 0; i < pageTree.count(); i++) {
halcanarybae235e2016-03-21 10:05:23 -0700260 pageTree[i]->drop();
halcanarya43b4152015-03-25 12:15:04 -0700261 }
262 pageTree.safeUnrefAll();
263 pages.unrefAll();
264 return true;
265}
266
267#if 0
268// TODO(halcanary): expose notEmbeddableCount in SkDocument
269void GetCountOfFontTypes(
270 const SkTDArray<SkPDFDevice*>& pageDevices,
271 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
272 int* notSubsettableCount,
273 int* notEmbeddableCount) {
274 sk_bzero(counts, sizeof(int) *
275 (SkAdvancedTypefaceMetrics::kOther_Font + 1));
276 SkTDArray<SkFontID> seenFonts;
277 int notSubsettable = 0;
278 int notEmbeddable = 0;
279
280 for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) {
281 const SkTDArray<SkPDFFont*>& fontResources =
282 pageDevices[pageNumber]->getFontResources();
283 for (int font = 0; font < fontResources.count(); font++) {
284 SkFontID fontID = fontResources[font]->typeface()->uniqueID();
285 if (seenFonts.find(fontID) == -1) {
286 counts[fontResources[font]->getType()]++;
287 seenFonts.push(fontID);
288 if (!fontResources[font]->canSubset()) {
289 notSubsettable++;
290 }
291 if (!fontResources[font]->canEmbed()) {
292 notEmbeddable++;
293 }
294 }
295 }
296 }
297 if (notSubsettableCount) {
298 *notSubsettableCount = notSubsettable;
299
300 }
301 if (notEmbeddableCount) {
302 *notEmbeddableCount = notEmbeddable;
303 }
304}
305#endif
halcanaryf12a1672015-09-23 12:45:49 -0700306
307template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullptr; }
halcanarya43b4152015-03-25 12:15:04 -0700308////////////////////////////////////////////////////////////////////////////////
reed@google.com99ac02b2013-06-07 20:30:16 +0000309
halcanary7a011842015-03-25 07:52:56 -0700310namespace {
halcanary23f4d4d2016-03-12 05:59:39 -0800311class SkPDFDocument : public SkDocument {
reed@google.com99ac02b2013-06-07 20:30:16 +0000312public:
halcanary23f4d4d2016-03-12 05:59:39 -0800313 SkPDFDocument(SkWStream* stream,
halcanary792c80f2015-02-20 07:21:05 -0800314 void (*doneProc)(SkWStream*, bool),
halcanary712fdf72015-12-10 08:59:43 -0800315 SkScalar rasterDpi,
316 SkPixelSerializer* jpegEncoder)
halcanary8c92dc12015-02-19 18:50:05 -0800317 : SkDocument(stream, doneProc)
halcanary712fdf72015-12-10 08:59:43 -0800318 , fRasterDpi(rasterDpi) {
halcanaryfcad44b2016-03-06 14:47:10 -0800319 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder));
halcanary712fdf72015-12-10 08:59:43 -0800320 }
skia.committer@gmail.com63193672013-06-08 07:01:13 +0000321
halcanary23f4d4d2016-03-12 05:59:39 -0800322 virtual ~SkPDFDocument() {
reed@google.com99ac02b2013-06-07 20:30:16 +0000323 // subclasses must call close() in their destructors
324 this->close();
325 }
326
327protected:
tfarinaf4219dd2015-04-27 17:18:28 -0700328 SkCanvas* onBeginPage(SkScalar width, SkScalar height,
329 const SkRect& trimBox) override {
halcanarya1f1ee92015-02-20 06:17:26 -0800330 SkASSERT(!fCanvas.get());
reed@google.com99ac02b2013-06-07 20:30:16 +0000331
halcanarya1f1ee92015-02-20 06:17:26 -0800332 SkISize pageSize = SkISize::Make(
333 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
halcanary48810a02016-03-07 14:57:50 -0800334 sk_sp<SkPDFDevice> device(
halcanary7a011842015-03-25 07:52:56 -0700335 SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon));
halcanary385fe4d2015-08-26 13:07:48 -0700336 fCanvas.reset(new SkCanvas(device.get()));
halcanary8103a342016-03-08 15:10:16 -0800337 fPageDevices.push_back(std::move(device));
halcanarybe519ad2014-11-10 14:22:14 -0800338 fCanvas->clipRect(trimBox);
halcanary93f81612014-11-10 14:01:57 -0800339 fCanvas->translate(trimBox.x(), trimBox.y());
halcanarya1f1ee92015-02-20 06:17:26 -0800340 return fCanvas.get();
reed@google.com99ac02b2013-06-07 20:30:16 +0000341 }
342
mtklein36352bf2015-03-25 18:17:31 -0700343 void onEndPage() override {
halcanarya1f1ee92015-02-20 06:17:26 -0800344 SkASSERT(fCanvas.get());
reed@google.com99ac02b2013-06-07 20:30:16 +0000345 fCanvas->flush();
halcanary96fcdcc2015-08-27 07:41:13 -0700346 fCanvas.reset(nullptr);
reed@google.com99ac02b2013-06-07 20:30:16 +0000347 }
348
mtklein36352bf2015-03-25 18:17:31 -0700349 bool onClose(SkWStream* stream) override {
halcanarya1f1ee92015-02-20 06:17:26 -0800350 SkASSERT(!fCanvas.get());
reed@google.com99ac02b2013-06-07 20:30:16 +0000351
halcanaryf12a1672015-09-23 12:45:49 -0700352 bool success = emit_pdf_document(fPageDevices, fMetadata, stream);
halcanary8103a342016-03-08 15:10:16 -0800353 fPageDevices.reset();
halcanary2e3f9d82015-02-27 12:41:03 -0800354 fCanon.reset();
commit-bot@chromium.orgb5a66512013-10-09 21:09:00 +0000355 return success;
356 }
357
mtklein36352bf2015-03-25 18:17:31 -0700358 void onAbort() override {
halcanary8103a342016-03-08 15:10:16 -0800359 fPageDevices.reset();
halcanary2e3f9d82015-02-27 12:41:03 -0800360 fCanon.reset();
reed@google.com99ac02b2013-06-07 20:30:16 +0000361 }
362
halcanary70015762016-02-11 07:59:59 -0800363 void setMetadata(const SkDocument::Attribute info[],
364 int infoCount,
halcanaryf12a1672015-09-23 12:45:49 -0700365 const SkTime::DateTime* creationDate,
366 const SkTime::DateTime* modifiedDate) override {
halcanary70015762016-02-11 07:59:59 -0800367 fMetadata.fInfo.reset(info, infoCount);
halcanaryf12a1672015-09-23 12:45:49 -0700368 fMetadata.fCreation.reset(clone(creationDate));
369 fMetadata.fModified.reset(clone(modifiedDate));
370 }
371
reed@google.com99ac02b2013-06-07 20:30:16 +0000372private:
halcanarya1f1ee92015-02-20 06:17:26 -0800373 SkPDFCanon fCanon;
halcanary8103a342016-03-08 15:10:16 -0800374 SkTArray<sk_sp<const SkPDFDevice>> fPageDevices;
halcanary48810a02016-03-07 14:57:50 -0800375 sk_sp<SkCanvas> fCanvas;
halcanarya1f1ee92015-02-20 06:17:26 -0800376 SkScalar fRasterDpi;
halcanary34422612015-10-12 10:11:18 -0700377 SkPDFMetadata fMetadata;
reed@google.com99ac02b2013-06-07 20:30:16 +0000378};
halcanary7a011842015-03-25 07:52:56 -0700379} // namespace
reed@google.com99ac02b2013-06-07 20:30:16 +0000380///////////////////////////////////////////////////////////////////////////////
381
halcanary23f4d4d2016-03-12 05:59:39 -0800382sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream,
383 void (*proc)(SkWStream*, bool),
384 SkScalar dpi,
385 SkPixelSerializer* jpeg) {
386 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, jpeg) : nullptr;
387}
388
halcanary8c92dc12015-02-19 18:50:05 -0800389SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
halcanary23f4d4d2016-03-12 05:59:39 -0800390 return SkPDFMakeDocument(stream, nullptr, dpi, nullptr).release();
halcanary712fdf72015-12-10 08:59:43 -0800391}
392
393SkDocument* SkDocument::CreatePDF(SkWStream* stream,
394 SkScalar dpi,
395 SkPixelSerializer* jpegEncoder) {
halcanary23f4d4d2016-03-12 05:59:39 -0800396 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release();
reed@google.com99ac02b2013-06-07 20:30:16 +0000397}
398
halcanary8c92dc12015-02-19 18:50:05 -0800399SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
halcanary385fe4d2015-08-26 13:07:48 -0700400 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
halcanary23f4d4d2016-03-12 05:59:39 -0800401 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path));
402 return stream->isValid()
mtklein18300a32016-03-16 13:53:35 -0700403 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).release()
halcanary23f4d4d2016-03-12 05:59:39 -0800404 : nullptr;
reed@google.com99ac02b2013-06-07 20:30:16 +0000405}