Chromium Code Reviews| Index: src/doc/SkDocument_PDF.cpp |
| diff --git a/src/doc/SkDocument_PDF.cpp b/src/doc/SkDocument_PDF.cpp |
| index fb22f18ec5da5af7807935476b07f29783ef4651..dde5339d48d3e875dd1de271e36f0471d38a1fec 100644 |
| --- a/src/doc/SkDocument_PDF.cpp |
| +++ b/src/doc/SkDocument_PDF.cpp |
| @@ -13,6 +13,7 @@ |
| #include "SkPDFTypes.h" |
| #include "SkPDFUtils.h" |
| #include "SkStream.h" |
| +#include "SkPDFMetadata.h" |
| class SkPDFDict; |
| @@ -30,15 +31,19 @@ static void emit_pdf_footer(SkWStream* stream, |
| SkPDFObject* docCatalog, |
| int64_t objCount, |
| int32_t xRefFileOffset, |
| - SkPDFDict* info) { |
| + SkPDFObject* info /* take ownership */, |
| + SkPDFObject* id /* take ownership */) { |
| SkPDFDict trailerDict; |
| - // TODO(vandebo): Linearized format will take a Prev entry too. |
| - // TODO(vandebo): PDF/A requires an ID entry. |
| + // TODO(http://crbug.com/80908): Linearized format will take a |
| + // Prev entry too. |
| trailerDict.insertInt("Size", int(objCount)); |
| trailerDict.insertObjRef("Root", SkRef(docCatalog)); |
| - SkASSERT(info); |
| - trailerDict.insertObjRef("Info", SkRef(info)); |
| - |
| + if (info) { |
| + trailerDict.insertObjRef("Info", info); |
|
tomhudson
2015/10/09 15:31:37
You made info optional?
hal.canary
2015/10/09 19:13:27
Done.
|
| + } |
| + if (id) { |
| + trailerDict.insertObject("ID", id); |
|
tomhudson
2015/10/09 15:31:37
Both info and id are noted "take ownership", but f
hal.canary
2015/10/09 19:13:27
a ref just means "don't inline this object"
|
| + } |
| stream->writeText("trailer\n"); |
| trailerDict.emitObject(stream, objNumMap, substitutes); |
| stream->writeText("\nstartxref\n"); |
| @@ -162,49 +167,8 @@ static void generate_page_tree(const SkTDArray<SkPDFDict*>& pages, |
| } |
| } |
| -struct Metadata { |
| - SkTArray<SkDocument::Attribute> fInfo; |
| - SkAutoTDelete<const SkTime::DateTime> fCreation; |
| - SkAutoTDelete<const SkTime::DateTime> fModified; |
| -}; |
| - |
| -static SkString pdf_date(const SkTime::DateTime& dt) { |
| - int timeZoneMinutes = SkToInt(dt.fTimeZoneMinutes); |
| - char timezoneSign = timeZoneMinutes >= 0 ? '+' : '-'; |
| - int timeZoneHours = SkTAbs(timeZoneMinutes) / 60; |
| - timeZoneMinutes = SkTAbs(timeZoneMinutes) % 60; |
| - return SkStringPrintf( |
| - "D:%04u%02u%02u%02u%02u%02u%c%02d'%02d'", |
| - static_cast<unsigned>(dt.fYear), static_cast<unsigned>(dt.fMonth), |
| - static_cast<unsigned>(dt.fDay), static_cast<unsigned>(dt.fHour), |
| - static_cast<unsigned>(dt.fMinute), |
| - static_cast<unsigned>(dt.fSecond), timezoneSign, timeZoneHours, |
| - timeZoneMinutes); |
| -} |
| - |
| -SkPDFDict* create_document_information_dict(const Metadata& metadata) { |
| - SkAutoTUnref<SkPDFDict> dict(new SkPDFDict); |
| - static const char* keys[] = { |
| - "Title", "Author", "Subject", "Keywords", "Creator" }; |
| - for (const char* key : keys) { |
| - for (const SkDocument::Attribute& keyValue : metadata.fInfo) { |
| - if (keyValue.fKey.equals(key)) { |
| - dict->insertString(key, keyValue.fValue); |
| - } |
| - } |
| - } |
| - dict->insertString("Producer", "Skia/PDF"); |
| - if (metadata.fCreation) { |
| - dict->insertString("CreationDate", pdf_date(*metadata.fCreation.get())); |
| - } |
| - if (metadata.fModified) { |
| - dict->insertString("ModDate", pdf_date(*metadata.fModified.get())); |
| - } |
| - return dict.detach(); |
| -} |
| - |
| static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, |
| - const Metadata& metadata, |
| + const SkPDFMetadata& metadata, |
| SkWStream* stream) { |
| if (pageDevices.isEmpty()) { |
| return false; |
| @@ -222,9 +186,29 @@ static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, |
| pages.push(page.detach()); |
| } |
| - SkTDArray<SkPDFDict*> pageTree; |
| SkAutoTUnref<SkPDFDict> docCatalog(new SkPDFDict("Catalog")); |
| + SkAutoTUnref<SkPDFObject> infoDict( |
| + metadata.createDocumentInformationDict()); |
| + |
| + SkAutoTUnref<SkPDFObject> id, xmp; |
| + #ifdef SK_PDF_GENERATE_PDFA |
| + SkPDFMetadata::UUID uuid = metadata.uuid(); |
| + id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid)); |
|
tomhudson
2015/10/09 15:31:37
CreatePdfId(uuid, uuid) calls the first one "doc"
hal.canary
2015/10/09 15:49:11
// We use the same UUID for Document ID and Instan
tomhudson
2015/10/09 19:21:37
Acknowledged.
|
| + xmp.reset(metadata.createXMPObject(uuid, uuid)); |
| + docCatalog->insertObjRef("Metadata", xmp.detach()); |
| + |
| + SkAutoTUnref<SkPDFDict> outputIntent(new SkPDFDict("OutputIntent")); |
| + outputIntent->insertName("S", "GTS_PDFA1"); |
| + outputIntent->insertString("RegistryName", "http://www.color.org"); |
| + outputIntent->insertString("OutputConditionIdentifier", |
| + "sRGB IEC61966-2.1"); |
|
tomhudson
2015/10/09 15:31:37
I thought we don't actually support sRGB yet (and
tomhudson
2015/10/09 15:31:37
Creating these intents even when we aren't writing
hal.canary
2015/10/09 15:49:11
HTML, CSS, and SVG all specify that they use sRGB.
hal.canary
2015/10/09 15:49:11
1. I want to save 139 bytes.
2. Nobody cares outsi
tomhudson
2015/10/09 19:21:37
Never mind, my question was wrong - this is inside
|
| + SkAutoTUnref<SkPDFArray> intentArray(new SkPDFArray); |
| + intentArray->appendObject(outputIntent.detach()); |
| + docCatalog->insertObject("OutputIntents", intentArray.detach()); |
| + #endif |
| + |
| + SkTDArray<SkPDFDict*> pageTree; |
| SkPDFDict* pageTreeRoot; |
| generate_page_tree(pages, &pageTree, &pageTreeRoot); |
| docCatalog->insertObjRef("Pages", SkRef(pageTreeRoot)); |
| @@ -233,28 +217,13 @@ static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, |
| docCatalog->insertObjRef("Dests", dests.detach()); |
| } |
| - /* TODO(vandebo): output intent |
| - SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); |
| - outputIntent->insertName("S", "GTS_PDFA1"); |
| - outputIntent->insertString("OutputConditionIdentifier", "sRGB"); |
| - SkAutoTUnref<SkPDFArray> intentArray(new SkPDFArray); |
| - intentArray->appendObject(SkRef(outputIntent.get())); |
| - docCatalog->insertObject("OutputIntent", intentArray.detach()); |
| - */ |
| - |
| // Build font subsetting info before proceeding. |
| SkPDFSubstituteMap substitutes; |
| perform_font_subsetting(pageDevices, &substitutes); |
| - SkAutoTUnref<SkPDFDict> infoDict( |
| - create_document_information_dict(metadata)); |
| SkPDFObjNumMap objNumMap; |
| - if (objNumMap.addObject(infoDict)) { |
| - infoDict->addResources(&objNumMap, substitutes); |
| - } |
| - if (objNumMap.addObject(docCatalog.get())) { |
| - docCatalog->addResources(&objNumMap, substitutes); |
| - } |
| + objNumMap.addObjectRecursively(infoDict, substitutes); |
| + objNumMap.addObjectRecursively(docCatalog.get(), substitutes); |
| size_t baseOffset = stream->bytesWritten(); |
| emit_pdf_header(stream); |
| SkTDArray<int32_t> offsets; |
| @@ -286,7 +255,7 @@ static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, |
| stream->writeText(" 00000 n \n"); |
| } |
| emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount, |
| - xRefFileOffset, infoDict); |
| + xRefFileOffset, infoDict.detach(), id.detach()); |
| // The page tree has both child and parent pointers, so it creates a |
| // reference cycle. We must clear that cycle to properly reclaim memory. |
| @@ -404,7 +373,7 @@ private: |
| SkTDArray<const SkPDFDevice*> fPageDevices; |
| SkAutoTUnref<SkCanvas> fCanvas; |
| SkScalar fRasterDpi; |
| - Metadata fMetadata; |
| + SkPDFMetadata fMetadata; |
| }; |
| } // namespace |
| /////////////////////////////////////////////////////////////////////////////// |