From bd9bb1bad83dc0ff16536205f2254fdf63610219 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 13 Jun 2013 10:12:57 +0530 Subject: [PATCH] DOCX: Add support for theme fonts --- src/calibre/ebooks/docx/char_styles.py | 8 +++++-- src/calibre/ebooks/docx/names.py | 1 + src/calibre/ebooks/docx/styles.py | 7 +++--- src/calibre/ebooks/docx/theme.py | 31 ++++++++++++++++++++++++++ src/calibre/ebooks/docx/to_html.py | 15 +++++++++++-- 5 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 src/calibre/ebooks/docx/theme.py diff --git a/src/calibre/ebooks/docx/char_styles.py b/src/calibre/ebooks/docx/char_styles.py index 512999afaf..02b8299c94 100644 --- a/src/calibre/ebooks/docx/char_styles.py +++ b/src/calibre/ebooks/docx/char_styles.py @@ -117,8 +117,12 @@ def read_vert_align(parent, dest): def read_font_family(parent, dest): ans = inherit - for col in XPath('./w:rFonts[@w:ascii]')(parent): - val = get(col, 'w:ascii') + for col in XPath('./w:rFonts')(parent): + val = get(col, 'w:asciiTheme') + if val: + val = '|%s|' % val + else: + val = get(col, 'w:ascii') if val: ans = val setattr(dest, 'font_family', ans) diff --git a/src/calibre/ebooks/docx/names.py b/src/calibre/ebooks/docx/names.py index 77ffb6d294..ed9b9ea51f 100644 --- a/src/calibre/ebooks/docx/names.py +++ b/src/calibre/ebooks/docx/names.py @@ -22,6 +22,7 @@ IMAGES = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships LINKS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink' FOOTNOTES = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes' ENDNOTES = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes' +THEMES = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme' namespaces = { 'mo': 'http://schemas.microsoft.com/office/mac/office/2008/main', diff --git a/src/calibre/ebooks/docx/styles.py b/src/calibre/ebooks/docx/styles.py index 2a97a6cd7e..8e4d811803 100644 --- a/src/calibre/ebooks/docx/styles.py +++ b/src/calibre/ebooks/docx/styles.py @@ -142,8 +142,8 @@ class Styles(object): def get(self, key, default=None): return self.id_map.get(key, default) - def __call__(self, root, fonts): - self.fonts = fonts + def __call__(self, root, fonts, theme): + self.fonts, self.theme = fonts, theme for s in XPath('//w:style')(root): s = Style(s) if s.style_id: @@ -304,7 +304,8 @@ class Styles(object): setattr(ans, attr, self.run_val(parent_styles, direct_formatting, attr)) if ans.font_family is not inherit: - ans.font_family = self.fonts.family_for(ans.font_family, ans.b, ans.i) + ff = self.theme.resolve_font_family(ans.font_family) + ans.font_family = self.fonts.family_for(ff, ans.b, ans.i) return ans diff --git a/src/calibre/ebooks/docx/theme.py b/src/calibre/ebooks/docx/theme.py new file mode 100644 index 0000000000..e4a75f629c --- /dev/null +++ b/src/calibre/ebooks/docx/theme.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2013, Kovid Goyal ' + +from calibre.ebooks.docx.names import XPath + + +class Theme(object): + + def __init__(self): + self.major_latin_font = 'Cambria' + self.minor_latin_font = 'Calibri' + + def __call__(self, root): + for fs in XPath('//a:fontScheme')(root): + for mj in XPath('./a:majorFont')(fs): + for l in XPath('./a:latin[@typeface]')(mj): + self.major_latin_font = l.get('typeface') + for mj in XPath('./a:minorFont')(fs): + for l in XPath('./a:latin[@typeface]')(mj): + self.minor_latin_font = l.get('typeface') + + def resolve_font_family(self, ff): + if ff.startswith('|'): + ff = ff[1:-1] + ff = self.major_latin_font if ff.startswith('major') else self.minor_latin_font + return ff diff --git a/src/calibre/ebooks/docx/to_html.py b/src/calibre/ebooks/docx/to_html.py index 4593d9c75a..2e57914e55 100644 --- a/src/calibre/ebooks/docx/to_html.py +++ b/src/calibre/ebooks/docx/to_html.py @@ -16,7 +16,7 @@ from lxml.html.builder import ( from calibre.ebooks.docx.container import DOCX, fromstring from calibre.ebooks.docx.names import ( XPath, is_tag, XML, STYLES, NUMBERING, FONTS, get, generate_anchor, - descendants, FOOTNOTES, ENDNOTES, children) + descendants, FOOTNOTES, ENDNOTES, children, THEMES) from calibre.ebooks.docx.styles import Styles, inherit, PageProperties from calibre.ebooks.docx.numbering import Numbering from calibre.ebooks.docx.fonts import Fonts @@ -24,6 +24,7 @@ from calibre.ebooks.docx.images import Images from calibre.ebooks.docx.tables import Tables from calibre.ebooks.docx.footnotes import Footnotes from calibre.ebooks.docx.cleanup import cleanup_markup +from calibre.ebooks.docx.theme import Theme from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.metadata.toc import TOC from calibre.ebooks.oeb.polish.toc import elem_to_toc_text @@ -49,6 +50,7 @@ class Convert(object): self.dest_dir = dest_dir or os.getcwdu() self.mi = self.docx.metadata self.body = BODY() + self.theme = Theme() self.tables = Tables() self.styles = Styles(self.tables) self.images = Images() @@ -203,6 +205,7 @@ class Convert(object): nname = get_name(NUMBERING, 'numbering.xml') sname = get_name(STYLES, 'styles.xml') fname = get_name(FONTS, 'fontTable.xml') + tname = get_name(THEMES, 'theme1.xml') foname = get_name(FOOTNOTES, 'footnotes.xml') enname = get_name(ENDNOTES, 'endnotes.xml') numbering = self.numbering = Numbering() @@ -231,13 +234,21 @@ class Convert(object): else: fonts(fromstring(raw), embed_relationships, self.docx, self.dest_dir) + if tname is not None: + try: + raw = self.docx.read(tname) + except KeyError: + self.log.warn('Styles %s do not exist' % sname) + else: + self.theme(fromstring(raw)) + if sname is not None: try: raw = self.docx.read(sname) except KeyError: self.log.warn('Styles %s do not exist' % sname) else: - self.styles(fromstring(raw), fonts) + self.styles(fromstring(raw), fonts, self.theme) if nname is not None: try: