OLD | NEW |
---|---|
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 "SkPDFDevice.h" | 9 #include "SkPDFDevice.h" |
10 #include "SkPDFDocument.h" | 10 #include "SkPDFDocument.h" |
11 #include "SkPDFFont.h" | 11 #include "SkPDFFont.h" |
12 #include "SkPDFMetadata.h" | |
13 #include "SkPDFStream.h" | 12 #include "SkPDFStream.h" |
14 #include "SkPDFTypes.h" | 13 #include "SkPDFTypes.h" |
15 #include "SkPDFUtils.h" | 14 #include "SkPDFUtils.h" |
16 #include "SkStream.h" | 15 #include "SkStream.h" |
17 | 16 |
18 static void emit_pdf_header(SkWStream* stream) { | 17 |
19 stream->writeText("%PDF-1.4\n%"); | 18 SkPDFObjectSerializer::SkPDFObjectSerializer() : fBaseOffset(0), fNextToBeSerial ized(0) {} |
20 // The PDF spec recommends including a comment with four bytes, all | 19 |
21 // 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; } |
22 stream->write32(0xD3EBE9E1); | 21 |
23 stream->writeText("\n"); | 22 void SkPDFObjectSerializer::addObjectRecursively(const sk_sp<SkPDFObject>& objec t) { |
23 fObjNumMap.addObjectRecursively(object.get(), fSubstituteMap); | |
24 } | 24 } |
25 | 25 |
26 static void emit_pdf_footer(SkWStream* stream, | 26 void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream, const SkPDFMetad ata& md) { |
27 const SkPDFObjNumMap& objNumMap, | 27 fBaseOffset = wStream->bytesWritten(); |
28 const SkPDFSubstituteMap& substitutes, | 28 static const char kHeader[] = "%PDF-1.4\n%\xE1\xE9\xEB\xD3\n"; |
29 SkPDFObject* docCatalog, | 29 wStream->write(kHeader, strlen(kHeader)); |
30 int64_t objCount, | 30 // The PDF spec recommends including a comment with four |
31 int32_t xRefFileOffset, | 31 // bytes, all with their high bits set. "\xD3\xEB\xE9\xE1" is |
32 sk_sp<SkPDFObject> info, | 32 // "Skia" with the high bits set. |
33 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 auto& objects = fObjNumMap.objects(); | |
tomhudson
2016/03/21 15:55:11
Still trying to stop worrying and love the auto; i
hal.canary
2016/03/21 18:49:18
Done.
| |
41 while (fNextToBeSerialized < objects.count()) { | |
42 SkPDFObject* object = objects[fNextToBeSerialized].get(); | |
43 int32_t index = fNextToBeSerialized + 1; // Skip object 0. | |
tomhudson
2016/03/21 15:55:11
I can see equivalent code in the previous version
hal.canary
2016/03/21 18:49:18
This is a PDF thing. Object zero is special.
"Th
| |
44 SkASSERT(fOffsets.count() == fNextToBeSerialized); | |
45 fOffsets.push(this->offset(wStream)); | |
46 SkASSERT(object == fSubstituteMap.getSubstitute(object)); | |
47 wStream->writeDecAsText(index); | |
48 wStream->writeText(" 0 obj\n"); // Generation number is always 0. | |
49 object->emitObject(wStream, fObjNumMap, fSubstituteMap); | |
50 wStream->writeText("\nendobj\n"); | |
51 object->dump(); | |
52 ++fNextToBeSerialized; | |
53 } | |
54 } | |
55 | |
56 // Xref table and footer | |
57 void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream, | |
58 const sk_sp<SkPDFObject> docCatalog, | |
59 sk_sp<SkPDFObject> id) { | |
60 this->serializeObjects(wStream); | |
61 int32_t xRefFileOffset = this->offset(wStream); | |
62 // Include the zeroth object in the count. | |
tomhudson
2016/03/21 15:55:12
Yeah, confusing after we previously skipped object
hal.canary
2016/03/21 18:49:18
Acknowledged.
| |
63 int32_t objCount = SkToS32(fOffsets.count() + 1); | |
tomhudson
2016/03/21 15:55:12
Switched from int64_t to int32_t?
hal.canary
2016/03/21 18:49:17
For whatever reason, `writeDecAsText` takes an int
| |
64 wStream->writeText("xref\n0 "); | |
65 wStream->writeDecAsText(objCount); | |
66 wStream->writeText("\n0000000000 65535 f \n"); | |
67 for (int i = 0; i < fOffsets.count(); i++) { | |
68 wStream->writeBigDecAsText(fOffsets[i], 10); | |
69 wStream->writeText(" 00000 n \n"); | |
70 } | |
34 SkPDFDict trailerDict; | 71 SkPDFDict trailerDict; |
35 // TODO(http://crbug.com/80908): Linearized format will take a | 72 trailerDict.insertInt("Size", objCount); |
36 // Prev entry too. | 73 SkASSERT(docCatalog); |
37 trailerDict.insertInt("Size", int(objCount)); | 74 trailerDict.insertObjRef("Root", docCatalog); |
tomhudson
2016/03/21 15:55:12
In the old code we explicitly ref'd this here?
hal.canary
2016/03/21 18:49:18
we switched to using sk_sp<T> no need.
| |
38 trailerDict.insertObjRef("Root", sk_ref_sp(docCatalog)); | 75 SkASSERT(fInfoDict); |
39 SkASSERT(info); | 76 trailerDict.insertObjRef("Info", std::move(fInfoDict)); |
40 trailerDict.insertObjRef("Info", std::move(info)); | |
41 if (id) { | 77 if (id) { |
42 trailerDict.insertObject("ID", std::move(id)); | 78 trailerDict.insertObject("ID", std::move(id)); |
43 } | 79 } |
44 stream->writeText("trailer\n"); | 80 wStream->writeText("trailer\n"); |
45 trailerDict.emitObject(stream, objNumMap, substitutes); | 81 trailerDict.emitObject(wStream, fObjNumMap, fSubstituteMap); |
46 stream->writeText("\nstartxref\n"); | 82 wStream->writeText("\nstartxref\n"); |
47 stream->writeBigDecAsText(xRefFileOffset); | 83 wStream->writeBigDecAsText(xRefFileOffset); |
48 stream->writeText("\n%%EOF"); | 84 wStream->writeText("\n%%EOF"); |
85 } | |
86 | |
87 int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) { | |
88 size_t offset = wStream->bytesWritten(); | |
89 SkASSERT(offset > fBaseOffset); | |
90 return SkToS32(offset - fBaseOffset); | |
49 } | 91 } |
50 | 92 |
51 static void perform_font_subsetting( | 93 static void perform_font_subsetting( |
52 const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices, | 94 const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices, |
53 SkPDFSubstituteMap* substituteMap) { | 95 SkPDFSubstituteMap* substituteMap) { |
54 SkASSERT(substituteMap); | 96 SkASSERT(substituteMap); |
55 | 97 |
56 SkPDFGlyphSetMap usage; | 98 SkPDFGlyphSetMap usage; |
57 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) { | 99 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) { |
58 usage.merge(pageDevice->getFontGlyphUsage()); | 100 usage.merge(pageDevice->getFontGlyphUsage()); |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
152 | 194 |
153 curNodes = nextRoundNodes; | 195 curNodes = nextRoundNodes; |
154 nextRoundNodes.rewind(); | 196 nextRoundNodes.rewind(); |
155 treeCapacity *= kNodeSize; | 197 treeCapacity *= kNodeSize; |
156 } while (curNodes.count() > 1); | 198 } while (curNodes.count() > 1); |
157 | 199 |
158 pageTree->push(curNodes[0]); // Transfer reference. | 200 pageTree->push(curNodes[0]); // Transfer reference. |
159 return sk_ref_sp(curNodes[0]); | 201 return sk_ref_sp(curNodes[0]); |
160 } | 202 } |
161 | 203 |
162 static bool emit_pdf_document(const SkTArray<sk_sp<const SkPDFDevice>>& pageDevi ces, | |
163 const SkPDFMetadata& metadata, | |
164 SkWStream* stream) { | |
165 if (pageDevices.empty()) { | |
166 return false; | |
167 } | |
168 | |
169 SkTDArray<SkPDFDict*> pages; // TODO: SkTArray<sk_sp<SkPDFDict>> | |
170 auto dests = sk_make_sp<SkPDFDict>(); | |
171 | |
172 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()); | |
177 pages.push(page.release()); | |
178 } | |
179 | |
180 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog"); | |
181 | |
182 sk_sp<SkPDFObject> infoDict(metadata.createDocumentInformationDict()); | |
183 | |
184 sk_sp<SkPDFObject> id, xmp; | |
185 #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)); | |
194 docCatalog->insertObjRef("Metadata", std::move(xmp)); | |
195 | |
196 // sRGB is specified by HTML, CSS, and SVG. | |
197 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent"); | |
198 outputIntent->insertName("S", "GTS_PDFA1"); | |
199 outputIntent->insertString("RegistryName", "http://www.color.org"); | |
200 outputIntent->insertString("OutputConditionIdentifier", | |
201 "sRGB IEC61966-2.1"); | |
202 auto intentArray = sk_make_sp<SkPDFArray>(); | |
203 intentArray->appendObject(std::move(outputIntent)); | |
204 // Don't specify OutputIntents if we are not in PDF/A mode since | |
205 // no one has ever asked for this feature. | |
206 docCatalog->insertObject("OutputIntents", std::move(intentArray)); | |
207 #endif | |
208 | |
209 SkTDArray<SkPDFDict*> pageTree; | |
210 docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree)); | |
211 | |
212 if (dests->size() > 0) { | |
213 docCatalog->insertObjRef("Dests", std::move(dests)); | |
214 } | |
215 | |
216 // Build font subsetting info before proceeding. | |
217 SkPDFSubstituteMap substitutes; | |
218 perform_font_subsetting(pageDevices, &substitutes); | |
219 | |
220 SkPDFObjNumMap objNumMap; | |
221 objNumMap.addObjectRecursively(infoDict.get(), substitutes); | |
222 objNumMap.addObjectRecursively(docCatalog.get(), substitutes); | |
223 size_t baseOffset = stream->bytesWritten(); | |
224 emit_pdf_header(stream); | |
225 SkTDArray<int32_t> offsets; | |
226 for (int i = 0; i < objNumMap.objects().count(); ++i) { | |
227 SkPDFObject* object = objNumMap.objects()[i].get(); | |
228 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)); | |
233 SkASSERT(object == substitutes.getSubstitute(object)); | |
234 SkASSERT(objNumMap.getObjectNumber(object) == i + 1); | |
235 stream->writeDecAsText(i + 1); | |
236 stream->writeText(" 0 obj\n"); // Generation number is always 0. | |
237 object->emitObject(stream, objNumMap, substitutes); | |
238 stream->writeText("\nendobj\n"); | |
239 object->dump(); | |
240 } | |
241 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); | |
242 | |
243 // Include the zeroth object in the count. | |
244 int32_t objCount = SkToS32(offsets.count() + 1); | |
245 | |
246 stream->writeText("xref\n0 "); | |
247 stream->writeDecAsText(objCount); | |
248 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 } | |
254 emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount, | |
255 xRefFileOffset, std::move(infoDict), std::move(id)); | |
256 | |
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++) { | |
260 pageTree[i]->dump(); | |
261 } | |
262 pageTree.safeUnrefAll(); | |
263 pages.unrefAll(); | |
264 return true; | |
265 } | |
266 | |
267 #if 0 | 204 #if 0 |
268 // TODO(halcanary): expose notEmbeddableCount in SkDocument | 205 // TODO(halcanary): expose notEmbeddableCount in SkDocument |
269 void GetCountOfFontTypes( | 206 void GetCountOfFontTypes( |
270 const SkTDArray<SkPDFDevice*>& pageDevices, | 207 const SkTDArray<SkPDFDevice*>& pageDevices, |
271 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], | 208 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], |
272 int* notSubsettableCount, | 209 int* notSubsettableCount, |
273 int* notEmbeddableCount) { | 210 int* notEmbeddableCount) { |
274 sk_bzero(counts, sizeof(int) * | 211 sk_bzero(counts, sizeof(int) * |
275 (SkAdvancedTypefaceMetrics::kOther_Font + 1)); | 212 (SkAdvancedTypefaceMetrics::kOther_Font + 1)); |
276 SkTDArray<SkFontID> seenFonts; | 213 SkTDArray<SkFontID> seenFonts; |
(...skipping 23 matching lines...) Expand all Loading... | |
300 } | 237 } |
301 if (notEmbeddableCount) { | 238 if (notEmbeddableCount) { |
302 *notEmbeddableCount = notEmbeddable; | 239 *notEmbeddableCount = notEmbeddable; |
303 } | 240 } |
304 } | 241 } |
305 #endif | 242 #endif |
306 | 243 |
307 template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullp tr; } | 244 template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullp tr; } |
308 //////////////////////////////////////////////////////////////////////////////// | 245 //////////////////////////////////////////////////////////////////////////////// |
309 | 246 |
310 namespace { | 247 SkPDFDocument::SkPDFDocument(SkWStream* stream, |
311 class SkPDFDocument : public SkDocument { | 248 void (*doneProc)(SkWStream*, bool), |
312 public: | 249 SkScalar rasterDpi, |
313 SkPDFDocument(SkWStream* stream, | 250 SkPixelSerializer* jpegEncoder) |
314 void (*doneProc)(SkWStream*, bool), | 251 : SkDocument(stream, doneProc) |
315 SkScalar rasterDpi, | 252 , fRasterDpi(rasterDpi) { |
316 SkPixelSerializer* jpegEncoder) | 253 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder)); |
317 : SkDocument(stream, doneProc) | 254 } |
318 , fRasterDpi(rasterDpi) { | 255 |
319 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder)); | 256 SkPDFDocument::~SkPDFDocument() { |
257 // subclasses must call close() in their destructors | |
tomhudson
2016/03/21 15:55:11
subclasses of SkDocument, or subclasses of SkPDFDo
hal.canary
2016/03/21 18:49:18
// subclasses of SkDocument
| |
258 this->close(); | |
259 } | |
260 | |
261 void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) { | |
tomhudson
2016/03/21 15:55:11
Name reads like it's going to serialize a single o
hal.canary
2016/03/21 18:49:18
new comments:
/**
Serialize the object
| |
262 fObjectSerializer.addObjectRecursively(object); | |
263 fObjectSerializer.serializeObjects(this->getStream()); | |
264 } | |
265 | |
266 SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height, | |
267 const SkRect& trimBox) { | |
268 SkASSERT(!fCanvas.get()); // end page was called. | |
tomhudson
2016/03/21 15:55:12
Nit: This comment unclear.
Reading through the cod
hal.canary
2016/03/21 18:49:18
Done.
| |
269 if (fPageDevices.empty()) { // if first page | |
270 fObjectSerializer.serializeHeader(this->getStream(), fMetadata); | |
271 } | |
272 SkISize pageSize = SkISize::Make( | |
273 SkScalarRoundToInt(width), SkScalarRoundToInt(height)); | |
274 sk_sp<SkPDFDevice> device( | |
275 SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon)); | |
276 fCanvas.reset(new SkCanvas(device.get())); | |
277 fPageDevices.push_back(std::move(device)); | |
278 fCanvas->clipRect(trimBox); | |
279 fCanvas->translate(trimBox.x(), trimBox.y()); | |
280 return fCanvas.get(); | |
281 } | |
282 | |
283 void SkPDFDocument::onEndPage() { | |
284 SkASSERT(fCanvas.get()); | |
285 fCanvas->flush(); | |
286 fCanvas.reset(nullptr); | |
287 } | |
288 | |
289 void SkPDFDocument::onAbort() { | |
290 fPageDevices.reset(); | |
291 fCanon.reset(); | |
292 renew(&fObjectSerializer); | |
tomhudson
2016/03/21 15:55:11
Should we be resetting fCanvas?
hal.canary
2016/03/21 18:49:18
Done.
| |
293 } | |
294 | |
295 void SkPDFDocument::setMetadata(const SkDocument::Attribute info[], | |
296 int infoCount, | |
297 const SkTime::DateTime* creationDate, | |
298 const SkTime::DateTime* modifiedDate) { | |
299 fMetadata.fInfo.reset(info, infoCount); | |
300 fMetadata.fCreation.reset(clone(creationDate)); | |
301 fMetadata.fModified.reset(clone(modifiedDate)); | |
302 } | |
303 | |
304 bool SkPDFDocument::onClose(SkWStream* stream) { | |
305 SkASSERT(!fCanvas.get()); | |
306 if (fPageDevices.empty()) { | |
307 fPageDevices.reset(); | |
308 fCanon.reset(); | |
309 renew(&fObjectSerializer); | |
310 return false; | |
311 } | |
312 SkTDArray<SkPDFDict*> pages; // TODO: SkTArray<sk_sp<SkPDFDict>> | |
313 auto dests = sk_make_sp<SkPDFDict>(); | |
314 | |
315 for (const sk_sp<const SkPDFDevice>& pageDevice : fPageDevices) { | |
316 SkASSERT(pageDevice); | |
317 SkASSERT(fPageDevices[0]->getCanon() == pageDevice->getCanon()); | |
318 sk_sp<SkPDFDict> page(create_pdf_page(pageDevice.get())); | |
319 pageDevice->appendDestinations(dests.get(), page.get()); | |
320 pages.push(page.release()); | |
320 } | 321 } |
321 | 322 |
322 virtual ~SkPDFDocument() { | 323 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog"); |
323 // subclasses must call close() in their destructors | 324 |
324 this->close(); | 325 sk_sp<SkPDFObject> id, xmp; |
326 #ifdef SK_PDF_GENERATE_PDFA | |
327 SkPDFMetadata::UUID uuid = metadata.uuid(); | |
328 // We use the same UUID for Document ID and Instance ID since this | |
329 // is the first revision of this document (and Skia does not | |
330 // support revising existing PDF documents). | |
331 // If we are not in PDF/A mode, don't use a UUID since testing | |
332 // works best with reproducible outputs. | |
333 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid)); | |
334 xmp.reset(metadata.createXMPObject(uuid, uuid)); | |
335 docCatalog->insertObjRef("Metadata", std::move(xmp)); | |
336 | |
337 // sRGB is specified by HTML, CSS, and SVG. | |
338 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent"); | |
339 outputIntent->insertName("S", "GTS_PDFA1"); | |
340 outputIntent->insertString("RegistryName", "http://www.color.org"); | |
341 outputIntent->insertString("OutputConditionIdentifier", | |
342 "sRGB IEC61966-2.1"); | |
343 auto intentArray = sk_make_sp<SkPDFArray>(); | |
344 intentArray->appendObject(std::move(outputIntent)); | |
345 // Don't specify OutputIntents if we are not in PDF/A mode since | |
346 // no one has ever asked for this feature. | |
347 docCatalog->insertObject("OutputIntents", std::move(intentArray)); | |
348 #endif | |
349 | |
350 SkTDArray<SkPDFDict*> pageTree; | |
351 docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree)); | |
352 | |
353 if (dests->size() > 0) { | |
354 docCatalog->insertObjRef("Dests", std::move(dests)); | |
325 } | 355 } |
326 | 356 |
327 protected: | 357 // Build font subsetting info before proceeding. |
tomhudson
2016/03/21 15:55:12
Nit: unnecessary comment?
hal.canary
2016/03/21 18:49:18
// Build font subsetting info before calling addOb
| |
328 SkCanvas* onBeginPage(SkScalar width, SkScalar height, | 358 perform_font_subsetting(fPageDevices, &fObjectSerializer.fSubstituteMap); |
329 const SkRect& trimBox) override { | |
330 SkASSERT(!fCanvas.get()); | |
331 | 359 |
332 SkISize pageSize = SkISize::Make( | 360 fObjectSerializer.addObjectRecursively(docCatalog); |
333 SkScalarRoundToInt(width), SkScalarRoundToInt(height)); | 361 fObjectSerializer.serializeObjects(this->getStream()); |
334 sk_sp<SkPDFDevice> device( | 362 fObjectSerializer.serializeFooter(this->getStream(), docCatalog, std::move(i d)); |
335 SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon)); | 363 // // The page tree has both child and parent pointers, so it creates a |
336 fCanvas.reset(new SkCanvas(device.get())); | 364 // // reference cycle. We must clear that cycle to properly reclaim memory. |
337 fPageDevices.push_back(std::move(device)); | 365 // for (int i = 0; i < pageTree.count(); i++) { |
338 fCanvas->clipRect(trimBox); | 366 // pageTree[i]->dump(); |
339 fCanvas->translate(trimBox.x(), trimBox.y()); | 367 // } |
tomhudson
2016/03/21 15:55:11
Nit: Delete before committing?
hal.canary
2016/03/21 18:49:18
Done.
| |
340 return fCanvas.get(); | 368 pageTree.unrefAll(); // TODO: necesary? |
341 } | 369 pages.unrefAll(); |
370 fPageDevices.reset(); | |
371 fCanon.reset(); | |
372 renew(&fObjectSerializer); | |
373 return true; | |
374 } | |
342 | 375 |
343 void onEndPage() override { | |
344 SkASSERT(fCanvas.get()); | |
345 fCanvas->flush(); | |
346 fCanvas.reset(nullptr); | |
347 } | |
348 | |
349 bool onClose(SkWStream* stream) override { | |
350 SkASSERT(!fCanvas.get()); | |
351 | |
352 bool success = emit_pdf_document(fPageDevices, fMetadata, stream); | |
353 fPageDevices.reset(); | |
354 fCanon.reset(); | |
355 return success; | |
356 } | |
357 | |
358 void onAbort() override { | |
359 fPageDevices.reset(); | |
360 fCanon.reset(); | |
361 } | |
362 | |
363 void setMetadata(const SkDocument::Attribute info[], | |
364 int infoCount, | |
365 const SkTime::DateTime* creationDate, | |
366 const SkTime::DateTime* modifiedDate) override { | |
367 fMetadata.fInfo.reset(info, infoCount); | |
368 fMetadata.fCreation.reset(clone(creationDate)); | |
369 fMetadata.fModified.reset(clone(modifiedDate)); | |
370 } | |
371 | |
372 private: | |
373 SkPDFCanon fCanon; | |
374 SkTArray<sk_sp<const SkPDFDevice>> fPageDevices; | |
375 sk_sp<SkCanvas> fCanvas; | |
376 SkScalar fRasterDpi; | |
377 SkPDFMetadata fMetadata; | |
378 }; | |
379 } // namespace | |
380 /////////////////////////////////////////////////////////////////////////////// | 376 /////////////////////////////////////////////////////////////////////////////// |
381 | 377 |
382 sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream, | 378 sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream, |
383 void (*proc)(SkWStream*, bool), | 379 void (*proc)(SkWStream*, bool), |
384 SkScalar dpi, | 380 SkScalar dpi, |
385 SkPixelSerializer* jpeg) { | 381 SkPixelSerializer* jpeg) { |
386 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, jpeg) : nullptr ; | 382 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, jpeg) : nullptr ; |
387 } | 383 } |
388 | 384 |
389 SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) { | 385 SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) { |
390 return SkPDFMakeDocument(stream, nullptr, dpi, nullptr).release(); | 386 return SkPDFMakeDocument(stream, nullptr, dpi, nullptr).release(); |
391 } | 387 } |
392 | 388 |
393 SkDocument* SkDocument::CreatePDF(SkWStream* stream, | 389 SkDocument* SkDocument::CreatePDF(SkWStream* stream, |
394 SkScalar dpi, | 390 SkScalar dpi, |
395 SkPixelSerializer* jpegEncoder) { | 391 SkPixelSerializer* jpegEncoder) { |
396 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release(); | 392 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release(); |
397 } | 393 } |
398 | 394 |
399 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { | 395 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { |
400 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; | 396 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; |
401 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path)); | 397 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path)); |
402 return stream->isValid() | 398 return stream->isValid() |
403 ? SkPDFMakeDocument(stream.detach(), delete_wstream, dpi, nullptr).relea se() | 399 ? SkPDFMakeDocument(stream.detach(), delete_wstream, dpi, nullptr).relea se() |
404 : nullptr; | 400 : nullptr; |
405 } | 401 } |
OLD | NEW |