Class: Fontisan::DfontCollection
- Inherits:
-
Object
- Object
- Fontisan::DfontCollection
- Includes:
- Collection::SharedLogic
- Defined in:
- lib/fontisan/dfont_collection.rb
Overview
DfontCollection represents an Apple dfont suitcase containing multiple fonts
dfont (Data Fork Font) is an Apple-specific format that stores Mac font suitcase resources in the data fork. It can contain multiple SFNT fonts (TrueType or OpenType).
This class provides a collection interface similar to TrueTypeCollection and OpenTypeCollection for consistency.
Instance Attribute Summary collapse
-
#num_fonts ⇒ Integer
(also: #font_count)
readonly
Number of fonts in collection.
-
#path ⇒ String
readonly
Path to dfont file.
Class Method Summary collapse
-
.collection_format ⇒ String
Get the collection format identifier.
-
.from_file(path) ⇒ DfontCollection
Load dfont collection from file.
Instance Method Summary collapse
-
#collection? ⇒ Boolean
Whether this object represents a font collection rather than a single font.
-
#collection_info(io, path) ⇒ Models::CollectionInfo
Get comprehensive collection metadata.
-
#extract_fonts(io) ⇒ Array<TrueTypeFont, OpenTypeFont>
Extract all fonts from dfont.
-
#font(index, io, mode: LoadingModes::FULL) ⇒ TrueTypeFont, OpenTypeFont
Get specific font from collection.
-
#font_offsets ⇒ Array<Integer>
Get font offsets (indices for dfont).
-
#format ⇒ Symbol
High-level pipeline format identifier.
-
#initialize(path, num_fonts) ⇒ DfontCollection
constructor
private
Initialize collection.
-
#list_fonts(io) ⇒ Models::CollectionListInfo
List fonts in collection (brief info).
-
#outline_type ⇒ Symbol
Outline representation.
-
#table_names ⇒ Array<String>
Collections have no single SFNT table directory.
-
#valid? ⇒ Boolean
Check if collection is valid.
-
#variation_type ⇒ Symbol
Variation profile.
-
#version_string ⇒ String
Get the collection version as a string.
Methods included from Collection::SharedLogic
#calculate_table_sharing_for_fonts
Constructor Details
#initialize(path, num_fonts) ⇒ DfontCollection
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.
Initialize collection
100 101 102 103 |
# File 'lib/fontisan/dfont_collection.rb', line 100 def initialize(path, num_fonts) @path = path @num_fonts = num_fonts end |
Instance Attribute Details
#num_fonts ⇒ Integer (readonly) Also known as: font_count
Number of fonts in collection
60 61 62 |
# File 'lib/fontisan/dfont_collection.rb', line 60 def num_fonts @num_fonts end |
#path ⇒ String (readonly)
Path to dfont file
56 57 58 |
# File 'lib/fontisan/dfont_collection.rb', line 56 def path @path end |
Class Method Details
.collection_format ⇒ String
Get the collection format identifier
75 76 77 |
# File 'lib/fontisan/dfont_collection.rb', line 75 def self.collection_format "dfont" end |
.from_file(path) ⇒ DfontCollection
Load dfont collection from file
84 85 86 87 88 89 90 91 92 93 |
# File 'lib/fontisan/dfont_collection.rb', line 84 def self.from_file(path) File.open(path, "rb") do |io| unless Parsers::DfontParser.dfont?(io) raise InvalidFontError, "Not a valid dfont file: #{path}" end num_fonts = Parsers::DfontParser.sfnt_count(io) new(path, num_fonts) end end |
Instance Method Details
#collection? ⇒ Boolean
Whether this object represents a font collection rather than a single font. Each font class is the authority on this question.
35 |
# File 'lib/fontisan/dfont_collection.rb', line 35 def collection? = true |
#collection_info(io, path) ⇒ Models::CollectionInfo
Get comprehensive collection metadata
Returns a CollectionInfo model with header information and
table sharing statistics for the dfont collection.
This is the API method used by the info command for collections.
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/fontisan/dfont_collection.rb', line 251 def collection_info(io, path) # Calculate table sharing statistics table_sharing = calculate_table_sharing(io) # Get file size file_size = path ? File.size(path) : 0 Models::CollectionInfo.new( collection_path: path, collection_format: self.class.collection_format, ttc_tag: "dfnt", # dfont doesn't use ttcf tag major_version: 0, # dfont doesn't have version minor_version: 0, num_fonts: @num_fonts, font_offsets: font_offsets, file_size_bytes: file_size, table_sharing: table_sharing, ) end |
#extract_fonts(io) ⇒ Array<TrueTypeFont, OpenTypeFont>
Extract all fonts from dfont
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 |
# File 'lib/fontisan/dfont_collection.rb', line 125 def extract_fonts(io) require "stringio" fonts = [] @num_fonts.times do |index| io.rewind sfnt_data = Parsers::DfontParser.extract_sfnt(io, index: index) # Load font from SFNT binary sfnt_io = StringIO.new(sfnt_data) signature = sfnt_io.read(4) sfnt_io.rewind # Create font based on signature font = case signature when [Constants::SFNT_VERSION_TRUETYPE].pack("N"), "true" TrueTypeFont.read(sfnt_io) when "OTTO" OpenTypeFont.read(sfnt_io) else raise InvalidFontError, "Invalid SFNT signature in dfont at index #{index}: #{signature.inspect}" end font.initialize_storage font.loading_mode = LoadingModes::FULL font.lazy_load_enabled = false font.read_table_data(sfnt_io) fonts << font end fonts end |
#font(index, io, mode: LoadingModes::FULL) ⇒ TrueTypeFont, OpenTypeFont
Get specific font from collection
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/fontisan/dfont_collection.rb', line 201 def font(index, io, mode: LoadingModes::FULL) if index >= @num_fonts raise InvalidFontError, "Font index #{index} out of range (collection has #{@num_fonts} fonts)" end io.rewind sfnt_data = Parsers::DfontParser.extract_sfnt(io, index: index) # Load font from SFNT binary require "stringio" sfnt_io = StringIO.new(sfnt_data) signature = sfnt_io.read(4) sfnt_io.rewind # Create font based on signature font = case signature when [Constants::SFNT_VERSION_TRUETYPE].pack("N"), "true" TrueTypeFont.read(sfnt_io) when "OTTO" OpenTypeFont.read(sfnt_io) else raise InvalidFontError, "Invalid SFNT signature: #{signature.inspect}" end font.initialize_storage font.loading_mode = mode font.lazy_load_enabled = false font.read_table_data(sfnt_io) font end |
#font_offsets ⇒ Array<Integer>
Get font offsets (indices for dfont)
dfont doesn't use byte offsets like TTC/OTC, so we return indices
68 69 70 |
# File 'lib/fontisan/dfont_collection.rb', line 68 def font_offsets (0...@num_fonts).to_a end |
#format ⇒ Symbol
High-level pipeline format identifier. Owned by the collection class so the conversion pipeline can dispatch without case statements (OCP).
29 |
# File 'lib/fontisan/dfont_collection.rb', line 29 def format = :dfont |
#list_fonts(io) ⇒ Models::CollectionListInfo
List fonts in collection (brief info)
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/fontisan/dfont_collection.rb', line 165 def list_fonts(io) fonts = extract_fonts(io) summaries = fonts.map.with_index do |font, index| name_table = font.table("name") family = name_table.english_name(Models::Tables::Name::FAMILY) || "Unknown" subfamily = name_table.english_name(Models::Tables::Name::SUBFAMILY) || "Regular" # Detect font format format = if font.has_table?("CFF ") || font.has_table?("CFF2") "OpenType" else "TrueType" end Models::CollectionFontSummary.new( index: index, family_name: family, subfamily_name: subfamily, font_format: format, ) end Models::CollectionListInfo.new( num_fonts: @num_fonts, fonts: summaries, ) end |
#outline_type ⇒ Symbol
Outline representation. dfonts may contain mixed-flavor fonts; per-font outline type requires loading an individual font.
47 |
# File 'lib/fontisan/dfont_collection.rb', line 47 def outline_type = :unknown |
#table_names ⇒ Array<String>
Collections have no single SFNT table directory.
52 |
# File 'lib/fontisan/dfont_collection.rb', line 52 def table_names = [] |
#valid? ⇒ Boolean
Check if collection is valid
108 109 110 |
# File 'lib/fontisan/dfont_collection.rb', line 108 def valid? File.exist?(@path) && @num_fonts.positive? end |
#variation_type ⇒ Symbol
Variation profile. dfonts are containers; per-font variation is not exposed at the collection level.
41 |
# File 'lib/fontisan/dfont_collection.rb', line 41 def variation_type = :static |
#version_string ⇒ String
Get the collection version as a string
dfont files don't have version numbers like TTC/OTC
117 118 119 |
# File 'lib/fontisan/dfont_collection.rb', line 117 def version_string "N/A" end |