Class: Fontisan::BaseCollection Abstract
- Inherits:
-
BinData::Record
- Object
- BinData::Record
- Fontisan::BaseCollection
- Includes:
- Collection::SharedLogic
- Defined in:
- lib/fontisan/base_collection.rb
Overview
Subclass and override BaseCollection.font_class and BaseCollection.collection_format
Abstract base class for font collections (TTC/OTC)
This class implements the shared logic for TrueTypeCollection and OpenTypeCollection using the Template Method pattern. Subclasses must implement the abstract methods to specify their font class and collection format.
The BinData structure definition is shared between both collection types since both TTC and OTC files use the same “ttcf” tag and binary format. The only differences are:
-
The type of fonts contained (TrueType vs OpenType)
-
The format string used for display (“TTC” vs “OTC”)
Direct Known Subclasses
Class Method Summary collapse
-
.collection_format ⇒ String
Abstract method: Get the collection format string.
-
.font_class ⇒ Class
Abstract method: Get the font class for this collection type.
-
.from_file(path) ⇒ BaseCollection
Read collection from a file.
Instance Method Summary collapse
-
#collection_info(io, path) ⇒ CollectionInfo
Get comprehensive collection metadata.
-
#extract_fonts(io) ⇒ Array
Extract fonts from the collection.
-
#font(index, io, mode: LoadingModes::FULL) ⇒ TrueTypeFont, ...
Get a single font from the collection.
-
#font_count ⇒ Integer
Get font count.
-
#list_fonts(io) ⇒ CollectionListInfo
List all fonts in the collection with basic metadata.
-
#valid? ⇒ Boolean
Validate format correctness.
-
#version ⇒ Integer
Get the collection version as a single integer.
-
#version_string ⇒ String
Get the collection version as a string.
Methods included from Collection::SharedLogic
#calculate_table_sharing_for_fonts
Class Method Details
.collection_format ⇒ String
Abstract method: Get the collection format string
Subclasses must override this to return “TTC” or “OTC”.
61 62 63 64 |
# File 'lib/fontisan/base_collection.rb', line 61 def self.collection_format raise NotImplementedError, "#{name} must implement self.collection_format" end |
.font_class ⇒ Class
Abstract method: Get the font class for this collection type
Subclasses must override this to return their specific font class (TrueTypeFont or OpenTypeFont).
50 51 52 53 |
# File 'lib/fontisan/base_collection.rb', line 50 def self.font_class raise NotImplementedError, "#{name} must implement self.font_class" end |
.from_file(path) ⇒ BaseCollection
Read collection from a file
73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/fontisan/base_collection.rb', line 73 def self.from_file(path) if path.nil? || path.to_s.empty? raise ArgumentError, "path cannot be nil or empty" end raise Errno::ENOENT, "File not found: #{path}" unless File.exist?(path) File.open(path, "rb") { |io| read(io) } rescue BinData::ValidityError => e raise "Invalid #{collection_format} file: #{e.}" rescue EOFError => e raise "Invalid #{collection_format} file: unexpected end of file - #{e.}" end |
Instance Method Details
#collection_info(io, path) ⇒ CollectionInfo
Get comprehensive collection metadata
Returns a CollectionInfo model with header information, offsets, and table sharing statistics. This is the API method used by the ‘info` command for collections.
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/fontisan/base_collection.rb', line 224 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: tag, major_version: major_version, minor_version: minor_version, num_fonts: num_fonts, font_offsets: font_offsets.to_a, file_size_bytes: file_size, table_sharing: table_sharing, ) end |
#extract_fonts(io) ⇒ Array
Extract fonts from the collection
Reads each font from the collection file and returns them as font objects.
93 94 95 96 97 98 99 |
# File 'lib/fontisan/base_collection.rb', line 93 def extract_fonts(io) font_class = self.class.font_class font_offsets.map do |offset| font_class.from_collection(io, offset) end end |
#font(index, io, mode: LoadingModes::FULL) ⇒ TrueTypeFont, ...
Get a single font from the collection
107 108 109 110 111 112 |
# File 'lib/fontisan/base_collection.rb', line 107 def font(index, io, mode: LoadingModes::FULL) return nil if index >= num_fonts font_class = self.class.font_class font_class.from_collection(io, font_offsets[index], mode: mode) end |
#font_count ⇒ Integer
Get font count
117 118 119 |
# File 'lib/fontisan/base_collection.rb', line 117 def font_count num_fonts end |
#list_fonts(io) ⇒ CollectionListInfo
List all fonts in the collection with basic metadata
Returns a CollectionListInfo model containing summaries of all fonts. This is the API method used by the ‘ls` command for collections.
158 159 160 161 162 163 164 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 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/fontisan/base_collection.rb', line 158 def list_fonts(io) require_relative "models/collection_list_info" require_relative "models/collection_font_summary" require_relative "tables/name" font_class = self.class.font_class fonts = font_offsets.map.with_index do |offset, index| font = font_class.from_collection(io, offset) # Extract basic font info name_table = font.table("name") post_table = font.table("post") family_name = name_table&.english_name(Tables::Name::FAMILY) || "Unknown" subfamily_name = name_table&.english_name(Tables::Name::SUBFAMILY) || "Regular" postscript_name = name_table&.english_name(Tables::Name::POSTSCRIPT_NAME) || "Unknown" # Determine font format sfnt = font.header.sfnt_version font_format = case sfnt when 0x00010000, 0x74727565 # 0x74727565 = 'true' "TrueType" when 0x4F54544F # 'OTTO' "OpenType" else "Unknown" end num_glyphs = post_table&.glyph_names&.length || 0 num_tables = font.table_names.length Models::CollectionFontSummary.new( index: index, family_name: family_name, subfamily_name: subfamily_name, postscript_name: postscript_name, font_format: font_format, num_glyphs: num_glyphs, num_tables: num_tables, ) end Models::CollectionListInfo.new( collection_path: nil, # Will be set by command num_fonts: num_fonts, fonts: fonts, ) end |
#valid? ⇒ Boolean
Validate format correctness
124 125 126 127 128 |
# File 'lib/fontisan/base_collection.rb', line 124 def valid? tag == Constants::TTC_TAG && num_fonts.positive? && font_offsets.length == num_fonts rescue StandardError false end |
#version ⇒ Integer
Get the collection version as a single integer
133 134 135 |
# File 'lib/fontisan/base_collection.rb', line 133 def version (major_version << 16) | minor_version end |
#version_string ⇒ String
Get the collection version as a string
140 141 142 |
# File 'lib/fontisan/base_collection.rb', line 140 def version_string "#{major_version}.#{minor_version}" end |