blob: 7e5e1a8ebaafc2ba0dec1d314939687d0fe04a9c [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
8#include "SkDocument.h"
halcanarya1f1ee92015-02-20 06:17:26 -08009#include "SkPDFCanon.h"
reed58677362014-10-09 05:30:10 -070010#include "SkPDFDevice.h"
halcanarya43b4152015-03-25 12:15:04 -070011#include "SkPDFFont.h"
halcanary2f7ebcb2015-03-25 12:45:28 -070012#include "SkPDFStream.h"
halcanarya43b4152015-03-25 12:15:04 -070013#include "SkPDFTypes.h"
halcanaryf12a1672015-09-23 12:45:49 -070014#include "SkPDFUtils.h"
halcanarya43b4152015-03-25 12:15:04 -070015#include "SkStream.h"
halcanary34422612015-10-12 10:11:18 -070016#include "SkPDFMetadata.h"
halcanarya43b4152015-03-25 12:15:04 -070017
halcanaryf12a1672015-09-23 12:45:49 -070018class SkPDFDict;
19
halcanarya43b4152015-03-25 12:15:04 -070020static void emit_pdf_header(SkWStream* stream) {
21 stream->writeText("%PDF-1.4\n%");
22 // The PDF spec recommends including a comment with four bytes, all
23 // with their high bits set. This is "Skia" with the high bits set.
24 stream->write32(0xD3EBE9E1);
25 stream->writeText("\n");
26}
27
28static void emit_pdf_footer(SkWStream* stream,
halcanary37c46ca2015-03-31 12:30:20 -070029 const SkPDFObjNumMap& objNumMap,
30 const SkPDFSubstituteMap& substitutes,
halcanarya43b4152015-03-25 12:15:04 -070031 SkPDFObject* docCatalog,
32 int64_t objCount,
halcanaryf12a1672015-09-23 12:45:49 -070033 int32_t xRefFileOffset,
halcanary34422612015-10-12 10:11:18 -070034 SkPDFObject* info /* take ownership */,
35 SkPDFObject* id /* take ownership */) {
halcanarya43b4152015-03-25 12:15:04 -070036 SkPDFDict trailerDict;
halcanary34422612015-10-12 10:11:18 -070037 // TODO(http://crbug.com/80908): Linearized format will take a
38 // Prev entry too.
halcanarya43b4152015-03-25 12:15:04 -070039 trailerDict.insertInt("Size", int(objCount));
halcanary72266fd2015-05-05 08:00:24 -070040 trailerDict.insertObjRef("Root", SkRef(docCatalog));
halcanaryf12a1672015-09-23 12:45:49 -070041 SkASSERT(info);
halcanary34422612015-10-12 10:11:18 -070042 trailerDict.insertObjRef("Info", info);
43 if (id) {
44 trailerDict.insertObject("ID", id);
45 }
halcanarya43b4152015-03-25 12:15:04 -070046 stream->writeText("trailer\n");
halcanary37c46ca2015-03-31 12:30:20 -070047 trailerDict.emitObject(stream, objNumMap, substitutes);
halcanarya43b4152015-03-25 12:15:04 -070048 stream->writeText("\nstartxref\n");
49 stream->writeBigDecAsText(xRefFileOffset);
50 stream->writeText("\n%%EOF");
51}
52
halcanary2f7ebcb2015-03-25 12:45:28 -070053static void perform_font_subsetting(
54 const SkTDArray<const SkPDFDevice*>& pageDevices,
halcanary37c46ca2015-03-31 12:30:20 -070055 SkPDFSubstituteMap* substituteMap) {
56 SkASSERT(substituteMap);
halcanarya43b4152015-03-25 12:15:04 -070057
58 SkPDFGlyphSetMap usage;
halcanary2f7ebcb2015-03-25 12:45:28 -070059 for (int i = 0; i < pageDevices.count(); ++i) {
60 usage.merge(pageDevices[i]->getFontGlyphUsage());
halcanarya43b4152015-03-25 12:15:04 -070061 }
62 SkPDFGlyphSetMap::F2BIter iterator(usage);
63 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
64 while (entry) {
halcanary48810a02016-03-07 14:57:50 -080065 sk_sp<SkPDFFont> subsetFont(
halcanarya43b4152015-03-25 12:15:04 -070066 entry->fFont->getFontSubset(entry->fGlyphSet));
67 if (subsetFont) {
halcanary37c46ca2015-03-31 12:30:20 -070068 substituteMap->setSubstitute(entry->fFont, subsetFont.get());
halcanarya43b4152015-03-25 12:15:04 -070069 }
70 entry = iterator.next();
71 }
72}
73
halcanary72266fd2015-05-05 08:00:24 -070074static SkPDFObject* create_pdf_page_content(const SkPDFDevice* pageDevice) {
75 SkAutoTDelete<SkStreamAsset> content(pageDevice->content());
halcanary385fe4d2015-08-26 13:07:48 -070076 return new SkPDFStream(content.get());
halcanary72266fd2015-05-05 08:00:24 -070077}
78
halcanary2f7ebcb2015-03-25 12:45:28 -070079static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) {
halcanaryece83922016-03-08 08:32:12 -080080 auto page = sk_make_sp<SkPDFDict>("Page");
halcanary72266fd2015-05-05 08:00:24 -070081 page->insertObject("Resources", pageDevice->createResourceDict());
82 page->insertObject("MediaBox", pageDevice->copyMediaBox());
halcanaryece83922016-03-08 08:32:12 -080083 auto annotations = sk_make_sp<SkPDFArray>();
halcanaryfcad44b2016-03-06 14:47:10 -080084 pageDevice->appendAnnotations(annotations.get());
wangxianzhuef6c50a2015-09-17 20:38:02 -070085 if (annotations->size() > 0) {
halcanaryfcad44b2016-03-06 14:47:10 -080086 page->insertObject("Annots", annotations.release());
halcanary2f7ebcb2015-03-25 12:45:28 -070087 }
halcanary72266fd2015-05-05 08:00:24 -070088 page->insertObjRef("Contents", create_pdf_page_content(pageDevice));
halcanaryfcad44b2016-03-06 14:47:10 -080089 return page.release();
halcanary2f7ebcb2015-03-25 12:45:28 -070090}
91
92static void generate_page_tree(const SkTDArray<SkPDFDict*>& pages,
93 SkTDArray<SkPDFDict*>* pageTree,
94 SkPDFDict** rootNode) {
95 // PDF wants a tree describing all the pages in the document. We arbitrary
96 // choose 8 (kNodeSize) as the number of allowed children. The internal
97 // nodes have type "Pages" with an array of children, a parent pointer, and
98 // the number of leaves below the node as "Count." The leaves are passed
99 // into the method, have type "Page" and need a parent pointer. This method
100 // builds the tree bottom up, skipping internal nodes that would have only
101 // one child.
102 static const int kNodeSize = 8;
103
halcanary2f7ebcb2015-03-25 12:45:28 -0700104 // curNodes takes a reference to its items, which it passes to pageTree.
105 SkTDArray<SkPDFDict*> curNodes;
106 curNodes.setReserve(pages.count());
107 for (int i = 0; i < pages.count(); i++) {
108 SkSafeRef(pages[i]);
109 curNodes.push(pages[i]);
110 }
111
112 // nextRoundNodes passes its references to nodes on to curNodes.
113 SkTDArray<SkPDFDict*> nextRoundNodes;
114 nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
115
116 int treeCapacity = kNodeSize;
117 do {
118 for (int i = 0; i < curNodes.count(); ) {
119 if (i > 0 && i + 1 == curNodes.count()) {
120 nextRoundNodes.push(curNodes[i]);
121 break;
122 }
123
halcanaryece83922016-03-08 08:32:12 -0800124 auto newNode = sk_make_sp<SkPDFDict>("Pages");
125 auto kids = sk_make_sp<SkPDFArray>();
halcanary2f7ebcb2015-03-25 12:45:28 -0700126 kids->reserve(kNodeSize);
127
128 int count = 0;
129 for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
halcanary72266fd2015-05-05 08:00:24 -0700130 curNodes[i]->insertObjRef("Parent", SkRef(newNode.get()));
131 kids->appendObjRef(SkRef(curNodes[i]));
halcanary2f7ebcb2015-03-25 12:45:28 -0700132
133 // TODO(vandebo): put the objects in strict access order.
134 // Probably doesn't matter because they are so small.
135 if (curNodes[i] != pages[0]) {
136 pageTree->push(curNodes[i]); // Transfer reference.
137 } else {
138 SkSafeUnref(curNodes[i]);
139 }
140 }
141
142 // treeCapacity is the number of leaf nodes possible for the
143 // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
144 // It is hard to count the number of leaf nodes in the current
145 // subtree. However, by construction, we know that unless it's the
146 // last subtree for the current depth, the leaf count will be
147 // treeCapacity, otherwise it's what ever is left over after
148 // consuming treeCapacity chunks.
149 int pageCount = treeCapacity;
150 if (i == curNodes.count()) {
151 pageCount = ((pages.count() - 1) % treeCapacity) + 1;
152 }
halcanary72266fd2015-05-05 08:00:24 -0700153 newNode->insertInt("Count", pageCount);
halcanaryfcad44b2016-03-06 14:47:10 -0800154 newNode->insertObject("Kids", kids.release());
155 nextRoundNodes.push(newNode.release()); // Transfer reference.
halcanary2f7ebcb2015-03-25 12:45:28 -0700156 }
157
158 curNodes = nextRoundNodes;
159 nextRoundNodes.rewind();
160 treeCapacity *= kNodeSize;
161 } while (curNodes.count() > 1);
162
163 pageTree->push(curNodes[0]); // Transfer reference.
164 if (rootNode) {
165 *rootNode = curNodes[0];
166 }
167}
168
halcanarya43b4152015-03-25 12:15:04 -0700169static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices,
halcanary34422612015-10-12 10:11:18 -0700170 const SkPDFMetadata& metadata,
halcanarya43b4152015-03-25 12:15:04 -0700171 SkWStream* stream) {
172 if (pageDevices.isEmpty()) {
173 return false;
174 }
175
halcanary2f7ebcb2015-03-25 12:45:28 -0700176 SkTDArray<SkPDFDict*> pages;
halcanaryece83922016-03-08 08:32:12 -0800177 auto dests = sk_make_sp<SkPDFDict>();
halcanarya43b4152015-03-25 12:15:04 -0700178
179 for (int i = 0; i < pageDevices.count(); i++) {
180 SkASSERT(pageDevices[i]);
181 SkASSERT(i == 0 ||
182 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon());
halcanary48810a02016-03-07 14:57:50 -0800183 sk_sp<SkPDFDict> page(create_pdf_page(pageDevices[i]));
halcanaryfcad44b2016-03-06 14:47:10 -0800184 pageDevices[i]->appendDestinations(dests.get(), page.get());
185 pages.push(page.release());
halcanarya43b4152015-03-25 12:15:04 -0700186 }
halcanarya43b4152015-03-25 12:15:04 -0700187
halcanaryece83922016-03-08 08:32:12 -0800188 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
halcanarya43b4152015-03-25 12:15:04 -0700189
halcanary48810a02016-03-07 14:57:50 -0800190 sk_sp<SkPDFObject> infoDict(
halcanary34422612015-10-12 10:11:18 -0700191 metadata.createDocumentInformationDict());
192
halcanary48810a02016-03-07 14:57:50 -0800193 sk_sp<SkPDFObject> id, xmp;
halcanary34422612015-10-12 10:11:18 -0700194#ifdef SK_PDF_GENERATE_PDFA
195 SkPDFMetadata::UUID uuid = metadata.uuid();
196 // We use the same UUID for Document ID and Instance ID since this
197 // is the first revision of this document (and Skia does not
198 // support revising existing PDF documents).
199 // If we are not in PDF/A mode, don't use a UUID since testing
200 // works best with reproducible outputs.
201 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid));
202 xmp.reset(metadata.createXMPObject(uuid, uuid));
halcanaryfcad44b2016-03-06 14:47:10 -0800203 docCatalog->insertObjRef("Metadata", xmp.release());
halcanary34422612015-10-12 10:11:18 -0700204
205 // sRGB is specified by HTML, CSS, and SVG.
halcanaryece83922016-03-08 08:32:12 -0800206 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
halcanary34422612015-10-12 10:11:18 -0700207 outputIntent->insertName("S", "GTS_PDFA1");
208 outputIntent->insertString("RegistryName", "http://www.color.org");
209 outputIntent->insertString("OutputConditionIdentifier",
210 "sRGB IEC61966-2.1");
halcanaryece83922016-03-08 08:32:12 -0800211 auto intentArray = sk_make_sp<SkPDFArray>();
halcanaryfcad44b2016-03-06 14:47:10 -0800212 intentArray->appendObject(outputIntent.release());
halcanary34422612015-10-12 10:11:18 -0700213 // Don't specify OutputIntents if we are not in PDF/A mode since
214 // no one has ever asked for this feature.
halcanaryfcad44b2016-03-06 14:47:10 -0800215 docCatalog->insertObject("OutputIntents", intentArray.release());
halcanary34422612015-10-12 10:11:18 -0700216#endif
217
218 SkTDArray<SkPDFDict*> pageTree;
halcanarya43b4152015-03-25 12:15:04 -0700219 SkPDFDict* pageTreeRoot;
halcanary2f7ebcb2015-03-25 12:45:28 -0700220 generate_page_tree(pages, &pageTree, &pageTreeRoot);
halcanary72266fd2015-05-05 08:00:24 -0700221 docCatalog->insertObjRef("Pages", SkRef(pageTreeRoot));
halcanarya43b4152015-03-25 12:15:04 -0700222
halcanary72266fd2015-05-05 08:00:24 -0700223 if (dests->size() > 0) {
halcanaryfcad44b2016-03-06 14:47:10 -0800224 docCatalog->insertObjRef("Dests", dests.release());
halcanary72266fd2015-05-05 08:00:24 -0700225 }
halcanarya43b4152015-03-25 12:15:04 -0700226
halcanarya43b4152015-03-25 12:15:04 -0700227 // Build font subsetting info before proceeding.
halcanary37c46ca2015-03-31 12:30:20 -0700228 SkPDFSubstituteMap substitutes;
229 perform_font_subsetting(pageDevices, &substitutes);
halcanarya43b4152015-03-25 12:15:04 -0700230
halcanary37c46ca2015-03-31 12:30:20 -0700231 SkPDFObjNumMap objNumMap;
halcanaryfcad44b2016-03-06 14:47:10 -0800232 objNumMap.addObjectRecursively(infoDict.get(), substitutes);
halcanary34422612015-10-12 10:11:18 -0700233 objNumMap.addObjectRecursively(docCatalog.get(), substitutes);
halcanary4f2b3312015-08-10 08:49:03 -0700234 size_t baseOffset = stream->bytesWritten();
halcanarya43b4152015-03-25 12:15:04 -0700235 emit_pdf_header(stream);
236 SkTDArray<int32_t> offsets;
halcanary37c46ca2015-03-31 12:30:20 -0700237 for (int i = 0; i < objNumMap.objects().count(); ++i) {
238 SkPDFObject* object = objNumMap.objects()[i];
halcanary4f2b3312015-08-10 08:49:03 -0700239 size_t offset = stream->bytesWritten();
240 // This assert checks that size(pdf_header) > 0 and that
241 // the output stream correctly reports bytesWritten().
242 SkASSERT(offset > baseOffset);
243 offsets.push(SkToS32(offset - baseOffset));
halcanary37c46ca2015-03-31 12:30:20 -0700244 SkASSERT(object == substitutes.getSubstitute(object));
245 SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
halcanarya43b4152015-03-25 12:15:04 -0700246 stream->writeDecAsText(i + 1);
247 stream->writeText(" 0 obj\n"); // Generation number is always 0.
halcanary37c46ca2015-03-31 12:30:20 -0700248 object->emitObject(stream, objNumMap, substitutes);
halcanarya43b4152015-03-25 12:15:04 -0700249 stream->writeText("\nendobj\n");
250 }
251 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);
252
halcanary41f88f02015-03-26 15:35:18 -0700253 // Include the zeroth object in the count.
halcanarya43b4152015-03-25 12:15:04 -0700254 int32_t objCount = SkToS32(offsets.count() + 1);
255
256 stream->writeText("xref\n0 ");
halcanary41f88f02015-03-26 15:35:18 -0700257 stream->writeDecAsText(objCount);
halcanarya43b4152015-03-25 12:15:04 -0700258 stream->writeText("\n0000000000 65535 f \n");
259 for (int i = 0; i < offsets.count(); i++) {
260 SkASSERT(offsets[i] > 0);
261 stream->writeBigDecAsText(offsets[i], 10);
262 stream->writeText(" 00000 n \n");
263 }
halcanary37c46ca2015-03-31 12:30:20 -0700264 emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount,
halcanaryfcad44b2016-03-06 14:47:10 -0800265 xRefFileOffset, infoDict.release(), id.release());
halcanarya43b4152015-03-25 12:15:04 -0700266
267 // The page tree has both child and parent pointers, so it creates a
268 // reference cycle. We must clear that cycle to properly reclaim memory.
269 for (int i = 0; i < pageTree.count(); i++) {
270 pageTree[i]->clear();
271 }
272 pageTree.safeUnrefAll();
273 pages.unrefAll();
274 return true;
275}
276
277#if 0
278// TODO(halcanary): expose notEmbeddableCount in SkDocument
279void GetCountOfFontTypes(
280 const SkTDArray<SkPDFDevice*>& pageDevices,
281 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
282 int* notSubsettableCount,
283 int* notEmbeddableCount) {
284 sk_bzero(counts, sizeof(int) *
285 (SkAdvancedTypefaceMetrics::kOther_Font + 1));
286 SkTDArray<SkFontID> seenFonts;
287 int notSubsettable = 0;
288 int notEmbeddable = 0;
289
290 for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) {
291 const SkTDArray<SkPDFFont*>& fontResources =
292 pageDevices[pageNumber]->getFontResources();
293 for (int font = 0; font < fontResources.count(); font++) {
294 SkFontID fontID = fontResources[font]->typeface()->uniqueID();
295 if (seenFonts.find(fontID) == -1) {
296 counts[fontResources[font]->getType()]++;
297 seenFonts.push(fontID);
298 if (!fontResources[font]->canSubset()) {
299 notSubsettable++;
300 }
301 if (!fontResources[font]->canEmbed()) {
302 notEmbeddable++;
303 }
304 }
305 }
306 }
307 if (notSubsettableCount) {
308 *notSubsettableCount = notSubsettable;
309
310 }
311 if (notEmbeddableCount) {
312 *notEmbeddableCount = notEmbeddable;
313 }
314}
315#endif
halcanaryf12a1672015-09-23 12:45:49 -0700316
317template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullptr; }
halcanarya43b4152015-03-25 12:15:04 -0700318////////////////////////////////////////////////////////////////////////////////
reed@google.com99ac02b2013-06-07 20:30:16 +0000319
halcanary7a011842015-03-25 07:52:56 -0700320namespace {
reed@google.com99ac02b2013-06-07 20:30:16 +0000321class SkDocument_PDF : public SkDocument {
322public:
halcanary8c92dc12015-02-19 18:50:05 -0800323 SkDocument_PDF(SkWStream* stream,
halcanary792c80f2015-02-20 07:21:05 -0800324 void (*doneProc)(SkWStream*, bool),
halcanary712fdf72015-12-10 08:59:43 -0800325 SkScalar rasterDpi,
326 SkPixelSerializer* jpegEncoder)
halcanary8c92dc12015-02-19 18:50:05 -0800327 : SkDocument(stream, doneProc)
halcanary712fdf72015-12-10 08:59:43 -0800328 , fRasterDpi(rasterDpi) {
halcanaryfcad44b2016-03-06 14:47:10 -0800329 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder));
halcanary712fdf72015-12-10 08:59:43 -0800330 }
skia.committer@gmail.com63193672013-06-08 07:01:13 +0000331
reed@google.com99ac02b2013-06-07 20:30:16 +0000332 virtual ~SkDocument_PDF() {
333 // subclasses must call close() in their destructors
334 this->close();
335 }
336
337protected:
tfarinaf4219dd2015-04-27 17:18:28 -0700338 SkCanvas* onBeginPage(SkScalar width, SkScalar height,
339 const SkRect& trimBox) override {
halcanarya1f1ee92015-02-20 06:17:26 -0800340 SkASSERT(!fCanvas.get());
reed@google.com99ac02b2013-06-07 20:30:16 +0000341
halcanarya1f1ee92015-02-20 06:17:26 -0800342 SkISize pageSize = SkISize::Make(
343 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
halcanary48810a02016-03-07 14:57:50 -0800344 sk_sp<SkPDFDevice> device(
halcanary7a011842015-03-25 07:52:56 -0700345 SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon));
halcanary385fe4d2015-08-26 13:07:48 -0700346 fCanvas.reset(new SkCanvas(device.get()));
halcanaryfcad44b2016-03-06 14:47:10 -0800347 fPageDevices.push(device.release());
halcanarybe519ad2014-11-10 14:22:14 -0800348 fCanvas->clipRect(trimBox);
halcanary93f81612014-11-10 14:01:57 -0800349 fCanvas->translate(trimBox.x(), trimBox.y());
halcanarya1f1ee92015-02-20 06:17:26 -0800350 return fCanvas.get();
reed@google.com99ac02b2013-06-07 20:30:16 +0000351 }
352
mtklein36352bf2015-03-25 18:17:31 -0700353 void onEndPage() override {
halcanarya1f1ee92015-02-20 06:17:26 -0800354 SkASSERT(fCanvas.get());
reed@google.com99ac02b2013-06-07 20:30:16 +0000355 fCanvas->flush();
halcanary96fcdcc2015-08-27 07:41:13 -0700356 fCanvas.reset(nullptr);
reed@google.com99ac02b2013-06-07 20:30:16 +0000357 }
358
mtklein36352bf2015-03-25 18:17:31 -0700359 bool onClose(SkWStream* stream) override {
halcanarya1f1ee92015-02-20 06:17:26 -0800360 SkASSERT(!fCanvas.get());
reed@google.com99ac02b2013-06-07 20:30:16 +0000361
halcanaryf12a1672015-09-23 12:45:49 -0700362 bool success = emit_pdf_document(fPageDevices, fMetadata, stream);
halcanary7a011842015-03-25 07:52:56 -0700363 fPageDevices.unrefAll();
halcanary2e3f9d82015-02-27 12:41:03 -0800364 fCanon.reset();
commit-bot@chromium.orgb5a66512013-10-09 21:09:00 +0000365 return success;
366 }
367
mtklein36352bf2015-03-25 18:17:31 -0700368 void onAbort() override {
halcanary7a011842015-03-25 07:52:56 -0700369 fPageDevices.unrefAll();
halcanary2e3f9d82015-02-27 12:41:03 -0800370 fCanon.reset();
reed@google.com99ac02b2013-06-07 20:30:16 +0000371 }
372
halcanary70015762016-02-11 07:59:59 -0800373 void setMetadata(const SkDocument::Attribute info[],
374 int infoCount,
halcanaryf12a1672015-09-23 12:45:49 -0700375 const SkTime::DateTime* creationDate,
376 const SkTime::DateTime* modifiedDate) override {
halcanary70015762016-02-11 07:59:59 -0800377 fMetadata.fInfo.reset(info, infoCount);
halcanaryf12a1672015-09-23 12:45:49 -0700378 fMetadata.fCreation.reset(clone(creationDate));
379 fMetadata.fModified.reset(clone(modifiedDate));
380 }
381
reed@google.com99ac02b2013-06-07 20:30:16 +0000382private:
halcanarya1f1ee92015-02-20 06:17:26 -0800383 SkPDFCanon fCanon;
halcanary6d622702015-03-25 08:45:42 -0700384 SkTDArray<const SkPDFDevice*> fPageDevices;
halcanary48810a02016-03-07 14:57:50 -0800385 sk_sp<SkCanvas> fCanvas;
halcanarya1f1ee92015-02-20 06:17:26 -0800386 SkScalar fRasterDpi;
halcanary34422612015-10-12 10:11:18 -0700387 SkPDFMetadata fMetadata;
reed@google.com99ac02b2013-06-07 20:30:16 +0000388};
halcanary7a011842015-03-25 07:52:56 -0700389} // namespace
reed@google.com99ac02b2013-06-07 20:30:16 +0000390///////////////////////////////////////////////////////////////////////////////
391
halcanary8c92dc12015-02-19 18:50:05 -0800392SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
halcanary712fdf72015-12-10 08:59:43 -0800393 return stream ? new SkDocument_PDF(stream, nullptr, dpi, nullptr) : nullptr;
394}
395
396SkDocument* SkDocument::CreatePDF(SkWStream* stream,
397 SkScalar dpi,
398 SkPixelSerializer* jpegEncoder) {
399 return stream
400 ? new SkDocument_PDF(stream, nullptr, dpi, jpegEncoder)
401 : nullptr;
reed@google.com99ac02b2013-06-07 20:30:16 +0000402}
403
halcanary8c92dc12015-02-19 18:50:05 -0800404SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
halcanary385fe4d2015-08-26 13:07:48 -0700405 SkFILEWStream* stream = new SkFILEWStream(path);
reed@google.com99ac02b2013-06-07 20:30:16 +0000406 if (!stream->isValid()) {
halcanary385fe4d2015-08-26 13:07:48 -0700407 delete stream;
halcanary96fcdcc2015-08-27 07:41:13 -0700408 return nullptr;
reed@google.com99ac02b2013-06-07 20:30:16 +0000409 }
halcanary385fe4d2015-08-26 13:07:48 -0700410 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
halcanary712fdf72015-12-10 08:59:43 -0800411 return new SkDocument_PDF(stream, delete_wstream, dpi, nullptr);
reed@google.com99ac02b2013-06-07 20:30:16 +0000412}