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
+
+ FontFamilyChooser
+ QComboBox
+ calibre/gui2/font_family_chooser.h
+