blob: 1cc4ffaca9520b8839a1ebfe50e6f376081fcef0 [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,
halcanary8103a342016-03-08 15:10:16 -080034 sk_sp<SkPDFObject> info,
35 sk_sp<SkPDFObject> id) {
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));
halcanary8103a342016-03-08 15:10:16 -080040 trailerDict.insertObjRef("Root", sk_sp<SkPDFObject>(SkRef(docCatalog)));
halcanaryf12a1672015-09-23 12:45:49 -070041 SkASSERT(info);
halcanary8103a342016-03-08 15:10:16 -080042 trailerDict.insertObjRef("Info", std::move(info));
halcanary34422612015-10-12 10:11:18 -070043 if (id) {
halcanary8103a342016-03-08 15:10:16 -080044 trailerDict.insertObject("ID", std::move(id));
halcanary34422612015-10-12 10:11:18 -070045 }
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(
halcanary8103a342016-03-08 15:10:16 -080054 const SkTArray<sk_sp<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;
halcanary8103a342016-03-08 15:10:16 -080059 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) {
60 usage.merge(pageDevice->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
halcanary8103a342016-03-08 15:10:16 -080074static sk_sp<SkPDFDict> create_pdf_page(const SkPDFDevice* pageDevice) {
halcanaryece83922016-03-08 08:32:12 -080075 auto page = sk_make_sp<SkPDFDict>("Page");
halcanary8103a342016-03-08 15:10:16 -080076 page->insertObject("Resources", pageDevice->makeResourceDict());
halcanary72266fd2015-05-05 08:00:24 -070077 page->insertObject("MediaBox", pageDevice->copyMediaBox());
halcanaryece83922016-03-08 08:32:12 -080078 auto annotations = sk_make_sp<SkPDFArray>();
halcanaryfcad44b2016-03-06 14:47:10 -080079 pageDevice->appendAnnotations(annotations.get());
wangxianzhuef6c50a2015-09-17 20:38:02 -070080 if (annotations->size() > 0) {
halcanary8103a342016-03-08 15:10:16 -080081 page->insertObject("Annots", std::move(annotations));
halcanary2f7ebcb2015-03-25 12:45:28 -070082 }
halcanary8103a342016-03-08 15:10:16 -080083 auto content = pageDevice->content();
84 page->insertObjRef("Contents", sk_make_sp<SkPDFStream>(content.get()));
85 return page;
halcanary2f7ebcb2015-03-25 12:45:28 -070086}
87
halcanary8103a342016-03-08 15:10:16 -080088// return root node.
89static sk_sp<SkPDFDict> generate_page_tree(
90 const SkTDArray<SkPDFDict*>& pages,
91 SkTDArray<SkPDFDict*>* pageTree) {
halcanary2f7ebcb2015-03-25 12:45:28 -070092 // PDF wants a tree describing all the pages in the document. We arbitrary
93 // choose 8 (kNodeSize) as the number of allowed children. The internal
94 // nodes have type "Pages" with an array of children, a parent pointer, and
95 // the number of leaves below the node as "Count." The leaves are passed
96 // into the method, have type "Page" and need a parent pointer. This method
97 // builds the tree bottom up, skipping internal nodes that would have only
98 // one child.
99 static const int kNodeSize = 8;
100
halcanary2f7ebcb2015-03-25 12:45:28 -0700101 // curNodes takes a reference to its items, which it passes to pageTree.
102 SkTDArray<SkPDFDict*> curNodes;
103 curNodes.setReserve(pages.count());
104 for (int i = 0; i < pages.count(); i++) {
105 SkSafeRef(pages[i]);
106 curNodes.push(pages[i]);
107 }
108
109 // nextRoundNodes passes its references to nodes on to curNodes.
110 SkTDArray<SkPDFDict*> nextRoundNodes;
111 nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
112
113 int treeCapacity = kNodeSize;
114 do {
115 for (int i = 0; i < curNodes.count(); ) {
116 if (i > 0 && i + 1 == curNodes.count()) {
117 nextRoundNodes.push(curNodes[i]);
118 break;
119 }
120
halcanaryece83922016-03-08 08:32:12 -0800121 auto newNode = sk_make_sp<SkPDFDict>("Pages");
122 auto kids = sk_make_sp<SkPDFArray>();
halcanary2f7ebcb2015-03-25 12:45:28 -0700123 kids->reserve(kNodeSize);
124
125 int count = 0;
126 for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
halcanary8103a342016-03-08 15:10:16 -0800127 curNodes[i]->insertObjRef("Parent", newNode);
128 kids->appendObjRef(sk_sp<SkPDFDict>(SkRef(curNodes[i])));
halcanary2f7ebcb2015-03-25 12:45:28 -0700129
130 // TODO(vandebo): put the objects in strict access order.
131 // Probably doesn't matter because they are so small.
132 if (curNodes[i] != pages[0]) {
133 pageTree->push(curNodes[i]); // Transfer reference.
134 } else {
135 SkSafeUnref(curNodes[i]);
136 }
137 }
138
139 // treeCapacity is the number of leaf nodes possible for the
140 // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
141 // It is hard to count the number of leaf nodes in the current
142 // subtree. However, by construction, we know that unless it's the
143 // last subtree for the current depth, the leaf count will be
144 // treeCapacity, otherwise it's what ever is left over after
145 // consuming treeCapacity chunks.
146 int pageCount = treeCapacity;
147 if (i == curNodes.count()) {
148 pageCount = ((pages.count() - 1) % treeCapacity) + 1;
149 }
halcanary72266fd2015-05-05 08:00:24 -0700150 newNode->insertInt("Count", pageCount);
halcanary8103a342016-03-08 15:10:16 -0800151 newNode->insertObject("Kids", std::move(kids));
halcanaryfcad44b2016-03-06 14:47:10 -0800152 nextRoundNodes.push(newNode.release()); // Transfer reference.
halcanary2f7ebcb2015-03-25 12:45:28 -0700153 }
154
155 curNodes = nextRoundNodes;
156 nextRoundNodes.rewind();
157 treeCapacity *= kNodeSize;
158 } while (curNodes.count() > 1);
159
160 pageTree->push(curNodes[0]); // Transfer reference.
halcanary8103a342016-03-08 15:10:16 -0800161 return sk_sp<SkPDFDict>(SkRef(curNodes[0]));
halcanary2f7ebcb2015-03-25 12:45:28 -0700162}
163
halcanary8103a342016-03-08 15:10:16 -0800164static bool emit_pdf_document(const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices,
halcanary34422612015-10-12 10:11:18 -0700165 const SkPDFMetadata& metadata,
halcanarya43b4152015-03-25 12:15:04 -0700166 SkWStream* stream) {
halcanary8103a342016-03-08 15:10:16 -0800167 if (pageDevices.empty()) {
halcanarya43b4152015-03-25 12:15:04 -0700168 return false;
169 }
170
halcanary8103a342016-03-08 15:10:16 -0800171 SkTDArray<SkPDFDict*> pages; // TODO: SkTArray<sk_sp<SkPDFDict>>
halcanaryece83922016-03-08 08:32:12 -0800172 auto dests = sk_make_sp<SkPDFDict>();
halcanarya43b4152015-03-25 12:15:04 -0700173
halcanary8103a342016-03-08 15:10:16 -0800174 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) {
175 SkASSERT(pageDevice);
176 SkASSERT(pageDevices[0]->getCanon() == pageDevice->getCanon());
177 sk_sp<SkPDFDict> page(create_pdf_page(pageDevice.get()));
178 pageDevice->appendDestinations(dests.get(), page.get());
halcanaryfcad44b2016-03-06 14:47:10 -0800179 pages.push(page.release());
halcanarya43b4152015-03-25 12:15:04 -0700180 }
halcanarya43b4152015-03-25 12:15:04 -0700181
halcanaryece83922016-03-08 08:32:12 -0800182 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
halcanarya43b4152015-03-25 12:15:04 -0700183
halcanary8103a342016-03-08 15:10:16 -0800184 sk_sp<SkPDFObject> infoDict(metadata.createDocumentInformationDict());
halcanary34422612015-10-12 10:11:18 -0700185
halcanary48810a02016-03-07 14:57:50 -0800186 sk_sp<SkPDFObject> id, xmp;
halcanary34422612015-10-12 10:11:18 -0700187#ifdef SK_PDF_GENERATE_PDFA
188 SkPDFMetadata::UUID uuid = metadata.uuid();
189 // We use the same UUID for Document ID and Instance ID since this
190 // is the first revision of this document (and Skia does not
191 // support revising existing PDF documents).
192 // If we are not in PDF/A mode, don't use a UUID since testing
193 // works best with reproducible outputs.
194 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid));
195 xmp.reset(metadata.createXMPObject(uuid, uuid));
halcanary8103a342016-03-08 15:10:16 -0800196 docCatalog->insertObjRef("Metadata", std::move(xmp));
halcanary34422612015-10-12 10:11:18 -0700197
198 // sRGB is specified by HTML, CSS, and SVG.
halcanaryece83922016-03-08 08:32:12 -0800199 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
halcanary34422612015-10-12 10:11:18 -0700200 outputIntent->insertName("S", "GTS_PDFA1");
201 outputIntent->insertString("RegistryName", "http://www.color.org");
202 outputIntent->insertString("OutputConditionIdentifier",
203 "sRGB IEC61966-2.1");
halcanaryece83922016-03-08 08:32:12 -0800204 auto intentArray = sk_make_sp<SkPDFArray>();
halcanary8103a342016-03-08 15:10:16 -0800205 intentArray->appendObject(std::move(outputIntent));
halcanary34422612015-10-12 10:11:18 -0700206 // Don't specify OutputIntents if we are not in PDF/A mode since
207 // no one has ever asked for this feature.
halcanary8103a342016-03-08 15:10:16 -0800208 docCatalog->insertObject("OutputIntents", std::move(intentArray));
halcanary34422612015-10-12 10:11:18 -0700209#endif
210
211 SkTDArray<SkPDFDict*> pageTree;
halcanary8103a342016-03-08 15:10:16 -0800212 docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree));
halcanarya43b4152015-03-25 12:15:04 -0700213
halcanary72266fd2015-05-05 08:00:24 -0700214 if (dests->size() > 0) {
halcanary8103a342016-03-08 15:10:16 -0800215 docCatalog->insertObjRef("Dests", std::move(dests));
halcanary72266fd2015-05-05 08:00:24 -0700216 }
halcanarya43b4152015-03-25 12:15:04 -0700217
halcanarya43b4152015-03-25 12:15:04 -0700218 // Build font subsetting info before proceeding.
halcanary37c46ca2015-03-31 12:30:20 -0700219 SkPDFSubstituteMap substitutes;
220 perform_font_subsetting(pageDevices, &substitutes);
halcanarya43b4152015-03-25 12:15:04 -0700221
halcanary37c46ca2015-03-31 12:30:20 -0700222 SkPDFObjNumMap objNumMap;
halcanaryfcad44b2016-03-06 14:47:10 -0800223 objNumMap.addObjectRecursively(infoDict.get(), substitutes);
halcanary34422612015-10-12 10:11:18 -0700224 objNumMap.addObjectRecursively(docCatalog.get(), substitutes);
halcanary4f2b3312015-08-10 08:49:03 -0700225 size_t baseOffset = stream->bytesWritten();
halcanarya43b4152015-03-25 12:15:04 -0700226 emit_pdf_header(stream);
227 SkTDArray<int32_t> offsets;
halcanary37c46ca2015-03-31 12:30:20 -0700228 for (int i = 0; i < objNumMap.objects().count(); ++i) {
229 SkPDFObject* object = objNumMap.objects()[i];
halcanary4f2b3312015-08-10 08:49:03 -0700230 size_t offset = stream->bytesWritten();
231 // This assert checks that size(pdf_header) > 0 and that
232 // the output stream correctly reports bytesWritten().
233 SkASSERT(offset > baseOffset);
234 offsets.push(SkToS32(offset - baseOffset));
halcanary37c46ca2015-03-31 12:30:20 -0700235 SkASSERT(object == substitutes.getSubstitute(object));
236 SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
halcanarya43b4152015-03-25 12:15:04 -0700237 stream->writeDecAsText(i + 1);
238 stream->writeText(" 0 obj\n"); // Generation number is always 0.
halcanary37c46ca2015-03-31 12:30:20 -0700239 object->emitObject(stream, objNumMap, substitutes);
halcanarya43b4152015-03-25 12:15:04 -0700240 stream->writeText("\nendobj\n");
241 }
242 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);
243
halcanary41f88f02015-03-26 15:35:18 -0700244 // Include the zeroth object in the count.
halcanarya43b4152015-03-25 12:15:04 -0700245 int32_t objCount = SkToS32(offsets.count() + 1);
246
247 stream->writeText("xref\n0 ");
halcanary41f88f02015-03-26 15:35:18 -0700248 stream->writeDecAsText(objCount);
halcanarya43b4152015-03-25 12:15:04 -0700249 stream->writeText("\n0000000000 65535 f \n");
250 for (int i = 0; i < offsets.count(); i++) {
251 SkASSERT(offsets[i] > 0);
252 stream->writeBigDecAsText(offsets[i], 10);
253 stream->writeText(" 00000 n \n");
254 }
halcanary37c46ca2015-03-31 12:30:20 -0700255 emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount,
halcanary8103a342016-03-08 15:10:16 -0800256 xRefFileOffset, std::move(infoDict), std::move(id));
halcanarya43b4152015-03-25 12:15:04 -0700257
258 // The page tree has both child and parent pointers, so it creates a
259 // reference cycle. We must clear that cycle to properly reclaim memory.
260 for (int i = 0; i < pageTree.count(); i++) {
261 pageTree[i]->clear();
262 }
263 pageTree.safeUnrefAll();
264 pages.unrefAll();
265 return true;
266}
267
268#if 0
269// TODO(halcanary): expose notEmbeddableCount in SkDocument
270void GetCountOfFontTypes(
271 const SkTDArray<SkPDFDevice*>& pageDevices,
272 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
273 int* notSubsettableCount,
274 int* notEmbeddableCount) {
275 sk_bzero(counts, sizeof(int) *
276 (SkAdvancedTypefaceMetrics::kOther_Font + 1));
277 SkTDArray<SkFontID> seenFonts;
278 int notSubsettable = 0;
279 int notEmbeddable = 0;
280
281 for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) {
282 const SkTDArray<SkPDFFont*>& fontResources =
283 pageDevices[pageNumber]->getFontResources();
284 for (int font = 0; font < fontResources.count(); font++) {
285 SkFontID fontID = fontResources[font]->typeface()->uniqueID();
286 if (seenFonts.find(fontID) == -1) {
287 counts[fontResources[font]->getType()]++;
288 seenFonts.push(fontID);
289 if (!fontResources[font]->canSubset()) {
290 notSubsettable++;
291 }
292 if (!fontResources[font]->canEmbed()) {
293 notEmbeddable++;
294 }
295 }
296 }
297 }
298 if (notSubsettableCount) {
299 *notSubsettableCount = notSubsettable;
300
301 }
302 if (notEmbeddableCount) {
303 *notEmbeddableCount = notEmbeddable;
304 }
305}
306#endif
halcanaryf12a1672015-09-23 12:45:49 -0700307
308template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullptr; }
halcanarya43b4152015-03-25 12:15:04 -0700309////////////////////////////////////////////////////////////////////////////////
reed@google.com99ac02b2013-06-07 20:30:16 +0000310
halcanary7a011842015-03-25 07:52:56 -0700311namespace {
reed@google.com99ac02b2013-06-07 20:30:16 +0000312class SkDocument_PDF : public SkDocument {
313public:
halcanary8c92dc12015-02-19 18:50:05 -0800314 SkDocument_PDF(SkWStream* stream,
halcanary792c80f2015-02-20 07:21:05 -0800315 void (*doneProc)(SkWStream*, bool),
halcanary712fdf72015-12-10 08:59:43 -0800316 SkScalar rasterDpi,
317 SkPixelSerializer* jpegEncoder)
halcanary8c92dc12015-02-19 18:50:05 -0800318 : SkDocument(stream, doneProc)
halcanary712fdf72015-12-10 08:59:43 -0800319 , fRasterDpi(rasterDpi) {
halcanaryfcad44b2016-03-06 14:47:10 -0800320 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder));
halcanary712fdf72015-12-10 08:59:43 -0800321 }
skia.committer@gmail.com63193672013-06-08 07:01:13 +0000322
reed@google.com99ac02b2013-06-07 20:30:16 +0000323 virtual ~SkDocument_PDF() {
324 // subclasses must call close() in their destructors
325 this->close();
326 }
327
328protected:
tfarinaf4219dd2015-04-27 17:18:28 -0700329 SkCanvas* onBeginPage(SkScalar width, SkScalar height,
330 const SkRect& trimBox) override {
halcanarya1f1ee92015-02-20 06:17:26 -0800331 SkASSERT(!fCanvas.get());
reed@google.com99ac02b2013-06-07 20:30:16 +0000332
halcanarya1f1ee92015-02-20 06:17:26 -0800333 SkISize pageSize = SkISize::Make(
334 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
halcanary48810a02016-03-07 14:57:50 -0800335 sk_sp<SkPDFDevice> device(
halcanary7a011842015-03-25 07:52:56 -0700336 SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon));
halcanary385fe4d2015-08-26 13:07:48 -0700337 fCanvas.reset(new SkCanvas(device.get()));
halcanary8103a342016-03-08 15:10:16 -0800338 fPageDevices.push_back(std::move(device));
halcanarybe519ad2014-11-10 14:22:14 -0800339 fCanvas->clipRect(trimBox);
halcanary93f81612014-11-10 14:01:57 -0800340 fCanvas->translate(trimBox.x(), trimBox.y());
halcanarya1f1ee92015-02-20 06:17:26 -0800341 return fCanvas.get();
reed@google.com99ac02b2013-06-07 20:30:16 +0000342 }
343
mtklein36352bf2015-03-25 18:17:31 -0700344 void onEndPage() override {
halcanarya1f1ee92015-02-20 06:17:26 -0800345 SkASSERT(fCanvas.get());
reed@google.com99ac02b2013-06-07 20:30:16 +0000346 fCanvas->flush();
halcanary96fcdcc2015-08-27 07:41:13 -0700347 fCanvas.reset(nullptr);
reed@google.com99ac02b2013-06-07 20:30:16 +0000348 }
349
mtklein36352bf2015-03-25 18:17:31 -0700350 bool onClose(SkWStream* stream) override {
halcanarya1f1ee92015-02-20 06:17:26 -0800351 SkASSERT(!fCanvas.get());
reed@google.com99ac02b2013-06-07 20:30:16 +0000352
halcanaryf12a1672015-09-23 12:45:49 -0700353 bool success = emit_pdf_document(fPageDevices, fMetadata, stream);
halcanary8103a342016-03-08 15:10:16 -0800354 fPageDevices.reset();
halcanary2e3f9d82015-02-27 12:41:03 -0800355 fCanon.reset();
commit-bot@chromium.orgb5a66512013-10-09 21:09:00 +0000356 return success;
357 }
358
mtklein36352bf2015-03-25 18:17:31 -0700359 void onAbort() override {
halcanary8103a342016-03-08 15:10:16 -0800360 fPageDevices.reset();
halcanary2e3f9d82015-02-27 12:41:03 -0800361 fCanon.reset();
reed@google.com99ac02b2013-06-07 20:30:16 +0000362 }
363
halcanary70015762016-02-11 07:59:59 -0800364 void setMetadata(const SkDocument::Attribute info[],
365 int infoCount,
halcanaryf12a1672015-09-23 12:45:49 -0700366 const SkTime::DateTime* creationDate,
367 const SkTime::DateTime* modifiedDate) override {
halcanary70015762016-02-11 07:59:59 -0800368 fMetadata.fInfo.reset(info, infoCount);
halcanaryf12a1672015-09-23 12:45:49 -0700369 fMetadata.fCreation.reset(clone(creationDate));
370 fMetadata.fModified.reset(clone(modifiedDate));
371 }
372
reed@google.com99ac02b2013-06-07 20:30:16 +0000373private:
halcanarya1f1ee92015-02-20 06:17:26 -0800374 SkPDFCanon fCanon;
halcanary8103a342016-03-08 15:10:16 -0800375 SkTArray<sk_sp<const SkPDFDevice>> fPageDevices;
halcanary48810a02016-03-07 14:57:50 -0800376 sk_sp<SkCanvas> fCanvas;
halcanarya1f1ee92015-02-20 06:17:26 -0800377 SkScalar fRasterDpi;
halcanary34422612015-10-12 10:11:18 -0700378 SkPDFMetadata fMetadata;
reed@google.com99ac02b2013-06-07 20:30:16 +0000379};
halcanary7a011842015-03-25 07:52:56 -0700380} // namespace
reed@google.com99ac02b2013-06-07 20:30:16 +0000381///////////////////////////////////////////////////////////////////////////////
382
halcanary8c92dc12015-02-19 18:50:05 -0800383SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
halcanary712fdf72015-12-10 08:59:43 -0800384 return stream ? new SkDocument_PDF(stream, nullptr, dpi, nullptr) : nullptr;
385}
386
387SkDocument* SkDocument::CreatePDF(SkWStream* stream,
388 SkScalar dpi,
389 SkPixelSerializer* jpegEncoder) {
390 return stream
391 ? new SkDocument_PDF(stream, nullptr, dpi, jpegEncoder)
392 : nullptr;
reed@google.com99ac02b2013-06-07 20:30:16 +0000393}
394
halcanary8c92dc12015-02-19 18:50:05 -0800395SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
halcanary385fe4d2015-08-26 13:07:48 -0700396 SkFILEWStream* stream = new SkFILEWStream(path);
reed@google.com99ac02b2013-06-07 20:30:16 +0000397 if (!stream->isValid()) {
halcanary385fe4d2015-08-26 13:07:48 -0700398 delete stream;
halcanary96fcdcc2015-08-27 07:41:13 -0700399 return nullptr;
reed@google.com99ac02b2013-06-07 20:30:16 +0000400 }
halcanary385fe4d2015-08-26 13:07:48 -0700401 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
halcanary712fdf72015-12-10 08:59:43 -0800402 return new SkDocument_PDF(stream, delete_wstream, dpi, nullptr);
reed@google.com99ac02b2013-06-07 20:30:16 +0000403}