From 90101bd6632e58d7ff2cb5324d6d4807333d3873 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 12 Dec 2017 10:07:14 +0530 Subject: [PATCH] Book polishing: Do not scan book for font usage when subsetting if no embedded fonts are available. Fixes #1737400 [Ebook edit: Subset embedded fonts does something even if there are no fonts](https://bugs.launchpad.net/calibre/+bug/1737400) --- src/calibre/ebooks/oeb/polish/main.py | 18 ++++-- src/calibre/ebooks/oeb/polish/subset.py | 85 +++++++++++++------------ 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/src/calibre/ebooks/oeb/polish/main.py b/src/calibre/ebooks/oeb/polish/main.py index ed31c9748d..f8070bacf9 100644 --- a/src/calibre/ebooks/oeb/polish/main.py +++ b/src/calibre/ebooks/oeb/polish/main.py @@ -13,7 +13,7 @@ from functools import partial from calibre.ebooks.oeb.polish.container import get_container from calibre.ebooks.oeb.polish.stats import StatsCollector -from calibre.ebooks.oeb.polish.subset import subset_all_fonts +from calibre.ebooks.oeb.polish.subset import subset_all_fonts, iter_subsettable_fonts from calibre.ebooks.oeb.polish.images import compress_images from calibre.ebooks.oeb.polish.embed import embed_all_fonts from calibre.ebooks.oeb.polish.cover import set_cover @@ -145,8 +145,12 @@ def polish_one(ebook, opts, report, customization=None): jacket = None changed = False customization = customization or CUSTOMIZATION.copy() + has_subsettable_fonts = False + for x in iter_subsettable_fonts(ebook): + has_subsettable_fonts = True + break - if opts.subset or opts.embed: + if (opts.subset and has_subsettable_fonts) or opts.embed: stats = StatsCollector(ebook, do_embed=opts.embed) if opts.opf: @@ -196,12 +200,16 @@ def polish_one(ebook, opts, report, customization=None): rt(_('Embedding referenced fonts')) if embed_all_fonts(ebook, stats, report): changed = True + has_subsettable_fonts = True report('') if opts.subset: - rt(_('Subsetting embedded fonts')) - if subset_all_fonts(ebook, stats.font_stats, report): - changed = True + if has_subsettable_fonts: + rt(_('Subsetting embedded fonts')) + if subset_all_fonts(ebook, stats.font_stats, report): + changed = True + else: + rt(_('No embedded fonts to subset')) report('') if opts.remove_unused_css: diff --git a/src/calibre/ebooks/oeb/polish/subset.py b/src/calibre/ebooks/oeb/polish/subset.py index 0abfd49e1e..888e6d169c 100644 --- a/src/calibre/ebooks/oeb/polish/subset.py +++ b/src/calibre/ebooks/oeb/polish/subset.py @@ -34,52 +34,57 @@ def remove_font_face_rules(container, sheet, remove_names, base): return changed +def iter_subsettable_fonts(container): + for name, mt in container.mime_map.iteritems(): + if (mt in OEB_FONTS or name.rpartition('.')[-1].lower() in {'otf', 'ttf'}) and mt != guess_type('a.woff'): + yield name, mt + + def subset_all_fonts(container, font_stats, report): remove = set() total_old = total_new = 0 changed = False - for name, mt in container.mime_map.iteritems(): - if (mt in OEB_FONTS or name.rpartition('.')[-1].lower() in {'otf', 'ttf'}) and mt != guess_type('a.woff'): - chars = font_stats.get(name, set()) - with container.open(name, 'rb') as f: - f.seek(0, os.SEEK_END) - total_old += f.tell() - if not chars: - remove.add(name) - report(_('Removed unused font: %s')%name) + for name, mt in iter_subsettable_fonts(container): + chars = font_stats.get(name, set()) + with container.open(name, 'rb') as f: + f.seek(0, os.SEEK_END) + total_old += f.tell() + if not chars: + remove.add(name) + report(_('Removed unused font: %s')%name) + continue + with container.open(name, 'r+b') as f: + raw = f.read() + try: + font_name = get_font_names(raw)[-1] + except Exception as e: + container.log.warning( + 'Corrupted font: %s, ignoring. Error: %s'%( + name, as_unicode(e))) + continue + warnings = [] + container.log('Subsetting font: %s'%(font_name or name)) + try: + nraw, old_sizes, new_sizes = subset(raw, chars, + warnings=warnings) + except UnsupportedFont as e: + container.log.warning( + 'Unsupported font: %s, ignoring. Error: %s'%( + name, as_unicode(e))) continue - with container.open(name, 'r+b') as f: - raw = f.read() - try: - font_name = get_font_names(raw)[-1] - except Exception as e: - container.log.warning( - 'Corrupted font: %s, ignoring. Error: %s'%( - name, as_unicode(e))) - continue - warnings = [] - container.log('Subsetting font: %s'%(font_name or name)) - try: - nraw, old_sizes, new_sizes = subset(raw, chars, - warnings=warnings) - except UnsupportedFont as e: - container.log.warning( - 'Unsupported font: %s, ignoring. Error: %s'%( - name, as_unicode(e))) - continue - for w in warnings: - container.log.warn(w) - olen = sum(old_sizes.itervalues()) - nlen = sum(new_sizes.itervalues()) - total_new += len(nraw) - if nlen == olen: - report(_('The font %s was already subset')%font_name) - else: - report(_('Decreased the font {0} to {1} of its original size').format( - font_name, ('%.1f%%' % (nlen/olen * 100)))) - changed = True - f.seek(0), f.truncate(), f.write(nraw) + for w in warnings: + container.log.warn(w) + olen = sum(old_sizes.itervalues()) + nlen = sum(new_sizes.itervalues()) + total_new += len(nraw) + if nlen == olen: + report(_('The font %s was already subset')%font_name) + else: + report(_('Decreased the font {0} to {1} of its original size').format( + font_name, ('%.1f%%' % (nlen/olen * 100)))) + changed = True + f.seek(0), f.truncate(), f.write(nraw) for name in remove: container.remove_item(name)