%PDF- %PDF-
| Direktori : /usr/lib/calibre/calibre/ebooks/oeb/polish/ |
| Current File : //usr/lib/calibre/calibre/ebooks/oeb/polish/subset.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, sys
from calibre import prints, as_unicode
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS, XPath, css_text
from calibre.ebooks.oeb.polish.container import OEB_FONTS
from calibre.ebooks.oeb.polish.utils import guess_type
from calibre.utils.fonts.sfnt.subset import subset
from calibre.utils.fonts.sfnt.errors import UnsupportedFont
from calibre.utils.fonts.utils import get_font_names
from polyglot.builtins import iteritems, itervalues
def remove_font_face_rules(container, sheet, remove_names, base):
changed = False
for rule in tuple(sheet.cssRules):
if rule.type != rule.FONT_FACE_RULE:
continue
try:
uri = rule.style.getProperty('src').propertyValue[0].uri
except (IndexError, KeyError, AttributeError, TypeError, ValueError):
continue
name = container.href_to_name(uri, base)
if name in remove_names:
sheet.deleteRule(rule)
changed = True
return changed
def iter_subsettable_fonts(container):
for name, mt in iteritems(container.mime_map):
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 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
for w in warnings:
container.log.warn(w)
olen = sum(itervalues(old_sizes))
nlen = sum(itervalues(new_sizes))
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)
changed = True
if remove:
for name, mt in iteritems(container.mime_map):
if mt in OEB_STYLES:
sheet = container.parsed(name)
if remove_font_face_rules(container, sheet, remove, name):
container.dirty(name)
elif mt in OEB_DOCS:
for style in XPath('//h:style')(container.parsed(name)):
if style.get('type', 'text/css') == 'text/css' and style.text:
sheet = container.parse_css(style.text, name)
if remove_font_face_rules(container, sheet, remove, name):
style.text = css_text(sheet)
container.dirty(name)
if total_old > 0:
report(_('Reduced total font size to %.1f%% of original')%(
total_new/total_old*100))
else:
report(_('No embedded fonts found'))
return changed
if __name__ == '__main__':
from calibre.ebooks.oeb.polish.container import get_container
from calibre.ebooks.oeb.polish.stats import StatsCollector
from calibre.utils.logging import default_log
default_log.filter_level = default_log.DEBUG
inbook = sys.argv[-1]
ebook = get_container(inbook, default_log)
report = []
stats = StatsCollector(ebook).font_stats
subset_all_fonts(ebook, stats, report.append)
outbook, ext = inbook.rpartition('.')[0::2]
outbook += '_subset.'+ext
ebook.commit(outbook)
prints('\nReport:')
for msg in report:
prints(msg)
print()
prints('Output written to:', outbook)