Class: Fontisan::Tables::Cff2::TableReader
- Inherits:
-
Object
- Object
- Fontisan::Tables::Cff2::TableReader
- Defined in:
- lib/fontisan/tables/cff2/table_reader.rb
Overview
Table reader for CFF2 with Variable Store support
CFF2TableReader parses CFF2 tables and extracts variation data from the Variable Store, which is essential for applying hints to variable fonts with CFF2 outlines.
Variable Store Structure:
-
RegionList: Defines variation regions (min/peak/max per axis)
-
ItemVariationData: Contains delta arrays per region
Reference: Adobe Technical Note #5177 (CFF2) Reference: OpenType spec - Item Variation Store
Constant Summary collapse
- VSTORE_OPERATOR =
CFF2-specific operators
24
Instance Attribute Summary collapse
-
#data ⇒ String
readonly
Binary CFF2 data.
-
#header ⇒ Hash
readonly
CFF2 header information.
-
#top_dict ⇒ Hash
readonly
Top DICT data.
-
#variable_store ⇒ Hash?
readonly
Variable Store data.
Instance Method Summary collapse
-
#initialize(data) ⇒ TableReader
constructor
Initialize reader with CFF2 data.
-
#read_charstrings(offset) ⇒ Cff::Index
Read CharStrings INDEX.
-
#read_header ⇒ Hash
Read CFF2 header.
-
#read_item_variation_data ⇒ Array<Hash>
Read Item Variation Data.
-
#read_private_dict(size, offset) ⇒ Hash
Read Private DICT with blend support.
-
#read_region ⇒ Hash
Read a single region.
-
#read_region_list ⇒ Array<Hash>
Read Region List from Variable Store.
-
#read_single_item_variation_data ⇒ Hash
Read a single Item Variation Data entry.
-
#read_top_dict ⇒ Hash
Read Top DICT.
-
#read_variable_store ⇒ Hash?
Read Variable Store from Top DICT.
Constructor Details
#initialize(data) ⇒ TableReader
Initialize reader with CFF2 data
46 47 48 49 50 51 52 53 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 46 def initialize(data) @data = data @io = StringIO.new(data) @io.set_encoding(Encoding::BINARY) @header = nil @top_dict = nil @variable_store = nil end |
Instance Attribute Details
#data ⇒ String (readonly)
Returns Binary CFF2 data.
29 30 31 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 29 def data @data end |
#header ⇒ Hash (readonly)
Returns CFF2 header information.
32 33 34 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 32 def header @header end |
#top_dict ⇒ Hash (readonly)
Returns Top DICT data.
35 36 37 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 35 def top_dict @top_dict end |
#variable_store ⇒ Hash? (readonly)
Returns Variable Store data.
38 39 40 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 38 def variable_store @variable_store end |
Instance Method Details
#read_charstrings(offset) ⇒ Cff::Index
Read CharStrings INDEX
234 235 236 237 238 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 234 def read_charstrings(offset) @io.seek(offset) require_relative "../cff/index" Cff::Index.new(@io, start_offset: offset) end |
#read_header ⇒ Hash
Read CFF2 header
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 58 def read_header @io.rewind @header = { major_version: read_uint8, minor_version: read_uint8, header_size: read_uint8, top_dict_length: read_uint16, } # Validate CFF2 version unless @header[:major_version] == 2 && @header[:minor_version].zero? raise CorruptedTableError, "Invalid CFF2 version: #{@header[:major_version]}.#{@header[:minor_version]}" end @header end |
#read_item_variation_data ⇒ Array<Hash>
Read Item Variation Data
Contains delta arrays per region for varying values
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 155 def read_item_variation_data data_count = read_uint16 return [] if data_count.zero? item_variation_data = [] data_count.times do |_idx| item_data = read_single_item_variation_data item_variation_data << item_data rescue EOFError # break end item_variation_data end |
#read_private_dict(size, offset) ⇒ Hash
Read Private DICT with blend support
Private DICT in CFF2 can contain blend operators for variable hint parameters.
224 225 226 227 228 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 224 def read_private_dict(size, offset) @io.seek(offset) private_dict_data = @io.read(size) parse_dict(private_dict_data) end |
#read_region ⇒ Hash
Read a single region
135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 135 def read_region axis_count = read_uint16 axes = [] axis_count.times do axes << { start_coord: read_f2dot14, peak_coord: read_f2dot14, end_coord: read_f2dot14, } end { axis_count: axis_count, axes: axes } end |
#read_region_list ⇒ Array<Hash>
Read Region List from Variable Store
Region List defines variation regions, where each region specifies min/peak/max values per axis.
120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 120 def read_region_list region_count = read_uint16 regions = [] region_count.times do region = read_region regions << region end regions end |
#read_single_item_variation_data ⇒ Hash
Read a single Item Variation Data entry
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 174 def read_single_item_variation_data item_count = read_uint16 short_delta_count = read_uint16 region_index_count = read_uint16 # Read region indices region_indices = [] region_index_count.times do region_indices << read_uint16 end # Read delta sets delta_sets = [] item_count.times do |_item_idx| deltas = [] # Short deltas (16-bit) short_delta_count.times do break if @io.eof? deltas << read_int16 end # Long deltas (8-bit) for remaining regions (region_index_count - short_delta_count).times do break if @io.eof? deltas << read_int8 end delta_sets << deltas rescue EOFError # break end { item_count: item_count, region_indices: region_indices, delta_sets: delta_sets, } end |
#read_top_dict ⇒ Hash
Read Top DICT
79 80 81 82 83 84 85 86 87 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 79 def read_top_dict read_header unless @header # Seek to Top DICT (after header) @io.seek(@header[:header_size]) top_dict_data = @io.read(@header[:top_dict_length]) @top_dict = parse_dict(top_dict_data) end |
#read_variable_store ⇒ Hash?
Read Variable Store from Top DICT
The Variable Store is referenced by the vstore operator (24) in the Top DICT. It contains regions and deltas for variation.
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/fontisan/tables/cff2/table_reader.rb', line 95 def read_variable_store read_top_dict unless @top_dict # Check if Variable Store is present (operator 24) vstore_offset = @top_dict[VSTORE_OPERATOR] return nil unless vstore_offset # Seek to Variable Store @io.seek(vstore_offset) # Parse Variable Store structure @variable_store = { regions: read_region_list, item_variation_data: read_item_variation_data, } @variable_store end |