mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 02:34:06 -04:00
Edit Book: When embedding fonts fails to find a matching font, give a more informative message that explains why matching failed.
This commit is contained in:
parent
94ca8a615b
commit
933f0f55d2
@ -37,6 +37,22 @@ def matching_rule(font, rules):
|
||||
return rule
|
||||
|
||||
|
||||
def format_failed_match_report(fonts, font_family, font, report):
|
||||
msg = _('Failed to find a font in the "%s" family matching the CSS font specification:') % font_family
|
||||
msg += '\n\n* font-weight: %s' % font['font-weight']
|
||||
msg += '\n* font-style: %s' % font['font-style']
|
||||
msg += '\n* font-stretch: %s' % font['font-stretch']
|
||||
msg += '\n\n' + _('Available fonts in the family are:') + '\n'
|
||||
for f in fonts:
|
||||
msg += '\n' + f['path']
|
||||
msg += '\n\n* font-weight: %s' % f.get('font-weight', '400').strip()
|
||||
msg += '\n* font-style: %s' % f.get('font-style', 'normal').strip()
|
||||
msg += '\n* font-stretch: %s' % f.get('font-stretch', 'normal').strip()
|
||||
msg += '\n\n'
|
||||
report(msg)
|
||||
report('')
|
||||
|
||||
|
||||
def embed_font(container, font, all_font_rules, report, warned):
|
||||
rule = matching_rule(font, all_font_rules)
|
||||
ff = font['font-family']
|
||||
@ -49,9 +65,12 @@ def embed_font(container, font, all_font_rules, report, warned):
|
||||
try:
|
||||
fonts = font_scanner.fonts_for_family(ff)
|
||||
except NoFonts:
|
||||
report(_('Failed to find fonts for family: %s, not embedding') % ff)
|
||||
warned.add(ff)
|
||||
return
|
||||
try:
|
||||
fonts = font_scanner.alt_fonts_for_family(ff)
|
||||
except NoFonts:
|
||||
report(_('Failed to find fonts for family: %s, not embedding') % ff)
|
||||
warned.add(ff)
|
||||
return
|
||||
wt = int(font.get('font-weight', '400'))
|
||||
for f in fonts:
|
||||
if f['weight'] == wt and f['font-style'] == font.get('font-style', 'normal') and f['font-stretch'] == font.get('font-stretch', 'normal'):
|
||||
@ -69,11 +88,10 @@ def embed_font(container, font, all_font_rules, report, warned):
|
||||
rule['src'] = 'url(%s)' % href
|
||||
rule['name'] = name
|
||||
return rule
|
||||
msg = _('Failed to find font matching: family: %(family)s; weight: %(weight)s; style: %(style)s; stretch: %(stretch)s') % dict(
|
||||
family=ff, weight=font['font-weight'], style=font['font-style'], stretch=font['font-stretch'])
|
||||
if msg not in warned:
|
||||
warned.add(msg)
|
||||
report(msg)
|
||||
wkey = ('spec-mismatch-', ff, wt, font['font-style'], font['font-stretch'])
|
||||
if wkey not in warned:
|
||||
warned.add(wkey)
|
||||
format_failed_match_report(fonts, ff, font, report)
|
||||
else:
|
||||
name = rule['src']
|
||||
href = container.name_to_href(name)
|
||||
@ -159,4 +177,3 @@ if __name__ == '__main__':
|
||||
prints(msg)
|
||||
print()
|
||||
prints('Output written to:', outbook)
|
||||
|
||||
|
@ -21,6 +21,8 @@ from calibre.utils.icu import sort_key
|
||||
class NoFonts(ValueError):
|
||||
pass
|
||||
|
||||
# Font dirs {{{
|
||||
|
||||
|
||||
def default_font_dirs():
|
||||
return [
|
||||
@ -115,6 +117,77 @@ def font_dirs():
|
||||
os.path.expanduser('~/Library/Fonts'),
|
||||
]
|
||||
return fc_list()
|
||||
# }}}
|
||||
|
||||
# Build font family maps {{{
|
||||
|
||||
|
||||
def font_priority(font):
|
||||
'''
|
||||
Try to ensure that the "Regular" face is the first font for a given
|
||||
family.
|
||||
'''
|
||||
style_normal = font['font-style'] == 'normal'
|
||||
width_normal = font['font-stretch'] == 'normal'
|
||||
weight_normal = font['font-weight'] == 'normal'
|
||||
num_normal = sum(filter(None, (style_normal, width_normal,
|
||||
weight_normal)))
|
||||
subfamily_name = (font['wws_subfamily_name'] or
|
||||
font['preferred_subfamily_name'] or font['subfamily_name'])
|
||||
if num_normal == 3 and subfamily_name == 'Regular':
|
||||
return 0
|
||||
if num_normal == 3:
|
||||
return 1
|
||||
if subfamily_name == 'Regular':
|
||||
return 2
|
||||
return 3 + (3 - num_normal)
|
||||
|
||||
|
||||
def path_significance(path, folders):
|
||||
path = os.path.normcase(os.path.abspath(path))
|
||||
for i, q in enumerate(folders):
|
||||
if path.startswith(q):
|
||||
return i
|
||||
return -1
|
||||
|
||||
|
||||
def build_families(cached_fonts, folders, family_attr='font-family'):
|
||||
families = defaultdict(list)
|
||||
for f in cached_fonts.itervalues():
|
||||
if not f:
|
||||
continue
|
||||
lf = icu_lower(f.get(family_attr) or '')
|
||||
if lf:
|
||||
families[lf].append(f)
|
||||
|
||||
for fonts in families.itervalues():
|
||||
# Look for duplicate font files and choose the copy that is from a
|
||||
# more significant font directory (prefer user directories over
|
||||
# system directories).
|
||||
fmap = {}
|
||||
remove = []
|
||||
for f in fonts:
|
||||
fingerprint = (icu_lower(f['font-family']), f['font-weight'],
|
||||
f['font-stretch'], f['font-style'])
|
||||
if fingerprint in fmap:
|
||||
opath = fmap[fingerprint]['path']
|
||||
npath = f['path']
|
||||
if path_significance(npath, folders) >= path_significance(opath, folders):
|
||||
remove.append(fmap[fingerprint])
|
||||
fmap[fingerprint] = f
|
||||
else:
|
||||
remove.append(f)
|
||||
else:
|
||||
fmap[fingerprint] = f
|
||||
for font in remove:
|
||||
fonts.remove(font)
|
||||
fonts.sort(key=font_priority)
|
||||
|
||||
font_family_map = dict.copy(families)
|
||||
font_families = tuple(sorted((f[0]['font-family'] for f in
|
||||
font_family_map.itervalues()), key=sort_key))
|
||||
return font_family_map, font_families
|
||||
# }}}
|
||||
|
||||
|
||||
class FontScanner(Thread):
|
||||
@ -149,6 +222,17 @@ class FontScanner(Thread):
|
||||
except KeyError:
|
||||
raise NoFonts('No fonts found for the family: %r'%family)
|
||||
|
||||
def alt_fonts_for_family(self, family):
|
||||
''' Same as fonts_for_family() except that it uses the family name key
|
||||
instead of the preferred_family_name key. This is needed because some
|
||||
software, like Word uses the family name to refer to fonts instead of
|
||||
the preferred family name. '''
|
||||
self.join()
|
||||
try:
|
||||
return self.alt_font_family_map[icu_lower(family)]
|
||||
except KeyError:
|
||||
raise NoFonts('No fonts found for the family: %r'%family)
|
||||
|
||||
def legacy_fonts_for_family(self, family):
|
||||
'''
|
||||
Return a simple set of regular, bold, italic and bold-italic faces for
|
||||
@ -285,68 +369,9 @@ class FontScanner(Thread):
|
||||
|
||||
self.build_families()
|
||||
|
||||
def font_priority(self, font):
|
||||
'''
|
||||
Try to ensure that the "Regular" face is the first font for a given
|
||||
family.
|
||||
'''
|
||||
style_normal = font['font-style'] == 'normal'
|
||||
width_normal = font['font-stretch'] == 'normal'
|
||||
weight_normal = font['font-weight'] == 'normal'
|
||||
num_normal = sum(filter(None, (style_normal, width_normal,
|
||||
weight_normal)))
|
||||
subfamily_name = (font['wws_subfamily_name'] or
|
||||
font['preferred_subfamily_name'] or font['subfamily_name'])
|
||||
if num_normal == 3 and subfamily_name == 'Regular':
|
||||
return 0
|
||||
if num_normal == 3:
|
||||
return 1
|
||||
if subfamily_name == 'Regular':
|
||||
return 2
|
||||
return 3 + (3 - num_normal)
|
||||
|
||||
def build_families(self):
|
||||
families = defaultdict(list)
|
||||
for f in self.cached_fonts.itervalues():
|
||||
if not f:
|
||||
continue
|
||||
lf = icu_lower(f['font-family'] or '')
|
||||
if lf:
|
||||
families[lf].append(f)
|
||||
|
||||
for fonts in families.itervalues():
|
||||
# Look for duplicate font files and choose the copy that is from a
|
||||
# more significant font directory (prefer user directories over
|
||||
# system directories).
|
||||
fmap = {}
|
||||
remove = []
|
||||
for f in fonts:
|
||||
fingerprint = (icu_lower(f['font-family']), f['font-weight'],
|
||||
f['font-stretch'], f['font-style'])
|
||||
if fingerprint in fmap:
|
||||
opath = fmap[fingerprint]['path']
|
||||
npath = f['path']
|
||||
if self.path_significance(npath) >= self.path_significance(opath):
|
||||
remove.append(fmap[fingerprint])
|
||||
fmap[fingerprint] = f
|
||||
else:
|
||||
remove.append(f)
|
||||
else:
|
||||
fmap[fingerprint] = f
|
||||
for font in remove:
|
||||
fonts.remove(font)
|
||||
fonts.sort(key=self.font_priority)
|
||||
|
||||
self.font_family_map = dict.copy(families)
|
||||
self.font_families = tuple(sorted((f[0]['font-family'] for f in
|
||||
self.font_family_map.itervalues()), key=sort_key))
|
||||
|
||||
def path_significance(self, path):
|
||||
path = os.path.normcase(os.path.abspath(path))
|
||||
for i, q in enumerate(self.folders):
|
||||
if path.startswith(q):
|
||||
return i
|
||||
return -1
|
||||
self.font_family_map, self.font_families = build_families(self.cached_fonts, self.folders)
|
||||
self.alt_font_family_map = build_families(self.cached_fonts, self.folders, 'family_name')[0]
|
||||
|
||||
def write_cache(self):
|
||||
with self.cache:
|
||||
@ -395,5 +420,3 @@ def force_rescan():
|
||||
|
||||
if __name__ == '__main__':
|
||||
font_scanner.dump_fonts()
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user