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

Unified Diff: src/doc/SkDocument_PDF.cpp

Issue 1359943003: SkPDF: add basic metadata support (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: style change Created 5 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « include/core/SkTArray.h ('k') | tests/PDFMetadataAttributeTest.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/doc/SkDocument_PDF.cpp
diff --git a/src/doc/SkDocument_PDF.cpp b/src/doc/SkDocument_PDF.cpp
index 4ea9d89dd7d83cc938617e1d892c420f76eead27..fb22f18ec5da5af7807935476b07f29783ef4651 100644
--- a/src/doc/SkDocument_PDF.cpp
+++ b/src/doc/SkDocument_PDF.cpp
@@ -11,8 +11,11 @@
#include "SkPDFFont.h"
#include "SkPDFStream.h"
#include "SkPDFTypes.h"
+#include "SkPDFUtils.h"
#include "SkStream.h"
+class SkPDFDict;
+
static void emit_pdf_header(SkWStream* stream) {
stream->writeText("%PDF-1.4\n%");
// The PDF spec recommends including a comment with four bytes, all
@@ -26,12 +29,15 @@ static void emit_pdf_footer(SkWStream* stream,
const SkPDFSubstituteMap& substitutes,
SkPDFObject* docCatalog,
int64_t objCount,
- int32_t xRefFileOffset) {
+ int32_t xRefFileOffset,
+ SkPDFDict* info) {
SkPDFDict trailerDict;
// TODO(vandebo): Linearized format will take a Prev entry too.
// TODO(vandebo): PDF/A requires an ID entry.
trailerDict.insertInt("Size", int(objCount));
trailerDict.insertObjRef("Root", SkRef(docCatalog));
+ SkASSERT(info);
+ trailerDict.insertObjRef("Info", SkRef(info));
stream->writeText("trailer\n");
trailerDict.emitObject(stream, objNumMap, substitutes);
@@ -156,7 +162,49 @@ 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,
SkWStream* stream) {
if (pageDevices.isEmpty()) {
return false;
@@ -198,7 +246,12 @@ static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices,
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);
}
@@ -233,7 +286,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);
+ xRefFileOffset, infoDict);
// The page tree has both child and parent pointers, so it creates a
// reference cycle. We must clear that cycle to properly reclaim memory.
@@ -284,6 +337,8 @@ void GetCountOfFontTypes(
}
}
#endif
+
+template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullptr; }
////////////////////////////////////////////////////////////////////////////////
namespace {
@@ -325,7 +380,7 @@ protected:
bool onClose(SkWStream* stream) override {
SkASSERT(!fCanvas.get());
- bool success = emit_pdf_document(fPageDevices, stream);
+ bool success = emit_pdf_document(fPageDevices, fMetadata, stream);
fPageDevices.unrefAll();
fCanon.reset();
return success;
@@ -336,11 +391,20 @@ protected:
fCanon.reset();
}
+ void setMetadata(const SkTArray<SkDocument::Attribute>& info,
+ const SkTime::DateTime* creationDate,
+ const SkTime::DateTime* modifiedDate) override {
+ fMetadata.fInfo = info;
+ fMetadata.fCreation.reset(clone(creationDate));
+ fMetadata.fModified.reset(clone(modifiedDate));
+ }
+
private:
SkPDFCanon fCanon;
SkTDArray<const SkPDFDevice*> fPageDevices;
SkAutoTUnref<SkCanvas> fCanvas;
SkScalar fRasterDpi;
+ Metadata fMetadata;
};
} // namespace
///////////////////////////////////////////////////////////////////////////////
« no previous file with comments | « include/core/SkTArray.h ('k') | tests/PDFMetadataAttributeTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698