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
+