TrueType Font File: Ruby parsing library

A TrueType font file contains data, in table format, that comprises an outline font.

File extension

ttf

KS implementation details

License: MIT

References

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.

Usage

Runtime library

All parsing code for Ruby generated by Kaitai Struct depends on the Ruby runtime library. You have to install it before you can parse data.

The Ruby runtime library can be installed from RubyGems:

gem install kaitai-struct

Code

Parse a local file and get structure in memory:

data = Ttf.from_file("path/to/local/file.ttf")

Or parse structure from a string of bytes:

bytes = "\x00\x01\x02..."
data = Ttf.new(Kaitai::Struct::Stream.new(bytes))

After that, one can get various attributes from the structure by invoking getter methods like:

data.offset_table # => get offset table

Ruby source code to parse TrueType Font File

ttf.rb

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

require 'kaitai/struct/struct'

unless Gem::Version.new(Kaitai::Struct::VERSION) >= Gem::Version.new('0.9')
  raise "Incompatible Kaitai Struct Ruby API: 0.9 or later is required, but you have #{Kaitai::Struct::VERSION}"
end


##
# A TrueType font file contains data, in table format, that comprises
# an outline font.
# @see https://web.archive.org/web/20160410081432/https://www.microsoft.com/typography/tt/ttf_spec/ttch02.doc Source
class Ttf < Kaitai::Struct::Struct
  def initialize(_io, _parent = nil, _root = self)
    super(_io, _parent, _root)
    _read
  end

  def _read
    @offset_table = OffsetTable.new(@_io, self, @_root)
    @directory_table = []
    (offset_table.num_tables).times { |i|
      @directory_table << DirTableEntry.new(@_io, self, @_root)
    }
    self
  end
  class Post < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @format = Fixed.new(@_io, self, @_root)
      @italic_angle = Fixed.new(@_io, self, @_root)
      @underline_position = @_io.read_s2be
      @underline_thichness = @_io.read_s2be
      @is_fixed_pitch = @_io.read_u4be
      @min_mem_type42 = @_io.read_u4be
      @max_mem_type42 = @_io.read_u4be
      @min_mem_type1 = @_io.read_u4be
      @max_mem_type1 = @_io.read_u4be
      if  ((format.major == 2) && (format.minor == 0)) 
        @format20 = Format20.new(@_io, self, @_root)
      end
      self
    end
    class Format20 < Kaitai::Struct::Struct
      def initialize(_io, _parent = nil, _root = self)
        super(_io, _parent, _root)
        _read
      end

      def _read
        @number_of_glyphs = @_io.read_u2be
        @glyph_name_index = []
        (number_of_glyphs).times { |i|
          @glyph_name_index << @_io.read_u2be
        }
        @glyph_names = []
        i = 0
        begin
          _ = PascalString.new(@_io, self, @_root)
          @glyph_names << _
          i += 1
        end until  ((_.length == 0) || (_io.eof?)) 
        self
      end
      class PascalString < Kaitai::Struct::Struct
        def initialize(_io, _parent = nil, _root = self)
          super(_io, _parent, _root)
          _read
        end

        def _read
          @length = @_io.read_u1
          if length != 0
            @value = (@_io.read_bytes(length)).force_encoding("ascii")
          end
          self
        end
        attr_reader :length
        attr_reader :value
      end
      attr_reader :number_of_glyphs
      attr_reader :glyph_name_index
      attr_reader :glyph_names
    end
    attr_reader :format
    attr_reader :italic_angle
    attr_reader :underline_position
    attr_reader :underline_thichness
    attr_reader :is_fixed_pitch
    attr_reader :min_mem_type42
    attr_reader :max_mem_type42
    attr_reader :min_mem_type1
    attr_reader :max_mem_type1
    attr_reader :format20
  end

  ##
  # 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.
  # @see https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html Source
  class Name < Kaitai::Struct::Struct

    PLATFORMS = {
      0 => :platforms_unicode,
      1 => :platforms_macintosh,
      2 => :platforms_reserved_2,
      3 => :platforms_microsoft,
    }
    I__PLATFORMS = PLATFORMS.invert

    NAMES = {
      0 => :names_copyright,
      1 => :names_font_family,
      2 => :names_font_subfamily,
      3 => :names_unique_subfamily_id,
      4 => :names_full_font_name,
      5 => :names_name_table_version,
      6 => :names_postscript_font_name,
      7 => :names_trademark,
      8 => :names_manufacturer,
      9 => :names_designer,
      10 => :names_description,
      11 => :names_url_vendor,
      12 => :names_url_designer,
      13 => :names_license,
      14 => :names_url_license,
      15 => :names_reserved_15,
      16 => :names_preferred_family,
      17 => :names_preferred_subfamily,
      18 => :names_compatible_full_name,
      19 => :names_sample_text,
    }
    I__NAMES = NAMES.invert
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @format_selector = @_io.read_u2be
      @num_name_records = @_io.read_u2be
      @ofs_strings = @_io.read_u2be
      @name_records = []
      (num_name_records).times { |i|
        @name_records << NameRecord.new(@_io, self, @_root)
      }
      self
    end
    class NameRecord < Kaitai::Struct::Struct
      def initialize(_io, _parent = nil, _root = self)
        super(_io, _parent, _root)
        _read
      end

      def _read
        @platform_id = Kaitai::Struct::Stream::resolve_enum(Ttf::Name::PLATFORMS, @_io.read_u2be)
        @encoding_id = @_io.read_u2be
        @language_id = @_io.read_u2be
        @name_id = Kaitai::Struct::Stream::resolve_enum(Ttf::Name::NAMES, @_io.read_u2be)
        @len_str = @_io.read_u2be
        @ofs_str = @_io.read_u2be
        self
      end
      def ascii_value
        return @ascii_value unless @ascii_value.nil?
        io = _parent._io
        _pos = io.pos
        io.seek((_parent.ofs_strings + ofs_str))
        @ascii_value = (io.read_bytes(len_str)).force_encoding("ascii")
        io.seek(_pos)
        @ascii_value
      end
      def unicode_value
        return @unicode_value unless @unicode_value.nil?
        io = _parent._io
        _pos = io.pos
        io.seek((_parent.ofs_strings + ofs_str))
        @unicode_value = (io.read_bytes(len_str)).force_encoding("utf-16be")
        io.seek(_pos)
        @unicode_value
      end
      attr_reader :platform_id
      attr_reader :encoding_id
      attr_reader :language_id
      attr_reader :name_id
      attr_reader :len_str
      attr_reader :ofs_str
    end
    attr_reader :format_selector
    attr_reader :num_name_records
    attr_reader :ofs_strings
    attr_reader :name_records
  end
  class Head < Kaitai::Struct::Struct

    FLAGS = {
      1 => :flags_baseline_at_y0,
      2 => :flags_left_sidebearing_at_x0,
      4 => :flags_flag_depend_on_point_size,
      8 => :flags_flag_force_ppem,
      16 => :flags_flag_may_advance_width,
    }
    I__FLAGS = FLAGS.invert

    FONT_DIRECTION_HINT = {
      0 => :font_direction_hint_fully_mixed_directional_glyphs,
      1 => :font_direction_hint_only_strongly_left_to_right,
      2 => :font_direction_hint_strongly_left_to_right_and_neutrals,
    }
    I__FONT_DIRECTION_HINT = FONT_DIRECTION_HINT.invert
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @version = Fixed.new(@_io, self, @_root)
      @font_revision = Fixed.new(@_io, self, @_root)
      @checksum_adjustment = @_io.read_u4be
      @magic_number = @_io.read_bytes(4)
      raise Kaitai::Struct::ValidationNotEqualError.new([95, 15, 60, 245].pack('C*'), magic_number, _io, "/types/head/seq/3") if not magic_number == [95, 15, 60, 245].pack('C*')
      @flags = Kaitai::Struct::Stream::resolve_enum(FLAGS, @_io.read_u2be)
      @units_per_em = @_io.read_u2be
      @created = @_io.read_u8be
      @modified = @_io.read_u8be
      @x_min = @_io.read_s2be
      @y_min = @_io.read_s2be
      @x_max = @_io.read_s2be
      @y_max = @_io.read_s2be
      @mac_style = @_io.read_u2be
      @lowest_rec_ppem = @_io.read_u2be
      @font_direction_hint = Kaitai::Struct::Stream::resolve_enum(FONT_DIRECTION_HINT, @_io.read_s2be)
      @index_to_loc_format = @_io.read_s2be
      @glyph_data_format = @_io.read_s2be
      self
    end
    attr_reader :version
    attr_reader :font_revision
    attr_reader :checksum_adjustment
    attr_reader :magic_number
    attr_reader :flags
    attr_reader :units_per_em
    attr_reader :created
    attr_reader :modified
    attr_reader :x_min
    attr_reader :y_min
    attr_reader :x_max
    attr_reader :y_max
    attr_reader :mac_style
    attr_reader :lowest_rec_ppem
    attr_reader :font_direction_hint
    attr_reader :index_to_loc_format
    attr_reader :glyph_data_format
  end
  class Prep < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @instructions = @_io.read_bytes_full
      self
    end
    attr_reader :instructions
  end
  class Hhea < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @version = Fixed.new(@_io, self, @_root)
      @ascender = @_io.read_s2be
      @descender = @_io.read_s2be
      @line_gap = @_io.read_s2be
      @advance_width_max = @_io.read_u2be
      @min_left_side_bearing = @_io.read_s2be
      @min_right_side_bearing = @_io.read_s2be
      @x_max_extend = @_io.read_s2be
      @caret_slope_rise = @_io.read_s2be
      @caret_slope_run = @_io.read_s2be
      @reserved = @_io.read_bytes(10)
      raise Kaitai::Struct::ValidationNotEqualError.new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0].pack('C*'), reserved, _io, "/types/hhea/seq/10") if not reserved == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0].pack('C*')
      @metric_data_format = @_io.read_s2be
      @number_of_hmetrics = @_io.read_u2be
      self
    end
    attr_reader :version

    ##
    # Typographic ascent
    attr_reader :ascender

    ##
    # Typographic descent
    attr_reader :descender

    ##
    # Typographic line gap. Negative LineGap values are treated as zero in Windows 3.1, System 6, and System 7.
    attr_reader :line_gap

    ##
    # Maximum advance width value in `hmtx` table.
    attr_reader :advance_width_max

    ##
    # Minimum left sidebearing value in `hmtx` table.
    attr_reader :min_left_side_bearing

    ##
    # Minimum right sidebearing value; calculated as Min(aw - lsb - (xMax - xMin)).
    attr_reader :min_right_side_bearing

    ##
    # Max(lsb + (xMax - xMin)).
    attr_reader :x_max_extend
    attr_reader :caret_slope_rise
    attr_reader :caret_slope_run
    attr_reader :reserved
    attr_reader :metric_data_format
    attr_reader :number_of_hmetrics
  end
  class Fpgm < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @instructions = @_io.read_bytes_full
      self
    end
    attr_reader :instructions
  end
  class Kern < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @version = @_io.read_u2be
      @subtable_count = @_io.read_u2be
      @subtables = []
      (subtable_count).times { |i|
        @subtables << Subtable.new(@_io, self, @_root)
      }
      self
    end
    class Subtable < Kaitai::Struct::Struct
      def initialize(_io, _parent = nil, _root = self)
        super(_io, _parent, _root)
        _read
      end

      def _read
        @version = @_io.read_u2be
        @length = @_io.read_u2be
        @format = @_io.read_u1
        @reserved = @_io.read_bits_int_be(4)
        @is_override = @_io.read_bits_int_be(1) != 0
        @is_cross_stream = @_io.read_bits_int_be(1) != 0
        @is_minimum = @_io.read_bits_int_be(1) != 0
        @is_horizontal = @_io.read_bits_int_be(1) != 0
        @_io.align_to_byte
        if format == 0
          @format0 = Format0.new(@_io, self, @_root)
        end
        self
      end
      class Format0 < Kaitai::Struct::Struct
        def initialize(_io, _parent = nil, _root = self)
          super(_io, _parent, _root)
          _read
        end

        def _read
          @pair_count = @_io.read_u2be
          @search_range = @_io.read_u2be
          @entry_selector = @_io.read_u2be
          @range_shift = @_io.read_u2be
          @kerning_pairs = []
          (pair_count).times { |i|
            @kerning_pairs << KerningPair.new(@_io, self, @_root)
          }
          self
        end
        class KerningPair < Kaitai::Struct::Struct
          def initialize(_io, _parent = nil, _root = self)
            super(_io, _parent, _root)
            _read
          end

          def _read
            @left = @_io.read_u2be
            @right = @_io.read_u2be
            @value = @_io.read_s2be
            self
          end
          attr_reader :left
          attr_reader :right
          attr_reader :value
        end
        attr_reader :pair_count
        attr_reader :search_range
        attr_reader :entry_selector
        attr_reader :range_shift
        attr_reader :kerning_pairs
      end
      attr_reader :version
      attr_reader :length
      attr_reader :format
      attr_reader :reserved
      attr_reader :is_override
      attr_reader :is_cross_stream
      attr_reader :is_minimum
      attr_reader :is_horizontal
      attr_reader :format0
    end
    attr_reader :version
    attr_reader :subtable_count
    attr_reader :subtables
  end
  class DirTableEntry < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @tag = (@_io.read_bytes(4)).force_encoding("ascii")
      @checksum = @_io.read_u4be
      @offset = @_io.read_u4be
      @length = @_io.read_u4be
      self
    end
    def value
      return @value unless @value.nil?
      io = _root._io
      _pos = io.pos
      io.seek(offset)
      case tag
      when "head"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Head.new(_io__raw_value, self, @_root)
      when "cvt "
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Cvt.new(_io__raw_value, self, @_root)
      when "prep"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Prep.new(_io__raw_value, self, @_root)
      when "kern"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Kern.new(_io__raw_value, self, @_root)
      when "hhea"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Hhea.new(_io__raw_value, self, @_root)
      when "post"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Post.new(_io__raw_value, self, @_root)
      when "OS/2"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Os2.new(_io__raw_value, self, @_root)
      when "name"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Name.new(_io__raw_value, self, @_root)
      when "maxp"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Maxp.new(_io__raw_value, self, @_root)
      when "glyf"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Glyf.new(_io__raw_value, self, @_root)
      when "fpgm"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Fpgm.new(_io__raw_value, self, @_root)
      when "cmap"
        @_raw_value = io.read_bytes(length)
        _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
        @value = Cmap.new(_io__raw_value, self, @_root)
      else
        @value = io.read_bytes(length)
      end
      io.seek(_pos)
      @value
    end
    attr_reader :tag
    attr_reader :checksum
    attr_reader :offset
    attr_reader :length
    attr_reader :_raw_value
  end

  ##
  # The OS/2 table consists of a set of metrics that are required by Windows and OS/2.
  class Os2 < Kaitai::Struct::Struct

    WEIGHT_CLASS = {
      100 => :weight_class_thin,
      200 => :weight_class_extra_light,
      300 => :weight_class_light,
      400 => :weight_class_normal,
      500 => :weight_class_medium,
      600 => :weight_class_semi_bold,
      700 => :weight_class_bold,
      800 => :weight_class_extra_bold,
      900 => :weight_class_black,
    }
    I__WEIGHT_CLASS = WEIGHT_CLASS.invert

    WIDTH_CLASS = {
      1 => :width_class_ultra_condensed,
      2 => :width_class_extra_condensed,
      3 => :width_class_condensed,
      4 => :width_class_semi_condensed,
      5 => :width_class_normal,
      6 => :width_class_semi_expanded,
      7 => :width_class_expanded,
      8 => :width_class_extra_expanded,
      9 => :width_class_ultra_expanded,
    }
    I__WIDTH_CLASS = WIDTH_CLASS.invert

    FS_TYPE = {
      2 => :fs_type_restricted_license_embedding,
      4 => :fs_type_preview_and_print_embedding,
      8 => :fs_type_editable_embedding,
    }
    I__FS_TYPE = FS_TYPE.invert

    FS_SELECTION = {
      1 => :fs_selection_italic,
      2 => :fs_selection_underscore,
      4 => :fs_selection_negative,
      8 => :fs_selection_outlined,
      16 => :fs_selection_strikeout,
      32 => :fs_selection_bold,
      64 => :fs_selection_regular,
    }
    I__FS_SELECTION = FS_SELECTION.invert
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @version = @_io.read_u2be
      @x_avg_char_width = @_io.read_s2be
      @weight_class = Kaitai::Struct::Stream::resolve_enum(WEIGHT_CLASS, @_io.read_u2be)
      @width_class = Kaitai::Struct::Stream::resolve_enum(WIDTH_CLASS, @_io.read_u2be)
      @fs_type = Kaitai::Struct::Stream::resolve_enum(FS_TYPE, @_io.read_s2be)
      @y_subscript_x_size = @_io.read_s2be
      @y_subscript_y_size = @_io.read_s2be
      @y_subscript_x_offset = @_io.read_s2be
      @y_subscript_y_offset = @_io.read_s2be
      @y_superscript_x_size = @_io.read_s2be
      @y_superscript_y_size = @_io.read_s2be
      @y_superscript_x_offset = @_io.read_s2be
      @y_superscript_y_offset = @_io.read_s2be
      @y_strikeout_size = @_io.read_s2be
      @y_strikeout_position = @_io.read_s2be
      @s_family_class = @_io.read_s2be
      @panose = Panose.new(@_io, self, @_root)
      @unicode_range = UnicodeRange.new(@_io, self, @_root)
      @ach_vend_id = (@_io.read_bytes(4)).force_encoding("ascii")
      @selection = Kaitai::Struct::Stream::resolve_enum(FS_SELECTION, @_io.read_u2be)
      @first_char_index = @_io.read_u2be
      @last_char_index = @_io.read_u2be
      @typo_ascender = @_io.read_s2be
      @typo_descender = @_io.read_s2be
      @typo_line_gap = @_io.read_s2be
      @win_ascent = @_io.read_u2be
      @win_descent = @_io.read_u2be
      @code_page_range = CodePageRange.new(@_io, self, @_root)
      self
    end
    class Panose < Kaitai::Struct::Struct

      WEIGHT = {
        0 => :weight_any,
        1 => :weight_no_fit,
        2 => :weight_very_light,
        3 => :weight_light,
        4 => :weight_thin,
        5 => :weight_book,
        6 => :weight_medium,
        7 => :weight_demi,
        8 => :weight_bold,
        9 => :weight_heavy,
        10 => :weight_black,
        11 => :weight_nord,
      }
      I__WEIGHT = WEIGHT.invert

      PROPORTION = {
        0 => :proportion_any,
        1 => :proportion_no_fit,
        2 => :proportion_old_style,
        3 => :proportion_modern,
        4 => :proportion_even_width,
        5 => :proportion_expanded,
        6 => :proportion_condensed,
        7 => :proportion_very_expanded,
        8 => :proportion_very_condensed,
        9 => :proportion_monospaced,
      }
      I__PROPORTION = PROPORTION.invert

      FAMILY_KIND = {
        0 => :family_kind_any,
        1 => :family_kind_no_fit,
        2 => :family_kind_text_and_display,
        3 => :family_kind_script,
        4 => :family_kind_decorative,
        5 => :family_kind_pictorial,
      }
      I__FAMILY_KIND = FAMILY_KIND.invert

      LETTER_FORM = {
        0 => :letter_form_any,
        1 => :letter_form_no_fit,
        2 => :letter_form_normal_contact,
        3 => :letter_form_normal_weighted,
        4 => :letter_form_normal_boxed,
        5 => :letter_form_normal_flattened,
        6 => :letter_form_normal_rounded,
        7 => :letter_form_normal_off_center,
        8 => :letter_form_normal_square,
        9 => :letter_form_oblique_contact,
        10 => :letter_form_oblique_weighted,
        11 => :letter_form_oblique_boxed,
        12 => :letter_form_oblique_flattened,
        13 => :letter_form_oblique_rounded,
        14 => :letter_form_oblique_off_center,
        15 => :letter_form_oblique_square,
      }
      I__LETTER_FORM = LETTER_FORM.invert

      SERIF_STYLE = {
        0 => :serif_style_any,
        1 => :serif_style_no_fit,
        2 => :serif_style_cove,
        3 => :serif_style_obtuse_cove,
        4 => :serif_style_square_cove,
        5 => :serif_style_obtuse_square_cove,
        6 => :serif_style_square,
        7 => :serif_style_thin,
        8 => :serif_style_bone,
        9 => :serif_style_exaggerated,
        10 => :serif_style_triangle,
        11 => :serif_style_normal_sans,
        12 => :serif_style_obtuse_sans,
        13 => :serif_style_perp_sans,
        14 => :serif_style_flared,
        15 => :serif_style_rounded,
      }
      I__SERIF_STYLE = SERIF_STYLE.invert

      X_HEIGHT = {
        0 => :x_height_any,
        1 => :x_height_no_fit,
        2 => :x_height_constant_small,
        3 => :x_height_constant_standard,
        4 => :x_height_constant_large,
        5 => :x_height_ducking_small,
        6 => :x_height_ducking_standard,
        7 => :x_height_ducking_large,
      }
      I__X_HEIGHT = X_HEIGHT.invert

      ARM_STYLE = {
        0 => :arm_style_any,
        1 => :arm_style_no_fit,
        2 => :arm_style_straight_arms_horizontal,
        3 => :arm_style_straight_arms_wedge,
        4 => :arm_style_straight_arms_vertical,
        5 => :arm_style_straight_arms_single_serif,
        6 => :arm_style_straight_arms_double_serif,
        7 => :arm_style_non_straight_arms_horizontal,
        8 => :arm_style_non_straight_arms_wedge,
        9 => :arm_style_non_straight_arms_vertical,
        10 => :arm_style_non_straight_arms_single_serif,
        11 => :arm_style_non_straight_arms_double_serif,
      }
      I__ARM_STYLE = ARM_STYLE.invert

      STROKE_VARIATION = {
        0 => :stroke_variation_any,
        1 => :stroke_variation_no_fit,
        2 => :stroke_variation_gradual_diagonal,
        3 => :stroke_variation_gradual_transitional,
        4 => :stroke_variation_gradual_vertical,
        5 => :stroke_variation_gradual_horizontal,
        6 => :stroke_variation_rapid_vertical,
        7 => :stroke_variation_rapid_horizontal,
        8 => :stroke_variation_instant_vertical,
      }
      I__STROKE_VARIATION = STROKE_VARIATION.invert

      CONTRAST = {
        0 => :contrast_any,
        1 => :contrast_no_fit,
        2 => :contrast_none,
        3 => :contrast_very_low,
        4 => :contrast_low,
        5 => :contrast_medium_low,
        6 => :contrast_medium,
        7 => :contrast_medium_high,
        8 => :contrast_high,
        9 => :contrast_very_high,
      }
      I__CONTRAST = CONTRAST.invert

      MIDLINE = {
        0 => :midline_any,
        1 => :midline_no_fit,
        2 => :midline_standard_trimmed,
        3 => :midline_standard_pointed,
        4 => :midline_standard_serifed,
        5 => :midline_high_trimmed,
        6 => :midline_high_pointed,
        7 => :midline_high_serifed,
        8 => :midline_constant_trimmed,
        9 => :midline_constant_pointed,
        10 => :midline_constant_serifed,
        11 => :midline_low_trimmed,
        12 => :midline_low_pointed,
        13 => :midline_low_serifed,
      }
      I__MIDLINE = MIDLINE.invert
      def initialize(_io, _parent = nil, _root = self)
        super(_io, _parent, _root)
        _read
      end

      def _read
        @family_type = Kaitai::Struct::Stream::resolve_enum(FAMILY_KIND, @_io.read_u1)
        @serif_style = Kaitai::Struct::Stream::resolve_enum(SERIF_STYLE, @_io.read_u1)
        @weight = Kaitai::Struct::Stream::resolve_enum(WEIGHT, @_io.read_u1)
        @proportion = Kaitai::Struct::Stream::resolve_enum(PROPORTION, @_io.read_u1)
        @contrast = Kaitai::Struct::Stream::resolve_enum(CONTRAST, @_io.read_u1)
        @stroke_variation = Kaitai::Struct::Stream::resolve_enum(STROKE_VARIATION, @_io.read_u1)
        @arm_style = Kaitai::Struct::Stream::resolve_enum(ARM_STYLE, @_io.read_u1)
        @letter_form = Kaitai::Struct::Stream::resolve_enum(LETTER_FORM, @_io.read_u1)
        @midline = Kaitai::Struct::Stream::resolve_enum(MIDLINE, @_io.read_u1)
        @x_height = Kaitai::Struct::Stream::resolve_enum(X_HEIGHT, @_io.read_u1)
        self
      end
      attr_reader :family_type
      attr_reader :serif_style
      attr_reader :weight
      attr_reader :proportion
      attr_reader :contrast
      attr_reader :stroke_variation
      attr_reader :arm_style
      attr_reader :letter_form
      attr_reader :midline
      attr_reader :x_height
    end
    class UnicodeRange < Kaitai::Struct::Struct
      def initialize(_io, _parent = nil, _root = self)
        super(_io, _parent, _root)
        _read
      end

      def _read
        @basic_latin = @_io.read_bits_int_be(1) != 0
        @latin_1_supplement = @_io.read_bits_int_be(1) != 0
        @latin_extended_a = @_io.read_bits_int_be(1) != 0
        @latin_extended_b = @_io.read_bits_int_be(1) != 0
        @ipa_extensions = @_io.read_bits_int_be(1) != 0
        @spacing_modifier_letters = @_io.read_bits_int_be(1) != 0
        @combining_diacritical_marks = @_io.read_bits_int_be(1) != 0
        @basic_greek = @_io.read_bits_int_be(1) != 0
        @greek_symbols_and_coptic = @_io.read_bits_int_be(1) != 0
        @cyrillic = @_io.read_bits_int_be(1) != 0
        @armenian = @_io.read_bits_int_be(1) != 0
        @basic_hebrew = @_io.read_bits_int_be(1) != 0
        @hebrew_extended = @_io.read_bits_int_be(1) != 0
        @basic_arabic = @_io.read_bits_int_be(1) != 0
        @arabic_extended = @_io.read_bits_int_be(1) != 0
        @devanagari = @_io.read_bits_int_be(1) != 0
        @bengali = @_io.read_bits_int_be(1) != 0
        @gurmukhi = @_io.read_bits_int_be(1) != 0
        @gujarati = @_io.read_bits_int_be(1) != 0
        @oriya = @_io.read_bits_int_be(1) != 0
        @tamil = @_io.read_bits_int_be(1) != 0
        @telugu = @_io.read_bits_int_be(1) != 0
        @kannada = @_io.read_bits_int_be(1) != 0
        @malayalam = @_io.read_bits_int_be(1) != 0
        @thai = @_io.read_bits_int_be(1) != 0
        @lao = @_io.read_bits_int_be(1) != 0
        @basic_georgian = @_io.read_bits_int_be(1) != 0
        @georgian_extended = @_io.read_bits_int_be(1) != 0
        @hangul_jamo = @_io.read_bits_int_be(1) != 0
        @latin_extended_additional = @_io.read_bits_int_be(1) != 0
        @greek_extended = @_io.read_bits_int_be(1) != 0
        @general_punctuation = @_io.read_bits_int_be(1) != 0
        @superscripts_and_subscripts = @_io.read_bits_int_be(1) != 0
        @currency_symbols = @_io.read_bits_int_be(1) != 0
        @combining_diacritical_marks_for_symbols = @_io.read_bits_int_be(1) != 0
        @letterlike_symbols = @_io.read_bits_int_be(1) != 0
        @number_forms = @_io.read_bits_int_be(1) != 0
        @arrows = @_io.read_bits_int_be(1) != 0
        @mathematical_operators = @_io.read_bits_int_be(1) != 0
        @miscellaneous_technical = @_io.read_bits_int_be(1) != 0
        @control_pictures = @_io.read_bits_int_be(1) != 0
        @optical_character_recognition = @_io.read_bits_int_be(1) != 0
        @enclosed_alphanumerics = @_io.read_bits_int_be(1) != 0
        @box_drawing = @_io.read_bits_int_be(1) != 0
        @block_elements = @_io.read_bits_int_be(1) != 0
        @geometric_shapes = @_io.read_bits_int_be(1) != 0
        @miscellaneous_symbols = @_io.read_bits_int_be(1) != 0
        @dingbats = @_io.read_bits_int_be(1) != 0
        @cjk_symbols_and_punctuation = @_io.read_bits_int_be(1) != 0
        @hiragana = @_io.read_bits_int_be(1) != 0
        @katakana = @_io.read_bits_int_be(1) != 0
        @bopomofo = @_io.read_bits_int_be(1) != 0
        @hangul_compatibility_jamo = @_io.read_bits_int_be(1) != 0
        @cjk_miscellaneous = @_io.read_bits_int_be(1) != 0
        @enclosed_cjk_letters_and_months = @_io.read_bits_int_be(1) != 0
        @cjk_compatibility = @_io.read_bits_int_be(1) != 0
        @hangul = @_io.read_bits_int_be(1) != 0
        @reserved_for_unicode_subranges1 = @_io.read_bits_int_be(1) != 0
        @reserved_for_unicode_subranges2 = @_io.read_bits_int_be(1) != 0
        @cjk_unified_ideographs = @_io.read_bits_int_be(1) != 0
        @private_use_area = @_io.read_bits_int_be(1) != 0
        @cjk_compatibility_ideographs = @_io.read_bits_int_be(1) != 0
        @alphabetic_presentation_forms = @_io.read_bits_int_be(1) != 0
        @arabic_presentation_forms_a = @_io.read_bits_int_be(1) != 0
        @combining_half_marks = @_io.read_bits_int_be(1) != 0
        @cjk_compatibility_forms = @_io.read_bits_int_be(1) != 0
        @small_form_variants = @_io.read_bits_int_be(1) != 0
        @arabic_presentation_forms_b = @_io.read_bits_int_be(1) != 0
        @halfwidth_and_fullwidth_forms = @_io.read_bits_int_be(1) != 0
        @specials = @_io.read_bits_int_be(1) != 0
        @_io.align_to_byte
        @reserved = @_io.read_bytes(7)
        self
      end
      attr_reader :basic_latin
      attr_reader :latin_1_supplement
      attr_reader :latin_extended_a
      attr_reader :latin_extended_b
      attr_reader :ipa_extensions
      attr_reader :spacing_modifier_letters
      attr_reader :combining_diacritical_marks
      attr_reader :basic_greek
      attr_reader :greek_symbols_and_coptic
      attr_reader :cyrillic
      attr_reader :armenian
      attr_reader :basic_hebrew
      attr_reader :hebrew_extended
      attr_reader :basic_arabic
      attr_reader :arabic_extended
      attr_reader :devanagari
      attr_reader :bengali
      attr_reader :gurmukhi
      attr_reader :gujarati
      attr_reader :oriya
      attr_reader :tamil
      attr_reader :telugu
      attr_reader :kannada
      attr_reader :malayalam
      attr_reader :thai
      attr_reader :lao
      attr_reader :basic_georgian
      attr_reader :georgian_extended
      attr_reader :hangul_jamo
      attr_reader :latin_extended_additional
      attr_reader :greek_extended
      attr_reader :general_punctuation
      attr_reader :superscripts_and_subscripts
      attr_reader :currency_symbols
      attr_reader :combining_diacritical_marks_for_symbols
      attr_reader :letterlike_symbols
      attr_reader :number_forms
      attr_reader :arrows
      attr_reader :mathematical_operators
      attr_reader :miscellaneous_technical
      attr_reader :control_pictures
      attr_reader :optical_character_recognition
      attr_reader :enclosed_alphanumerics
      attr_reader :box_drawing
      attr_reader :block_elements
      attr_reader :geometric_shapes
      attr_reader :miscellaneous_symbols
      attr_reader :dingbats
      attr_reader :cjk_symbols_and_punctuation
      attr_reader :hiragana
      attr_reader :katakana
      attr_reader :bopomofo
      attr_reader :hangul_compatibility_jamo
      attr_reader :cjk_miscellaneous
      attr_reader :enclosed_cjk_letters_and_months
      attr_reader :cjk_compatibility
      attr_reader :hangul
      attr_reader :reserved_for_unicode_subranges1
      attr_reader :reserved_for_unicode_subranges2
      attr_reader :cjk_unified_ideographs
      attr_reader :private_use_area
      attr_reader :cjk_compatibility_ideographs
      attr_reader :alphabetic_presentation_forms
      attr_reader :arabic_presentation_forms_a
      attr_reader :combining_half_marks
      attr_reader :cjk_compatibility_forms
      attr_reader :small_form_variants
      attr_reader :arabic_presentation_forms_b
      attr_reader :halfwidth_and_fullwidth_forms
      attr_reader :specials
      attr_reader :reserved
    end
    class CodePageRange < Kaitai::Struct::Struct
      def initialize(_io, _parent = nil, _root = self)
        super(_io, _parent, _root)
        _read
      end

      def _read
        @symbol_character_set = @_io.read_bits_int_be(1) != 0
        @oem_character_set = @_io.read_bits_int_be(1) != 0
        @macintosh_character_set = @_io.read_bits_int_be(1) != 0
        @reserved_for_alternate_ansi_oem = @_io.read_bits_int_be(7)
        @cp1361_korean_johab = @_io.read_bits_int_be(1) != 0
        @cp950_chinese_traditional_chars_taiwan_and_hong_kong = @_io.read_bits_int_be(1) != 0
        @cp949_korean_wansung = @_io.read_bits_int_be(1) != 0
        @cp936_chinese_simplified_chars_prc_and_singapore = @_io.read_bits_int_be(1) != 0
        @cp932_jis_japan = @_io.read_bits_int_be(1) != 0
        @cp874_thai = @_io.read_bits_int_be(1) != 0
        @reserved_for_alternate_ansi = @_io.read_bits_int_be(8)
        @cp1257_windows_baltic = @_io.read_bits_int_be(1) != 0
        @cp1256_arabic = @_io.read_bits_int_be(1) != 0
        @cp1255_hebrew = @_io.read_bits_int_be(1) != 0
        @cp1254_turkish = @_io.read_bits_int_be(1) != 0
        @cp1253_greek = @_io.read_bits_int_be(1) != 0
        @cp1251_cyrillic = @_io.read_bits_int_be(1) != 0
        @cp1250_latin_2_eastern_europe = @_io.read_bits_int_be(1) != 0
        @cp1252_latin_1 = @_io.read_bits_int_be(1) != 0
        @cp437_us = @_io.read_bits_int_be(1) != 0
        @cp850_we_latin_1 = @_io.read_bits_int_be(1) != 0
        @cp708_arabic_asmo_708 = @_io.read_bits_int_be(1) != 0
        @cp737_greek_former_437_g = @_io.read_bits_int_be(1) != 0
        @cp775_ms_dos_baltic = @_io.read_bits_int_be(1) != 0
        @cp852_latin_2 = @_io.read_bits_int_be(1) != 0
        @cp855_ibm_cyrillic_primarily_russian = @_io.read_bits_int_be(1) != 0
        @cp857_ibm_turkish = @_io.read_bits_int_be(1) != 0
        @cp860_ms_dos_portuguese = @_io.read_bits_int_be(1) != 0
        @cp861_ms_dos_icelandic = @_io.read_bits_int_be(1) != 0
        @cp862_hebrew = @_io.read_bits_int_be(1) != 0
        @cp863_ms_dos_canadian_french = @_io.read_bits_int_be(1) != 0
        @cp864_arabic = @_io.read_bits_int_be(1) != 0
        @cp865_ms_dos_nordic = @_io.read_bits_int_be(1) != 0
        @cp866_ms_dos_russian = @_io.read_bits_int_be(1) != 0
        @cp869_ibm_greek = @_io.read_bits_int_be(1) != 0
        @reserved_for_oem = @_io.read_bits_int_be(16)
        self
      end
      attr_reader :symbol_character_set
      attr_reader :oem_character_set
      attr_reader :macintosh_character_set
      attr_reader :reserved_for_alternate_ansi_oem
      attr_reader :cp1361_korean_johab
      attr_reader :cp950_chinese_traditional_chars_taiwan_and_hong_kong
      attr_reader :cp949_korean_wansung
      attr_reader :cp936_chinese_simplified_chars_prc_and_singapore
      attr_reader :cp932_jis_japan
      attr_reader :cp874_thai
      attr_reader :reserved_for_alternate_ansi
      attr_reader :cp1257_windows_baltic
      attr_reader :cp1256_arabic
      attr_reader :cp1255_hebrew
      attr_reader :cp1254_turkish
      attr_reader :cp1253_greek
      attr_reader :cp1251_cyrillic
      attr_reader :cp1250_latin_2_eastern_europe
      attr_reader :cp1252_latin_1
      attr_reader :cp437_us
      attr_reader :cp850_we_latin_1
      attr_reader :cp708_arabic_asmo_708
      attr_reader :cp737_greek_former_437_g
      attr_reader :cp775_ms_dos_baltic
      attr_reader :cp852_latin_2
      attr_reader :cp855_ibm_cyrillic_primarily_russian
      attr_reader :cp857_ibm_turkish
      attr_reader :cp860_ms_dos_portuguese
      attr_reader :cp861_ms_dos_icelandic
      attr_reader :cp862_hebrew
      attr_reader :cp863_ms_dos_canadian_french
      attr_reader :cp864_arabic
      attr_reader :cp865_ms_dos_nordic
      attr_reader :cp866_ms_dos_russian
      attr_reader :cp869_ibm_greek
      attr_reader :reserved_for_oem
    end

    ##
    # The version number for this OS/2 table.
    attr_reader :version

    ##
    # The Average Character Width parameter specifies the arithmetic average of the escapement (width) of all of the 26 lowercase letters a through z of the Latin alphabet and the space character. If any of the 26 lowercase letters are not present, this parameter should equal the weighted average of all glyphs in the font. For non-UGL (platform 3, encoding 0) fonts, use the unweighted average.
    attr_reader :x_avg_char_width

    ##
    # Indicates the visual weight (degree of blackness or thickness of strokes) of the characters in the font.
    attr_reader :weight_class

    ##
    # Indicates a relative change from the normal aspect ratio (width to height ratio) as specified by a font designer for the glyphs in a font.
    attr_reader :width_class

    ##
    # Indicates font embedding licensing rights for the font. Embeddable fonts may be stored in a document. When a document with embedded fonts is opened on a system that does not have the font installed (the remote system), the embedded font may be loaded for temporary (and in some cases, permanent) use on that system by an embedding-aware application. Embedding licensing rights are granted by the vendor of the font.
    attr_reader :fs_type

    ##
    # The recommended horizontal size in font design units for subscripts for this font.
    attr_reader :y_subscript_x_size

    ##
    # The recommended vertical size in font design units for subscripts for this font.
    attr_reader :y_subscript_y_size

    ##
    # The recommended horizontal offset in font design untis for subscripts for this font.
    attr_reader :y_subscript_x_offset

    ##
    # The recommended vertical offset in font design units from the baseline for subscripts for this font.
    attr_reader :y_subscript_y_offset

    ##
    # The recommended horizontal size in font design units for superscripts for this font.
    attr_reader :y_superscript_x_size

    ##
    # The recommended vertical size in font design units for superscripts for this font.
    attr_reader :y_superscript_y_size

    ##
    # The recommended horizontal offset in font design units for superscripts for this font.
    attr_reader :y_superscript_x_offset

    ##
    # The recommended vertical offset in font design units from the baseline for superscripts for this font.
    attr_reader :y_superscript_y_offset

    ##
    # Width of the strikeout stroke in font design units.
    attr_reader :y_strikeout_size

    ##
    # The position of the strikeout stroke relative to the baseline in font design units.
    attr_reader :y_strikeout_position

    ##
    # This parameter is a classification of font-family design.
    attr_reader :s_family_class
    attr_reader :panose
    attr_reader :unicode_range

    ##
    # The four character identifier for the vendor of the given type face.
    attr_reader :ach_vend_id

    ##
    # Contains information concerning the nature of the font patterns
    attr_reader :selection

    ##
    # The minimum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and encoding ID 0 or 1.
    attr_reader :first_char_index

    ##
    # The maximum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and encoding ID 0 or 1.
    attr_reader :last_char_index

    ##
    # The typographic ascender for this font.
    attr_reader :typo_ascender

    ##
    # The typographic descender for this font.
    attr_reader :typo_descender

    ##
    # The typographic line gap for this font.
    attr_reader :typo_line_gap

    ##
    # The ascender metric for Windows.
    attr_reader :win_ascent

    ##
    # The descender metric for Windows.
    attr_reader :win_descent

    ##
    # This field is used to specify the code pages encompassed by the font file in the `cmap` subtable for platform 3, encoding ID 1 (Microsoft platform).
    attr_reader :code_page_range
  end
  class Fixed < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @major = @_io.read_u2be
      @minor = @_io.read_u2be
      self
    end
    attr_reader :major
    attr_reader :minor
  end
  class Glyf < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @number_of_contours = @_io.read_s2be
      @x_min = @_io.read_s2be
      @y_min = @_io.read_s2be
      @x_max = @_io.read_s2be
      @y_max = @_io.read_s2be
      if number_of_contours > 0
        @value = SimpleGlyph.new(@_io, self, @_root)
      end
      self
    end
    class SimpleGlyph < Kaitai::Struct::Struct
      def initialize(_io, _parent = nil, _root = self)
        super(_io, _parent, _root)
        _read
      end

      def _read
        @end_pts_of_contours = []
        (_parent.number_of_contours).times { |i|
          @end_pts_of_contours << @_io.read_u2be
        }
        @instruction_length = @_io.read_u2be
        @instructions = @_io.read_bytes(instruction_length)
        @flags = []
        (point_count).times { |i|
          @flags << Flag.new(@_io, self, @_root)
        }
        self
      end
      class Flag < Kaitai::Struct::Struct
        def initialize(_io, _parent = nil, _root = self)
          super(_io, _parent, _root)
          _read
        end

        def _read
          @reserved = @_io.read_bits_int_be(2)
          @y_is_same = @_io.read_bits_int_be(1) != 0
          @x_is_same = @_io.read_bits_int_be(1) != 0
          @repeat = @_io.read_bits_int_be(1) != 0
          @y_short_vector = @_io.read_bits_int_be(1) != 0
          @x_short_vector = @_io.read_bits_int_be(1) != 0
          @on_curve = @_io.read_bits_int_be(1) != 0
          @_io.align_to_byte
          if repeat
            @repeat_value = @_io.read_u1
          end
          self
        end
        attr_reader :reserved
        attr_reader :y_is_same
        attr_reader :x_is_same
        attr_reader :repeat
        attr_reader :y_short_vector
        attr_reader :x_short_vector
        attr_reader :on_curve
        attr_reader :repeat_value
      end
      def point_count
        return @point_count unless @point_count.nil?
        @point_count = (end_pts_of_contours.max + 1)
        @point_count
      end
      attr_reader :end_pts_of_contours
      attr_reader :instruction_length
      attr_reader :instructions
      attr_reader :flags
    end
    attr_reader :number_of_contours
    attr_reader :x_min
    attr_reader :y_min
    attr_reader :x_max
    attr_reader :y_max
    attr_reader :value
  end

  ##
  # 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.
  class Cvt < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @fwords = []
      i = 0
      while not @_io.eof?
        @fwords << @_io.read_s2be
        i += 1
      end
      self
    end
    attr_reader :fwords
  end
  class Maxp < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @table_version_number = Fixed.new(@_io, self, @_root)
      @num_glyphs = @_io.read_u2be
      if is_version10
        @version10_body = MaxpVersion10Body.new(@_io, self, @_root)
      end
      self
    end
    def is_version10
      return @is_version10 unless @is_version10.nil?
      @is_version10 =  ((table_version_number.major == 1) && (table_version_number.minor == 0)) 
      @is_version10
    end

    ##
    # 0x00010000 for version 1.0.
    attr_reader :table_version_number

    ##
    # The number of glyphs in the font.
    attr_reader :num_glyphs
    attr_reader :version10_body
  end
  class MaxpVersion10Body < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @max_points = @_io.read_u2be
      @max_contours = @_io.read_u2be
      @max_composite_points = @_io.read_u2be
      @max_composite_contours = @_io.read_u2be
      @max_zones = @_io.read_u2be
      @max_twilight_points = @_io.read_u2be
      @max_storage = @_io.read_u2be
      @max_function_defs = @_io.read_u2be
      @max_instruction_defs = @_io.read_u2be
      @max_stack_elements = @_io.read_u2be
      @max_size_of_instructions = @_io.read_u2be
      @max_component_elements = @_io.read_u2be
      @max_component_depth = @_io.read_u2be
      self
    end

    ##
    # Maximum points in a non-composite glyph.
    attr_reader :max_points

    ##
    # Maximum contours in a non-composite glyph.
    attr_reader :max_contours

    ##
    # Maximum points in a composite glyph.
    attr_reader :max_composite_points

    ##
    # Maximum contours in a composite glyph.
    attr_reader :max_composite_contours

    ##
    # 1 if instructions do not use the twilight zone (Z0), or 2 if instructions do use Z0; should be set to 2 in most cases.
    attr_reader :max_zones

    ##
    # Maximum points used in Z0.
    attr_reader :max_twilight_points

    ##
    # Number of Storage Area locations.
    attr_reader :max_storage

    ##
    # Number of FDEFs.
    attr_reader :max_function_defs

    ##
    # Number of IDEFs.
    attr_reader :max_instruction_defs

    ##
    # Maximum stack depth.
    attr_reader :max_stack_elements

    ##
    # Maximum byte count for glyph instructions.
    attr_reader :max_size_of_instructions

    ##
    # Maximum number of components referenced at "top level" for any composite glyph.
    attr_reader :max_component_elements

    ##
    # Maximum levels of recursion; 1 for simple components.
    attr_reader :max_component_depth
  end
  class OffsetTable < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @sfnt_version = Fixed.new(@_io, self, @_root)
      @num_tables = @_io.read_u2be
      @search_range = @_io.read_u2be
      @entry_selector = @_io.read_u2be
      @range_shift = @_io.read_u2be
      self
    end
    attr_reader :sfnt_version
    attr_reader :num_tables
    attr_reader :search_range
    attr_reader :entry_selector
    attr_reader :range_shift
  end

  ##
  # cmap - Character To Glyph Index Mapping Table This table defines the mapping of character codes to the glyph index values used in the font.
  class Cmap < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @version_number = @_io.read_u2be
      @number_of_encoding_tables = @_io.read_u2be
      @tables = []
      (number_of_encoding_tables).times { |i|
        @tables << SubtableHeader.new(@_io, self, @_root)
      }
      self
    end
    class SubtableHeader < Kaitai::Struct::Struct
      def initialize(_io, _parent = nil, _root = self)
        super(_io, _parent, _root)
        _read
      end

      def _read
        @platform_id = @_io.read_u2be
        @encoding_id = @_io.read_u2be
        @subtable_offset = @_io.read_u4be
        self
      end
      def table
        return @table unless @table.nil?
        io = _parent._io
        _pos = io.pos
        io.seek(subtable_offset)
        @table = Subtable.new(io, self, @_root)
        io.seek(_pos)
        @table
      end
      attr_reader :platform_id
      attr_reader :encoding_id
      attr_reader :subtable_offset
    end
    class Subtable < Kaitai::Struct::Struct

      SUBTABLE_FORMAT = {
        0 => :subtable_format_byte_encoding_table,
        2 => :subtable_format_high_byte_mapping_through_table,
        4 => :subtable_format_segment_mapping_to_delta_values,
        6 => :subtable_format_trimmed_table_mapping,
      }
      I__SUBTABLE_FORMAT = SUBTABLE_FORMAT.invert
      def initialize(_io, _parent = nil, _root = self)
        super(_io, _parent, _root)
        _read
      end

      def _read
        @format = Kaitai::Struct::Stream::resolve_enum(SUBTABLE_FORMAT, @_io.read_u2be)
        @length = @_io.read_u2be
        @version = @_io.read_u2be
        case format
        when :subtable_format_byte_encoding_table
          @_raw_value = @_io.read_bytes((length - 6))
          _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
          @value = ByteEncodingTable.new(_io__raw_value, self, @_root)
        when :subtable_format_segment_mapping_to_delta_values
          @_raw_value = @_io.read_bytes((length - 6))
          _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
          @value = SegmentMappingToDeltaValues.new(_io__raw_value, self, @_root)
        when :subtable_format_high_byte_mapping_through_table
          @_raw_value = @_io.read_bytes((length - 6))
          _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
          @value = HighByteMappingThroughTable.new(_io__raw_value, self, @_root)
        when :subtable_format_trimmed_table_mapping
          @_raw_value = @_io.read_bytes((length - 6))
          _io__raw_value = Kaitai::Struct::Stream.new(@_raw_value)
          @value = TrimmedTableMapping.new(_io__raw_value, self, @_root)
        else
          @value = @_io.read_bytes((length - 6))
        end
        self
      end
      class ByteEncodingTable < Kaitai::Struct::Struct
        def initialize(_io, _parent = nil, _root = self)
          super(_io, _parent, _root)
          _read
        end

        def _read
          @glyph_id_array = @_io.read_bytes(256)
          self
        end
        attr_reader :glyph_id_array
      end
      class HighByteMappingThroughTable < Kaitai::Struct::Struct
        def initialize(_io, _parent = nil, _root = self)
          super(_io, _parent, _root)
          _read
        end

        def _read
          @sub_header_keys = []
          (256).times { |i|
            @sub_header_keys << @_io.read_u2be
          }
          self
        end
        attr_reader :sub_header_keys
      end
      class SegmentMappingToDeltaValues < Kaitai::Struct::Struct
        def initialize(_io, _parent = nil, _root = self)
          super(_io, _parent, _root)
          _read
        end

        def _read
          @seg_count_x2 = @_io.read_u2be
          @search_range = @_io.read_u2be
          @entry_selector = @_io.read_u2be
          @range_shift = @_io.read_u2be
          @end_count = []
          (seg_count).times { |i|
            @end_count << @_io.read_u2be
          }
          @reserved_pad = @_io.read_u2be
          @start_count = []
          (seg_count).times { |i|
            @start_count << @_io.read_u2be
          }
          @id_delta = []
          (seg_count).times { |i|
            @id_delta << @_io.read_u2be
          }
          @id_range_offset = []
          (seg_count).times { |i|
            @id_range_offset << @_io.read_u2be
          }
          @glyph_id_array = []
          i = 0
          while not @_io.eof?
            @glyph_id_array << @_io.read_u2be
            i += 1
          end
          self
        end
        def seg_count
          return @seg_count unless @seg_count.nil?
          @seg_count = (seg_count_x2 / 2)
          @seg_count
        end
        attr_reader :seg_count_x2
        attr_reader :search_range
        attr_reader :entry_selector
        attr_reader :range_shift
        attr_reader :end_count
        attr_reader :reserved_pad
        attr_reader :start_count
        attr_reader :id_delta
        attr_reader :id_range_offset
        attr_reader :glyph_id_array
      end
      class TrimmedTableMapping < Kaitai::Struct::Struct
        def initialize(_io, _parent = nil, _root = self)
          super(_io, _parent, _root)
          _read
        end

        def _read
          @first_code = @_io.read_u2be
          @entry_count = @_io.read_u2be
          @glyph_id_array = []
          (entry_count).times { |i|
            @glyph_id_array << @_io.read_u2be
          }
          self
        end
        attr_reader :first_code
        attr_reader :entry_count
        attr_reader :glyph_id_array
      end
      attr_reader :format
      attr_reader :length
      attr_reader :version
      attr_reader :value
      attr_reader :_raw_value
    end
    attr_reader :version_number
    attr_reader :number_of_encoding_tables
    attr_reader :tables
  end
  attr_reader :offset_table
  attr_reader :directory_table
end