From e07bd9b022415d89974aa4bb2cc64d41d644fd6c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 26 Mar 2015 15:19:13 +0530 Subject: [PATCH] DOCX Output: Plumbing for font embedding --- src/calibre/ebooks/docx/writer/container.py | 8 ++++++- src/calibre/ebooks/docx/writer/fonts.py | 26 +++++++++++++++++++++ src/calibre/ebooks/docx/writer/from_html.py | 3 +++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/calibre/ebooks/docx/writer/fonts.py diff --git a/src/calibre/ebooks/docx/writer/container.py b/src/calibre/ebooks/docx/writer/container.py index de7a802d05..aab5350714 100644 --- a/src/calibre/ebooks/docx/writer/container.py +++ b/src/calibre/ebooks/docx/writer/container.py @@ -14,7 +14,7 @@ from lxml.builder import ElementMaker from calibre import guess_type from calibre.constants import numeric_version, __appname__ -from calibre.ebooks.docx.names import namespaces, STYLES, WEB_SETTINGS, IMAGES +from calibre.ebooks.docx.names import namespaces, STYLES, WEB_SETTINGS, IMAGES, FONTS from calibre.ebooks.metadata import authors_to_string from calibre.ebooks.metadata.opf2 import OPF as ReadOPF from calibre.ebooks.oeb.base import OPF, OPF2_NS @@ -54,6 +54,7 @@ class DocumentRelationships(object): for typ, target in { STYLES: 'styles.xml', WEB_SETTINGS: 'webSettings.xml', + FONTS: 'fontTable.xml', }.iteritems(): self.add_relationship(target, typ) @@ -85,6 +86,9 @@ class DOCX(object): def __init__(self, opts, log): self.opts, self.log = opts, log self.document_relationships = DocumentRelationships() + self.font_table = etree.Element('{%s}fonts' % namespaces['w'], nsmap={k:namespaces[k] for k in 'wr'}) + E = ElementMaker(namespace=namespaces['pr'], nsmap={None:namespaces['pr']}) + self.embedded_fonts = E.Relationships() # Boilerplate {{{ @property @@ -181,7 +185,9 @@ class DOCX(object): zf.writestr('word/webSettings.xml', self.websettings) zf.writestr('word/document.xml', xml2str(self.document)) zf.writestr('word/styles.xml', xml2str(self.styles)) + zf.writestr('word/fontTable.xml', xml2str(self.font_table)) zf.writestr('word/_rels/document.xml.rels', self.document_relationships.serialize()) + zf.writestr('word/_rels/fontTable.xml.rels', xml2str(self.embedded_fonts)) for fname, data_getter in self.images.iteritems(): zf.writestr(fname, data_getter()) diff --git a/src/calibre/ebooks/docx/writer/fonts.py b/src/calibre/ebooks/docx/writer/fonts.py new file mode 100644 index 0000000000..470fe2a535 --- /dev/null +++ b/src/calibre/ebooks/docx/writer/fonts.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python2 +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2015, Kovid Goyal ' + +from calibre.ebooks.docx.names import makeelement + +class FontsManager(object): + + def __init__(self, oeb): + self.oeb, self.log = oeb, oeb.log + + def serialize(self, text_styles, fonts, embed_relationships): + font_families, seen = set(), set() + for ts in text_styles: + if ts.font_family: + lf = ts.font_family.lower() + if lf not in seen: + seen.add(lf) + font_families.add(ts.font_family) + for family in sorted(font_families): + makeelement(fonts, 'w:font', w_name=family) + diff --git a/src/calibre/ebooks/docx/writer/from_html.py b/src/calibre/ebooks/docx/writer/from_html.py index ee81df8835..928c31ed40 100644 --- a/src/calibre/ebooks/docx/writer/from_html.py +++ b/src/calibre/ebooks/docx/writer/from_html.py @@ -14,6 +14,7 @@ from lxml.builder import ElementMaker from calibre.ebooks.docx.names import namespaces from calibre.ebooks.docx.writer.styles import w, StylesManager from calibre.ebooks.docx.writer.images import ImagesManager +from calibre.ebooks.docx.writer.fonts import FontsManager from calibre.ebooks.oeb.stylizer import Stylizer as Sz, Style as St from calibre.ebooks.oeb.base import XPath, barename from calibre.ebooks.pdf.render.common import PAPER_SIZES @@ -170,6 +171,7 @@ class Convert(object): self.styles_manager = StylesManager() self.images_manager = ImagesManager(self.oeb, self.docx.document_relationships) + self.fonts_manager = FontsManager(self.oeb) for item in self.oeb.spine: self.process_item(item) @@ -296,3 +298,4 @@ class Convert(object): self.docx.images = {} self.styles_manager.serialize(self.docx.styles) self.images_manager.serialize(self.docx.images) + self.fonts_manager.serialize(self.styles_manager.text_styles, self.docx.font_table, self.docx.embedded_fonts)