diff --git a/src/calibre/utils/fonts/sfnt/container.py b/src/calibre/utils/fonts/sfnt/container.py index 5ef3c157c6..a634b1a80e 100644 --- a/src/calibre/utils/fonts/sfnt/container.py +++ b/src/calibre/utils/fonts/sfnt/container.py @@ -21,6 +21,7 @@ from calibre.utils.fonts.sfnt.maxp import MaxpTable from calibre.utils.fonts.sfnt.loca import LocaTable from calibre.utils.fonts.sfnt.glyf import GlyfTable from calibre.utils.fonts.sfnt.cmap import CmapTable +from calibre.utils.fonts.sfnt.kern import KernTable from calibre.utils.fonts.sfnt.cff.table import CFFTable # OpenType spec: http://www.microsoft.com/typography/otspec/otff.htm @@ -44,6 +45,7 @@ class Sfnt(object): b'glyf' : GlyfTable, b'cmap' : CmapTable, b'CFF ' : CFFTable, + b'kern' : KernTable, }.get(table_tag, UnknownTable)(table) def __getitem__(self, key): diff --git a/src/calibre/utils/fonts/sfnt/kern.py b/src/calibre/utils/fonts/sfnt/kern.py new file mode 100644 index 0000000000..11a8402e6d --- /dev/null +++ b/src/calibre/utils/fonts/sfnt/kern.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2012, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +from struct import unpack_from, calcsize, pack, error as struct_error + +from calibre.utils.fonts.sfnt import (UnknownTable, FixedProperty, + max_power_of_two) +from calibre.utils.fonts.sfnt.errors import UnsupportedFont + +class KernTable(UnknownTable): + + version = FixedProperty('_version') + + def __init__(self, *args, **kwargs): + super(KernTable, self).__init__(*args, **kwargs) + self._version, self.num_tables = unpack_from(b'>HH', self.raw) + if self._version == 1 and len(self.raw) >= 8: + self._version, self.num_tables = unpack_from(b'>LL', self.raw) + self.headerfmt = b'>HH' if self._version == 0 else b'>LL' + + def restrict_to_glyphs(self, glyph_ids): + if self._version not in {0, 0x10000}: + raise UnsupportedFont('kern table has version: %x'%self._version) + offset = 4 if (self._version == 0) else 8 + tables = [] + for i in xrange(self.num_tables): + if self._version == 0: + version, length, coverage = unpack_from(b'>3H', self.raw, offset) + table_format = version + else: + length, coverage = unpack_from(b'>LH', self.raw, offset) + table_format = coverage & 0xff + raw = self.raw[offset:offset+length] + if table_format == 0: + raw = self.restrict_format_0(raw, glyph_ids) + if not raw: + continue + tables.append(raw) + offset += length + self.raw = pack(self.headerfmt, self._version, len(tables)) + b''.join(tables) + + def restrict_format_0(self, raw, glyph_ids): + if self._version == 0: + version, length, coverage, npairs = unpack_from(b'>4H', raw) + headerfmt = b'>3H' + else: + length, coverage, tuple_index, npairs = unpack_from(b'>L3H', raw) + headerfmt = b'>L2H' + + offset = calcsize(headerfmt + b'4H') + entries = [] + entrysz = calcsize(b'>2Hh') + for i in xrange(npairs): + try: + left, right, value = unpack_from(b'>2Hh', raw, offset) + except struct_error: + offset = len(raw) + break # Buggy kern table + if left in glyph_ids and right in glyph_ids: + entries.append(pack(b'>2Hh', left, right, value)) + offset += entrysz + + if offset != len(raw): + raise UnsupportedFont('This font has extra data at the end of' + ' a Format 0 kern subtable') + + npairs = len(entries) + if npairs == 0: + return b'' + + entry_selector = max_power_of_two(npairs) + search_range = (2 ** entry_selector) * 6 + range_shift = (npairs - (2 ** entry_selector)) * 6 + + entries = b''.join(entries) + length = calcsize(headerfmt + b'4H') + len(entries) + if self._version == 0: + header = pack(headerfmt, version, length, coverage) + else: + header = pack(headerfmt, length, coverage, tuple_index) + return header + pack(b'>4H', npairs, search_range, entry_selector, + range_shift) + entries + diff --git a/src/calibre/utils/fonts/sfnt/subset.py b/src/calibre/utils/fonts/sfnt/subset.py index 1f675a73ba..0e1bc0ea1c 100644 --- a/src/calibre/utils/fonts/sfnt/subset.py +++ b/src/calibre/utils/fonts/sfnt/subset.py @@ -104,6 +104,16 @@ def subset(raw, individual_chars, ranges=()): # Restrict the cmap table to only contain entries for the resolved glyphs cmap.set_character_map(character_map) + if b'kern' in sfnt: + try: + sfnt[b'kern'].restrict_to_glyphs(frozenset(character_map.itervalues())) + except UnsupportedFont as e: + print ('Subsetting of kern table failed, ignoring: %s'%e) + except Exception as e: + print ('Subsetting of kern table failed, ignoring') + import traceback + traceback.print_exc() + raw, new_sizes = sfnt() return raw, old_sizes, new_sizes