From 8b804f8811bef297c1ca9ac15232f0558d62a58f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 17 Feb 2014 21:50:30 +0530 Subject: [PATCH] PDF Output: Write XMP metadata when creating PDF files --- .../ebooks/conversion/plugins/pdf_output.py | 27 ++++++++++++------- src/calibre/ebooks/pdf/render/from_html.py | 2 +- src/calibre/ebooks/pdf/render/serialize.py | 18 +++++++++++-- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/calibre/ebooks/conversion/plugins/pdf_output.py b/src/calibre/ebooks/conversion/plugins/pdf_output.py index e270239b30..2779a29861 100644 --- a/src/calibre/ebooks/conversion/plugins/pdf_output.py +++ b/src/calibre/ebooks/conversion/plugins/pdf_output.py @@ -22,20 +22,21 @@ PAPER_SIZES = [u'a0', u'a1', u'a2', u'a3', u'a4', u'a5', u'a6', u'b0', u'b1', u'b2', u'b3', u'b4', u'b5', u'b6', u'legal', u'letter'] class PDFMetadata(object): # {{{ - def __init__(self, oeb_metadata=None): + def __init__(self, mi=None): from calibre import force_unicode from calibre.ebooks.metadata import authors_to_string self.title = _(u'Unknown') self.author = _(u'Unknown') self.tags = u'' + self.mi = mi - if oeb_metadata is not None: - if len(oeb_metadata.title) >= 1: - self.title = oeb_metadata.title[0].value - if len(oeb_metadata.creator) >= 1: - self.author = authors_to_string([x.value for x in oeb_metadata.creator]) - if oeb_metadata.subject: - self.tags = u', '.join(map(unicode, oeb_metadata.subject)) + if mi is not None: + if mi.title: + self.title = mi.title + if mi.authors: + self.author = authors_to_string(mi.authors) + if mi.tags: + self.tags = u', '.join(mi.tags) self.title = force_unicode(self.title) self.author = force_unicode(self.author) @@ -125,7 +126,15 @@ class PDFOutput(OutputFormatPlugin): self.oeb = oeb_book self.input_plugin, self.opts, self.log = input_plugin, opts, log self.output_path = output_path - self.metadata = oeb_book.metadata + from calibre.ebooks.oeb.base import OPF, OPF2_NS + from lxml import etree + from io import BytesIO + package = etree.Element(OPF('package'), + attrib={'version': '2.0', 'unique-identifier': 'dummy'}, + nsmap={None: OPF2_NS}) + from calibre.ebooks.metadata.opf2 import OPF + self.oeb.metadata.to_opf2(package) + self.metadata = OPF(BytesIO(etree.tostring(package))).to_book_metadata() self.cover_data = None if input_plugin.is_image_collection: diff --git a/src/calibre/ebooks/pdf/render/from_html.py b/src/calibre/ebooks/pdf/render/from_html.py index aaa4fbd5a2..d6367223db 100644 --- a/src/calibre/ebooks/pdf/render/from_html.py +++ b/src/calibre/ebooks/pdf/render/from_html.py @@ -209,7 +209,7 @@ class PDFWriter(QObject): self.painter = QPainter(self.doc) self.doc.set_metadata(title=pdf_metadata.title, author=pdf_metadata.author, - tags=pdf_metadata.tags) + tags=pdf_metadata.tags, mi=pdf_metadata.mi) self.doc_title = pdf_metadata.title self.doc_author = pdf_metadata.author self.painter.save() diff --git a/src/calibre/ebooks/pdf/render/serialize.py b/src/calibre/ebooks/pdf/render/serialize.py index e38944820a..90dad86ac0 100644 --- a/src/calibre/ebooks/pdf/render/serialize.py +++ b/src/calibre/ebooks/pdf/render/serialize.py @@ -20,7 +20,7 @@ from calibre.ebooks.pdf.render.fonts import FontManager from calibre.ebooks.pdf.render.links import Links from calibre.utils.date import utcnow -PDFVER = b'%PDF-1.3' +PDFVER = b'%PDF-1.4' # 1.4 is needed for XMP metadata class IndirectObjects(object): @@ -239,6 +239,17 @@ class Image(Stream): if self.soft_mask is not None: d['SMask'] = self.soft_mask +class Metadata(Stream): + + def __init__(self, mi): + Stream.__init__(self) + from calibre.ebooks.metadata.xmp import metadata_to_xmp_packet + self.write(metadata_to_xmp_packet(mi)) + + def add_extra_keys(self, d): + d['Type'] = Name('Metadata') + d['Subtype'] = Name('XML') + class PDFStream(object): PATH_OPS = { @@ -292,13 +303,16 @@ class PDFStream(object): def get_pageref(self, pagenum): return self.page_tree.obj.get_ref(pagenum) - def set_metadata(self, title=None, author=None, tags=None): + def set_metadata(self, title=None, author=None, tags=None, mi=None): if title: self.info['Title'] = String(title) if author: self.info['Author'] = String(author) if tags: self.info['Keywords'] = String(tags) + if mi is not None: + self.metadata = self.objects.add(Metadata(mi)) + self.catalog.obj['Metadata'] = self.metadata def write_line(self, byts=b''): byts = byts if isinstance(byts, bytes) else byts.encode('ascii')