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_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).
-
#initialize(path, num_fonts) ⇒ DfontCollection
constructor
private
Initialize collection.
-
#list_fonts(io) ⇒ Models::CollectionListInfo
List fonts in collection (brief info).
-
#valid? ⇒ Boolean
Check if collection is valid.
-
#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
75 76 77 78 |
# File 'lib/fontisan/dfont_collection.rb', line 75 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
35 36 37 |
# File 'lib/fontisan/dfont_collection.rb', line 35 def num_fonts @num_fonts end |
#path ⇒ String (readonly)
Path to dfont file
31 32 33 |
# File 'lib/fontisan/dfont_collection.rb', line 31 def path @path end |
Class Method Details
.collection_format ⇒ String
Get the collection format identifier
50 51 52 |
# File 'lib/fontisan/dfont_collection.rb', line 50 def self.collection_format "dfont" end |
.from_file(path) ⇒ DfontCollection
Load dfont collection from file
59 60 61 62 63 64 65 66 67 68 |
# File 'lib/fontisan/dfont_collection.rb', line 59 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_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.
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/fontisan/dfont_collection.rb', line 229 def collection_info(io, path) require_relative "models/collection_info" require_relative "models/table_sharing_info" # 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
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/fontisan/dfont_collection.rb', line 100 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
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 |
# File 'lib/fontisan/dfont_collection.rb', line 179 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
43 44 45 |
# File 'lib/fontisan/dfont_collection.rb', line 43 def font_offsets (0...@num_fonts).to_a end |
#list_fonts(io) ⇒ Models::CollectionListInfo
List fonts in collection (brief info)
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 165 166 167 168 169 170 |
# File 'lib/fontisan/dfont_collection.rb', line 140 def list_fonts(io) require_relative "models/collection_list_info" require_relative "models/collection_font_summary" 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 |
#valid? ⇒ Boolean
Check if collection is valid
83 84 85 |
# File 'lib/fontisan/dfont_collection.rb', line 83 def valid? File.exist?(@path) && @num_fonts.positive? end |
#version_string ⇒ String
Get the collection version as a string
dfont files don’t have version numbers like TTC/OTC
92 93 94 |
# File 'lib/fontisan/dfont_collection.rb', line 92 def version_string "N/A" end |