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 "SkPDFCanvas.h" | 9 #include "SkPDFCanvas.h" |
10 #include "SkPDFDevice.h" | 10 #include "SkPDFDevice.h" |
(...skipping 17 matching lines...) Expand all Loading... | |
28 fObjNumMap.addObjectRecursively(object.get(), fSubstituteMap); | 28 fObjNumMap.addObjectRecursively(object.get(), fSubstituteMap); |
29 } | 29 } |
30 | 30 |
31 #define SKPDF_MAGIC "\xD3\xEB\xE9\xE1" | 31 #define SKPDF_MAGIC "\xD3\xEB\xE9\xE1" |
32 #ifndef SK_BUILD_FOR_WIN32 | 32 #ifndef SK_BUILD_FOR_WIN32 |
33 static_assert((SKPDF_MAGIC[0] & 0x7F) == "Skia"[0], ""); | 33 static_assert((SKPDF_MAGIC[0] & 0x7F) == "Skia"[0], ""); |
34 static_assert((SKPDF_MAGIC[1] & 0x7F) == "Skia"[1], ""); | 34 static_assert((SKPDF_MAGIC[1] & 0x7F) == "Skia"[1], ""); |
35 static_assert((SKPDF_MAGIC[2] & 0x7F) == "Skia"[2], ""); | 35 static_assert((SKPDF_MAGIC[2] & 0x7F) == "Skia"[2], ""); |
36 static_assert((SKPDF_MAGIC[3] & 0x7F) == "Skia"[3], ""); | 36 static_assert((SKPDF_MAGIC[3] & 0x7F) == "Skia"[3], ""); |
37 #endif | 37 #endif |
38 void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream, const SkPDFMetad ata& md) { | 38 void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream, |
39 const SkDocument::PDFMetadata& md) { | |
39 fBaseOffset = wStream->bytesWritten(); | 40 fBaseOffset = wStream->bytesWritten(); |
40 static const char kHeader[] = "%PDF-1.4\n%" SKPDF_MAGIC "\n"; | 41 static const char kHeader[] = "%PDF-1.4\n%" SKPDF_MAGIC "\n"; |
41 wStream->write(kHeader, strlen(kHeader)); | 42 wStream->write(kHeader, strlen(kHeader)); |
42 // The PDF spec recommends including a comment with four | 43 // The PDF spec recommends including a comment with four |
43 // bytes, all with their high bits set. "\xD3\xEB\xE9\xE1" is | 44 // bytes, all with their high bits set. "\xD3\xEB\xE9\xE1" is |
44 // "Skia" with the high bits set. | 45 // "Skia" with the high bits set. |
45 fInfoDict.reset(md.createDocumentInformationDict()); | 46 fInfoDict = SkPDFMetadata::MakeDocumentInformationDict(md); |
46 this->addObjectRecursively(fInfoDict); | 47 this->addObjectRecursively(fInfoDict); |
47 this->serializeObjects(wStream); | 48 this->serializeObjects(wStream); |
48 } | 49 } |
49 #undef SKPDF_MAGIC | 50 #undef SKPDF_MAGIC |
50 | 51 |
51 // Serialize all objects in the fObjNumMap that have not yet been serialized; | 52 // Serialize all objects in the fObjNumMap that have not yet been serialized; |
52 void SkPDFObjectSerializer::serializeObjects(SkWStream* wStream) { | 53 void SkPDFObjectSerializer::serializeObjects(SkWStream* wStream) { |
53 const SkTArray<sk_sp<SkPDFObject>>& objects = fObjNumMap.objects(); | 54 const SkTArray<sk_sp<SkPDFObject>>& objects = fObjNumMap.objects(); |
54 while (fNextToBeSerialized < objects.count()) { | 55 while (fNextToBeSerialized < objects.count()) { |
55 SkPDFObject* object = objects[fNextToBeSerialized].get(); | 56 SkPDFObject* object = objects[fNextToBeSerialized].get(); |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
208 } | 209 } |
209 } | 210 } |
210 #endif | 211 #endif |
211 | 212 |
212 template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullp tr; } | 213 template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullp tr; } |
213 //////////////////////////////////////////////////////////////////////////////// | 214 //////////////////////////////////////////////////////////////////////////////// |
214 | 215 |
215 SkPDFDocument::SkPDFDocument(SkWStream* stream, | 216 SkPDFDocument::SkPDFDocument(SkWStream* stream, |
216 void (*doneProc)(SkWStream*, bool), | 217 void (*doneProc)(SkWStream*, bool), |
217 SkScalar rasterDpi, | 218 SkScalar rasterDpi, |
218 SkPixelSerializer* jpegEncoder, | 219 const SkDocument::PDFMetadata& metadata, |
220 sk_sp<SkPixelSerializer> jpegEncoder, | |
219 bool pdfa) | 221 bool pdfa) |
220 : SkDocument(stream, doneProc) | 222 : SkDocument(stream, doneProc) |
221 , fRasterDpi(rasterDpi) | 223 , fRasterDpi(rasterDpi) |
224 , fMetadata(metadata) | |
222 , fPDFA(pdfa) { | 225 , fPDFA(pdfa) { |
223 fCanon.setPixelSerializer(SkSafeRef(jpegEncoder)); | 226 fCanon.setPixelSerializer(std::move(jpegEncoder)); |
224 } | 227 } |
225 | 228 |
226 SkPDFDocument::~SkPDFDocument() { | 229 SkPDFDocument::~SkPDFDocument() { |
227 // subclasses of SkDocument must call close() in their destructors. | 230 // subclasses of SkDocument must call close() in their destructors. |
228 this->close(); | 231 this->close(); |
229 } | 232 } |
230 | 233 |
231 void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) { | 234 void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) { |
232 fObjectSerializer.addObjectRecursively(object); | 235 fObjectSerializer.addObjectRecursively(object); |
233 fObjectSerializer.serializeObjects(this->getStream()); | 236 fObjectSerializer.serializeObjects(this->getStream()); |
234 } | 237 } |
235 | 238 |
236 SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height, | 239 SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height, |
237 const SkRect& trimBox) { | 240 const SkRect& trimBox) { |
238 SkASSERT(!fCanvas.get()); // endPage() was called before this. | 241 SkASSERT(!fCanvas.get()); // endPage() was called before this. |
239 if (fPages.empty()) { | 242 if (fPages.empty()) { |
240 // if this is the first page if the document. | 243 // if this is the first page if the document. |
241 fObjectSerializer.serializeHeader(this->getStream(), fMetadata); | 244 fObjectSerializer.serializeHeader(this->getStream(), fMetadata); |
242 fDests = sk_make_sp<SkPDFDict>(); | 245 fDests = sk_make_sp<SkPDFDict>(); |
243 if (fPDFA) { | 246 if (fPDFA) { |
244 SkPDFMetadata::UUID uuid = fMetadata.uuid(); | 247 SkPDFMetadata::UUID uuid = SkPDFMetadata::CreateUUID(fMetadata); |
245 // We use the same UUID for Document ID and Instance ID since this | 248 // We use the same UUID for Document ID and Instance ID since this |
246 // is the first revision of this document (and Skia does not | 249 // is the first revision of this document (and Skia does not |
247 // support revising existing PDF documents). | 250 // support revising existing PDF documents). |
248 // If we are not in PDF/A mode, don't use a UUID since testing | 251 // If we are not in PDF/A mode, don't use a UUID since testing |
249 // works best with reproducible outputs. | 252 // works best with reproducible outputs. |
250 fID.reset(SkPDFMetadata::CreatePdfId(uuid, uuid)); | 253 fID = SkPDFMetadata::MakePdfId(uuid, uuid); |
251 fXMP.reset(fMetadata.createXMPObject(uuid, uuid)); | 254 fXMP = SkPDFMetadata::MakeXMPObject(fMetadata, uuid, uuid); |
252 fObjectSerializer.addObjectRecursively(fXMP); | 255 fObjectSerializer.addObjectRecursively(fXMP); |
253 fObjectSerializer.serializeObjects(this->getStream()); | 256 fObjectSerializer.serializeObjects(this->getStream()); |
254 } | 257 } |
255 } | 258 } |
256 SkISize pageSize = SkISize::Make( | 259 SkISize pageSize = SkISize::Make( |
257 SkScalarRoundToInt(width), SkScalarRoundToInt(height)); | 260 SkScalarRoundToInt(width), SkScalarRoundToInt(height)); |
258 fPageDevice.reset( | 261 fPageDevice.reset( |
259 SkPDFDevice::Create(pageSize, fRasterDpi, this)); | 262 SkPDFDevice::Create(pageSize, fRasterDpi, this)); |
260 fCanvas = sk_make_sp<SkPDFCanvas>(fPageDevice); | 263 fCanvas = sk_make_sp<SkPDFCanvas>(fPageDevice); |
261 fCanvas->clipRect(trimBox); | 264 fCanvas->clipRect(trimBox); |
(...skipping 24 matching lines...) Expand all Loading... | |
286 fPageDevice.reset(nullptr); | 289 fPageDevice.reset(nullptr); |
287 } | 290 } |
288 | 291 |
289 void SkPDFDocument::onAbort() { | 292 void SkPDFDocument::onAbort() { |
290 fCanvas.reset(nullptr); | 293 fCanvas.reset(nullptr); |
291 fPages.reset(); | 294 fPages.reset(); |
292 fCanon.reset(); | 295 fCanon.reset(); |
293 renew(&fObjectSerializer); | 296 renew(&fObjectSerializer); |
294 } | 297 } |
295 | 298 |
299 #ifdef SK_SUPPORT_LEGACY_DOCUMENT_API | |
300 static const struct { | |
301 const char* const key; | |
302 SkString SkDocument::PDFMetadata::*const valuePtr; | |
303 } gMetadataKeys[] = { | |
304 {"Title", &SkDocument::PDFMetadata::fTitle}, | |
305 {"Author", &SkDocument::PDFMetadata::fAuthor}, | |
306 {"Subject", &SkDocument::PDFMetadata::fSubject}, | |
307 {"Keywords", &SkDocument::PDFMetadata::fKeywords}, | |
308 {"Creator", &SkDocument::PDFMetadata::fCreator}, | |
309 }; | |
296 void SkPDFDocument::setMetadata(const SkDocument::Attribute info[], | 310 void SkPDFDocument::setMetadata(const SkDocument::Attribute info[], |
297 int infoCount, | 311 int infoCount, |
298 const SkTime::DateTime* creationDate, | 312 const SkTime::DateTime* creationDate, |
299 const SkTime::DateTime* modifiedDate) { | 313 const SkTime::DateTime* modifiedDate) { |
300 fMetadata.fInfo.reset(info, infoCount); | 314 for (const auto keyValuePtr : gMetadataKeys) { |
tomhudson
2016/04/26 19:18:16
Ugh, but I suppose it works and is small.
hal.canary
2016/04/26 19:56:50
I combined the two tables. Does that make it bett
| |
301 fMetadata.fCreation.reset(clone(creationDate)); | 315 const char* key = keyValuePtr.key; |
302 fMetadata.fModified.reset(clone(modifiedDate)); | 316 for (int i = 0; i < infoCount; ++i) { |
317 const SkDocument::Attribute& keyValue = info[i]; | |
318 if (keyValue.fKey.equals(key)) { | |
319 fMetadata.*(keyValuePtr.valuePtr) = keyValue.fValue; | |
320 } | |
321 } | |
322 } | |
323 if (creationDate) { | |
324 fMetadata.fCreation.fDateTime = *creationDate; | |
325 } | |
326 if (modifiedDate) { | |
327 fMetadata.fModified.fDateTime = *modifiedDate; | |
328 } | |
303 } | 329 } |
330 #endif // SK_SUPPORT_LEGACY_DOCUMENT_API | |
304 | 331 |
305 static sk_sp<SkData> SkSrgbIcm() { | 332 static sk_sp<SkData> SkSrgbIcm() { |
306 // Source: http://www.argyllcms.com/icclibsrc.html | 333 // Source: http://www.argyllcms.com/icclibsrc.html |
307 static const char kProfile[] = | 334 static const char kProfile[] = |
308 "\0\0\14\214argl\2 \0\0mntrRGB XYZ \7\336\0\1\0\6\0\26\0\17\0:acspM" | 335 "\0\0\14\214argl\2 \0\0mntrRGB XYZ \7\336\0\1\0\6\0\26\0\17\0:acspM" |
309 "SFT\0\0\0\0IEC sRGB\0\0\0\0\0\0\0\0\0\0\0\0\0\0\366\326\0\1\0\0\0\0" | 336 "SFT\0\0\0\0IEC sRGB\0\0\0\0\0\0\0\0\0\0\0\0\0\0\366\326\0\1\0\0\0\0" |
310 "\323-argl\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" | 337 "\323-argl\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
311 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\21desc\0\0\1P\0\0\0\231cprt\0" | 338 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\21desc\0\0\1P\0\0\0\231cprt\0" |
312 "\0\1\354\0\0\0gdmnd\0\0\2T\0\0\0pdmdd\0\0\2\304\0\0\0\210tech\0\0\3" | 339 "\0\1\354\0\0\0gdmnd\0\0\2T\0\0\0pdmdd\0\0\2\304\0\0\0\210tech\0\0\3" |
313 "L\0\0\0\14vued\0\0\3X\0\0\0gview\0\0\3\300\0\0\0$lumi\0\0\3\344\0\0" | 340 "L\0\0\0\14vued\0\0\3X\0\0\0gview\0\0\3\300\0\0\0$lumi\0\0\3\344\0\0" |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
486 fCanon.reset(); | 513 fCanon.reset(); |
487 renew(&fObjectSerializer); | 514 renew(&fObjectSerializer); |
488 return true; | 515 return true; |
489 } | 516 } |
490 | 517 |
491 /////////////////////////////////////////////////////////////////////////////// | 518 /////////////////////////////////////////////////////////////////////////////// |
492 | 519 |
493 sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream, | 520 sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream, |
494 void (*proc)(SkWStream*, bool), | 521 void (*proc)(SkWStream*, bool), |
495 SkScalar dpi, | 522 SkScalar dpi, |
496 SkPixelSerializer* jpeg, | 523 const SkDocument::PDFMetadata& metadata, |
524 sk_sp<SkPixelSerializer> jpeg, | |
497 bool pdfa) { | 525 bool pdfa) { |
498 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, jpeg, pdfa) | 526 return stream ? sk_make_sp<SkPDFDocument>(stream, proc, dpi, metadata, |
527 std::move(jpeg), pdfa) | |
499 : nullptr; | 528 : nullptr; |
500 } | 529 } |
501 | 530 |
502 #ifdef SK_PDF_GENERATE_PDFA | 531 sk_sp<SkDocument> SkDocument::MakePDF(const char path[], SkScalar dpi) { |
503 static const bool kPDFA = true; | |
504 #else | |
505 static const bool kPDFA = false; | |
506 #endif | |
507 | |
508 SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) { | |
509 return SkPDFMakeDocument(stream, nullptr, dpi, nullptr, kPDFA).release(); | |
510 } | |
511 | |
512 SkDocument* SkDocument::CreatePDF(SkWStream* stream, | |
513 SkScalar dpi, | |
514 SkPixelSerializer* jpegEncoder) { | |
515 return SkPDFMakeDocument(stream, nullptr, dpi, | |
516 jpegEncoder, kPDFA).release(); | |
517 } | |
518 | |
519 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { | |
520 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; | 532 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; |
521 std::unique_ptr<SkFILEWStream> stream(new SkFILEWStream(path)); | 533 std::unique_ptr<SkFILEWStream> stream(new SkFILEWStream(path)); |
522 return stream->isValid() | 534 return stream->isValid() |
523 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, | 535 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, |
524 nullptr, kPDFA).release() | 536 SkDocument::PDFMetadata(), nullptr, |
525 : nullptr; | 537 false) |
538 : nullptr; | |
526 } | 539 } |
540 | |
541 sk_sp<SkDocument> SkDocument::MakePDF(SkWStream* stream, | |
542 SkScalar dpi, | |
543 const SkDocument::PDFMetadata& metadata, | |
544 sk_sp<SkPixelSerializer> jpegEncoder, | |
545 bool pdfa) { | |
546 return SkPDFMakeDocument(stream, nullptr, dpi, metadata, | |
547 std::move(jpegEncoder), pdfa); | |
548 } | |
OLD | NEW |