Class: Fontisan::Parsers::DfontParser
- Inherits:
-
Object
- Object
- Fontisan::Parsers::DfontParser
- Defined in:
- lib/fontisan/parsers/dfont_parser.rb
Overview
Parser for Apple dfont (Data Fork Font) resource fork format.
dfont files store resource fork data in the data fork, containing TrueType or OpenType SFNT data embedded in a resource fork structure.
Defined Under Namespace
Classes: ResourceHeader, ResourceReference, ResourceType
Class Method Summary collapse
-
.dfont?(io) ⇒ Boolean
Check if file is valid dfont resource fork.
-
.extract_resource_data(io, header, resource_info) ⇒ String
private
Extract resource data at specific offset.
-
.extract_sfnt(io, index: 0) ⇒ String
Extract SFNT data from dfont file.
-
.find_sfnt_resources(io, header) ⇒ Array<Hash>
private
Find all sfnt resources in resource map.
-
.parse_header(io) ⇒ ResourceHeader
private
Parse resource fork header.
-
.sfnt_count(io) ⇒ Integer
Count number of sfnt resources (fonts) in dfont.
Class Method Details
.dfont?(io) ⇒ Boolean
Check if file is valid dfont resource fork
81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/fontisan/parsers/dfont_parser.rb', line 81 def self.dfont?(io) io.rewind header_bytes = io.read(16) io.rewind return false if header_bytes.nil? || header_bytes.length < 16 # Basic sanity check on resource fork structure data_offset = header_bytes[0..3].unpack1("N") map_offset = header_bytes[4..7].unpack1("N") data_offset.positive? && map_offset > data_offset end |
.extract_resource_data(io, header, resource_info) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Extract resource data at specific offset
173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/fontisan/parsers/dfont_parser.rb', line 173 def self.extract_resource_data(io, header, resource_info) # Calculate absolute offset to resource data # The offset in the reference is relative to the start of the resource data section data_offset = header.resource_data_offset + resource_info[:offset] io.seek(data_offset) # Read data length (first 4 bytes of resource data) data_length = io.read(4).unpack1("N") # Read the actual data io.read(data_length) end |
.extract_sfnt(io, index: 0) ⇒ String
Extract SFNT data from dfont file
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/fontisan/parsers/dfont_parser.rb', line 51 def self.extract_sfnt(io, index: 0) header = parse_header(io) sfnt_resources = find_sfnt_resources(io, header) if sfnt_resources.empty? raise InvalidFontError, "No sfnt resources found in dfont file" end if index >= sfnt_resources.length raise InvalidFontError, "Font index #{index} out of range (dfont has #{sfnt_resources.length} fonts)" end extract_resource_data(io, header, sfnt_resources[index]) end |
.find_sfnt_resources(io, header) ⇒ Array<Hash>
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Find all sfnt resources in resource map
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/fontisan/parsers/dfont_parser.rb', line 114 def self.find_sfnt_resources(io, header) # Seek to resource map io.seek(header.resource_map_offset) # Skip resource map header (22 bytes reserved + 4 bytes attributes + 2 bytes type list offset + 2 bytes name list offset) # The actual layout is: # - Bytes 0-15: Copy of resource header (16 bytes) # - Bytes 16-19: Reserved for handle to next resource map (4 bytes) # - Bytes 20-21: Reserved for file reference number (2 bytes) # - Bytes 22-23: Resource file attributes (2 bytes) # - Bytes 24-25: Offset to type list (2 bytes) # - Bytes 26-27: Offset to name list (2 bytes) io.seek(header.resource_map_offset + 24) # Read type list offset (relative to start of resource map) type_list_offset = io.read(2).unpack1("n") # Seek to type list io.seek(header.resource_map_offset + type_list_offset) # Read number of types minus 1 type_count_minus_1 = io.read(2).unpack1("n") type_count = type_count_minus_1 + 1 # Find 'sfnt' type in type list sfnt_type = nil type_count.times do type_entry = ResourceType.read(io) if type_entry.type_code == "sfnt" sfnt_type = type_entry break end end return [] unless sfnt_type # Read reference list for sfnt resources reference_list_offset = header.resource_map_offset + type_list_offset + sfnt_type.reference_list_offset io.seek(reference_list_offset) resource_count = sfnt_type.resource_count_minus_1 + 1 resources = [] resource_count.times do ref = ResourceReference.read(io) resources << { id: ref.resource_id, offset: ref.data_offset } end resources end |
.parse_header(io) ⇒ ResourceHeader
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Parse resource fork header
101 102 103 104 105 106 |
# File 'lib/fontisan/parsers/dfont_parser.rb', line 101 def self.parse_header(io) io.rewind ResourceHeader.read(io) rescue BinData::ValidityError => e raise InvalidFontError, "Invalid dfont resource header: #{e.}" end |
.sfnt_count(io) ⇒ Integer
Count number of sfnt resources (fonts) in dfont
71 72 73 74 75 |
# File 'lib/fontisan/parsers/dfont_parser.rb', line 71 def self.sfnt_count(io) header = parse_header(io) sfnt_resources = find_sfnt_resources(io, header) sfnt_resources.length end |