PDF Output: Add support for font-family aliasing in @font-face rules. Fixes #1395176 [truetype fonts with formatting not translating from EPUB to PDF](https://bugs.launchpad.net/calibre/+bug/1395176)

This commit is contained in:
Kovid Goyal 2014-11-22 08:56:11 +05:30
parent e3d95929ac
commit b2d36cfcf0

View File

@ -157,29 +157,26 @@ class PDFOutput(OutputFormatPlugin):
self.cover_data = item.data self.cover_data = item.data
def handle_embedded_fonts(self): def handle_embedded_fonts(self):
''' ''' On windows, Qt uses GDI which does not support OpenType
Because of QtWebKit's inability to handle embedded fonts correctly, we (CFF) fonts, so we need to nuke references to OpenType
remove the embedded fonts and make them available system wide instead. fonts. Qt's directwrite text backend is not mature.
If you ever move to Qt WebKit 2.3+ then this will be unnecessary. Also make sure all fonts are embeddable. '''
'''
from calibre.ebooks.oeb.base import urlnormalize from calibre.ebooks.oeb.base import urlnormalize
from calibre.utils.fonts.utils import remove_embed_restriction from calibre.utils.fonts.utils import remove_embed_restriction
from PyQt5.Qt import QFontDatabase, QByteArray, QRawFont, QFont from PyQt5.Qt import QByteArray, QRawFont
# First find all @font-face rules and remove them, adding the embedded font_warnings = set()
# fonts to Qt processed = set()
family_map = {} is_cff = {}
for item in list(self.oeb.manifest): for item in list(self.oeb.manifest):
if not hasattr(item.data, 'cssRules'): if not hasattr(item.data, 'cssRules'):
continue continue
remove = set() remove = set()
for i, rule in enumerate(item.data.cssRules): for i, rule in enumerate(item.data.cssRules):
if rule.type == rule.FONT_FACE_RULE: if rule.type == rule.FONT_FACE_RULE:
remove.add(i)
try: try:
s = rule.style s = rule.style
src = s.getProperty('src').propertyValue[0].uri src = s.getProperty('src').propertyValue[0].uri
font_family = s.getProperty('font-family').propertyValue[0].value
except: except:
continue continue
path = item.abshref(src) path = item.abshref(src)
@ -187,62 +184,29 @@ class PDFOutput(OutputFormatPlugin):
if ff is None: if ff is None:
continue continue
raw = ff.data raw = nraw = ff.data
self.oeb.manifest.remove(ff) if path not in processed:
processed.add(path)
try: try:
raw = remove_embed_restriction(raw) nraw = remove_embed_restriction(raw)
except: except:
continue continue
fid = QFontDatabase.addApplicationFontFromData(QByteArray(raw)) if nraw != raw:
family_name = None ff.data = nraw
if fid > -1: self.oeb.container.write(path, nraw)
try:
family_name = unicode(QFontDatabase.applicationFontFamilies(fid)[0])
except (IndexError, KeyError):
pass
if family_name:
family_map[icu_lower(font_family)] = family_name
if iswindows:
if path not in is_cff:
f = QRawFont(QByteArray(nraw), 12)
is_cff[path] = f.isValid() and len(f.fontTable('head')) == 0
if is_cff[path]:
if path not in font_warnings:
font_warnings.add(path)
self.log.warn('CFF OpenType fonts are not supported on windows, ignoring: %s' % path)
remove.add(i)
for i in sorted(remove, reverse=True): for i in sorted(remove, reverse=True):
item.data.cssRules.pop(i) item.data.cssRules.pop(i)
# Now map the font family name specified in the css to the actual
# family name of the embedded font (they may be different in general).
font_warnings = set()
for item in self.oeb.manifest:
if not hasattr(item.data, 'cssRules'):
continue
for i, rule in enumerate(item.data.cssRules):
if rule.type != rule.STYLE_RULE:
continue
ff = rule.style.getProperty('font-family')
if ff is None:
continue
val = ff.propertyValue
for i in xrange(val.length):
try:
k = icu_lower(val[i].value)
except (AttributeError, TypeError):
val[i].value = k = 'times'
if k in family_map:
val[i].value = family_map[k]
if iswindows:
# On windows, Qt uses GDI which does not support OpenType
# (CFF) fonts, so we need to nuke references to OpenType
# fonts. Note that you could compile QT with configure
# -directwrite, but that requires atleast Vista SP2
for i in xrange(val.length):
family = val[i].value
if family:
f = QRawFont.fromFont(QFont(family))
if len(f.fontTable('head')) == 0:
if family not in font_warnings:
self.log.warn('Ignoring unsupported font: %s'
%family)
font_warnings.add(family)
# Either a bitmap or (more likely) a CFF font
val[i].value = 'times'
def convert_text(self, oeb_book): def convert_text(self, oeb_book):
from calibre.ebooks.metadata.opf2 import OPF from calibre.ebooks.metadata.opf2 import OPF
if self.opts.old_pdf_engine: if self.opts.old_pdf_engine: