Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(564)

Side by Side Diff: src/pdf/SkPDFDocument.cpp

Issue 1802033003: SkPDF: SkPDFDocument reorganized so that some objects can be serialized early. (Closed) Base URL: https://skia.googlesource.com/skia.git@SkPdfDumpMethod
Patch Set: 2016-03-21 (Monday) 16:31:34 EDT Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pdf/SkPDFDocument.h ('k') | tests/PDFDocumentTest.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2011 Google Inc. 2 * Copyright 2011 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkPDFCanon.h" 8 #include "SkPDFCanon.h"
9 #include "SkPDFCanvas.h" 9 #include "SkPDFCanvas.h"
10 #include "SkPDFDevice.h" 10 #include "SkPDFDevice.h"
11 #include "SkPDFDocument.h" 11 #include "SkPDFDocument.h"
12 #include "SkPDFFont.h" 12 #include "SkPDFFont.h"
13 #include "SkPDFMetadata.h"
14 #include "SkPDFStream.h" 13 #include "SkPDFStream.h"
15 #include "SkPDFTypes.h"
16 #include "SkPDFUtils.h" 14 #include "SkPDFUtils.h"
17 #include "SkStream.h" 15 #include "SkStream.h"
18 16
19 static void emit_pdf_header(SkWStream* stream) { 17
20 stream->writeText("%PDF-1.4\n%"); 18 SkPDFObjectSerializer::SkPDFObjectSerializer() : fBaseOffset(0), fNextToBeSerial ized(0) {}
21 // The PDF spec recommends including a comment with four bytes, all 19
22 // with their high bits set. This is "Skia" with the high bits set. 20 template <class T> static void renew(T* t) { t->~T(); new (t) T; }
23 stream->write32(0xD3EBE9E1); 21
24 stream->writeText("\n"); 22 void SkPDFObjectSerializer::addObjectRecursively(const sk_sp<SkPDFObject>& objec t) {
23 fObjNumMap.addObjectRecursively(object.get(), fSubstituteMap);
25 } 24 }
26 25
27 static void emit_pdf_footer(SkWStream* stream, 26 void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream, const SkPDFMetad ata& md) {
28 const SkPDFObjNumMap& objNumMap, 27 fBaseOffset = wStream->bytesWritten();
29 const SkPDFSubstituteMap& substitutes, 28 static const char kHeader[] = "%PDF-1.4\n%\xE1\xE9\xEB\xD3\n";
30 SkPDFObject* docCatalog, 29 wStream->write(kHeader, strlen(kHeader));
31 int64_t objCount, 30 // The PDF spec recommends including a comment with four
32 int32_t xRefFileOffset, 31 // bytes, all with their high bits set. "\xD3\xEB\xE9\xE1" is
33 sk_sp<SkPDFObject> info, 32 // "Skia" with the high bits set.
34 sk_sp<SkPDFObject> id) { 33 fInfoDict.reset(md.createDocumentInformationDict());
34 this->addObjectRecursively(fInfoDict);
35 this->serializeObjects(wStream);
36 }
37
38 // Serialize all objects in the fObjNumMap that have not yet been serialized;
39 void SkPDFObjectSerializer::serializeObjects(SkWStream* wStream) {
40 const SkTArray<sk_sp<SkPDFObject>>& objects = fObjNumMap.objects();
41 while (fNextToBeSerialized < objects.count()) {
42 SkPDFObject* object = objects[fNextToBeSerialized].get();
43 int32_t index = fNextToBeSerialized + 1; // Skip object 0.
44 // "The first entry in the [XREF] table (object number 0) is
45 // always free and has a generation number of 65,535; it is
46 // the head of the linked list of free objects."
47 SkASSERT(fOffsets.count() == fNextToBeSerialized);
48 fOffsets.push(this->offset(wStream));
49 SkASSERT(object == fSubstituteMap.getSubstitute(object));
50 wStream->writeDecAsText(index);
51 wStream->writeText(" 0 obj\n"); // Generation number is always 0.
52 object->emitObject(wStream, fObjNumMap, fSubstituteMap);
53 wStream->writeText("\nendobj\n");
54 object->drop();
55 ++fNextToBeSerialized;
56 }
57 }
58
59 // Xref table and footer
60 void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
61 const sk_sp<SkPDFObject> docCatalog,
62 sk_sp<SkPDFObject> id) {
63 this->serializeObjects(wStream);
64 int32_t xRefFileOffset = this->offset(wStream);
65 // Include the special zeroth object in the count.
66 int32_t objCount = SkToS32(fOffsets.count() + 1);
67 wStream->writeText("xref\n0 ");
68 wStream->writeDecAsText(objCount);
69 wStream->writeText("\n0000000000 65535 f \n");
70 for (int i = 0; i < fOffsets.count(); i++) {
71 wStream->writeBigDecAsText(fOffsets[i], 10);
72 wStream->writeText(" 00000 n \n");
73 }
35 SkPDFDict trailerDict; 74 SkPDFDict trailerDict;
36 // TODO(http://crbug.com/80908): Linearized format will take a 75 trailerDict.insertInt("Size", objCount);
37 // Prev entry too. 76 SkASSERT(docCatalog);
38 trailerDict.insertInt("Size", int(objCount)); 77 trailerDict.insertObjRef("Root", docCatalog);
39 trailerDict.insertObjRef("Root", sk_ref_sp(docCatalog)); 78 SkASSERT(fInfoDict);
40 SkASSERT(info); 79 trailerDict.insertObjRef("Info", std::move(fInfoDict));
41 trailerDict.insertObjRef("Info", std::move(info));
42 if (id) { 80 if (id) {
43 trailerDict.insertObject("ID", std::move(id)); 81 trailerDict.insertObject("ID", std::move(id));
44 } 82 }
45 stream->writeText("trailer\n"); 83 wStream->writeText("trailer\n");
46 trailerDict.emitObject(stream, objNumMap, substitutes); 84 trailerDict.emitObject(wStream, fObjNumMap, fSubstituteMap);
47 stream->writeText("\nstartxref\n"); 85 wStream->writeText("\nstartxref\n");
48 stream->writeBigDecAsText(xRefFileOffset); 86 wStream->writeBigDecAsText(xRefFileOffset);
49 stream->writeText("\n%%EOF"); 87 wStream->writeText("\n%%EOF");
88 }
89
90 int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) {
91 size_t offset = wStream->bytesWritten();
92 SkASSERT(offset > fBaseOffset);
93 return SkToS32(offset - fBaseOffset);
50 } 94 }
51 95
52 static void perform_font_subsetting( 96 static void perform_font_subsetting(
53 const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices, 97 const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices,
54 SkPDFSubstituteMap* substituteMap) { 98 SkPDFSubstituteMap* substituteMap) {
55 SkASSERT(substituteMap); 99 SkASSERT(substituteMap);
56 100
57 SkPDFGlyphSetMap usage; 101 SkPDFGlyphSetMap usage;
58 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) { 102 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) {
59 usage.merge(pageDevice->getFontGlyphUsage()); 103 usage.merge(pageDevice->getFontGlyphUsage());
(...skipping 19 matching lines...) Expand all
79 if (annotations->size() > 0) { 123 if (annotations->size() > 0) {
80 page->insertObject("Annots", std::move(annotations)); 124 page->insertObject("Annots", std::move(annotations));
81 } 125 }
82 auto content = pageDevice->content(); 126 auto content = pageDevice->content();
83 page->insertObjRef("Contents", sk_make_sp<SkPDFStream>(content.get())); 127 page->insertObjRef("Contents", sk_make_sp<SkPDFStream>(content.get()));
84 return page; 128 return page;
85 } 129 }
86 130
87 // return root node. 131 // return root node.
88 static sk_sp<SkPDFDict> generate_page_tree( 132 static sk_sp<SkPDFDict> generate_page_tree(
89 const SkTDArray<SkPDFDict*>& pages, 133 SkTDArray<SkPDFDict*>& pages,
90 SkTDArray<SkPDFDict*>* pageTree) { 134 SkTDArray<SkPDFDict*>* pageTree) {
91 // PDF wants a tree describing all the pages in the document. We arbitrary 135 // 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 136 // 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 137 // 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 138 // 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 139 // 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 140 // builds the tree bottom up, skipping internal nodes that would have only
97 // one child. 141 // one child.
98 static const int kNodeSize = 8; 142 static const int kNodeSize = 8;
99 143
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
153 197
154 curNodes = nextRoundNodes; 198 curNodes = nextRoundNodes;
155 nextRoundNodes.rewind(); 199 nextRoundNodes.rewind();
156 treeCapacity *= kNodeSize; 200 treeCapacity *= kNodeSize;
157 } while (curNodes.count() > 1); 201 } while (curNodes.count() > 1);
158 202
159 pageTree->push(curNodes[0]); // Transfer reference. 203 pageTree->push(curNodes[0]); // Transfer reference.
160 return sk_ref_sp(curNodes[0]); 204 return sk_ref_sp(curNodes[0]);
161 } 205 }
162 206
163 static bool emit_pdf_document(const SkTArray<sk_sp<const SkPDFDevice>>& pageDevi ces,
164 const SkPDFMetadata& metadata,
165 SkWStream* stream) {
166 if (pageDevices.empty()) {
167 return false;
168 }
169
170 SkTDArray<SkPDFDict*> pages; // TODO: SkTArray<sk_sp<SkPDFDict>>
171 auto dests = sk_make_sp<SkPDFDict>();
172
173 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());
178 pages.push(page.release());
179 }
180
181 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
182
183 sk_sp<SkPDFObject> infoDict(metadata.createDocumentInformationDict());
184
185 sk_sp<SkPDFObject> id, xmp;
186 #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));
195 docCatalog->insertObjRef("Metadata", std::move(xmp));
196
197 // sRGB is specified by HTML, CSS, and SVG.
198 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
199 outputIntent->insertName("S", "GTS_PDFA1");
200 outputIntent->insertString("RegistryName", "http://www.color.org");
201 outputIntent->insertString("OutputConditionIdentifier",
202 "sRGB IEC61966-2.1");
203 auto intentArray = sk_make_sp<SkPDFArray>();
204 intentArray->appendObject(std::move(outputIntent));
205 // Don't specify OutputIntents if we are not in PDF/A mode since
206 // no one has ever asked for this feature.
207 docCatalog->insertObject("OutputIntents", std::move(intentArray));
208 #endif
209
210 SkTDArray<SkPDFDict*> pageTree;
211 docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree));
212
213 if (dests->size() > 0) {
214 docCatalog->insertObjRef("Dests", std::move(dests));
215 }
216
217 // Build font subsetting info before proceeding.
218 SkPDFSubstituteMap substitutes;
219 perform_font_subsetting(pageDevices, &substitutes);
220
221 SkPDFObjNumMap objNumMap;
222 objNumMap.addObjectRecursively(infoDict.get(), substitutes);
223 objNumMap.addObjectRecursively(docCatalog.get(), substitutes);
224 size_t baseOffset = stream->bytesWritten();
225 emit_pdf_header(stream);
226 SkTDArray<int32_t> offsets;
227 for (int i = 0; i < objNumMap.objects().count(); ++i) {
228 SkPDFObject* object = objNumMap.objects()[i].get();
229 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));
234 SkASSERT(object == substitutes.getSubstitute(object));
235 SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
236 stream->writeDecAsText(i + 1);
237 stream->writeText(" 0 obj\n"); // Generation number is always 0.
238 object->emitObject(stream, objNumMap, substitutes);
239 stream->writeText("\nendobj\n");
240 object->drop();
241 }
242 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);
243
244 // Include the zeroth object in the count.
245 int32_t objCount = SkToS32(offsets.count() + 1);
246
247 stream->writeText("xref\n0 ");
248 stream->writeDecAsText(objCount);
249 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 }
255 emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount,
256 xRefFileOffset, std::move(infoDict), std::move(id));
257
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]->drop();
262 }
263 pageTree.safeUnrefAll();
264 pages.unrefAll();
265 return true;
266 }
267
268 #if 0 207 #if 0
269 // TODO(halcanary): expose notEmbeddableCount in SkDocument 208 // TODO(halcanary): expose notEmbeddableCount in SkDocument
270 void GetCountOfFontTypes( 209 void GetCountOfFontTypes(
271 const SkTDArray<SkPDFDevice*>& pageDevices, 210 const SkTDArray<SkPDFDevice*>& pageDevices,
272 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], 211 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
273 int* notSubsettableCount, 212 int* notSubsettableCount,
274 int* notEmbeddableCount) { 213 int* notEmbeddableCount) {
275 sk_bzero(counts, sizeof(int) * 214 sk_bzero(counts, sizeof(int) *
276 (SkAdvancedTypefaceMetrics::kOther_Font + 1)); 215 (SkAdvancedTypefaceMetrics::kOther_Font + 1));
277 SkTDArray<SkFontID> seenFonts; 216 SkTDArray<SkFontID> seenFonts;
(...skipping 23 matching lines...) Expand all
301 } 240 }
302 if (notEmbeddableCount) { 241 if (notEmbeddableCount) {
303 *notEmbeddableCount = notEmbeddable; 242 *notEmbeddableCount = notEmbeddable;
304 } 243 }
305 } 244 }
306 #endif 245 #endif
307 246
308 template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullp tr; } 247 template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullp tr; }
309 //////////////////////////////////////////////////////////////////////////////// 248 ////////////////////////////////////////////////////////////////////////////////
310 249
311 namespace { 250 SkPDFDocument::SkPDFDocument(SkWStream* stream,
312 class SkPDFDocument : public SkDocument { 251 void (*doneProc)(SkWStream*, bool),
313 public: 252 SkScalar rasterDpi,
314 SkPDFDocument(SkWStream* stream, 253 SkPixelSerializer* jpegEncoder)
315 void (*doneProc)(SkWStream*, bool), 254 : SkDocument(stream, doneProc)
316 SkScalar rasterDpi, 255 , fRasterDpi(rasterDpi) {
317 SkPixelSerializer* jpegEncoder) 256 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder));
318 : SkDocument(stream, doneProc) 257 }
319 , fRasterDpi(rasterDpi) { 258
320 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder)); 259 SkPDFDocument::~SkPDFDocument() {
260 // subclasses of SkDocument must call close() in their destructors.
261 this->close();
262 }
263
264 void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
265 fObjectSerializer.addObjectRecursively(object);
266 fObjectSerializer.serializeObjects(this->getStream());
267 }
268
269 SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height,
270 const SkRect& trimBox) {
271 SkASSERT(!fCanvas.get()); // endPage() was called before this.
272 if (fPageDevices.empty()) {
273 // if this is the first page if the document.
274 fObjectSerializer.serializeHeader(this->getStream(), fMetadata);
275 }
276 SkISize pageSize = SkISize::Make(
277 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
278 sk_sp<SkPDFDevice> device(
279 SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon));
280 fCanvas = sk_make_sp<SkPDFCanvas>(device);
281 fPageDevices.push_back(std::move(device));
282 fCanvas->clipRect(trimBox);
283 fCanvas->translate(trimBox.x(), trimBox.y());
284 return fCanvas.get();
285 }
286
287 void SkPDFDocument::onEndPage() {
288 SkASSERT(fCanvas.get());
289 fCanvas->flush();
290 fCanvas.reset(nullptr);
291 }
292
293 void SkPDFDocument::onAbort() {
294 fCanvas.reset(nullptr);
295 fPageDevices.reset();
296 fCanon.reset();
297 renew(&fObjectSerializer);
298 }
299
300 void SkPDFDocument::setMetadata(const SkDocument::Attribute info[],
301 int infoCount,
302 const SkTime::DateTime* creationDate,
303 const SkTime::DateTime* modifiedDate) {
304 fMetadata.fInfo.reset(info, infoCount);
305 fMetadata.fCreation.reset(clone(creationDate));
306 fMetadata.fModified.reset(clone(modifiedDate));
307 }
308
309 bool SkPDFDocument::onClose(SkWStream* stream) {
310 SkASSERT(!fCanvas.get());
311 if (fPageDevices.empty()) {
312 fPageDevices.reset();
313 fCanon.reset();
314 renew(&fObjectSerializer);
315 return false;
316 }
317 SkTDArray<SkPDFDict*> pages; // TODO: SkTArray<sk_sp<SkPDFDict>>
318 auto dests = sk_make_sp<SkPDFDict>();
319
320 for (const sk_sp<const SkPDFDevice>& pageDevice : fPageDevices) {
321 SkASSERT(pageDevice);
322 SkASSERT(fPageDevices[0]->getCanon() == pageDevice->getCanon());
323 sk_sp<SkPDFDict> page(create_pdf_page(pageDevice.get()));
324 pageDevice->appendDestinations(dests.get(), page.get());
325 pages.push(page.release());
321 } 326 }
322 327
323 virtual ~SkPDFDocument() { 328 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
324 // subclasses must call close() in their destructors 329
325 this->close(); 330 sk_sp<SkPDFObject> id, xmp;
331 #ifdef SK_PDF_GENERATE_PDFA
332 SkPDFMetadata::UUID uuid = metadata.uuid();
333 // We use the same UUID for Document ID and Instance ID since this
334 // is the first revision of this document (and Skia does not
335 // support revising existing PDF documents).
336 // If we are not in PDF/A mode, don't use a UUID since testing
337 // works best with reproducible outputs.
338 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid));
339 xmp.reset(metadata.createXMPObject(uuid, uuid));
340 docCatalog->insertObjRef("Metadata", std::move(xmp));
341
342 // sRGB is specified by HTML, CSS, and SVG.
343 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
344 outputIntent->insertName("S", "GTS_PDFA1");
345 outputIntent->insertString("RegistryName", "http://www.color.org");
346 outputIntent->insertString("OutputConditionIdentifier",
347 "sRGB IEC61966-2.1");
348 auto intentArray = sk_make_sp<SkPDFArray>();
349 intentArray->appendObject(std::move(outputIntent));
350 // Don't specify OutputIntents if we are not in PDF/A mode since
351 // no one has ever asked for this feature.
352 docCatalog->insertObject("OutputIntents", std::move(intentArray));
353 #endif
354
355 SkTDArray<SkPDFDict*> pageTree;
356 docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree));
357
358 if (dests->size() > 0) {
359 docCatalog->insertObjRef("Dests", std::move(dests));
326 } 360 }
327 361
328 protected: 362 // Build font subsetting info before calling addObjectRecursively().
329 SkCanvas* onBeginPage(SkScalar width, SkScalar height, 363 perform_font_subsetting(fPageDevices, &fObjectSerializer.fSubstituteMap);
330 const SkRect& trimBox) override {
331 SkASSERT(!fCanvas.get());
332 364
333 SkISize pageSize = SkISize::Make( 365 fObjectSerializer.addObjectRecursively(docCatalog);
334 SkScalarRoundToInt(width), SkScalarRoundToInt(height)); 366 fObjectSerializer.serializeObjects(this->getStream());
335 sk_sp<SkPDFDevice> device( 367 fObjectSerializer.serializeFooter(
336 SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon)); 368 this->getStream(), docCatalog, std::move(id));
337 fCanvas = sk_make_sp<SkPDFCanvas>(device); 369 pageTree.unrefAll(); // TODO(halcanary): make this unnecesary by
338 fPageDevices.push_back(std::move(device)); 370 // refactoring generate_page_tree().
339 fCanvas->clipRect(trimBox); 371 pages.unrefAll();
340 fCanvas->translate(trimBox.x(), trimBox.y()); 372 fPageDevices.reset();
341 return fCanvas.get(); 373 fCanon.reset();
342 } 374 renew(&fObjectSerializer);
375 return true;
376 }
343 377
344 void onEndPage() override {
345 SkASSERT(fCanvas.get());
346 fCanvas->flush();
347 fCanvas.reset(nullptr);
348 }
349
350 bool onClose(SkWStream* stream) override {
351 SkASSERT(!fCanvas.get());
352
353 bool success = emit_pdf_document(fPageDevices, fMetadata, stream);
354 fPageDevices.reset();
355 fCanon.reset();
356 return success;
357 }
358
359 void onAbort() override {
360 fPageDevices.reset();
361 fCanon.reset();
362 }
363
364 void setMetadata(const SkDocument::Attribute info[],
365 int infoCount,
366 const SkTime::DateTime* creationDate,
367 const SkTime::DateTime* modifiedDate) override {
368 fMetadata.fInfo.reset(info, infoCount);
369 fMetadata.fCreation.reset(clone(creationDate));
370 fMetadata.fModified.reset(clone(modifiedDate));
371 }
372
373 private:
374 SkPDFCanon fCanon;
375 SkTArray<sk_sp<const SkPDFDevice>> fPageDevices;
376 sk_sp<SkCanvas> fCanvas;
377 SkScalar fRasterDpi;
378 SkPDFMetadata fMetadata;
379 };
380 } // namespace
381 /////////////////////////////////////////////////////////////////////////////// 378 ///////////////////////////////////////////////////////////////////////////////
382 379
383 sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream, 380 sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream,
384 void (*proc)(SkWStream*, bool), 381 void (*proc)(SkWStream*, bool),
385 SkScalar dpi, 382 SkScalar dpi,
386 SkPixelSerializer* jpeg) { 383 SkPixelSerializer* jpeg) {
387 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, jpeg) : nullptr ; 384 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, jpeg) : nullptr ;
388 } 385 }
389 386
390 SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) { 387 SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
391 return SkPDFMakeDocument(stream, nullptr, dpi, nullptr).release(); 388 return SkPDFMakeDocument(stream, nullptr, dpi, nullptr).release();
392 } 389 }
393 390
394 SkDocument* SkDocument::CreatePDF(SkWStream* stream, 391 SkDocument* SkDocument::CreatePDF(SkWStream* stream,
395 SkScalar dpi, 392 SkScalar dpi,
396 SkPixelSerializer* jpegEncoder) { 393 SkPixelSerializer* jpegEncoder) {
397 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release(); 394 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release();
398 } 395 }
399 396
400 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { 397 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
401 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; 398 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
402 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path)); 399 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path));
403 return stream->isValid() 400 return stream->isValid()
404 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).rele ase() 401 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).rele ase()
405 : nullptr; 402 : nullptr;
406 } 403 }
OLDNEW
« no previous file with comments | « src/pdf/SkPDFDocument.h ('k') | tests/PDFDocumentTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698