blob: 2a01b807fe479bcdac3a1d47112423f72aa99762 [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"
halcanary23f4d4d2016-03-12 05:59:39 -080013#include "SkPDFMetadata.h"
halcanary2f7ebcb2015-03-25 12:45:28 -070014#include "SkPDFStream.h"
halcanarya43b4152015-03-25 12:15:04 -070015#include "SkPDFTypes.h"
halcanaryf12a1672015-09-23 12:45:49 -070016#include "SkPDFUtils.h"
halcanarya43b4152015-03-25 12:15:04 -070017#include "SkStream.h"
halcanaryf12a1672015-09-23 12:45:49 -070018
halcanarya43b4152015-03-25 12:15:04 -070019static void emit_pdf_header(SkWStream* stream) {
20 stream->writeText("%PDF-1.4\n%");
21 // The PDF spec recommends including a comment with four bytes, all
22 // with their high bits set. This is "Skia" with the high bits set.
23 stream->write32(0xD3EBE9E1);
24 stream->writeText("\n");
25}
26
27static void emit_pdf_footer(SkWStream* stream,
halcanary37c46ca2015-03-31 12:30:20 -070028 const SkPDFObjNumMap& objNumMap,
29 const SkPDFSubstituteMap& substitutes,
halcanarya43b4152015-03-25 12:15:04 -070030 SkPDFObject* docCatalog,
31 int64_t objCount,
halcanaryf12a1672015-09-23 12:45:49 -070032 int32_t xRefFileOffset,
halcanary8103a342016-03-08 15:10:16 -080033 sk_sp<SkPDFObject> info,
34 sk_sp<SkPDFObject> id) {
halcanarya43b4152015-03-25 12:15:04 -070035 SkPDFDict trailerDict;
halcanary34422612015-10-12 10:11:18 -070036 // TODO(http://crbug.com/80908): Linearized format will take a
37 // Prev entry too.
halcanarya43b4152015-03-25 12:15:04 -070038 trailerDict.insertInt("Size", int(objCount));
halcanarye94ea622016-03-09 07:52:09 -080039 trailerDict.insertObjRef("Root", sk_ref_sp(docCatalog));
halcanaryf12a1672015-09-23 12:45:49 -070040 SkASSERT(info);
halcanary8103a342016-03-08 15:10:16 -080041 trailerDict.insertObjRef("Info", std::move(info));
halcanary34422612015-10-12 10:11:18 -070042 if (id) {
halcanary8103a342016-03-08 15:10:16 -080043 trailerDict.insertObject("ID", std::move(id));
halcanary34422612015-10-12 10:11:18 -070044 }
halcanarya43b4152015-03-25 12:15:04 -070045 stream->writeText("trailer\n");
halcanary37c46ca2015-03-31 12:30:20 -070046 trailerDict.emitObject(stream, objNumMap, substitutes);
halcanarya43b4152015-03-25 12:15:04 -070047 stream->writeText("\nstartxref\n");
48 stream->writeBigDecAsText(xRefFileOffset);
49 stream->writeText("\n%%EOF");
50}
51
halcanary2f7ebcb2015-03-25 12:45:28 -070052static void perform_font_subsetting(
halcanary8103a342016-03-08 15:10:16 -080053 const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices,
halcanary37c46ca2015-03-31 12:30:20 -070054 SkPDFSubstituteMap* substituteMap) {
55 SkASSERT(substituteMap);
halcanarya43b4152015-03-25 12:15:04 -070056
57 SkPDFGlyphSetMap usage;
halcanary8103a342016-03-08 15:10:16 -080058 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) {
59 usage.merge(pageDevice->getFontGlyphUsage());
halcanarya43b4152015-03-25 12:15:04 -070060 }
61 SkPDFGlyphSetMap::F2BIter iterator(usage);
62 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
63 while (entry) {
halcanary48810a02016-03-07 14:57:50 -080064 sk_sp<SkPDFFont> subsetFont(
halcanarya43b4152015-03-25 12:15:04 -070065 entry->fFont->getFontSubset(entry->fGlyphSet));
66 if (subsetFont) {
halcanary37c46ca2015-03-31 12:30:20 -070067 substituteMap->setSubstitute(entry->fFont, subsetFont.get());
halcanarya43b4152015-03-25 12:15:04 -070068 }
69 entry = iterator.next();
70 }
71}
72
halcanary8103a342016-03-08 15:10:16 -080073static sk_sp<SkPDFDict> create_pdf_page(const SkPDFDevice* pageDevice) {
halcanaryece83922016-03-08 08:32:12 -080074 auto page = sk_make_sp<SkPDFDict>("Page");
halcanary8103a342016-03-08 15:10:16 -080075 page->insertObject("Resources", pageDevice->makeResourceDict());
halcanary72266fd2015-05-05 08:00:24 -070076 page->insertObject("MediaBox", pageDevice->copyMediaBox());
halcanaryece83922016-03-08 08:32:12 -080077 auto annotations = sk_make_sp<SkPDFArray>();
halcanaryfcad44b2016-03-06 14:47:10 -080078 pageDevice->appendAnnotations(annotations.get());
wangxianzhuef6c50a2015-09-17 20:38:02 -070079 if (annotations->size() > 0) {
halcanary8103a342016-03-08 15:10:16 -080080 page->insertObject("Annots", std::move(annotations));
halcanary2f7ebcb2015-03-25 12:45:28 -070081 }
halcanary8103a342016-03-08 15:10:16 -080082 auto content = pageDevice->content();
83 page->insertObjRef("Contents", sk_make_sp<SkPDFStream>(content.get()));
84 return page;
halcanary2f7ebcb2015-03-25 12:45:28 -070085}
86
halcanary8103a342016-03-08 15:10:16 -080087// return root node.
88static sk_sp<SkPDFDict> generate_page_tree(
89 const SkTDArray<SkPDFDict*>& pages,
90 SkTDArray<SkPDFDict*>* pageTree) {
halcanary2f7ebcb2015-03-25 12:45:28 -070091 // PDF wants a tree describing all the pages in the document. We arbitrary
92 // choose 8 (kNodeSize) as the number of allowed children. The internal
93 // nodes have type "Pages" with an array of children, a parent pointer, and
94 // the number of leaves below the node as "Count." The leaves are passed
95 // into the method, have type "Page" and need a parent pointer. This method
96 // builds the tree bottom up, skipping internal nodes that would have only
97 // one child.
98 static const int kNodeSize = 8;
99
halcanary2f7ebcb2015-03-25 12:45:28 -0700100 // curNodes takes a reference to its items, which it passes to pageTree.
101 SkTDArray<SkPDFDict*> curNodes;
102 curNodes.setReserve(pages.count());
103 for (int i = 0; i < pages.count(); i++) {
104 SkSafeRef(pages[i]);
105 curNodes.push(pages[i]);
106 }
107
108 // nextRoundNodes passes its references to nodes on to curNodes.
109 SkTDArray<SkPDFDict*> nextRoundNodes;
110 nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
111
112 int treeCapacity = kNodeSize;
113 do {
114 for (int i = 0; i < curNodes.count(); ) {
115 if (i > 0 && i + 1 == curNodes.count()) {
116 nextRoundNodes.push(curNodes[i]);
117 break;
118 }
119
halcanaryece83922016-03-08 08:32:12 -0800120 auto newNode = sk_make_sp<SkPDFDict>("Pages");
121 auto kids = sk_make_sp<SkPDFArray>();
halcanary2f7ebcb2015-03-25 12:45:28 -0700122 kids->reserve(kNodeSize);
123
124 int count = 0;
125 for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
halcanary8103a342016-03-08 15:10:16 -0800126 curNodes[i]->insertObjRef("Parent", newNode);
halcanarye94ea622016-03-09 07:52:09 -0800127 kids->appendObjRef(sk_ref_sp(curNodes[i]));
halcanary2f7ebcb2015-03-25 12:45:28 -0700128
129 // TODO(vandebo): put the objects in strict access order.
130 // Probably doesn't matter because they are so small.
131 if (curNodes[i] != pages[0]) {
132 pageTree->push(curNodes[i]); // Transfer reference.
133 } else {
134 SkSafeUnref(curNodes[i]);
135 }
136 }
137
138 // treeCapacity is the number of leaf nodes possible for the
139 // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
140 // It is hard to count the number of leaf nodes in the current
141 // subtree. However, by construction, we know that unless it's the
142 // last subtree for the current depth, the leaf count will be
143 // treeCapacity, otherwise it's what ever is left over after
144 // consuming treeCapacity chunks.
145 int pageCount = treeCapacity;
146 if (i == curNodes.count()) {
147 pageCount = ((pages.count() - 1) % treeCapacity) + 1;
148 }
halcanary72266fd2015-05-05 08:00:24 -0700149 newNode->insertInt("Count", pageCount);
halcanary8103a342016-03-08 15:10:16 -0800150 newNode->insertObject("Kids", std::move(kids));
halcanaryfcad44b2016-03-06 14:47:10 -0800151 nextRoundNodes.push(newNode.release()); // Transfer reference.
halcanary2f7ebcb2015-03-25 12:45:28 -0700152 }
153
154 curNodes = nextRoundNodes;
155 nextRoundNodes.rewind();
156 treeCapacity *= kNodeSize;
157 } while (curNodes.count() > 1);
158
159 pageTree->push(curNodes[0]); // Transfer reference.
halcanarye94ea622016-03-09 07:52:09 -0800160 return sk_ref_sp(curNodes[0]);
halcanary2f7ebcb2015-03-25 12:45:28 -0700161}
162
halcanary8103a342016-03-08 15:10:16 -0800163static bool emit_pdf_document(const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices,
halcanary34422612015-10-12 10:11:18 -0700164 const SkPDFMetadata& metadata,
halcanarya43b4152015-03-25 12:15:04 -0700165 SkWStream* stream) {
halcanary8103a342016-03-08 15:10:16 -0800166 if (pageDevices.empty()) {
halcanarya43b4152015-03-25 12:15:04 -0700167 return false;
168 }
169
halcanary8103a342016-03-08 15:10:16 -0800170 SkTDArray<SkPDFDict*> pages; // TODO: SkTArray<sk_sp<SkPDFDict>>
halcanaryece83922016-03-08 08:32:12 -0800171 auto dests = sk_make_sp<SkPDFDict>();
halcanarya43b4152015-03-25 12:15:04 -0700172
halcanary8103a342016-03-08 15:10:16 -0800173 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) {
174 SkASSERT(pageDevice);
175 SkASSERT(pageDevices[0]->getCanon() == pageDevice->getCanon());
176 sk_sp<SkPDFDict> page(create_pdf_page(pageDevice.get()));
177 pageDevice->appendDestinations(dests.get(), page.get());
halcanaryfcad44b2016-03-06 14:47:10 -0800178 pages.push(page.release());
halcanarya43b4152015-03-25 12:15:04 -0700179 }
halcanarya43b4152015-03-25 12:15:04 -0700180
halcanaryece83922016-03-08 08:32:12 -0800181 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
halcanarya43b4152015-03-25 12:15:04 -0700182
halcanary8103a342016-03-08 15:10:16 -0800183 sk_sp<SkPDFObject> infoDict(metadata.createDocumentInformationDict());
halcanary34422612015-10-12 10:11:18 -0700184
halcanary48810a02016-03-07 14:57:50 -0800185 sk_sp<SkPDFObject> id, xmp;
halcanary34422612015-10-12 10:11:18 -0700186#ifdef SK_PDF_GENERATE_PDFA
187 SkPDFMetadata::UUID uuid = metadata.uuid();
188 // We use the same UUID for Document ID and Instance ID since this
189 // is the first revision of this document (and Skia does not
190 // support revising existing PDF documents).
191 // If we are not in PDF/A mode, don't use a UUID since testing
192 // works best with reproducible outputs.
193 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid));
194 xmp.reset(metadata.createXMPObject(uuid, uuid));
halcanary8103a342016-03-08 15:10:16 -0800195 docCatalog->insertObjRef("Metadata", std::move(xmp));
halcanary34422612015-10-12 10:11:18 -0700196
197 // sRGB is specified by HTML, CSS, and SVG.
halcanaryece83922016-03-08 08:32:12 -0800198 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
halcanary34422612015-10-12 10:11:18 -0700199 outputIntent->insertName("S", "GTS_PDFA1");
200 outputIntent->insertString("RegistryName", "http://www.color.org");
201 outputIntent->insertString("OutputConditionIdentifier",
202 "sRGB IEC61966-2.1");
halcanaryece83922016-03-08 08:32:12 -0800203 auto intentArray = sk_make_sp<SkPDFArray>();
halcanary8103a342016-03-08 15:10:16 -0800204 intentArray->appendObject(std::move(outputIntent));
halcanary34422612015-10-12 10:11:18 -0700205 // Don't specify OutputIntents if we are not in PDF/A mode since
206 // no one has ever asked for this feature.
halcanary8103a342016-03-08 15:10:16 -0800207 docCatalog->insertObject("OutputIntents", std::move(intentArray));
halcanary34422612015-10-12 10:11:18 -0700208#endif
209
210 SkTDArray<SkPDFDict*> pageTree;
halcanary8103a342016-03-08 15:10:16 -0800211 docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree));
halcanarya43b4152015-03-25 12:15:04 -0700212
halcanary72266fd2015-05-05 08:00:24 -0700213 if (dests->size() > 0) {
halcanary8103a342016-03-08 15:10:16 -0800214 docCatalog->insertObjRef("Dests", std::move(dests));
halcanary72266fd2015-05-05 08:00:24 -0700215 }
halcanarya43b4152015-03-25 12:15:04 -0700216
halcanarya43b4152015-03-25 12:15:04 -0700217 // Build font subsetting info before proceeding.
halcanary37c46ca2015-03-31 12:30:20 -0700218 SkPDFSubstituteMap substitutes;
219 perform_font_subsetting(pageDevices, &substitutes);
halcanarya43b4152015-03-25 12:15:04 -0700220
halcanary37c46ca2015-03-31 12:30:20 -0700221 SkPDFObjNumMap objNumMap;
halcanaryfcad44b2016-03-06 14:47:10 -0800222 objNumMap.addObjectRecursively(infoDict.get(), substitutes);
halcanary34422612015-10-12 10:11:18 -0700223 objNumMap.addObjectRecursively(docCatalog.get(), substitutes);
halcanary4f2b3312015-08-10 08:49:03 -0700224 size_t baseOffset = stream->bytesWritten();
halcanarya43b4152015-03-25 12:15:04 -0700225 emit_pdf_header(stream);
226 SkTDArray<int32_t> offsets;
halcanary37c46ca2015-03-31 12:30:20 -0700227 for (int i = 0; i < objNumMap.objects().count(); ++i) {
halcanarybae235e2016-03-21 10:05:23 -0700228 SkPDFObject* object = objNumMap.objects()[i].get();
halcanary4f2b3312015-08-10 08:49:03 -0700229 size_t offset = stream->bytesWritten();
230 // This assert checks that size(pdf_header) > 0 and that
231 // the output stream correctly reports bytesWritten().
232 SkASSERT(offset > baseOffset);
233 offsets.push(SkToS32(offset - baseOffset));
halcanary37c46ca2015-03-31 12:30:20 -0700234 SkASSERT(object == substitutes.getSubstitute(object));
235 SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
halcanarya43b4152015-03-25 12:15:04 -0700236 stream->writeDecAsText(i + 1);
237 stream->writeText(" 0 obj\n"); // Generation number is always 0.
halcanary37c46ca2015-03-31 12:30:20 -0700238 object->emitObject(stream, objNumMap, substitutes);
halcanarya43b4152015-03-25 12:15:04 -0700239 stream->writeText("\nendobj\n");
halcanarybae235e2016-03-21 10:05:23 -0700240 object->drop();
halcanarya43b4152015-03-25 12:15:04 -0700241 }
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++) {
halcanarybae235e2016-03-21 10:05:23 -0700261 pageTree[i]->drop();
halcanarya43b4152015-03-25 12:15:04 -0700262 }
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 {
halcanary23f4d4d2016-03-12 05:59:39 -0800312class SkPDFDocument : public SkDocument {
reed@google.com99ac02b2013-06-07 20:30:16 +0000313public:
halcanary23f4d4d2016-03-12 05:59:39 -0800314 SkPDFDocument(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
halcanary23f4d4d2016-03-12 05:59:39 -0800323 virtual ~SkPDFDocument() {
reed@google.com99ac02b2013-06-07 20:30:16 +0000324 // 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));
halcanary66be6262016-03-21 13:01:34 -0700337 fCanvas = sk_make_sp<SkPDFCanvas>(device);
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
halcanary23f4d4d2016-03-12 05:59:39 -0800383sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream,
384 void (*proc)(SkWStream*, bool),
385 SkScalar dpi,
386 SkPixelSerializer* jpeg) {
387 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, jpeg) : nullptr;
388}
389
halcanary8c92dc12015-02-19 18:50:05 -0800390SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
halcanary23f4d4d2016-03-12 05:59:39 -0800391 return SkPDFMakeDocument(stream, nullptr, dpi, nullptr).release();
halcanary712fdf72015-12-10 08:59:43 -0800392}
393
394SkDocument* SkDocument::CreatePDF(SkWStream* stream,
395 SkScalar dpi,
396 SkPixelSerializer* jpegEncoder) {
halcanary23f4d4d2016-03-12 05:59:39 -0800397 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release();
reed@google.com99ac02b2013-06-07 20:30:16 +0000398}
399
halcanary8c92dc12015-02-19 18:50:05 -0800400SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
halcanary385fe4d2015-08-26 13:07:48 -0700401 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
halcanary23f4d4d2016-03-12 05:59:39 -0800402 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path));
403 return stream->isValid()
mtklein18300a32016-03-16 13:53:35 -0700404 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).release()
halcanary23f4d4d2016-03-12 05:59:39 -0800405 : nullptr;
reed@google.com99ac02b2013-06-07 20:30:16 +0000406}