From 7e96216b58304927fe85537f894f52d07d9d921b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 21 Oct 2012 13:31:07 +0530 Subject: [PATCH] Conversion: Add an option to embed a font family into the book. The embedded font is used as the base font for all text that does not specify its own font family in the input document. Works only with output formats that support font embedding, principally EPUB/AZW3. Option is found under Look & Feel in the conversion dialog --- src/calibre/ebooks/conversion/cli.py | 2 +- src/calibre/ebooks/conversion/plumber.py | 11 + src/calibre/ebooks/oeb/transforms/flatcss.py | 57 +++++- src/calibre/gui2/convert/look_and_feel.py | 1 + src/calibre/gui2/convert/look_and_feel.ui | 204 ++++++++++--------- 5 files changed, 180 insertions(+), 95 deletions(-) diff --git a/src/calibre/ebooks/conversion/cli.py b/src/calibre/ebooks/conversion/cli.py index fb1974f93b..2aa0add3ee 100644 --- a/src/calibre/ebooks/conversion/cli.py +++ b/src/calibre/ebooks/conversion/cli.py @@ -132,7 +132,7 @@ def add_pipeline_options(parser, plumber): _('Options to control the look and feel of the output'), [ 'base_font_size', 'disable_font_rescaling', - 'font_size_mapping', + 'font_size_mapping', 'embed_font_family', 'line_height', 'minimum_line_height', 'linearize_tables', 'extra_css', 'filter_css', diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py index 60cce24121..bfd2e36359 100644 --- a/src/calibre/ebooks/conversion/plumber.py +++ b/src/calibre/ebooks/conversion/plumber.py @@ -193,6 +193,17 @@ OptionRecommendation(name='line_height', ) ), +OptionRecommendation(name='embed_font_family', + recommended_value=None, level=OptionRecommendation.LOW, + help=_( + 'Embed the specified font family into the book. This specifies ' + 'the "base" font used for the book. If the input document ' + 'specifies its own fonts, they may override this base font. ' + 'You can use the filter style information option to remove fonts from the ' + 'input document. Note that font embedding only works ' + 'with some output formats, principally EPUB and AZW3.') + ), + OptionRecommendation(name='linearize_tables', recommended_value=False, level=OptionRecommendation.LOW, help=_('Some badly designed documents use tables to control the ' diff --git a/src/calibre/ebooks/oeb/transforms/flatcss.py b/src/calibre/ebooks/oeb/transforms/flatcss.py index 10b7e259ac..2f2fd727c0 100644 --- a/src/calibre/ebooks/oeb/transforms/flatcss.py +++ b/src/calibre/ebooks/oeb/transforms/flatcss.py @@ -14,9 +14,11 @@ from lxml import etree import cssutils from cssutils.css import Property +from calibre import guess_type from calibre.ebooks.oeb.base import (XHTML, XHTML_NS, CSS_MIME, OEB_STYLES, namespace, barename, XPath) from calibre.ebooks.oeb.stylizer import Stylizer +from calibre.utils.filenames import ascii_filename COLLAPSE = re.compile(r'[ \t\r\n\v]+') STRIPNUM = re.compile(r'[-0-9]+$') @@ -144,11 +146,61 @@ class CSSFlattener(object): cssutils.replaceUrls(item.data, item.abshref, ignoreImportRules=True) + self.body_font_family, self.embed_font_rules = self.get_embed_font_info( + self.opts.embed_font_family) self.stylize_spine() self.sbase = self.baseline_spine() if self.fbase else None self.fmap = FontMapper(self.sbase, self.fbase, self.fkey) self.flatten_spine() + def get_embed_font_info(self, family, failure_critical=True): + efi = [] + body_font_family = None + if not family: + return body_font_family, efi + from calibre.utils.fonts import fontconfig + from calibre.utils.fonts.utils import (get_font_characteristics, + panose_to_css_generic_family, get_font_names) + faces = fontconfig.fonts_for_family(family) + if not faces or not u'normal' in faces: + msg = (u'No embeddable fonts found for family: %r'%self.opts.embed_font_family) + if failure_critical: + raise ValueError(msg) + self.oeb.log.warn(msg) + return body_font_family, efi + + for k, v in faces.iteritems(): + ext, data = v[0::2] + weight, is_italic, is_bold, is_regular, fs_type, panose = \ + get_font_characteristics(data) + generic_family = panose_to_css_generic_family(panose) + family_name, subfamily_name, full_name = get_font_names(data) + if k == u'normal': + body_font_family = u"'%s',%s"%(family_name, generic_family) + if family_name.lower() != family.lower(): + self.oeb.log.warn(u'Failed to find an exact match for font:' + u' %r, using %r instead'%(family, family_name)) + else: + self.oeb.log(u'Embedding font: %s'%family_name) + font = {u'font-family':u'"%s"'%family_name} + if is_italic: + font[u'font-style'] = u'italic' + if is_bold: + font[u'font-weight'] = u'bold' + fid, href = self.oeb.manifest.generate(id=u'font', + href=u'%s.%s'%(ascii_filename(full_name).replace(u' ', u'-'), ext)) + item = self.oeb.manifest.add(fid, href, + guess_type(full_name+'.'+ext)[0], + data=data) + item.unload_data_from_memory() + font[u'src'] = u'url(%s)'%item.href + rule = '@font-face { %s }'%('; '.join(u'%s:%s'%(k, v) for k, v in + font.iteritems())) + rule = cssutils.parseString(rule) + efi.append(rule) + + return body_font_family, efi + def stylize_spine(self): self.stylizers = {} profile = self.context.source @@ -170,6 +222,8 @@ class CSSFlattener(object): bs.extend(['page-break-before: always']) if self.context.change_justification != 'original': bs.append('text-align: '+ self.context.change_justification) + if self.body_font_family: + bs.append(u'font-family: '+self.body_font_family) body.set('style', '; '.join(bs)) stylizer = Stylizer(html, item.href, self.oeb, self.context, profile, user_css=self.context.extra_css, @@ -450,7 +504,8 @@ class CSSFlattener(object): items.sort() css = ';\n'.join("%s: %s" % (key, val) for key, val in items) css = ('@page {\n%s\n}\n'%css) if items else '' - rules = [r.cssText for r in stylizer.font_face_rules] + rules = [r.cssText for r in stylizer.font_face_rules + + self.embed_font_rules] raw = '\n\n'.join(rules) css += '\n\n' + raw global_css[css].append(item) diff --git a/src/calibre/gui2/convert/look_and_feel.py b/src/calibre/gui2/convert/look_and_feel.py index ad604ec4e3..1609a0add3 100644 --- a/src/calibre/gui2/convert/look_and_feel.py +++ b/src/calibre/gui2/convert/look_and_feel.py @@ -32,6 +32,7 @@ class LookAndFeelWidget(Widget, Ui_Form): Widget.__init__(self, parent, ['change_justification', 'extra_css', 'base_font_size', 'font_size_mapping', 'line_height', 'minimum_line_height', + 'embed_font_family', 'smarten_punctuation', 'unsmarten_punctuation', 'disable_font_rescaling', 'insert_blank_line', 'remove_paragraph_spacing', diff --git a/src/calibre/gui2/convert/look_and_feel.ui b/src/calibre/gui2/convert/look_and_feel.ui index d6ecb1b61a..039625bc25 100644 --- a/src/calibre/gui2/convert/look_and_feel.ui +++ b/src/calibre/gui2/convert/look_and_feel.ui @@ -14,6 +14,26 @@ Form + + + + pt + + + 1 + + + + + + + Line &height: + + + opt_line_height + + + @@ -37,26 +57,6 @@ - - - - Line &height: - - - opt_line_height - - - - - - - pt - - - 1 - - - @@ -140,14 +140,14 @@ - + Remove &spacing between paragraphs - + &Indent size: @@ -160,7 +160,7 @@ - + <p>When calibre removes inter paragraph spacing, it automatically sets a paragraph indent, to ensure that paragraphs can be easily distinguished. This option controls the width of that indent. @@ -182,72 +182,7 @@ - - - - Insert &blank line between paragraphs - - - - - - - em - - - 1 - - - - - - - Text &justification: - - - opt_change_justification - - - - - - - - - - Smarten &punctuation - - - - - - - &Transliterate unicode characters to ASCII - - - - - - - &UnSmarten punctuation - - - - - - - Keep &ligatures - - - - - - - &Linearize tables - - - - + 0 @@ -365,10 +300,68 @@ - - + + - &Disable font size rescaling + Insert &blank line between paragraphs + + + + + + + em + + + 1 + + + + + + + Text &justification: + + + opt_change_justification + + + + + + + + + + Smarten &punctuation + + + + + + + &Transliterate unicode characters to ASCII + + + + + + + &UnSmarten punctuation + + + + + + + Keep &ligatures + + + + + + + &Linearize tables @@ -382,7 +375,7 @@ - + &Line size: @@ -395,6 +388,26 @@ + + + + &Embed font family: + + + opt_embed_font_family + + + + + + + &Disable font size rescaling + + + + + + @@ -403,6 +416,11 @@ QComboBox
widgets.h
+ + FontFamilyChooser + QComboBox +
calibre/gui2/font_family_chooser.h
+