Class: Fontisan::FontLoader

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/font_loader.rb

Overview

FontLoader provides unified font loading with automatic format detection.

This class is the primary entry point for loading fonts in Fontisan. It automatically detects the font format and returns the appropriate domain object (TrueTypeFont, OpenTypeFont, TrueTypeCollection, or OpenTypeCollection).

Examples:

Load any font type

font = FontLoader.load("font.ttf")  # => TrueTypeFont
font = FontLoader.load("font.otf")  # => OpenTypeFont
font = FontLoader.load("fonts.ttc") # => TrueTypeFont (first in collection)
font = FontLoader.load("fonts.ttc", font_index: 2) # => TrueTypeFont (third in collection)

Loading modes

font = FontLoader.load("font.ttf", mode: :metadata)  # Load only metadata tables
font = FontLoader.load("font.ttf", mode: :full)      # Load all tables

Lazy loading control

font = FontLoader.load("font.ttf", lazy: true)   # Tables loaded on-demand
font = FontLoader.load("font.ttf", lazy: false)  # All tables loaded upfront

Class Method Summary collapse

Class Method Details

.collection?(path) ⇒ Boolean

Check if a file is a collection (TTC or OTC)

Examples:

Check if file is collection

FontLoader.collection?("fonts.ttc") # => true
FontLoader.collection?("font.ttf")  # => false

Parameters:

  • path (String)

    Path to the font file

Returns:

  • (Boolean)

    true if file is a TTC/OTC collection

Raises:

  • (Errno::ENOENT)

    if file does not exist



87
88
89
90
91
92
93
94
# File 'lib/fontisan/font_loader.rb', line 87

def self.collection?(path)
  raise Errno::ENOENT, "File not found: #{path}" unless File.exist?(path)

  File.open(path, "rb") do |io|
    signature = io.read(4)
    signature == Constants::TTC_TAG
  end
end

.load(path, font_index: 0, mode: nil, lazy: nil) ⇒ TrueTypeFont, ...

Load a font from file with automatic format detection

Parameters:

  • path (String)

    Path to the font file

  • font_index (Integer) (defaults to: 0)

    Index of font in collection (0-based, default: 0)

  • mode (Symbol) (defaults to: nil)

    Loading mode (:metadata or :full, default: from ENV or :full)

  • lazy (Boolean) (defaults to: nil)

    If true, load tables on demand (default: false for eager loading)

Returns:

Raises:



44
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
70
71
72
73
74
75
76
# File 'lib/fontisan/font_loader.rb', line 44

def self.load(path, font_index: 0, mode: nil, lazy: nil)
  raise Errno::ENOENT, "File not found: #{path}" unless File.exist?(path)

  # Resolve mode and lazy parameters with environment variables
  resolved_mode = mode || env_mode || LoadingModes::FULL
  resolved_lazy = lazy.nil? ? (env_lazy.nil? ? false : env_lazy) : lazy

  # Validate mode
  LoadingModes.validate_mode!(resolved_mode)

  File.open(path, "rb") do |io|
    signature = io.read(4)
    io.rewind

    case signature
    when Constants::TTC_TAG
      load_from_collection(io, path, font_index, mode: resolved_mode, lazy: resolved_lazy)
    when pack_uint32(Constants::SFNT_VERSION_TRUETYPE)
      TrueTypeFont.from_file(path, mode: resolved_mode, lazy: resolved_lazy)
    when "OTTO"
      OpenTypeFont.from_file(path, mode: resolved_mode, lazy: resolved_lazy)
    when "wOFF"
      raise UnsupportedFormatError,
            "Unsupported font format: WOFF. Please convert to TTF/OTF first."
    when "wOF2"
      raise UnsupportedFormatError,
            "Unsupported font format: WOFF2. Please convert to TTF/OTF first."
    else
      raise InvalidFontError,
            "Unknown font format. Expected TTF, OTF, TTC, or OTC file."
    end
  end
end

.load_collection(path) ⇒ TrueTypeCollection, OpenTypeCollection

Load a collection object without extracting fonts

Returns the collection object (TrueTypeCollection or OpenTypeCollection) without extracting individual fonts. Useful for inspecting collection metadata and structure.

Examples:

Load collection for inspection

collection = FontLoader.load_collection("fonts.ttc")
puts "Collection has #{collection.num_fonts} fonts"

Parameters:

  • path (String)

    Path to the collection file

Returns:

Raises:

  • (Errno::ENOENT)

    if file does not exist

  • (InvalidFontError)

    if file is not a collection or type cannot be determined



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
135
136
137
138
139
140
# File 'lib/fontisan/font_loader.rb', line 110

def self.load_collection(path)
  raise Errno::ENOENT, "File not found: #{path}" unless File.exist?(path)

  File.open(path, "rb") do |io|
    signature = io.read(4)

    unless signature == Constants::TTC_TAG
      raise InvalidFontError,
            "File is not a collection (TTC/OTC). Use FontLoader.load instead."
    end

    # Read first font offset to detect collection type
    io.seek(12) # Skip tag (4) + versions (4) + num_fonts (4)
    first_offset = io.read(4).unpack1("N")

    # Peek at first font's sfnt_version
    io.seek(first_offset)
    sfnt_version = io.read(4).unpack1("N")
    io.rewind

    case sfnt_version
    when Constants::SFNT_VERSION_TRUETYPE
      TrueTypeCollection.from_file(path)
    when Constants::SFNT_VERSION_OTTO
      OpenTypeCollection.from_file(path)
    else
      raise InvalidFontError,
            "Unknown font type in collection (sfnt version: 0x#{sfnt_version.to_s(16)})"
    end
  end
end