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
import kaitaistruct
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
from enum import Enum
if getattr(kaitaistruct, 'API_VERSION', (0, 9)) < (0, 9):
raise Exception("Incompatible Kaitai Struct Python API: 0.9 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):
self._io = _io
self._parent = _parent
self._root = _root if _root else 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))
class Post(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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)) :
self.format20 = Ttf.Post.Format20(self._io, self, self._root)
class Format20(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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
class PascalString(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self._read()
def _read(self):
self.length = self._io.read_u1()
if self.length != 0:
self.value = (self._io.read_bytes(self.length)).decode(u"ascii")
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 Platforms(Enum):
unicode = 0
macintosh = 1
reserved_2 = 2
microsoft = 3
class Names(Enum):
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
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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))
class NameRecord(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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()
@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 Head(KaitaiStruct):
class Flags(Enum):
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(Enum):
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):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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()
class Prep(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self._read()
def _read(self):
self.instructions = self._io.read_bytes_full()
class Hhea(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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()
class Fpgm(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self._read()
def _read(self):
self.instructions = self._io.read_bytes_full()
class Kern(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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))
class Subtable(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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
self._io.align_to_byte()
if self.format == 0:
self.format0 = Ttf.Kern.Subtable.Format0(self._io, self, self._root)
class Format0(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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))
class KerningPair(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self._read()
def _read(self):
self.left = self._io.read_u2be()
self.right = self._io.read_u2be()
self.value = self._io.read_s2be()
class DirTableEntry(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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()
@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"head":
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"cvt ":
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"prep":
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)
elif _on == u"kern":
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"hhea":
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"post":
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"OS/2":
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"name":
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"maxp":
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"glyf":
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"fpgm":
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"cmap":
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)
else:
self._m_value = io.read_bytes(self.length)
io.seek(_pos)
return getattr(self, '_m_value', None)
class Os2(KaitaiStruct):
"""The OS/2 table consists of a set of metrics that are required by Windows and OS/2."""
class WeightClass(Enum):
thin = 100
extra_light = 200
light = 300
normal = 400
medium = 500
semi_bold = 600
bold = 700
extra_bold = 800
black = 900
class WidthClass(Enum):
ultra_condensed = 1
extra_condensed = 2
condensed = 3
semi_condensed = 4
normal = 5
semi_expanded = 6
expanded = 7
extra_expanded = 8
ultra_expanded = 9
class FsType(Enum):
restricted_license_embedding = 2
preview_and_print_embedding = 4
editable_embedding = 8
class FsSelection(Enum):
italic = 1
underscore = 2
negative = 4
outlined = 8
strikeout = 16
bold = 32
regular = 64
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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)
class Panose(KaitaiStruct):
class Weight(Enum):
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 Proportion(Enum):
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 FamilyKind(Enum):
any = 0
no_fit = 1
text_and_display = 2
script = 3
decorative = 4
pictorial = 5
class LetterForm(Enum):
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 SerifStyle(Enum):
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 XHeight(Enum):
any = 0
no_fit = 1
constant_small = 2
constant_standard = 3
constant_large = 4
ducking_small = 5
ducking_standard = 6
ducking_large = 7
class ArmStyle(Enum):
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 StrokeVariation(Enum):
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 Contrast(Enum):
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 Midline(Enum):
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
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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())
class UnicodeRange(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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._io.align_to_byte()
self.reserved = self._io.read_bytes(7)
class CodePageRange(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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)
class Fixed(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self._read()
def _read(self):
self.major = self._io.read_u2be()
self.minor = self._io.read_u2be()
class Glyf(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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:
self.value = Ttf.Glyf.SimpleGlyph(self._io, self, self._root)
class SimpleGlyph(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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))
class Flag(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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
self._io.align_to_byte()
if self.repeat:
self.repeat_value = self._io.read_u1()
@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 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):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self._read()
def _read(self):
self.fwords = []
i = 0
while not self._io.is_eof():
self.fwords.append(self._io.read_s2be())
i += 1
class Maxp(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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:
self.version10_body = Ttf.MaxpVersion10Body(self._io, self, self._root)
@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):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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()
class OffsetTable(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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()
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):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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))
class SubtableHeader(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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()
@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 Subtable(KaitaiStruct):
class SubtableFormat(Enum):
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):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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:
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.segment_mapping_to_delta_values:
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.high_byte_mapping_through_table:
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.trimmed_table_mapping:
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:
self.value = self._io.read_bytes((self.length - 6))
class ByteEncodingTable(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self._read()
def _read(self):
self.glyph_id_array = self._io.read_bytes(256)
class HighByteMappingThroughTable(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self._read()
def _read(self):
self.sub_header_keys = []
for i in range(256):
self.sub_header_keys.append(self._io.read_u2be())
class SegmentMappingToDeltaValues(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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
@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):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
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())