134422610ac22adceeabb66023120f27b96cae953halcanary/* 234422610ac22adceeabb66023120f27b96cae953halcanary * Copyright 2015 Google Inc. 334422610ac22adceeabb66023120f27b96cae953halcanary * 434422610ac22adceeabb66023120f27b96cae953halcanary * Use of this source code is governed by a BSD-style license that can be 534422610ac22adceeabb66023120f27b96cae953halcanary * found in the LICENSE file. 634422610ac22adceeabb66023120f27b96cae953halcanary */ 734422610ac22adceeabb66023120f27b96cae953halcanary 8488165e689baf0f215d5798c87d0031b58e4bc8dhalcanary#include "SkMD5.h" 9ffe54004b92f84b65ee4569aaccbc178c51b017fhalcanary#include "SkMilestone.h" 1034422610ac22adceeabb66023120f27b96cae953halcanary#include "SkPDFMetadata.h" 1134422610ac22adceeabb66023120f27b96cae953halcanary#include "SkPDFTypes.h" 12d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary#include "SkUtils.h" 13d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary 14221524de3be1fc343ad328c5e99562f32b5cad9cbungeman#include <utility> 1534422610ac22adceeabb66023120f27b96cae953halcanary 169f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary#define SKPDF_STRING(X) SKPDF_STRING_IMPL(X) 179f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary#define SKPDF_STRING_IMPL(X) #X 189f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary#define SKPDF_PRODUCER "Skia/PDF m" SKPDF_STRING(SK_MILESTONE) 199f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary#define SKPDF_CUSTOM_PRODUCER_KEY "ProductionLibrary" 209f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary 2134422610ac22adceeabb66023120f27b96cae953halcanarystatic SkString pdf_date(const SkTime::DateTime& dt) { 2234422610ac22adceeabb66023120f27b96cae953halcanary int timeZoneMinutes = SkToInt(dt.fTimeZoneMinutes); 2334422610ac22adceeabb66023120f27b96cae953halcanary char timezoneSign = timeZoneMinutes >= 0 ? '+' : '-'; 2434422610ac22adceeabb66023120f27b96cae953halcanary int timeZoneHours = SkTAbs(timeZoneMinutes) / 60; 2534422610ac22adceeabb66023120f27b96cae953halcanary timeZoneMinutes = SkTAbs(timeZoneMinutes) % 60; 2634422610ac22adceeabb66023120f27b96cae953halcanary return SkStringPrintf( 2734422610ac22adceeabb66023120f27b96cae953halcanary "D:%04u%02u%02u%02u%02u%02u%c%02d'%02d'", 2834422610ac22adceeabb66023120f27b96cae953halcanary static_cast<unsigned>(dt.fYear), static_cast<unsigned>(dt.fMonth), 2934422610ac22adceeabb66023120f27b96cae953halcanary static_cast<unsigned>(dt.fDay), static_cast<unsigned>(dt.fHour), 3034422610ac22adceeabb66023120f27b96cae953halcanary static_cast<unsigned>(dt.fMinute), 3134422610ac22adceeabb66023120f27b96cae953halcanary static_cast<unsigned>(dt.fSecond), timezoneSign, timeZoneHours, 3234422610ac22adceeabb66023120f27b96cae953halcanary timeZoneMinutes); 3334422610ac22adceeabb66023120f27b96cae953halcanary} 3434422610ac22adceeabb66023120f27b96cae953halcanary 354b6566644f704cf9e30c71fa547c9b5915752792halcanarynamespace { 364b6566644f704cf9e30c71fa547c9b5915752792halcanarystatic const struct { 374b6566644f704cf9e30c71fa547c9b5915752792halcanary const char* const key; 384b6566644f704cf9e30c71fa547c9b5915752792halcanary SkString SkDocument::PDFMetadata::*const valuePtr; 394b6566644f704cf9e30c71fa547c9b5915752792halcanary} gMetadataKeys[] = { 404b6566644f704cf9e30c71fa547c9b5915752792halcanary {"Title", &SkDocument::PDFMetadata::fTitle}, 414b6566644f704cf9e30c71fa547c9b5915752792halcanary {"Author", &SkDocument::PDFMetadata::fAuthor}, 424b6566644f704cf9e30c71fa547c9b5915752792halcanary {"Subject", &SkDocument::PDFMetadata::fSubject}, 434b6566644f704cf9e30c71fa547c9b5915752792halcanary {"Keywords", &SkDocument::PDFMetadata::fKeywords}, 444b6566644f704cf9e30c71fa547c9b5915752792halcanary {"Creator", &SkDocument::PDFMetadata::fCreator}, 454b6566644f704cf9e30c71fa547c9b5915752792halcanary}; 464b6566644f704cf9e30c71fa547c9b5915752792halcanary} // namespace 474b6566644f704cf9e30c71fa547c9b5915752792halcanary 484b6566644f704cf9e30c71fa547c9b5915752792halcanarysk_sp<SkPDFObject> SkPDFMetadata::MakeDocumentInformationDict( 494b6566644f704cf9e30c71fa547c9b5915752792halcanary const SkDocument::PDFMetadata& metadata) { 50ece83924384b2e9e8cd422324c44797deb99ec90halcanary auto dict = sk_make_sp<SkPDFDict>(); 514b6566644f704cf9e30c71fa547c9b5915752792halcanary for (const auto keyValuePtr : gMetadataKeys) { 524b6566644f704cf9e30c71fa547c9b5915752792halcanary const SkString& value = metadata.*(keyValuePtr.valuePtr); 534b6566644f704cf9e30c71fa547c9b5915752792halcanary if (value.size() > 0) { 544b6566644f704cf9e30c71fa547c9b5915752792halcanary dict->insertString(keyValuePtr.key, value); 5534422610ac22adceeabb66023120f27b96cae953halcanary } 5634422610ac22adceeabb66023120f27b96cae953halcanary } 579f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary if (metadata.fProducer.isEmpty()) { 589f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary dict->insertString("Producer", SKPDF_PRODUCER); 599f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary } else { 609f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary dict->insertString("Producer", metadata.fProducer); 619f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary dict->insertString(SKPDF_CUSTOM_PRODUCER_KEY, SKPDF_PRODUCER); 629f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary } 634b6566644f704cf9e30c71fa547c9b5915752792halcanary if (metadata.fCreation.fEnabled) { 644b6566644f704cf9e30c71fa547c9b5915752792halcanary dict->insertString("CreationDate", 654b6566644f704cf9e30c71fa547c9b5915752792halcanary pdf_date(metadata.fCreation.fDateTime)); 6634422610ac22adceeabb66023120f27b96cae953halcanary } 674b6566644f704cf9e30c71fa547c9b5915752792halcanary if (metadata.fModified.fEnabled) { 684b6566644f704cf9e30c71fa547c9b5915752792halcanary dict->insertString("ModDate", pdf_date(metadata.fModified.fDateTime)); 6934422610ac22adceeabb66023120f27b96cae953halcanary } 704b6566644f704cf9e30c71fa547c9b5915752792halcanary return dict; 7134422610ac22adceeabb66023120f27b96cae953halcanary} 7234422610ac22adceeabb66023120f27b96cae953halcanary 734b6566644f704cf9e30c71fa547c9b5915752792halcanarySkPDFMetadata::UUID SkPDFMetadata::CreateUUID( 744b6566644f704cf9e30c71fa547c9b5915752792halcanary const SkDocument::PDFMetadata& metadata) { 7534422610ac22adceeabb66023120f27b96cae953halcanary // The main requirement is for the UUID to be unique; the exact 7634422610ac22adceeabb66023120f27b96cae953halcanary // format of the data that will be hashed is not important. 7734422610ac22adceeabb66023120f27b96cae953halcanary SkMD5 md5; 7834422610ac22adceeabb66023120f27b96cae953halcanary const char uuidNamespace[] = "org.skia.pdf\n"; 7934422610ac22adceeabb66023120f27b96cae953halcanary md5.write(uuidNamespace, strlen(uuidNamespace)); 80ec4d4d784dbb250e572f8e04d18d0fd2ebeee851benjaminwagner double msec = SkTime::GetMSecs(); 8134422610ac22adceeabb66023120f27b96cae953halcanary md5.write(&msec, sizeof(msec)); 8234422610ac22adceeabb66023120f27b96cae953halcanary SkTime::DateTime dateTime; 8334422610ac22adceeabb66023120f27b96cae953halcanary SkTime::GetDateTime(&dateTime); 8434422610ac22adceeabb66023120f27b96cae953halcanary md5.write(&dateTime, sizeof(dateTime)); 854b6566644f704cf9e30c71fa547c9b5915752792halcanary if (metadata.fCreation.fEnabled) { 864b6566644f704cf9e30c71fa547c9b5915752792halcanary md5.write(&metadata.fCreation.fDateTime, 874b6566644f704cf9e30c71fa547c9b5915752792halcanary sizeof(metadata.fCreation.fDateTime)); 8834422610ac22adceeabb66023120f27b96cae953halcanary } 894b6566644f704cf9e30c71fa547c9b5915752792halcanary if (metadata.fModified.fEnabled) { 904b6566644f704cf9e30c71fa547c9b5915752792halcanary md5.write(&metadata.fModified.fDateTime, 914b6566644f704cf9e30c71fa547c9b5915752792halcanary sizeof(metadata.fModified.fDateTime)); 9234422610ac22adceeabb66023120f27b96cae953halcanary } 934b6566644f704cf9e30c71fa547c9b5915752792halcanary 944b6566644f704cf9e30c71fa547c9b5915752792halcanary for (const auto keyValuePtr : gMetadataKeys) { 954b6566644f704cf9e30c71fa547c9b5915752792halcanary md5.write(keyValuePtr.key, strlen(keyValuePtr.key)); 9634422610ac22adceeabb66023120f27b96cae953halcanary md5.write("\037", 1); 974b6566644f704cf9e30c71fa547c9b5915752792halcanary const SkString& value = metadata.*(keyValuePtr.valuePtr); 984b6566644f704cf9e30c71fa547c9b5915752792halcanary md5.write(value.c_str(), value.size()); 9934422610ac22adceeabb66023120f27b96cae953halcanary md5.write("\036", 1); 10034422610ac22adceeabb66023120f27b96cae953halcanary } 10134422610ac22adceeabb66023120f27b96cae953halcanary SkMD5::Digest digest; 10234422610ac22adceeabb66023120f27b96cae953halcanary md5.finish(digest); 10334422610ac22adceeabb66023120f27b96cae953halcanary // See RFC 4122, page 6-7. 10434422610ac22adceeabb66023120f27b96cae953halcanary digest.data[6] = (digest.data[6] & 0x0F) | 0x30; 10534422610ac22adceeabb66023120f27b96cae953halcanary digest.data[8] = (digest.data[6] & 0x3F) | 0x80; 10634422610ac22adceeabb66023120f27b96cae953halcanary static_assert(sizeof(digest) == sizeof(UUID), "uuid_size"); 10734422610ac22adceeabb66023120f27b96cae953halcanary SkPDFMetadata::UUID uuid; 10834422610ac22adceeabb66023120f27b96cae953halcanary memcpy(&uuid, &digest, sizeof(digest)); 10934422610ac22adceeabb66023120f27b96cae953halcanary return uuid; 11034422610ac22adceeabb66023120f27b96cae953halcanary} 11134422610ac22adceeabb66023120f27b96cae953halcanary 1124b6566644f704cf9e30c71fa547c9b5915752792halcanarysk_sp<SkPDFObject> SkPDFMetadata::MakePdfId(const UUID& doc, 1134b6566644f704cf9e30c71fa547c9b5915752792halcanary const UUID& instance) { 11434422610ac22adceeabb66023120f27b96cae953halcanary // /ID [ <81b14aafa313db63dbd6f981e49f94f4> 11534422610ac22adceeabb66023120f27b96cae953halcanary // <81b14aafa313db63dbd6f981e49f94f4> ] 116ece83924384b2e9e8cd422324c44797deb99ec90halcanary auto array = sk_make_sp<SkPDFArray>(); 1174b6566644f704cf9e30c71fa547c9b5915752792halcanary static_assert(sizeof(SkPDFMetadata::UUID) == 16, "uuid_size"); 11834422610ac22adceeabb66023120f27b96cae953halcanary array->appendString( 11934422610ac22adceeabb66023120f27b96cae953halcanary SkString(reinterpret_cast<const char*>(&doc), sizeof(UUID))); 12034422610ac22adceeabb66023120f27b96cae953halcanary array->appendString( 12134422610ac22adceeabb66023120f27b96cae953halcanary SkString(reinterpret_cast<const char*>(&instance), sizeof(UUID))); 1224b6566644f704cf9e30c71fa547c9b5915752792halcanary return array; 12334422610ac22adceeabb66023120f27b96cae953halcanary} 12434422610ac22adceeabb66023120f27b96cae953halcanary 125d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary// Convert a block of memory to hexadecimal. Input and output pointers will be 126d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary// moved to end of the range. 127d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canarystatic void hexify(const uint8_t** inputPtr, char** outputPtr, int count) { 128d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary SkASSERT(inputPtr && *inputPtr); 129d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary SkASSERT(outputPtr && *outputPtr); 130d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary while (count-- > 0) { 131d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary uint8_t value = *(*inputPtr)++; 132d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary *(*outputPtr)++ = SkHexadecimalDigits::gLower[value >> 4]; 133d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary *(*outputPtr)++ = SkHexadecimalDigits::gLower[value & 0xF]; 134d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary } 135d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary} 136d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary 13734422610ac22adceeabb66023120f27b96cae953halcanarystatic SkString uuid_to_string(const SkPDFMetadata::UUID& uuid) { 13834422610ac22adceeabb66023120f27b96cae953halcanary // 8-4-4-4-12 13934422610ac22adceeabb66023120f27b96cae953halcanary char buffer[36]; // [32 + 4] 14034422610ac22adceeabb66023120f27b96cae953halcanary char* ptr = buffer; 14134422610ac22adceeabb66023120f27b96cae953halcanary const uint8_t* data = uuid.fData; 142d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary hexify(&data, &ptr, 4); 14334422610ac22adceeabb66023120f27b96cae953halcanary *ptr++ = '-'; 144d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary hexify(&data, &ptr, 2); 14534422610ac22adceeabb66023120f27b96cae953halcanary *ptr++ = '-'; 146d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary hexify(&data, &ptr, 2); 14734422610ac22adceeabb66023120f27b96cae953halcanary *ptr++ = '-'; 148d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary hexify(&data, &ptr, 2); 14934422610ac22adceeabb66023120f27b96cae953halcanary *ptr++ = '-'; 150d6e6e6699a85c6ed79e7d3b01e30d19e361ae049Hal Canary hexify(&data, &ptr, 6); 15134422610ac22adceeabb66023120f27b96cae953halcanary SkASSERT(ptr == buffer + 36); 15234422610ac22adceeabb66023120f27b96cae953halcanary SkASSERT(data == uuid.fData + 16); 15334422610ac22adceeabb66023120f27b96cae953halcanary return SkString(buffer, 36); 15434422610ac22adceeabb66023120f27b96cae953halcanary} 15534422610ac22adceeabb66023120f27b96cae953halcanary 15634422610ac22adceeabb66023120f27b96cae953halcanarynamespace { 15770d1554d8ea08ddb887550f98b9cfd0afd395d65halcanaryclass PDFXMLObject final : public SkPDFObject { 15834422610ac22adceeabb66023120f27b96cae953halcanarypublic: 159221524de3be1fc343ad328c5e99562f32b5cad9cbungeman PDFXMLObject(SkString xml) : fXML(std::move(xml)) {} 16034422610ac22adceeabb66023120f27b96cae953halcanary void emitObject(SkWStream* stream, 161530032a18e373ee673ae96fdbfa1fae6292f8f08halcanary const SkPDFObjNumMap& omap) const override { 16234422610ac22adceeabb66023120f27b96cae953halcanary SkPDFDict dict("Metadata"); 16334422610ac22adceeabb66023120f27b96cae953halcanary dict.insertName("Subtype", "XML"); 16434422610ac22adceeabb66023120f27b96cae953halcanary dict.insertInt("Length", fXML.size()); 165530032a18e373ee673ae96fdbfa1fae6292f8f08halcanary dict.emitObject(stream, omap); 16634422610ac22adceeabb66023120f27b96cae953halcanary static const char streamBegin[] = " stream\n"; 16734422610ac22adceeabb66023120f27b96cae953halcanary stream->write(streamBegin, strlen(streamBegin)); 16834422610ac22adceeabb66023120f27b96cae953halcanary // Do not compress this. The standard requires that a 16934422610ac22adceeabb66023120f27b96cae953halcanary // program that does not understand PDF can grep for 17055325b7c59fe5e8fac809adea7bbec4683d26fabHal Canary // "<?xpacket" and extract the entire XML. 17134422610ac22adceeabb66023120f27b96cae953halcanary stream->write(fXML.c_str(), fXML.size()); 17234422610ac22adceeabb66023120f27b96cae953halcanary static const char streamEnd[] = "\nendstream"; 17334422610ac22adceeabb66023120f27b96cae953halcanary stream->write(streamEnd, strlen(streamEnd)); 17434422610ac22adceeabb66023120f27b96cae953halcanary } 17534422610ac22adceeabb66023120f27b96cae953halcanary 17634422610ac22adceeabb66023120f27b96cae953halcanaryprivate: 17734422610ac22adceeabb66023120f27b96cae953halcanary const SkString fXML; 17834422610ac22adceeabb66023120f27b96cae953halcanary}; 17934422610ac22adceeabb66023120f27b96cae953halcanary} // namespace 18034422610ac22adceeabb66023120f27b96cae953halcanary 18134422610ac22adceeabb66023120f27b96cae953halcanarystatic int count_xml_escape_size(const SkString& input) { 18234422610ac22adceeabb66023120f27b96cae953halcanary int extra = 0; 18334422610ac22adceeabb66023120f27b96cae953halcanary for (size_t i = 0; i < input.size(); ++i) { 18434422610ac22adceeabb66023120f27b96cae953halcanary if (input[i] == '&') { 18534422610ac22adceeabb66023120f27b96cae953halcanary extra += 4; // strlen("&") - strlen("&") 18634422610ac22adceeabb66023120f27b96cae953halcanary } else if (input[i] == '<') { 18734422610ac22adceeabb66023120f27b96cae953halcanary extra += 3; // strlen("<") - strlen("<") 18834422610ac22adceeabb66023120f27b96cae953halcanary } 18934422610ac22adceeabb66023120f27b96cae953halcanary } 19034422610ac22adceeabb66023120f27b96cae953halcanary return extra; 19134422610ac22adceeabb66023120f27b96cae953halcanary} 19234422610ac22adceeabb66023120f27b96cae953halcanary 19334422610ac22adceeabb66023120f27b96cae953halcanaryconst SkString escape_xml(const SkString& input, 19434422610ac22adceeabb66023120f27b96cae953halcanary const char* before = nullptr, 19534422610ac22adceeabb66023120f27b96cae953halcanary const char* after = nullptr) { 19634422610ac22adceeabb66023120f27b96cae953halcanary if (input.size() == 0) { 19734422610ac22adceeabb66023120f27b96cae953halcanary return input; 19834422610ac22adceeabb66023120f27b96cae953halcanary } 19934422610ac22adceeabb66023120f27b96cae953halcanary // "&" --> "&" and "<" --> "<" 20034422610ac22adceeabb66023120f27b96cae953halcanary // text is assumed to be in UTF-8 20134422610ac22adceeabb66023120f27b96cae953halcanary // all strings are xml content, not attribute values. 20234422610ac22adceeabb66023120f27b96cae953halcanary size_t beforeLen = before ? strlen(before) : 0; 20334422610ac22adceeabb66023120f27b96cae953halcanary size_t afterLen = after ? strlen(after) : 0; 20434422610ac22adceeabb66023120f27b96cae953halcanary int extra = count_xml_escape_size(input); 20534422610ac22adceeabb66023120f27b96cae953halcanary SkString output(input.size() + extra + beforeLen + afterLen); 20634422610ac22adceeabb66023120f27b96cae953halcanary char* out = output.writable_str(); 20734422610ac22adceeabb66023120f27b96cae953halcanary if (before) { 20834422610ac22adceeabb66023120f27b96cae953halcanary strncpy(out, before, beforeLen); 20934422610ac22adceeabb66023120f27b96cae953halcanary out += beforeLen; 21034422610ac22adceeabb66023120f27b96cae953halcanary } 21134422610ac22adceeabb66023120f27b96cae953halcanary static const char kAmp[] = "&"; 21234422610ac22adceeabb66023120f27b96cae953halcanary static const char kLt[] = "<"; 21334422610ac22adceeabb66023120f27b96cae953halcanary for (size_t i = 0; i < input.size(); ++i) { 21434422610ac22adceeabb66023120f27b96cae953halcanary if (input[i] == '&') { 21534422610ac22adceeabb66023120f27b96cae953halcanary strncpy(out, kAmp, strlen(kAmp)); 21634422610ac22adceeabb66023120f27b96cae953halcanary out += strlen(kAmp); 21734422610ac22adceeabb66023120f27b96cae953halcanary } else if (input[i] == '<') { 21834422610ac22adceeabb66023120f27b96cae953halcanary strncpy(out, kLt, strlen(kLt)); 21934422610ac22adceeabb66023120f27b96cae953halcanary out += strlen(kLt); 22034422610ac22adceeabb66023120f27b96cae953halcanary } else { 22134422610ac22adceeabb66023120f27b96cae953halcanary *out++ = input[i]; 22234422610ac22adceeabb66023120f27b96cae953halcanary } 22334422610ac22adceeabb66023120f27b96cae953halcanary } 22434422610ac22adceeabb66023120f27b96cae953halcanary if (after) { 22534422610ac22adceeabb66023120f27b96cae953halcanary strncpy(out, after, afterLen); 22634422610ac22adceeabb66023120f27b96cae953halcanary out += afterLen; 22734422610ac22adceeabb66023120f27b96cae953halcanary } 22834422610ac22adceeabb66023120f27b96cae953halcanary // Validate that we haven't written outside of our string. 22934422610ac22adceeabb66023120f27b96cae953halcanary SkASSERT(out == &output.writable_str()[output.size()]); 23034422610ac22adceeabb66023120f27b96cae953halcanary *out = '\0'; 23178daeff1f9c1639d4921abb7acd9b76b77660802halcanary return output; 23234422610ac22adceeabb66023120f27b96cae953halcanary} 23334422610ac22adceeabb66023120f27b96cae953halcanary 2344b6566644f704cf9e30c71fa547c9b5915752792halcanarysk_sp<SkPDFObject> SkPDFMetadata::MakeXMPObject( 2354b6566644f704cf9e30c71fa547c9b5915752792halcanary const SkDocument::PDFMetadata& metadata, 2364b6566644f704cf9e30c71fa547c9b5915752792halcanary const UUID& doc, 2374b6566644f704cf9e30c71fa547c9b5915752792halcanary const UUID& instance) { 23834422610ac22adceeabb66023120f27b96cae953halcanary static const char templateString[] = 23934422610ac22adceeabb66023120f27b96cae953halcanary "<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" 24034422610ac22adceeabb66023120f27b96cae953halcanary "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\"\n" 24134422610ac22adceeabb66023120f27b96cae953halcanary " x:xmptk=\"Adobe XMP Core 5.4-c005 78.147326, " 24234422610ac22adceeabb66023120f27b96cae953halcanary "2012/08/23-13:03:03\">\n" 24334422610ac22adceeabb66023120f27b96cae953halcanary "<rdf:RDF " 24434422610ac22adceeabb66023120f27b96cae953halcanary "xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" 24534422610ac22adceeabb66023120f27b96cae953halcanary "<rdf:Description rdf:about=\"\"\n" 24634422610ac22adceeabb66023120f27b96cae953halcanary " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\"\n" 24734422610ac22adceeabb66023120f27b96cae953halcanary " xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n" 24834422610ac22adceeabb66023120f27b96cae953halcanary " xmlns:xmpMM=\"http://ns.adobe.com/xap/1.0/mm/\"\n" 24934422610ac22adceeabb66023120f27b96cae953halcanary " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\"\n" 25034422610ac22adceeabb66023120f27b96cae953halcanary " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" 25134422610ac22adceeabb66023120f27b96cae953halcanary "<pdfaid:part>2</pdfaid:part>\n" 25234422610ac22adceeabb66023120f27b96cae953halcanary "<pdfaid:conformance>B</pdfaid:conformance>\n" 25334422610ac22adceeabb66023120f27b96cae953halcanary "%s" // ModifyDate 25434422610ac22adceeabb66023120f27b96cae953halcanary "%s" // CreateDate 25534422610ac22adceeabb66023120f27b96cae953halcanary "%s" // xmp:CreatorTool 25634422610ac22adceeabb66023120f27b96cae953halcanary "<dc:format>application/pdf</dc:format>\n" 25734422610ac22adceeabb66023120f27b96cae953halcanary "%s" // dc:title 25834422610ac22adceeabb66023120f27b96cae953halcanary "%s" // dc:description 25934422610ac22adceeabb66023120f27b96cae953halcanary "%s" // author 26034422610ac22adceeabb66023120f27b96cae953halcanary "%s" // keywords 26134422610ac22adceeabb66023120f27b96cae953halcanary "<xmpMM:DocumentID>uuid:%s</xmpMM:DocumentID>\n" 26234422610ac22adceeabb66023120f27b96cae953halcanary "<xmpMM:InstanceID>uuid:%s</xmpMM:InstanceID>\n" 2639f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary "%s" // pdf:Producer 26434422610ac22adceeabb66023120f27b96cae953halcanary "%s" // pdf:Keywords 26534422610ac22adceeabb66023120f27b96cae953halcanary "</rdf:Description>\n" 26634422610ac22adceeabb66023120f27b96cae953halcanary "</rdf:RDF>\n" 26734422610ac22adceeabb66023120f27b96cae953halcanary "</x:xmpmeta>\n" // Note: the standard suggests 4k of padding. 26834422610ac22adceeabb66023120f27b96cae953halcanary "<?xpacket end=\"w\"?>\n"; 26934422610ac22adceeabb66023120f27b96cae953halcanary 27034422610ac22adceeabb66023120f27b96cae953halcanary SkString creationDate; 27134422610ac22adceeabb66023120f27b96cae953halcanary SkString modificationDate; 2724b6566644f704cf9e30c71fa547c9b5915752792halcanary if (metadata.fCreation.fEnabled) { 27334422610ac22adceeabb66023120f27b96cae953halcanary SkString tmp; 2744b6566644f704cf9e30c71fa547c9b5915752792halcanary metadata.fCreation.fDateTime.toISO8601(&tmp); 27534422610ac22adceeabb66023120f27b96cae953halcanary SkASSERT(0 == count_xml_escape_size(tmp)); 27634422610ac22adceeabb66023120f27b96cae953halcanary // YYYY-mm-ddTHH:MM:SS[+|-]ZZ:ZZ; no need to escape 277d51bdae4e145bfede693f97cf0d54a56d33d3c9ehalcanary creationDate = SkStringPrintf("<xmp:CreateDate>%s</xmp:CreateDate>\n", 278d51bdae4e145bfede693f97cf0d54a56d33d3c9ehalcanary tmp.c_str()); 27934422610ac22adceeabb66023120f27b96cae953halcanary } 2804b6566644f704cf9e30c71fa547c9b5915752792halcanary if (metadata.fModified.fEnabled) { 28134422610ac22adceeabb66023120f27b96cae953halcanary SkString tmp; 2824b6566644f704cf9e30c71fa547c9b5915752792halcanary metadata.fModified.fDateTime.toISO8601(&tmp); 28334422610ac22adceeabb66023120f27b96cae953halcanary SkASSERT(0 == count_xml_escape_size(tmp)); 284d51bdae4e145bfede693f97cf0d54a56d33d3c9ehalcanary modificationDate = SkStringPrintf( 28534422610ac22adceeabb66023120f27b96cae953halcanary "<xmp:ModifyDate>%s</xmp:ModifyDate>\n", tmp.c_str()); 28634422610ac22adceeabb66023120f27b96cae953halcanary } 2874b6566644f704cf9e30c71fa547c9b5915752792halcanary SkString title = 2884b6566644f704cf9e30c71fa547c9b5915752792halcanary escape_xml(metadata.fTitle, 2894b6566644f704cf9e30c71fa547c9b5915752792halcanary "<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">", 2904b6566644f704cf9e30c71fa547c9b5915752792halcanary "</rdf:li></rdf:Alt></dc:title>\n"); 2914b6566644f704cf9e30c71fa547c9b5915752792halcanary SkString author = 2924b6566644f704cf9e30c71fa547c9b5915752792halcanary escape_xml(metadata.fAuthor, "<dc:creator><rdf:Bag><rdf:li>", 2934b6566644f704cf9e30c71fa547c9b5915752792halcanary "</rdf:li></rdf:Bag></dc:creator>\n"); 29434422610ac22adceeabb66023120f27b96cae953halcanary // TODO: in theory, XMP can support multiple authors. Split on a delimiter? 29578daeff1f9c1639d4921abb7acd9b76b77660802halcanary SkString subject = escape_xml( 2964b6566644f704cf9e30c71fa547c9b5915752792halcanary metadata.fSubject, 29778daeff1f9c1639d4921abb7acd9b76b77660802halcanary "<dc:description><rdf:Alt><rdf:li xml:lang=\"x-default\">", 29878daeff1f9c1639d4921abb7acd9b76b77660802halcanary "</rdf:li></rdf:Alt></dc:description>\n"); 2994b6566644f704cf9e30c71fa547c9b5915752792halcanary SkString keywords1 = 3004b6566644f704cf9e30c71fa547c9b5915752792halcanary escape_xml(metadata.fKeywords, "<dc:subject><rdf:Bag><rdf:li>", 3014b6566644f704cf9e30c71fa547c9b5915752792halcanary "</rdf:li></rdf:Bag></dc:subject>\n"); 3024b6566644f704cf9e30c71fa547c9b5915752792halcanary SkString keywords2 = escape_xml(metadata.fKeywords, "<pdf:Keywords>", 3034b6566644f704cf9e30c71fa547c9b5915752792halcanary "</pdf:Keywords>\n"); 30434422610ac22adceeabb66023120f27b96cae953halcanary // TODO: in theory, keywords can be a list too. 3059f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary 306492d6b5b06ce586a19e779ad7867e8f2e9d254b1halcanary SkString producer("<pdf:Producer>" SKPDF_PRODUCER "</pdf:Producer>\n"); 3079f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary if (!metadata.fProducer.isEmpty()) { 3089f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary // TODO: register a developer prefix to make 3099f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary // <skia:SKPDF_CUSTOM_PRODUCER_KEY> a real XML tag. 3109f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary producer = escape_xml( 3119f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary metadata.fProducer, "<pdf:Producer>", 3129f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary "</pdf:Producer>\n<!-- <skia:" SKPDF_CUSTOM_PRODUCER_KEY ">" 3139f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary SKPDF_PRODUCER "</skia:" SKPDF_CUSTOM_PRODUCER_KEY "> -->\n"); 3149f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary } 3159f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary 3164b6566644f704cf9e30c71fa547c9b5915752792halcanary SkString creator = escape_xml(metadata.fCreator, "<xmp:CreatorTool>", 31734422610ac22adceeabb66023120f27b96cae953halcanary "</xmp:CreatorTool>\n"); 31834422610ac22adceeabb66023120f27b96cae953halcanary SkString documentID = uuid_to_string(doc); // no need to escape 31934422610ac22adceeabb66023120f27b96cae953halcanary SkASSERT(0 == count_xml_escape_size(documentID)); 32034422610ac22adceeabb66023120f27b96cae953halcanary SkString instanceID = uuid_to_string(instance); 32134422610ac22adceeabb66023120f27b96cae953halcanary SkASSERT(0 == count_xml_escape_size(instanceID)); 3224b6566644f704cf9e30c71fa547c9b5915752792halcanary return sk_make_sp<PDFXMLObject>(SkStringPrintf( 32334422610ac22adceeabb66023120f27b96cae953halcanary templateString, modificationDate.c_str(), creationDate.c_str(), 3244b6566644f704cf9e30c71fa547c9b5915752792halcanary creator.c_str(), title.c_str(), subject.c_str(), author.c_str(), 3254b6566644f704cf9e30c71fa547c9b5915752792halcanary keywords1.c_str(), documentID.c_str(), instanceID.c_str(), 3269f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary producer.c_str(), keywords2.c_str())); 32734422610ac22adceeabb66023120f27b96cae953halcanary} 32834422610ac22adceeabb66023120f27b96cae953halcanary 3299f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary#undef SKPDF_CUSTOM_PRODUCER_KEY 3309f4b332f59a67658106d9aaedc8e75a93e04481dhalcanary#undef SKPDF_PRODUCER 3318cd4a24236ffc26522d0372c0d0ab0d96e301b3bhalcanary#undef SKPDF_STRING 3328cd4a24236ffc26522d0372c0d0ab0d96e301b3bhalcanary#undef SKPDF_STRING_IMPL 333