Class: Fontisan::Commands::InfoCommand
- Inherits:
-
BaseCommand
- Object
- BaseCommand
- Fontisan::Commands::InfoCommand
- Defined in:
- lib/fontisan/commands/info_command.rb
Overview
Command to extract font or collection metadata information.
This command auto-detects whether the input is a collection (TTC/OTC) or individual font (TTF/OTF) and returns the appropriate model:
- CollectionInfo for TTC/OTC files
- FontInfo for TTF/OTF files
For individual fonts, extracts comprehensive information from various tables:
- name table: family names, version, copyright, etc.
- OS/2 table: vendor ID, embedding permissions
- head table: font revision, units per em
Instance Method Summary collapse
-
#collection_info ⇒ Models::CollectionInfo, Models::CollectionBriefInfo
Get collection information.
-
#font_info ⇒ Models::FontInfo
Get individual font information.
-
#format_permissions(flags) ⇒ String
Format OS/2 embedding permission flags into a human-readable string.
-
#load_collection_fonts(collection, collection_path) ⇒ Array<Models::FontInfo>
Load font information for all fonts in a collection.
-
#populate_bitmap_info(info) ⇒ Object
Populate FontInfo with bitmap table information (CBDT/CBLC, sbix).
-
#populate_brief_fields(info) ⇒ Object
Populate essential fields for brief mode (metadata tables only).
-
#populate_color_info(info) ⇒ Object
Populate FontInfo with color font information from COLR/CPAL tables.
-
#populate_font_format(info) ⇒ Object
Populate font format and variable status based on font class and table presence.
-
#populate_from_head_table(info) ⇒ Object
Populate FontInfo from the head table.
-
#populate_from_name_table(info) ⇒ Object
Populate FontInfo from the name table.
-
#populate_from_os2_table(info) ⇒ Object
Populate FontInfo from the OS/2 table.
-
#populate_full_fields(info) ⇒ Object
Populate all fields for full mode.
-
#populate_sfnt_brief_fields(info) ⇒ Object
Populate SFNT font brief fields (name, head, OS/2 tables).
-
#populate_sfnt_full_fields(info) ⇒ Object
Populate SFNT font full fields.
-
#populate_svg_info(info) ⇒ Object
Populate FontInfo with SVG table information.
-
#populate_type1_brief_fields(info) ⇒ Object
Populate Type 1 font brief fields.
-
#populate_type1_full_fields(info) ⇒ Object
Populate Type 1 font full fields.
-
#run ⇒ Models::FontInfo, Models::CollectionInfo
Extract information from font or collection.
Methods inherited from BaseCommand
Constructor Details
This class inherits a constructor from Fontisan::Commands::BaseCommand
Instance Method Details
#collection_info ⇒ Models::CollectionInfo, Models::CollectionBriefInfo
Get collection information
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/fontisan/commands/info_command.rb', line 45 def collection_info collection = FontLoader.load_collection(@font_path) File.open(@font_path, "rb") do |io| if @options[:brief] # Brief mode: load each font and populate brief info brief_info = Models::CollectionBriefInfo.new brief_info.collection_path = @font_path brief_info.collection_type = collection.class.collection_format brief_info.collection_version = collection.version_string brief_info.num_fonts = collection.num_fonts brief_info.fonts = load_collection_fonts(collection, @font_path) brief_info else # Full mode: show detailed sharing statistics AND font information full_info = collection.collection_info(io, @font_path) # Add font information to full mode full_info.fonts = load_collection_fonts(collection, @font_path) full_info end end end |
#font_info ⇒ Models::FontInfo
Get individual font information
132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/fontisan/commands/info_command.rb', line 132 def font_info info = Models::FontInfo.new populate_font_format(info) # In brief mode, only populate essential fields for fast identification if @options[:brief] populate_brief_fields(info) else populate_full_fields(info) end info end |
#format_permissions(flags) ⇒ String
Format OS/2 embedding permission flags into a human-readable string.
431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/fontisan/commands/info_command.rb', line 431 def (flags) emb = flags & 15 result = case emb when 0 then "Installable" when 2 then "Restricted License" when 4 then "Preview & Print" when 8 then "Editable" else "Unknown (#{emb})" end result += ", No subsetting" if flags.anybits?(0x100) result += ", Bitmap only" if flags.anybits?(0x200) result end |
#load_collection_fonts(collection, collection_path) ⇒ Array<Models::FontInfo>
Load font information for all fonts in a collection
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 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 |
# File 'lib/fontisan/commands/info_command.rb', line 76 def load_collection_fonts(collection, collection_path) fonts = [] collection.num_fonts.times do |index| # Load individual font from collection font = FontLoader.load(collection_path, font_index: index, mode: LoadingModes::METADATA) # Populate font info info = Models::FontInfo.new # Font format and variable status info.font_format = case font when TrueTypeFont "truetype" when OpenTypeFont "cff" else "unknown" end info.is_variable = font.has_table?(Constants::FVAR_TAG) # Collection offset (only populated for fonts in collections) info.collection_offset = collection.font_offsets[index] # Essential names if font.has_table?(Constants::NAME_TAG) name_table = font.table(Constants::NAME_TAG) info.family_name = name_table.english_name(Tables::Name::FAMILY) info.subfamily_name = name_table.english_name(Tables::Name::SUBFAMILY) info.full_name = name_table.english_name(Tables::Name::FULL_NAME) info.postscript_name = name_table.english_name(Tables::Name::POSTSCRIPT_NAME) info.version = name_table.english_name(Tables::Name::VERSION) end # Essential metrics if font.has_table?(Constants::HEAD_TAG) head = font.table(Constants::HEAD_TAG) info.font_revision = head.font_revision info.units_per_em = head.units_per_em end # Vendor ID if font.has_table?(Constants::OS2_TAG) os2_table = font.table(Constants::OS2_TAG) info.vendor_id = os2_table.vendor_id end fonts << info end fonts end |
#populate_bitmap_info(info) ⇒ Object
Populate FontInfo with bitmap table information (CBDT/CBLC, sbix)
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
# File 'lib/fontisan/commands/info_command.rb', line 378 def populate_bitmap_info(info) bitmap_strikes = [] ppem_sizes = [] formats = [] # Check for CBDT/CBLC (Google format) if font.has_table?("CBLC") && font.has_table?("CBDT") cblc = font.table("CBLC") info.has_bitmap_glyphs = true ppem_sizes.concat(cblc.ppem_sizes) cblc.strikes.each do |strike_rec| bitmap_strikes << Models::BitmapStrike.new( ppem: strike_rec.ppem, start_glyph_id: strike_rec.start_glyph_index, end_glyph_id: strike_rec.end_glyph_index, bit_depth: strike_rec.bit_depth, num_glyphs: strike_rec.glyph_range.size, ) end formats << "PNG" # CBDT typically contains PNG data end # Check for sbix (Apple format) if font.has_table?("sbix") sbix = font.table("sbix") info.has_bitmap_glyphs = true ppem_sizes.concat(sbix.ppem_sizes) formats.concat(sbix.supported_formats) sbix.strikes.each do |strike| bitmap_strikes << Models::BitmapStrike.new( ppem: strike[:ppem], start_glyph_id: 0, end_glyph_id: strike[:num_glyphs] - 1, bit_depth: 32, # sbix is typically 32-bit num_glyphs: strike[:num_glyphs], ) end end info.bitmap_strikes = bitmap_strikes unless bitmap_strikes.empty? info.bitmap_ppem_sizes = ppem_sizes.uniq.sort info.bitmap_formats = formats.uniq rescue StandardError => e warn "Failed to populate bitmap info: #{e.}" info.has_bitmap_glyphs = false end |
#populate_brief_fields(info) ⇒ Object
Populate essential fields for brief mode (metadata tables only).
Brief mode provides fast font identification by loading only 13 essential attributes from metadata tables (name, head, OS/2). This is 5x faster than full mode and optimized for font indexing systems.
177 178 179 180 181 182 183 |
# File 'lib/fontisan/commands/info_command.rb', line 177 def populate_brief_fields(info) if font.is_a?(Type1Font) populate_type1_brief_fields(info) else populate_sfnt_brief_fields(info) end end |
#populate_color_info(info) ⇒ Object
Populate FontInfo with color font information from COLR/CPAL tables
345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/fontisan/commands/info_command.rb', line 345 def populate_color_info(info) colr_table = font.table("COLR") cpal_table = font.table("CPAL") return unless colr_table && cpal_table info.is_color_font = true info.color_glyphs = colr_table.num_color_glyphs info.color_palettes = cpal_table.num_palettes info.colors_per_palette = cpal_table.num_palette_entries rescue StandardError => e warn "Failed to populate color font info: #{e.}" info.is_color_font = false end |
#populate_font_format(info) ⇒ Object
Populate font format and variable status based on font class and table presence.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/fontisan/commands/info_command.rb', line 149 def populate_font_format(info) # Determine base format from font class info.font_format = case font when TrueTypeFont "truetype" when OpenTypeFont "cff" when Type1Font "type1" else "unknown" end # Check if variable font (Type1 fonts are never variable) info.is_variable = if font.is_a?(Type1Font) false else font.has_table?(Constants::FVAR_TAG) end end |
#populate_from_head_table(info) ⇒ Object
Populate FontInfo from the head table.
334 335 336 337 338 339 340 |
# File 'lib/fontisan/commands/info_command.rb', line 334 def populate_from_head_table(info) head_table = font.table(Constants::HEAD_TAG) return unless head_table info.font_revision = head_table.font_revision info.units_per_em = head_table.units_per_em end |
#populate_from_name_table(info) ⇒ Object
Populate FontInfo from the name table.
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
# File 'lib/fontisan/commands/info_command.rb', line 294 def populate_from_name_table(info) name_table = font.table(Constants::NAME_TAG) return unless name_table info.family_name = name_table.english_name(Tables::Name::FAMILY) info.subfamily_name = name_table.english_name(Tables::Name::SUBFAMILY) info.full_name = name_table.english_name(Tables::Name::FULL_NAME) info.postscript_name = name_table.english_name(Tables::Name::POSTSCRIPT_NAME) info.postscript_cid_name = name_table.english_name(Tables::Name::POSTSCRIPT_CID) info.preferred_family = name_table.english_name(Tables::Name::PREFERRED_FAMILY) info.preferred_subfamily = name_table.english_name(Tables::Name::PREFERRED_SUBFAMILY) info. = name_table.english_name(Tables::Name::COMPATIBLE_FULL) info.version = name_table.english_name(Tables::Name::VERSION) info.unique_id = name_table.english_name(Tables::Name::UNIQUE_ID) info.description = name_table.english_name(Tables::Name::DESCRIPTION) info.designer = name_table.english_name(Tables::Name::DESIGNER) info.designer_url = name_table.english_name(Tables::Name::DESIGNER_URL) info.manufacturer = name_table.english_name(Tables::Name::MANUFACTURER) info.vendor_url = name_table.english_name(Tables::Name::VENDOR_URL) info.trademark = name_table.english_name(Tables::Name::TRADEMARK) info.copyright = name_table.english_name(Tables::Name::COPYRIGHT) info.license_description = name_table.english_name(Tables::Name::LICENSE_DESCRIPTION) info.license_url = name_table.english_name(Tables::Name::LICENSE_URL) info.sample_text = name_table.english_name(Tables::Name::SAMPLE_TEXT) end |
#populate_from_os2_table(info) ⇒ Object
Populate FontInfo from the OS/2 table.
323 324 325 326 327 328 329 |
# File 'lib/fontisan/commands/info_command.rb', line 323 def populate_from_os2_table(info) os2_table = font.table(Constants::OS2_TAG) return unless os2_table info.vendor_id = os2_table.vendor_id info. = (os2_table.type_flags) end |
#populate_full_fields(info) ⇒ Object
Populate all fields for full mode.
Full mode extracts comprehensive metadata from all available tables.
240 241 242 243 244 245 246 |
# File 'lib/fontisan/commands/info_command.rb', line 240 def populate_full_fields(info) if font.is_a?(Type1Font) populate_type1_full_fields(info) else populate_sfnt_full_fields(info) end end |
#populate_sfnt_brief_fields(info) ⇒ Object
Populate SFNT font brief fields (name, head, OS/2 tables).
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/commands/info_command.rb', line 188 def populate_sfnt_brief_fields(info) # Essential names from name table if font.has_table?(Constants::NAME_TAG) name_table = font.table(Constants::NAME_TAG) info.family_name = name_table.english_name(Tables::Name::FAMILY) info.subfamily_name = name_table.english_name(Tables::Name::SUBFAMILY) info.full_name = name_table.english_name(Tables::Name::FULL_NAME) info.postscript_name = name_table.english_name(Tables::Name::POSTSCRIPT_NAME) info.version = name_table.english_name(Tables::Name::VERSION) end # Essential metrics from head table if font.has_table?(Constants::HEAD_TAG) head = font.table(Constants::HEAD_TAG) info.font_revision = head.font_revision info.units_per_em = head.units_per_em end # Vendor ID from OS/2 table if font.has_table?(Constants::OS2_TAG) os2_table = font.table(Constants::OS2_TAG) info.vendor_id = os2_table.vendor_id end end |
#populate_sfnt_full_fields(info) ⇒ Object
Populate SFNT font full fields.
251 252 253 254 255 256 257 258 |
# File 'lib/fontisan/commands/info_command.rb', line 251 def populate_sfnt_full_fields(info) populate_from_name_table(info) if font.has_table?(Constants::NAME_TAG) populate_from_os2_table(info) if font.has_table?(Constants::OS2_TAG) populate_from_head_table(info) if font.has_table?(Constants::HEAD_TAG) populate_color_info(info) if font.has_table?("COLR") && font.has_table?("CPAL") populate_svg_info(info) if font.has_table?("SVG ") populate_bitmap_info(info) if font.has_table?("CBLC") || font.has_table?("sbix") end |
#populate_svg_info(info) ⇒ Object
Populate FontInfo with SVG table information
363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/fontisan/commands/info_command.rb', line 363 def populate_svg_info(info) svg_table = font.table("SVG ") return unless svg_table info.has_svg_table = true info.svg_glyph_count = svg_table.glyph_ids_with_svg.length rescue StandardError => e warn "Failed to populate SVG info: #{e.}" info.has_svg_table = false end |
#populate_type1_brief_fields(info) ⇒ Object
Populate Type 1 font brief fields.
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/fontisan/commands/info_command.rb', line 216 def populate_type1_brief_fields(info) # Get Type 1 font metadata font_dict = font.font_dictionary font_info = font_dict&.font_info if font_dict return unless font_info # Essential names from Type 1 font info.family_name = font_info.family_name info.full_name = font_info.full_name info.postscript_name = font.font_name info.version = font_info.version # Metrics from font dictionary if font_dict&.font_b_box info.bounding_box = font_dict.font_b_box end end |
#populate_type1_full_fields(info) ⇒ Object
Populate Type 1 font full fields.
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
# File 'lib/fontisan/commands/info_command.rb', line 263 def populate_type1_full_fields(info) font_dict = font.font_dictionary font_info = font_dict&.font_info if font_dict return unless font_info # Names from Type 1 font info.family_name = font_info.family_name info.full_name = font_info.full_name info.postscript_name = font.font_name info.version = font_info.version info.copyright = font_info.copyright info.description = font_info.notice info.designer = nil # Type 1 fonts may not have designer info # Metrics if font_dict&.font_b_box info.bounding_box = font_dict.font_b_box end if font_dict&.font_matrix info.font_matrix = font_dict.font_matrix end # Glyph count info.glyph_count = font.charstrings&.count || 0 end |
#run ⇒ Models::FontInfo, Models::CollectionInfo
Extract information from font or collection.
Auto-detects file type and returns appropriate model.
32 33 34 35 36 37 38 |
# File 'lib/fontisan/commands/info_command.rb', line 32 def run if FontLoader.collection?(@font_path) collection_info else font_info end end |