%PDF- %PDF-
| Direktori : /usr/lib/calibre/calibre/utils/fonts/sfnt/ |
| Current File : //usr/lib/calibre/calibre/utils/fonts/sfnt/common.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from struct import unpack_from, calcsize
from collections import OrderedDict, namedtuple
from calibre.utils.fonts.sfnt.errors import UnsupportedFont
from polyglot.builtins import iteritems
class Unpackable:
def __init__(self, raw, offset):
self.raw, self.offset = raw, offset
self.start_pos = offset
def unpack(self, fmt, single_special=True):
fmt = fmt.encode('ascii') if not isinstance(fmt, bytes) else fmt
ans = unpack_from(b'>'+fmt, self.raw, self.offset)
if single_special and len(ans) == 1:
ans = ans[0]
self.offset += calcsize(fmt)
return ans
class SimpleListTable(list):
'A table that contains a list of subtables'
child_class = None
def __init__(self, raw, offset):
list.__init__(self)
data = Unpackable(raw, offset)
self.read_extra_header(data)
count = data.unpack('H')
for i in range(count):
offset = data.unpack('H')
self.append(self.child_class(raw, data.start_pos + offset))
self.read_extra_footer(data)
def read_extra_header(self, data):
pass
def read_extra_footer(self, data):
pass
class ListTable(OrderedDict):
'A table that contains an ordered mapping of table tag to subtable'
child_class = None
def __init__(self, raw, offset):
OrderedDict.__init__(self)
data = Unpackable(raw, offset)
self.read_extra_header(data)
count = data.unpack('H')
for i in range(count):
tag, coffset = data.unpack('4sH')
self[tag] = self.child_class(raw, data.start_pos + coffset)
self.read_extra_footer(data)
def read_extra_header(self, data):
pass
def read_extra_footer(self, data):
pass
def dump(self, prefix=''):
print(prefix, self.__class__.__name__, sep='')
prefix += ' '
for tag, child in iteritems(self):
print(prefix, tag, sep='')
child.dump(prefix=prefix+' ')
class IndexTable(list):
def __init__(self, raw, offset):
data = Unpackable(raw, offset)
self.read_extra_header(data)
count = data.unpack('H')
for i in range(count):
self.append(data.unpack('H'))
def read_extra_header(self, data):
pass
def dump(self, prefix=''):
print(prefix, self.__class__.__name__, sep='')
class LanguageSystemTable(IndexTable):
def read_extra_header(self, data):
self.lookup_order, self.required_feature_index = data.unpack('2H')
if self.lookup_order != 0:
raise UnsupportedFont('This LanguageSystemTable has an unknown'
' lookup order: 0x%x'%self.lookup_order)
class ScriptTable(ListTable):
child_class = LanguageSystemTable
def __init__(self, raw, offset):
ListTable.__init__(self, raw, offset)
def read_extra_header(self, data):
start_pos = data.offset
default_offset = data.unpack('H')
self[b'default'] = (LanguageSystemTable(data.raw, start_pos +
default_offset) if default_offset else None)
class ScriptListTable(ListTable):
child_class = ScriptTable
class FeatureTable(IndexTable):
def read_extra_header(self, data):
self.feature_params = data.unpack('H')
if False and self.feature_params != 0:
# Source code pro sets this to non NULL
raise UnsupportedFont(
'This FeatureTable has non NULL FeatureParams: 0x%x'%self.feature_params)
class FeatureListTable(ListTable):
child_class = FeatureTable
class LookupTable(SimpleListTable):
def read_extra_header(self, data):
self.lookup_type, self.lookup_flag = data.unpack('2H')
self.set_child_class()
def set_child_class(self):
raise NotImplementedError()
def read_extra_footer(self, data):
if self.lookup_flag & 0x0010:
self.mark_filtering_set = data.unpack('H')
def ExtensionSubstitution(raw, offset, subtable_map={}):
data = Unpackable(raw, offset)
subst_format, extension_lookup_type, offset = data.unpack('2HL')
if subst_format != 1:
raise UnsupportedFont('ExtensionSubstitution has unknown format: 0x%x'%subst_format)
return subtable_map[extension_lookup_type](raw, offset+data.start_pos)
CoverageRange = namedtuple('CoverageRange', 'start end start_coverage_index')
class Coverage:
def __init__(self, raw, offset, parent_table_name):
data = Unpackable(raw, offset)
self.format, count = data.unpack('2H')
if self.format not in {1, 2}:
raise UnsupportedFont('Unknown Coverage format: 0x%x in %s'%(
self.format, parent_table_name))
if self.format == 1:
self.glyph_ids = data.unpack('%dH'%count, single_special=False)
self.glyph_ids_map = {gid:i for i, gid in
enumerate(self.glyph_ids)}
else:
self.ranges = []
ranges = data.unpack('%dH'%(3*count), single_special=False)
for i in range(count):
start, end, start_coverage_index = ranges[i*3:(i+1)*3]
self.ranges.append(CoverageRange(start, end, start_coverage_index))
def coverage_indices(self, glyph_ids):
'''Return map of glyph_id -> coverage index. Map contains only those
glyph_ids that are covered by this table and that are present in
glyph_ids.'''
ans = OrderedDict()
for gid in glyph_ids:
if self.format == 1:
idx = self.glyph_ids_map.get(gid, None)
if idx is not None:
ans[gid] = idx
else:
for start, end, start_coverage_index in self.ranges:
if start <= gid <= end:
ans[gid] = start_coverage_index + (gid-start)
return ans
class UnknownLookupSubTable:
formats = {}
def __init__(self, raw, offset):
data = Unpackable(raw, offset)
self.format = data.unpack('H')
if self.format not in self.formats:
raise UnsupportedFont('Unknown format for Lookup Subtable %s: 0x%x'%(
self.__class__.__name__, self.format))
if self.has_initial_coverage:
coverage_offset = data.unpack('H') + data.start_pos
self.coverage = Coverage(raw, coverage_offset, self.__class__.__name__)
self.initialize(data)
@property
def has_initial_coverage(self):
return True
def all_substitutions(self, glyph_ids):
''' Return a set of all glyph ids that could be substituted for any
subset of the specified glyph ids (which must be a set)'''
raise NotImplementedError()
def read_sets(self, data, read_item=None, set_is_index=False):
count = data.unpack('H')
sets = data.unpack('%dH'%count, single_special=False)
coverage_to_items_map = []
for offset in sets:
# Read items in the set
data.offset = start_pos = offset + data.start_pos
count = data.unpack('H')
item_offsets = data.unpack('%dH'%count, single_special=False)
items = []
for offset in item_offsets:
data.offset = offset + start_pos
if set_is_index:
items.append(offset)
else:
items.append(read_item(data))
coverage_to_items_map.append(items)
return coverage_to_items_map