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? ⇒ Boolean
Whether this object represents a font collection rather than a single font.
-
#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.
-
#outline_type ⇒ Symbol
Outline representation.
-
#table_names ⇒ Array<String>
Collections have no single SFNT table directory.
-
#valid? ⇒ Boolean
Validate format correctness.
-
#variation_type ⇒ Symbol
Variation profile.
-
#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".
59 60 61 62 |
# File 'lib/fontisan/base_collection.rb', line 59 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).
48 49 50 51 |
# File 'lib/fontisan/base_collection.rb', line 48 def self.font_class raise NotImplementedError, "#{name} must implement self.font_class" end |
.from_file(path) ⇒ BaseCollection
Read collection from a file
71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/fontisan/base_collection.rb', line 71 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? ⇒ Boolean
Whether this object represents a font collection rather than a single font. Each font class is the authority on this question.
123 |
# File 'lib/fontisan/base_collection.rb', line 123 def collection? = true |
#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.
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/fontisan/base_collection.rb', line 241 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: 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.
91 92 93 94 95 96 97 |
# File 'lib/fontisan/base_collection.rb', line 91 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
105 106 107 108 109 110 |
# File 'lib/fontisan/base_collection.rb', line 105 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
115 116 117 |
# File 'lib/fontisan/base_collection.rb', line 115 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.
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 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/fontisan/base_collection.rb', line 179 def list_fonts(io) 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 |
#outline_type ⇒ Symbol
Outline representation. Collections may contain mixed-flavor fonts; per-font outline type requires loading an individual font.
135 |
# File 'lib/fontisan/base_collection.rb', line 135 def outline_type = :unknown |
#table_names ⇒ Array<String>
Collections have no single SFNT table directory.
140 |
# File 'lib/fontisan/base_collection.rb', line 140 def table_names = [] |
#valid? ⇒ Boolean
Validate format correctness
145 146 147 148 149 |
# File 'lib/fontisan/base_collection.rb', line 145 def valid? tag == Constants::TTC_TAG && num_fonts.positive? && font_offsets.length == num_fonts rescue StandardError false end |
#variation_type ⇒ Symbol
Variation profile. Collections are containers; per-font variation is not exposed at the collection level.
129 |
# File 'lib/fontisan/base_collection.rb', line 129 def variation_type = :static |
#version ⇒ Integer
Get the collection version as a single integer
154 155 156 |
# File 'lib/fontisan/base_collection.rb', line 154 def version (major_version << 16) | minor_version end |
#version_string ⇒ String
Get the collection version as a string
161 162 163 |
# File 'lib/fontisan/base_collection.rb', line 161 def version_string "#{major_version}.#{minor_version}" end |