A TrueType font file contains data, in table format, that comprises an outline font.
This page hosts a formal specification of TrueType Font File using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
All parsing code for Python generated by Kaitai Struct depends on the Python runtime library. You have to install it before you can parse data.
The Python runtime library can be installed from PyPI:
python3 -m pip install kaitaistruct
Parse a local file and get structure in memory:
data = Ttf.from_file("path/to/local/file.ttf")
Or parse structure from a bytes:
from kaitaistruct import KaitaiStream, BytesIO
raw = b"\x00\x01\x02..."
data = Ttf(KaitaiStream(BytesIO(raw)))
After that, one can get various attributes from the structure by invoking getter methods like:
data.offset_table # => get offset table
# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
# type: ignore
import kaitaistruct
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
from enum import IntEnum
if getattr(kaitaistruct, 'API_VERSION', (0, 9)) < (0, 11):
raise Exception("Incompatible Kaitai Struct Python API: 0.11 or later is required, but you have %s" % (kaitaistruct.__version__))
class Ttf(KaitaiStruct):
"""A TrueType font file contains data, in table format, that comprises
an outline font.
.. seealso::
Source - https://web.archive.org/web/20160410081432/https://www.microsoft.com/typography/tt/ttf_spec/ttch02.doc
"""
def __init__(self, _io, _parent=None, _root=None):
super(Ttf, self).__init__(_io)
self._parent = _parent
self._root = _root or self
self._read()
def _read(self):
self.offset_table = Ttf.OffsetTable(self._io, self, self._root)
self.directory_table = []
for i in range(self.offset_table.num_tables):
self.directory_table.append(Ttf.DirTableEntry(self._io, self, self._root))
def _fetch_instances(self):
pass
self.offset_table._fetch_instances()
for i in range(len(self.directory_table)):
pass
self.directory_table[i]._fetch_instances()
class Cmap(KaitaiStruct):
"""cmap - Character To Glyph Index Mapping Table This table defines the mapping of character codes to the glyph index values used in the font.
"""
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Cmap, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.version_number = self._io.read_u2be()
self.number_of_encoding_tables = self._io.read_u2be()
self.tables = []
for i in range(self.number_of_encoding_tables):
self.tables.append(Ttf.Cmap.SubtableHeader(self._io, self, self._root))
def _fetch_instances(self):
pass
for i in range(len(self.tables)):
pass
self.tables[i]._fetch_instances()
class Subtable(KaitaiStruct):
class SubtableFormat(IntEnum):
byte_encoding_table = 0
high_byte_mapping_through_table = 2
segment_mapping_to_delta_values = 4
trimmed_table_mapping = 6
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Cmap.Subtable, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.format = KaitaiStream.resolve_enum(Ttf.Cmap.Subtable.SubtableFormat, self._io.read_u2be())
self.length = self._io.read_u2be()
self.version = self._io.read_u2be()
_on = self.format
if _on == Ttf.Cmap.Subtable.SubtableFormat.byte_encoding_table:
pass
self._raw_value = self._io.read_bytes(self.length - 6)
_io__raw_value = KaitaiStream(BytesIO(self._raw_value))
self.value = Ttf.Cmap.Subtable.ByteEncodingTable(_io__raw_value, self, self._root)
elif _on == Ttf.Cmap.Subtable.SubtableFormat.high_byte_mapping_through_table:
pass
self._raw_value = self._io.read_bytes(self.length - 6)
_io__raw_value = KaitaiStream(BytesIO(self._raw_value))
self.value = Ttf.Cmap.Subtable.HighByteMappingThroughTable(_io__raw_value, self, self._root)
elif _on == Ttf.Cmap.Subtable.SubtableFormat.segment_mapping_to_delta_values:
pass
self._raw_value = self._io.read_bytes(self.length - 6)
_io__raw_value = KaitaiStream(BytesIO(self._raw_value))
self.value = Ttf.Cmap.Subtable.SegmentMappingToDeltaValues(_io__raw_value, self, self._root)
elif _on == Ttf.Cmap.Subtable.SubtableFormat.trimmed_table_mapping:
pass
self._raw_value = self._io.read_bytes(self.length - 6)
_io__raw_value = KaitaiStream(BytesIO(self._raw_value))
self.value = Ttf.Cmap.Subtable.TrimmedTableMapping(_io__raw_value, self, self._root)
else:
pass
self.value = self._io.read_bytes(self.length - 6)
def _fetch_instances(self):
pass
_on = self.format
if _on == Ttf.Cmap.Subtable.SubtableFormat.byte_encoding_table:
pass
self.value._fetch_instances()
elif _on == Ttf.Cmap.Subtable.SubtableFormat.high_byte_mapping_through_table:
pass
self.value._fetch_instances()
elif _on == Ttf.Cmap.Subtable.SubtableFormat.segment_mapping_to_delta_values:
pass
self.value._fetch_instances()
elif _on == Ttf.Cmap.Subtable.SubtableFormat.trimmed_table_mapping:
pass
self.value._fetch_instances()
else:
pass
class ByteEncodingTable(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Cmap.Subtable.ByteEncodingTable, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.glyph_id_array = self._io.read_bytes(256)
def _fetch_instances(self):
pass
class HighByteMappingThroughTable(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Cmap.Subtable.HighByteMappingThroughTable, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.sub_header_keys = []
for i in range(256):
self.sub_header_keys.append(self._io.read_u2be())
def _fetch_instances(self):
pass
for i in range(len(self.sub_header_keys)):
pass
class SegmentMappingToDeltaValues(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Cmap.Subtable.SegmentMappingToDeltaValues, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.seg_count_x2 = self._io.read_u2be()
self.search_range = self._io.read_u2be()
self.entry_selector = self._io.read_u2be()
self.range_shift = self._io.read_u2be()
self.end_count = []
for i in range(self.seg_count):
self.end_count.append(self._io.read_u2be())
self.reserved_pad = self._io.read_u2be()
self.start_count = []
for i in range(self.seg_count):
self.start_count.append(self._io.read_u2be())
self.id_delta = []
for i in range(self.seg_count):
self.id_delta.append(self._io.read_u2be())
self.id_range_offset = []
for i in range(self.seg_count):
self.id_range_offset.append(self._io.read_u2be())
self.glyph_id_array = []
i = 0
while not self._io.is_eof():
self.glyph_id_array.append(self._io.read_u2be())
i += 1
def _fetch_instances(self):
pass
for i in range(len(self.end_count)):
pass
for i in range(len(self.start_count)):
pass
for i in range(len(self.id_delta)):
pass
for i in range(len(self.id_range_offset)):
pass
for i in range(len(self.glyph_id_array)):
pass
@property
def seg_count(self):
if hasattr(self, '_m_seg_count'):
return self._m_seg_count
self._m_seg_count = self.seg_count_x2 // 2
return getattr(self, '_m_seg_count', None)
class TrimmedTableMapping(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Cmap.Subtable.TrimmedTableMapping, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.first_code = self._io.read_u2be()
self.entry_count = self._io.read_u2be()
self.glyph_id_array = []
for i in range(self.entry_count):
self.glyph_id_array.append(self._io.read_u2be())
def _fetch_instances(self):
pass
for i in range(len(self.glyph_id_array)):
pass
class SubtableHeader(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Cmap.SubtableHeader, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.platform_id = self._io.read_u2be()
self.encoding_id = self._io.read_u2be()
self.subtable_offset = self._io.read_u4be()
def _fetch_instances(self):
pass
_ = self.table
if hasattr(self, '_m_table'):
pass
self._m_table._fetch_instances()
@property
def table(self):
if hasattr(self, '_m_table'):
return self._m_table
io = self._parent._io
_pos = io.pos()
io.seek(self.subtable_offset)
self._m_table = Ttf.Cmap.Subtable(io, self, self._root)
io.seek(_pos)
return getattr(self, '_m_table', None)
class Cvt(KaitaiStruct):
"""cvt - Control Value Table This table contains a list of values that can be referenced by instructions. They can be used, among other things, to control characteristics for different glyphs.
"""
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Cvt, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.fwords = []
i = 0
while not self._io.is_eof():
self.fwords.append(self._io.read_s2be())
i += 1
def _fetch_instances(self):
pass
for i in range(len(self.fwords)):
pass
class DirTableEntry(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.DirTableEntry, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.tag = (self._io.read_bytes(4)).decode(u"ASCII")
self.checksum = self._io.read_u4be()
self.offset = self._io.read_u4be()
self.length = self._io.read_u4be()
def _fetch_instances(self):
pass
_ = self.value
if hasattr(self, '_m_value'):
pass
_on = self.tag
if _on == u"OS/2":
pass
self._m_value._fetch_instances()
elif _on == u"cmap":
pass
self._m_value._fetch_instances()
elif _on == u"cvt ":
pass
self._m_value._fetch_instances()
elif _on == u"fpgm":
pass
self._m_value._fetch_instances()
elif _on == u"glyf":
pass
self._m_value._fetch_instances()
elif _on == u"head":
pass
self._m_value._fetch_instances()
elif _on == u"hhea":
pass
self._m_value._fetch_instances()
elif _on == u"kern":
pass
self._m_value._fetch_instances()
elif _on == u"maxp":
pass
self._m_value._fetch_instances()
elif _on == u"name":
pass
self._m_value._fetch_instances()
elif _on == u"post":
pass
self._m_value._fetch_instances()
elif _on == u"prep":
pass
self._m_value._fetch_instances()
else:
pass
@property
def value(self):
if hasattr(self, '_m_value'):
return self._m_value
io = self._root._io
_pos = io.pos()
io.seek(self.offset)
_on = self.tag
if _on == u"OS/2":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Os2(_io__raw__m_value, self, self._root)
elif _on == u"cmap":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Cmap(_io__raw__m_value, self, self._root)
elif _on == u"cvt ":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Cvt(_io__raw__m_value, self, self._root)
elif _on == u"fpgm":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Fpgm(_io__raw__m_value, self, self._root)
elif _on == u"glyf":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Glyf(_io__raw__m_value, self, self._root)
elif _on == u"head":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Head(_io__raw__m_value, self, self._root)
elif _on == u"hhea":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Hhea(_io__raw__m_value, self, self._root)
elif _on == u"kern":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Kern(_io__raw__m_value, self, self._root)
elif _on == u"maxp":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Maxp(_io__raw__m_value, self, self._root)
elif _on == u"name":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Name(_io__raw__m_value, self, self._root)
elif _on == u"post":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Post(_io__raw__m_value, self, self._root)
elif _on == u"prep":
pass
self._raw__m_value = io.read_bytes(self.length)
_io__raw__m_value = KaitaiStream(BytesIO(self._raw__m_value))
self._m_value = Ttf.Prep(_io__raw__m_value, self, self._root)
else:
pass
self._m_value = io.read_bytes(self.length)
io.seek(_pos)
return getattr(self, '_m_value', None)
class Fixed(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Fixed, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.major = self._io.read_u2be()
self.minor = self._io.read_u2be()
def _fetch_instances(self):
pass
class Fpgm(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Fpgm, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.instructions = self._io.read_bytes_full()
def _fetch_instances(self):
pass
class Glyf(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Glyf, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.number_of_contours = self._io.read_s2be()
self.x_min = self._io.read_s2be()
self.y_min = self._io.read_s2be()
self.x_max = self._io.read_s2be()
self.y_max = self._io.read_s2be()
if self.number_of_contours > 0:
pass
self.value = Ttf.Glyf.SimpleGlyph(self._io, self, self._root)
def _fetch_instances(self):
pass
if self.number_of_contours > 0:
pass
self.value._fetch_instances()
class SimpleGlyph(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Glyf.SimpleGlyph, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.end_pts_of_contours = []
for i in range(self._parent.number_of_contours):
self.end_pts_of_contours.append(self._io.read_u2be())
self.instruction_length = self._io.read_u2be()
self.instructions = self._io.read_bytes(self.instruction_length)
self.flags = []
for i in range(self.point_count):
self.flags.append(Ttf.Glyf.SimpleGlyph.Flag(self._io, self, self._root))
def _fetch_instances(self):
pass
for i in range(len(self.end_pts_of_contours)):
pass
for i in range(len(self.flags)):
pass
self.flags[i]._fetch_instances()
class Flag(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Glyf.SimpleGlyph.Flag, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.reserved = self._io.read_bits_int_be(2)
self.y_is_same = self._io.read_bits_int_be(1) != 0
self.x_is_same = self._io.read_bits_int_be(1) != 0
self.repeat = self._io.read_bits_int_be(1) != 0
self.y_short_vector = self._io.read_bits_int_be(1) != 0
self.x_short_vector = self._io.read_bits_int_be(1) != 0
self.on_curve = self._io.read_bits_int_be(1) != 0
if self.repeat:
pass
self.repeat_value = self._io.read_u1()
def _fetch_instances(self):
pass
if self.repeat:
pass
@property
def point_count(self):
if hasattr(self, '_m_point_count'):
return self._m_point_count
self._m_point_count = max(self.end_pts_of_contours) + 1
return getattr(self, '_m_point_count', None)
class Head(KaitaiStruct):
class Flags(IntEnum):
baseline_at_y0 = 1
left_sidebearing_at_x0 = 2
flag_depend_on_point_size = 4
flag_force_ppem = 8
flag_may_advance_width = 16
class FontDirectionHint(IntEnum):
fully_mixed_directional_glyphs = 0
only_strongly_left_to_right = 1
strongly_left_to_right_and_neutrals = 2
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Head, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.version = Ttf.Fixed(self._io, self, self._root)
self.font_revision = Ttf.Fixed(self._io, self, self._root)
self.checksum_adjustment = self._io.read_u4be()
self.magic_number = self._io.read_bytes(4)
if not self.magic_number == b"\x5F\x0F\x3C\xF5":
raise kaitaistruct.ValidationNotEqualError(b"\x5F\x0F\x3C\xF5", self.magic_number, self._io, u"/types/head/seq/3")
self.flags = KaitaiStream.resolve_enum(Ttf.Head.Flags, self._io.read_u2be())
self.units_per_em = self._io.read_u2be()
self.created = self._io.read_u8be()
self.modified = self._io.read_u8be()
self.x_min = self._io.read_s2be()
self.y_min = self._io.read_s2be()
self.x_max = self._io.read_s2be()
self.y_max = self._io.read_s2be()
self.mac_style = self._io.read_u2be()
self.lowest_rec_ppem = self._io.read_u2be()
self.font_direction_hint = KaitaiStream.resolve_enum(Ttf.Head.FontDirectionHint, self._io.read_s2be())
self.index_to_loc_format = self._io.read_s2be()
self.glyph_data_format = self._io.read_s2be()
def _fetch_instances(self):
pass
self.version._fetch_instances()
self.font_revision._fetch_instances()
class Hhea(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Hhea, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.version = Ttf.Fixed(self._io, self, self._root)
self.ascender = self._io.read_s2be()
self.descender = self._io.read_s2be()
self.line_gap = self._io.read_s2be()
self.advance_width_max = self._io.read_u2be()
self.min_left_side_bearing = self._io.read_s2be()
self.min_right_side_bearing = self._io.read_s2be()
self.x_max_extend = self._io.read_s2be()
self.caret_slope_rise = self._io.read_s2be()
self.caret_slope_run = self._io.read_s2be()
self.reserved = self._io.read_bytes(10)
if not self.reserved == b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00":
raise kaitaistruct.ValidationNotEqualError(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", self.reserved, self._io, u"/types/hhea/seq/10")
self.metric_data_format = self._io.read_s2be()
self.number_of_hmetrics = self._io.read_u2be()
def _fetch_instances(self):
pass
self.version._fetch_instances()
class Kern(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Kern, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.version = self._io.read_u2be()
self.subtable_count = self._io.read_u2be()
self.subtables = []
for i in range(self.subtable_count):
self.subtables.append(Ttf.Kern.Subtable(self._io, self, self._root))
def _fetch_instances(self):
pass
for i in range(len(self.subtables)):
pass
self.subtables[i]._fetch_instances()
class Subtable(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Kern.Subtable, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.version = self._io.read_u2be()
self.length = self._io.read_u2be()
self.format = self._io.read_u1()
self.reserved = self._io.read_bits_int_be(4)
self.is_override = self._io.read_bits_int_be(1) != 0
self.is_cross_stream = self._io.read_bits_int_be(1) != 0
self.is_minimum = self._io.read_bits_int_be(1) != 0
self.is_horizontal = self._io.read_bits_int_be(1) != 0
if self.format == 0:
pass
self.format0 = Ttf.Kern.Subtable.Format0(self._io, self, self._root)
def _fetch_instances(self):
pass
if self.format == 0:
pass
self.format0._fetch_instances()
class Format0(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Kern.Subtable.Format0, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.pair_count = self._io.read_u2be()
self.search_range = self._io.read_u2be()
self.entry_selector = self._io.read_u2be()
self.range_shift = self._io.read_u2be()
self.kerning_pairs = []
for i in range(self.pair_count):
self.kerning_pairs.append(Ttf.Kern.Subtable.Format0.KerningPair(self._io, self, self._root))
def _fetch_instances(self):
pass
for i in range(len(self.kerning_pairs)):
pass
self.kerning_pairs[i]._fetch_instances()
class KerningPair(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Kern.Subtable.Format0.KerningPair, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.left = self._io.read_u2be()
self.right = self._io.read_u2be()
self.value = self._io.read_s2be()
def _fetch_instances(self):
pass
class Maxp(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Maxp, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.table_version_number = Ttf.Fixed(self._io, self, self._root)
self.num_glyphs = self._io.read_u2be()
if self.is_version10:
pass
self.version10_body = Ttf.MaxpVersion10Body(self._io, self, self._root)
def _fetch_instances(self):
pass
self.table_version_number._fetch_instances()
if self.is_version10:
pass
self.version10_body._fetch_instances()
@property
def is_version10(self):
if hasattr(self, '_m_is_version10'):
return self._m_is_version10
self._m_is_version10 = ((self.table_version_number.major == 1) and (self.table_version_number.minor == 0))
return getattr(self, '_m_is_version10', None)
class MaxpVersion10Body(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.MaxpVersion10Body, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.max_points = self._io.read_u2be()
self.max_contours = self._io.read_u2be()
self.max_composite_points = self._io.read_u2be()
self.max_composite_contours = self._io.read_u2be()
self.max_zones = self._io.read_u2be()
self.max_twilight_points = self._io.read_u2be()
self.max_storage = self._io.read_u2be()
self.max_function_defs = self._io.read_u2be()
self.max_instruction_defs = self._io.read_u2be()
self.max_stack_elements = self._io.read_u2be()
self.max_size_of_instructions = self._io.read_u2be()
self.max_component_elements = self._io.read_u2be()
self.max_component_depth = self._io.read_u2be()
def _fetch_instances(self):
pass
class Name(KaitaiStruct):
"""Name table is meant to include human-readable string metadata
that describes font: name of the font, its styles, copyright &
trademark notices, vendor and designer info, etc.
The table includes a list of "name records", each of which
corresponds to a single metadata entry.
.. seealso::
Source - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
"""
class Names(IntEnum):
copyright = 0
font_family = 1
font_subfamily = 2
unique_subfamily_id = 3
full_font_name = 4
name_table_version = 5
postscript_font_name = 6
trademark = 7
manufacturer = 8
designer = 9
description = 10
url_vendor = 11
url_designer = 12
license = 13
url_license = 14
reserved_15 = 15
preferred_family = 16
preferred_subfamily = 17
compatible_full_name = 18
sample_text = 19
class Platforms(IntEnum):
unicode = 0
macintosh = 1
reserved_2 = 2
microsoft = 3
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Name, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.format_selector = self._io.read_u2be()
self.num_name_records = self._io.read_u2be()
self.ofs_strings = self._io.read_u2be()
self.name_records = []
for i in range(self.num_name_records):
self.name_records.append(Ttf.Name.NameRecord(self._io, self, self._root))
def _fetch_instances(self):
pass
for i in range(len(self.name_records)):
pass
self.name_records[i]._fetch_instances()
class NameRecord(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Name.NameRecord, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.platform_id = KaitaiStream.resolve_enum(Ttf.Name.Platforms, self._io.read_u2be())
self.encoding_id = self._io.read_u2be()
self.language_id = self._io.read_u2be()
self.name_id = KaitaiStream.resolve_enum(Ttf.Name.Names, self._io.read_u2be())
self.len_str = self._io.read_u2be()
self.ofs_str = self._io.read_u2be()
def _fetch_instances(self):
pass
_ = self.ascii_value
if hasattr(self, '_m_ascii_value'):
pass
_ = self.unicode_value
if hasattr(self, '_m_unicode_value'):
pass
@property
def ascii_value(self):
if hasattr(self, '_m_ascii_value'):
return self._m_ascii_value
io = self._parent._io
_pos = io.pos()
io.seek(self._parent.ofs_strings + self.ofs_str)
self._m_ascii_value = (io.read_bytes(self.len_str)).decode(u"ASCII")
io.seek(_pos)
return getattr(self, '_m_ascii_value', None)
@property
def unicode_value(self):
if hasattr(self, '_m_unicode_value'):
return self._m_unicode_value
io = self._parent._io
_pos = io.pos()
io.seek(self._parent.ofs_strings + self.ofs_str)
self._m_unicode_value = (io.read_bytes(self.len_str)).decode(u"UTF-16BE")
io.seek(_pos)
return getattr(self, '_m_unicode_value', None)
class OffsetTable(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.OffsetTable, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.sfnt_version = Ttf.Fixed(self._io, self, self._root)
self.num_tables = self._io.read_u2be()
self.search_range = self._io.read_u2be()
self.entry_selector = self._io.read_u2be()
self.range_shift = self._io.read_u2be()
def _fetch_instances(self):
pass
self.sfnt_version._fetch_instances()
class Os2(KaitaiStruct):
"""The OS/2 table consists of a set of metrics that are required by Windows and OS/2."""
class FsSelection(IntEnum):
italic = 1
underscore = 2
negative = 4
outlined = 8
strikeout = 16
bold = 32
regular = 64
class FsType(IntEnum):
restricted_license_embedding = 2
preview_and_print_embedding = 4
editable_embedding = 8
class WeightClass(IntEnum):
thin = 100
extra_light = 200
light = 300
normal = 400
medium = 500
semi_bold = 600
bold = 700
extra_bold = 800
black = 900
class WidthClass(IntEnum):
ultra_condensed = 1
extra_condensed = 2
condensed = 3
semi_condensed = 4
normal = 5
semi_expanded = 6
expanded = 7
extra_expanded = 8
ultra_expanded = 9
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Os2, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.version = self._io.read_u2be()
self.x_avg_char_width = self._io.read_s2be()
self.weight_class = KaitaiStream.resolve_enum(Ttf.Os2.WeightClass, self._io.read_u2be())
self.width_class = KaitaiStream.resolve_enum(Ttf.Os2.WidthClass, self._io.read_u2be())
self.fs_type = KaitaiStream.resolve_enum(Ttf.Os2.FsType, self._io.read_s2be())
self.y_subscript_x_size = self._io.read_s2be()
self.y_subscript_y_size = self._io.read_s2be()
self.y_subscript_x_offset = self._io.read_s2be()
self.y_subscript_y_offset = self._io.read_s2be()
self.y_superscript_x_size = self._io.read_s2be()
self.y_superscript_y_size = self._io.read_s2be()
self.y_superscript_x_offset = self._io.read_s2be()
self.y_superscript_y_offset = self._io.read_s2be()
self.y_strikeout_size = self._io.read_s2be()
self.y_strikeout_position = self._io.read_s2be()
self.s_family_class = self._io.read_s2be()
self.panose = Ttf.Os2.Panose(self._io, self, self._root)
self.unicode_range = Ttf.Os2.UnicodeRange(self._io, self, self._root)
self.ach_vend_id = (self._io.read_bytes(4)).decode(u"ASCII")
self.selection = KaitaiStream.resolve_enum(Ttf.Os2.FsSelection, self._io.read_u2be())
self.first_char_index = self._io.read_u2be()
self.last_char_index = self._io.read_u2be()
self.typo_ascender = self._io.read_s2be()
self.typo_descender = self._io.read_s2be()
self.typo_line_gap = self._io.read_s2be()
self.win_ascent = self._io.read_u2be()
self.win_descent = self._io.read_u2be()
self.code_page_range = Ttf.Os2.CodePageRange(self._io, self, self._root)
def _fetch_instances(self):
pass
self.panose._fetch_instances()
self.unicode_range._fetch_instances()
self.code_page_range._fetch_instances()
class CodePageRange(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Os2.CodePageRange, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.symbol_character_set = self._io.read_bits_int_be(1) != 0
self.oem_character_set = self._io.read_bits_int_be(1) != 0
self.macintosh_character_set = self._io.read_bits_int_be(1) != 0
self.reserved_for_alternate_ansi_oem = self._io.read_bits_int_be(7)
self.cp1361_korean_johab = self._io.read_bits_int_be(1) != 0
self.cp950_chinese_traditional_chars_taiwan_and_hong_kong = self._io.read_bits_int_be(1) != 0
self.cp949_korean_wansung = self._io.read_bits_int_be(1) != 0
self.cp936_chinese_simplified_chars_prc_and_singapore = self._io.read_bits_int_be(1) != 0
self.cp932_jis_japan = self._io.read_bits_int_be(1) != 0
self.cp874_thai = self._io.read_bits_int_be(1) != 0
self.reserved_for_alternate_ansi = self._io.read_bits_int_be(8)
self.cp1257_windows_baltic = self._io.read_bits_int_be(1) != 0
self.cp1256_arabic = self._io.read_bits_int_be(1) != 0
self.cp1255_hebrew = self._io.read_bits_int_be(1) != 0
self.cp1254_turkish = self._io.read_bits_int_be(1) != 0
self.cp1253_greek = self._io.read_bits_int_be(1) != 0
self.cp1251_cyrillic = self._io.read_bits_int_be(1) != 0
self.cp1250_latin_2_eastern_europe = self._io.read_bits_int_be(1) != 0
self.cp1252_latin_1 = self._io.read_bits_int_be(1) != 0
self.cp437_us = self._io.read_bits_int_be(1) != 0
self.cp850_we_latin_1 = self._io.read_bits_int_be(1) != 0
self.cp708_arabic_asmo_708 = self._io.read_bits_int_be(1) != 0
self.cp737_greek_former_437_g = self._io.read_bits_int_be(1) != 0
self.cp775_ms_dos_baltic = self._io.read_bits_int_be(1) != 0
self.cp852_latin_2 = self._io.read_bits_int_be(1) != 0
self.cp855_ibm_cyrillic_primarily_russian = self._io.read_bits_int_be(1) != 0
self.cp857_ibm_turkish = self._io.read_bits_int_be(1) != 0
self.cp860_ms_dos_portuguese = self._io.read_bits_int_be(1) != 0
self.cp861_ms_dos_icelandic = self._io.read_bits_int_be(1) != 0
self.cp862_hebrew = self._io.read_bits_int_be(1) != 0
self.cp863_ms_dos_canadian_french = self._io.read_bits_int_be(1) != 0
self.cp864_arabic = self._io.read_bits_int_be(1) != 0
self.cp865_ms_dos_nordic = self._io.read_bits_int_be(1) != 0
self.cp866_ms_dos_russian = self._io.read_bits_int_be(1) != 0
self.cp869_ibm_greek = self._io.read_bits_int_be(1) != 0
self.reserved_for_oem = self._io.read_bits_int_be(16)
def _fetch_instances(self):
pass
class Panose(KaitaiStruct):
class ArmStyle(IntEnum):
any = 0
no_fit = 1
straight_arms_horizontal = 2
straight_arms_wedge = 3
straight_arms_vertical = 4
straight_arms_single_serif = 5
straight_arms_double_serif = 6
non_straight_arms_horizontal = 7
non_straight_arms_wedge = 8
non_straight_arms_vertical = 9
non_straight_arms_single_serif = 10
non_straight_arms_double_serif = 11
class Contrast(IntEnum):
any = 0
no_fit = 1
none = 2
very_low = 3
low = 4
medium_low = 5
medium = 6
medium_high = 7
high = 8
very_high = 9
class FamilyKind(IntEnum):
any = 0
no_fit = 1
text_and_display = 2
script = 3
decorative = 4
pictorial = 5
class LetterForm(IntEnum):
any = 0
no_fit = 1
normal_contact = 2
normal_weighted = 3
normal_boxed = 4
normal_flattened = 5
normal_rounded = 6
normal_off_center = 7
normal_square = 8
oblique_contact = 9
oblique_weighted = 10
oblique_boxed = 11
oblique_flattened = 12
oblique_rounded = 13
oblique_off_center = 14
oblique_square = 15
class Midline(IntEnum):
any = 0
no_fit = 1
standard_trimmed = 2
standard_pointed = 3
standard_serifed = 4
high_trimmed = 5
high_pointed = 6
high_serifed = 7
constant_trimmed = 8
constant_pointed = 9
constant_serifed = 10
low_trimmed = 11
low_pointed = 12
low_serifed = 13
class Proportion(IntEnum):
any = 0
no_fit = 1
old_style = 2
modern = 3
even_width = 4
expanded = 5
condensed = 6
very_expanded = 7
very_condensed = 8
monospaced = 9
class SerifStyle(IntEnum):
any = 0
no_fit = 1
cove = 2
obtuse_cove = 3
square_cove = 4
obtuse_square_cove = 5
square = 6
thin = 7
bone = 8
exaggerated = 9
triangle = 10
normal_sans = 11
obtuse_sans = 12
perp_sans = 13
flared = 14
rounded = 15
class StrokeVariation(IntEnum):
any = 0
no_fit = 1
gradual_diagonal = 2
gradual_transitional = 3
gradual_vertical = 4
gradual_horizontal = 5
rapid_vertical = 6
rapid_horizontal = 7
instant_vertical = 8
class Weight(IntEnum):
any = 0
no_fit = 1
very_light = 2
light = 3
thin = 4
book = 5
medium = 6
demi = 7
bold = 8
heavy = 9
black = 10
nord = 11
class XHeight(IntEnum):
any = 0
no_fit = 1
constant_small = 2
constant_standard = 3
constant_large = 4
ducking_small = 5
ducking_standard = 6
ducking_large = 7
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Os2.Panose, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.family_type = KaitaiStream.resolve_enum(Ttf.Os2.Panose.FamilyKind, self._io.read_u1())
self.serif_style = KaitaiStream.resolve_enum(Ttf.Os2.Panose.SerifStyle, self._io.read_u1())
self.weight = KaitaiStream.resolve_enum(Ttf.Os2.Panose.Weight, self._io.read_u1())
self.proportion = KaitaiStream.resolve_enum(Ttf.Os2.Panose.Proportion, self._io.read_u1())
self.contrast = KaitaiStream.resolve_enum(Ttf.Os2.Panose.Contrast, self._io.read_u1())
self.stroke_variation = KaitaiStream.resolve_enum(Ttf.Os2.Panose.StrokeVariation, self._io.read_u1())
self.arm_style = KaitaiStream.resolve_enum(Ttf.Os2.Panose.ArmStyle, self._io.read_u1())
self.letter_form = KaitaiStream.resolve_enum(Ttf.Os2.Panose.LetterForm, self._io.read_u1())
self.midline = KaitaiStream.resolve_enum(Ttf.Os2.Panose.Midline, self._io.read_u1())
self.x_height = KaitaiStream.resolve_enum(Ttf.Os2.Panose.XHeight, self._io.read_u1())
def _fetch_instances(self):
pass
class UnicodeRange(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Os2.UnicodeRange, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.basic_latin = self._io.read_bits_int_be(1) != 0
self.latin_1_supplement = self._io.read_bits_int_be(1) != 0
self.latin_extended_a = self._io.read_bits_int_be(1) != 0
self.latin_extended_b = self._io.read_bits_int_be(1) != 0
self.ipa_extensions = self._io.read_bits_int_be(1) != 0
self.spacing_modifier_letters = self._io.read_bits_int_be(1) != 0
self.combining_diacritical_marks = self._io.read_bits_int_be(1) != 0
self.basic_greek = self._io.read_bits_int_be(1) != 0
self.greek_symbols_and_coptic = self._io.read_bits_int_be(1) != 0
self.cyrillic = self._io.read_bits_int_be(1) != 0
self.armenian = self._io.read_bits_int_be(1) != 0
self.basic_hebrew = self._io.read_bits_int_be(1) != 0
self.hebrew_extended = self._io.read_bits_int_be(1) != 0
self.basic_arabic = self._io.read_bits_int_be(1) != 0
self.arabic_extended = self._io.read_bits_int_be(1) != 0
self.devanagari = self._io.read_bits_int_be(1) != 0
self.bengali = self._io.read_bits_int_be(1) != 0
self.gurmukhi = self._io.read_bits_int_be(1) != 0
self.gujarati = self._io.read_bits_int_be(1) != 0
self.oriya = self._io.read_bits_int_be(1) != 0
self.tamil = self._io.read_bits_int_be(1) != 0
self.telugu = self._io.read_bits_int_be(1) != 0
self.kannada = self._io.read_bits_int_be(1) != 0
self.malayalam = self._io.read_bits_int_be(1) != 0
self.thai = self._io.read_bits_int_be(1) != 0
self.lao = self._io.read_bits_int_be(1) != 0
self.basic_georgian = self._io.read_bits_int_be(1) != 0
self.georgian_extended = self._io.read_bits_int_be(1) != 0
self.hangul_jamo = self._io.read_bits_int_be(1) != 0
self.latin_extended_additional = self._io.read_bits_int_be(1) != 0
self.greek_extended = self._io.read_bits_int_be(1) != 0
self.general_punctuation = self._io.read_bits_int_be(1) != 0
self.superscripts_and_subscripts = self._io.read_bits_int_be(1) != 0
self.currency_symbols = self._io.read_bits_int_be(1) != 0
self.combining_diacritical_marks_for_symbols = self._io.read_bits_int_be(1) != 0
self.letterlike_symbols = self._io.read_bits_int_be(1) != 0
self.number_forms = self._io.read_bits_int_be(1) != 0
self.arrows = self._io.read_bits_int_be(1) != 0
self.mathematical_operators = self._io.read_bits_int_be(1) != 0
self.miscellaneous_technical = self._io.read_bits_int_be(1) != 0
self.control_pictures = self._io.read_bits_int_be(1) != 0
self.optical_character_recognition = self._io.read_bits_int_be(1) != 0
self.enclosed_alphanumerics = self._io.read_bits_int_be(1) != 0
self.box_drawing = self._io.read_bits_int_be(1) != 0
self.block_elements = self._io.read_bits_int_be(1) != 0
self.geometric_shapes = self._io.read_bits_int_be(1) != 0
self.miscellaneous_symbols = self._io.read_bits_int_be(1) != 0
self.dingbats = self._io.read_bits_int_be(1) != 0
self.cjk_symbols_and_punctuation = self._io.read_bits_int_be(1) != 0
self.hiragana = self._io.read_bits_int_be(1) != 0
self.katakana = self._io.read_bits_int_be(1) != 0
self.bopomofo = self._io.read_bits_int_be(1) != 0
self.hangul_compatibility_jamo = self._io.read_bits_int_be(1) != 0
self.cjk_miscellaneous = self._io.read_bits_int_be(1) != 0
self.enclosed_cjk_letters_and_months = self._io.read_bits_int_be(1) != 0
self.cjk_compatibility = self._io.read_bits_int_be(1) != 0
self.hangul = self._io.read_bits_int_be(1) != 0
self.reserved_for_unicode_subranges1 = self._io.read_bits_int_be(1) != 0
self.reserved_for_unicode_subranges2 = self._io.read_bits_int_be(1) != 0
self.cjk_unified_ideographs = self._io.read_bits_int_be(1) != 0
self.private_use_area = self._io.read_bits_int_be(1) != 0
self.cjk_compatibility_ideographs = self._io.read_bits_int_be(1) != 0
self.alphabetic_presentation_forms = self._io.read_bits_int_be(1) != 0
self.arabic_presentation_forms_a = self._io.read_bits_int_be(1) != 0
self.combining_half_marks = self._io.read_bits_int_be(1) != 0
self.cjk_compatibility_forms = self._io.read_bits_int_be(1) != 0
self.small_form_variants = self._io.read_bits_int_be(1) != 0
self.arabic_presentation_forms_b = self._io.read_bits_int_be(1) != 0
self.halfwidth_and_fullwidth_forms = self._io.read_bits_int_be(1) != 0
self.specials = self._io.read_bits_int_be(1) != 0
self.reserved = self._io.read_bytes(7)
def _fetch_instances(self):
pass
class Post(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Post, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.format = Ttf.Fixed(self._io, self, self._root)
self.italic_angle = Ttf.Fixed(self._io, self, self._root)
self.underline_position = self._io.read_s2be()
self.underline_thichness = self._io.read_s2be()
self.is_fixed_pitch = self._io.read_u4be()
self.min_mem_type42 = self._io.read_u4be()
self.max_mem_type42 = self._io.read_u4be()
self.min_mem_type1 = self._io.read_u4be()
self.max_mem_type1 = self._io.read_u4be()
if ((self.format.major == 2) and (self.format.minor == 0)) :
pass
self.format20 = Ttf.Post.Format20(self._io, self, self._root)
def _fetch_instances(self):
pass
self.format._fetch_instances()
self.italic_angle._fetch_instances()
if ((self.format.major == 2) and (self.format.minor == 0)) :
pass
self.format20._fetch_instances()
class Format20(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Post.Format20, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.number_of_glyphs = self._io.read_u2be()
self.glyph_name_index = []
for i in range(self.number_of_glyphs):
self.glyph_name_index.append(self._io.read_u2be())
self.glyph_names = []
i = 0
while True:
_ = Ttf.Post.Format20.PascalString(self._io, self, self._root)
self.glyph_names.append(_)
if ((_.length == 0) or (self._io.is_eof())) :
break
i += 1
def _fetch_instances(self):
pass
for i in range(len(self.glyph_name_index)):
pass
for i in range(len(self.glyph_names)):
pass
self.glyph_names[i]._fetch_instances()
class PascalString(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Post.Format20.PascalString, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.length = self._io.read_u1()
if self.length != 0:
pass
self.value = (self._io.read_bytes(self.length)).decode(u"ASCII")
def _fetch_instances(self):
pass
if self.length != 0:
pass
class Prep(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
super(Ttf.Prep, self).__init__(_io)
self._parent = _parent
self._root = _root
self._read()
def _read(self):
self.instructions = self._io.read_bytes_full()
def _fetch_instances(self):
pass