Class: Fontisan::Tables::Sbix
- Inherits:
-
Binary::BaseRecord
- Object
- BinData::Record
- Binary::BaseRecord
- Fontisan::Tables::Sbix
- Defined in:
- lib/fontisan/tables/sbix.rb
Overview
sbix (Standard Bitmap Graphics) table parser
The sbix table contains embedded bitmap graphics (PNG, JPEG, TIFF) organized by strike sizes. This is Apple’s format for color emoji.
sbix Table Structure: “‘ sbix Table = Header (8 bytes)
+ Strike Offsets Array (4 bytes × numStrikes)
+ Strike Data (variable)
“‘
Header (8 bytes):
-
version (uint16): Table version (1)
-
flags (uint16): Flags (0)
-
numStrikes (uint32): Number of bitmap strikes
Each Strike contains:
-
ppem (uint16): Pixels per em
-
ppi (uint16): Pixels per inch (usually 72)
-
glyphDataOffsets (uint32 × numGlyphs+1): Array of glyph data offsets
-
glyph data records (variable)
Glyph Data Record:
-
originOffsetX (int16): X offset
-
originOffsetY (int16): Y offset
-
graphicType (uint32): ‘png ’, ‘jpg ’, ‘tiff’, ‘dupe’, ‘mask’
-
data (variable): Image data
Reference: docs.microsoft.com/en-us/typography/opentype/spec/sbix
Constant Summary collapse
- TAG =
OpenType table tag for sbix
"sbix"- VERSION_1 =
Supported sbix version
1- GRAPHIC_TYPE_PNG =
Graphic type constants (4-byte ASCII codes)
0x706E6720- GRAPHIC_TYPE_JPG =
‘png ’
0x6A706720- GRAPHIC_TYPE_TIFF =
‘jpg ’
0x74696666- GRAPHIC_TYPE_DUPE =
‘tiff’
0x64757065- GRAPHIC_TYPE_MASK =
‘dupe’
0x6D61736B- GRAPHIC_TYPE_NAMES =
Graphic type names
{ GRAPHIC_TYPE_PNG => "PNG", GRAPHIC_TYPE_JPG => "JPEG", GRAPHIC_TYPE_TIFF => "TIFF", GRAPHIC_TYPE_DUPE => "dupe", GRAPHIC_TYPE_MASK => "mask", }.freeze
Instance Attribute Summary collapse
-
#flags ⇒ Integer
readonly
Flags (reserved, should be 0).
-
#num_strikes ⇒ Integer
readonly
Number of bitmap strikes.
-
#raw_data ⇒ String
readonly
Raw binary data for the entire sbix table.
-
#strike_offsets ⇒ Array<Integer>
readonly
Offsets to strike data from start of table.
-
#strikes ⇒ Array<Hash>
readonly
Parsed strike records.
-
#version ⇒ Integer
readonly
Sbix version (should be 1).
Class Method Summary collapse
-
.read(io) ⇒ Sbix
Override read to parse sbix structure.
Instance Method Summary collapse
-
#glyph_data(glyph_id, ppem) ⇒ Hash?
Get glyph data at specific ppem.
-
#has_glyph_at_ppem?(glyph_id, ppem) ⇒ Boolean
Check if glyph has bitmap at ppem.
-
#parse!(data) ⇒ Object
Parse the sbix table structure.
-
#ppem_sizes ⇒ Array<Integer>
Get all ppem sizes.
-
#strike_for_ppem(ppem) ⇒ Hash?
Get strike for specific ppem.
-
#supported_formats ⇒ Array<String>
Get supported graphic formats across all strikes.
-
#valid? ⇒ Boolean
Validate the sbix table structure.
Instance Attribute Details
#flags ⇒ Integer (readonly)
Returns Flags (reserved, should be 0).
71 72 73 |
# File 'lib/fontisan/tables/sbix.rb', line 71 def flags @flags end |
#num_strikes ⇒ Integer (readonly)
Returns Number of bitmap strikes.
74 75 76 |
# File 'lib/fontisan/tables/sbix.rb', line 74 def num_strikes @num_strikes end |
#raw_data ⇒ String (readonly)
Returns Raw binary data for the entire sbix table.
83 84 85 |
# File 'lib/fontisan/tables/sbix.rb', line 83 def raw_data @raw_data end |
#strike_offsets ⇒ Array<Integer> (readonly)
Returns Offsets to strike data from start of table.
77 78 79 |
# File 'lib/fontisan/tables/sbix.rb', line 77 def strike_offsets @strike_offsets end |
#strikes ⇒ Array<Hash> (readonly)
Returns Parsed strike records.
80 81 82 |
# File 'lib/fontisan/tables/sbix.rb', line 80 def strikes @strikes end |
#version ⇒ Integer (readonly)
Returns sbix version (should be 1).
68 69 70 |
# File 'lib/fontisan/tables/sbix.rb', line 68 def version @version end |
Class Method Details
.read(io) ⇒ Sbix
Override read to parse sbix structure
89 90 91 92 93 94 95 96 |
# File 'lib/fontisan/tables/sbix.rb', line 89 def self.read(io) sbix = new return sbix if io.nil? data = io.is_a?(String) ? io : io.read sbix.parse!(data) sbix end |
Instance Method Details
#glyph_data(glyph_id, ppem) ⇒ Hash?
Get glyph data at specific ppem
124 125 126 127 128 129 |
# File 'lib/fontisan/tables/sbix.rb', line 124 def glyph_data(glyph_id, ppem) strike = strike_for_ppem(ppem) return nil unless strike extract_glyph_data(strike, glyph_id) end |
#has_glyph_at_ppem?(glyph_id, ppem) ⇒ Boolean
Check if glyph has bitmap at ppem
153 154 155 156 |
# File 'lib/fontisan/tables/sbix.rb', line 153 def has_glyph_at_ppem?(glyph_id, ppem) data = glyph_data(glyph_id, ppem) !data.nil? && data[:data] && !data[:data].empty? end |
#parse!(data) ⇒ Object
Parse the sbix table structure
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/fontisan/tables/sbix.rb', line 102 def parse!(data) @raw_data = data io = StringIO.new(data) # Parse sbix header (8 bytes) parse_header(io) validate_header! # Parse strike offsets parse_strike_offsets(io) # Parse strike records parse_strikes rescue StandardError => e raise CorruptedTableError, "Failed to parse sbix table: #{e.}" end |
#ppem_sizes ⇒ Array<Integer>
Get all ppem sizes
142 143 144 145 146 |
# File 'lib/fontisan/tables/sbix.rb', line 142 def ppem_sizes return [] unless strikes strikes.map { |s| s[:ppem] }.uniq.sort end |
#strike_for_ppem(ppem) ⇒ Hash?
Get strike for specific ppem
135 136 137 |
# File 'lib/fontisan/tables/sbix.rb', line 135 def strike_for_ppem(ppem) strikes&.find { |s| s[:ppem] == ppem } end |
#supported_formats ⇒ Array<String>
Get supported graphic formats across all strikes
161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/fontisan/tables/sbix.rb', line 161 def supported_formats return [] unless strikes formats = [] strikes.each do |strike| # Sample first few glyphs to detect formats strike[:graphic_types]&.each do |type| format_name = GRAPHIC_TYPE_NAMES[type] formats << format_name if format_name && !["dupe", "mask"].include?(format_name) end end formats.uniq.compact end |
#valid? ⇒ Boolean
Validate the sbix table structure
179 180 181 182 183 184 185 186 |
# File 'lib/fontisan/tables/sbix.rb', line 179 def valid? return false if version.nil? return false if version != VERSION_1 return false if num_strikes.nil? || num_strikes.negative? return false unless strikes true end |