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
|
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):
|
def embed_font(container, font, all_font_rules, report, warned):
|
||||||
rule = matching_rule(font, all_font_rules)
|
rule = matching_rule(font, all_font_rules)
|
||||||
ff = font['font-family']
|
ff = font['font-family']
|
||||||
@ -49,9 +65,12 @@ def embed_font(container, font, all_font_rules, report, warned):
|
|||||||
try:
|
try:
|
||||||
fonts = font_scanner.fonts_for_family(ff)
|
fonts = font_scanner.fonts_for_family(ff)
|
||||||
except NoFonts:
|
except NoFonts:
|
||||||
report(_('Failed to find fonts for family: %s, not embedding') % ff)
|
try:
|
||||||
warned.add(ff)
|
fonts = font_scanner.alt_fonts_for_family(ff)
|
||||||
return
|
except NoFonts:
|
||||||
|
report(_('Failed to find fonts for family: %s, not embedding') % ff)
|
||||||
|
warned.add(ff)
|
||||||
|
return
|
||||||
wt = int(font.get('font-weight', '400'))
|
wt = int(font.get('font-weight', '400'))
|
||||||
for f in fonts:
|
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'):
|
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['src'] = 'url(%s)' % href
|
||||||
rule['name'] = name
|
rule['name'] = name
|
||||||
return rule
|
return rule
|
||||||
msg = _('Failed to find font matching: family: %(family)s; weight: %(weight)s; style: %(style)s; stretch: %(stretch)s') % dict(
|
wkey = ('spec-mismatch-', ff, wt, font['font-style'], font['font-stretch'])
|
||||||
family=ff, weight=font['font-weight'], style=font['font-style'], stretch=font['font-stretch'])
|
if wkey not in warned:
|
||||||
if msg not in warned:
|
warned.add(wkey)
|
||||||
warned.add(msg)
|
format_failed_match_report(fonts, ff, font, report)
|
||||||
report(msg)
|
|
||||||
else:
|
else:
|
||||||
name = rule['src']
|
name = rule['src']
|
||||||
href = container.name_to_href(name)
|
href = container.name_to_href(name)
|
||||||
@ -159,4 +177,3 @@ if __name__ == '__main__':
|
|||||||
prints(msg)
|
prints(msg)
|
||||||
print()
|
print()
|
||||||
prints('Output written to:', outbook)
|
prints('Output written to:', outbook)
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@ from calibre.utils.icu import sort_key
|
|||||||
class NoFonts(ValueError):
|
class NoFonts(ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Font dirs {{{
|
||||||
|
|
||||||
|
|
||||||
def default_font_dirs():
|
def default_font_dirs():
|
||||||
return [
|
return [
|
||||||
@ -115,6 +117,77 @@ def font_dirs():
|
|||||||
os.path.expanduser('~/Library/Fonts'),
|
os.path.expanduser('~/Library/Fonts'),
|
||||||
]
|
]
|
||||||
return fc_list()
|
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):
|
class FontScanner(Thread):
|
||||||
@ -149,6 +222,17 @@ class FontScanner(Thread):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
raise NoFonts('No fonts found for the family: %r'%family)
|
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):
|
def legacy_fonts_for_family(self, family):
|
||||||
'''
|
'''
|
||||||
Return a simple set of regular, bold, italic and bold-italic faces for
|
Return a simple set of regular, bold, italic and bold-italic faces for
|
||||||
@ -285,68 +369,9 @@ class FontScanner(Thread):
|
|||||||
|
|
||||||
self.build_families()
|
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):
|
def build_families(self):
|
||||||
families = defaultdict(list)
|
self.font_family_map, self.font_families = build_families(self.cached_fonts, self.folders)
|
||||||
for f in self.cached_fonts.itervalues():
|
self.alt_font_family_map = build_families(self.cached_fonts, self.folders, 'family_name')[0]
|
||||||
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
|
|
||||||
|
|
||||||
def write_cache(self):
|
def write_cache(self):
|
||||||
with self.cache:
|
with self.cache:
|
||||||
@ -395,5 +420,3 @@ def force_rescan():
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
font_scanner.dump_fonts()
|
font_scanner.dump_fonts()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user