Class: Fontist::Formula

Inherits:
Lutaml::Model::Serializable
  • Object
show all
Defined in:
lib/fontist/formula.rb

Overview

Formula - v5 schema with multi-format font support

This class handles formulas with schema_version 5, which supports:

  • Multiple font formats (TTF, WOFF2, variable fonts)

  • Format metadata on resources

  • Variable axes for variable font filtering

For v4 formulas, use the migration script to convert to v5 format.

Constant Summary collapse

NAMESPACES =
{
  "sil" => "SIL",
  "macos" => "macOS",
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.allObject



99
100
101
102
103
104
105
# File 'lib/fontist/formula.rb', line 99

def all
  formulas = Dir[Fontist.formulas_path.join("**/*.yml").to_s].filter_map do |path|
    Formula.from_file(path)
  end

  FormulaCollection.new(formulas)
end

.all_keysObject



107
108
109
110
111
# File 'lib/fontist/formula.rb', line 107

def all_keys
  Dir[Fontist.formulas_path.join("**/*.yml").to_s].map do |path|
    path.sub("#{Fontist.formulas_path}/", "").sub(".yml", "")
  end
end

.find(font_name) ⇒ Object



113
114
115
# File 'lib/fontist/formula.rb', line 113

def find(font_name)
  Indexes::FontIndex.from_file.load_formulas(font_name).first
end

.find_by_font_file(font_file) ⇒ Object



164
165
166
167
168
169
170
171
# File 'lib/fontist/formula.rb', line 164

def find_by_font_file(font_file)
  key = Indexes::FilenameIndex.from_file
    .load_index_formulas(File.basename(font_file))
    .flat_map(&:name)
    .first

  find_by_key(key)
end

.find_by_key(key) ⇒ Object



147
148
149
150
151
152
# File 'lib/fontist/formula.rb', line 147

def find_by_key(key)
  path = Fontist.formulas_path.join("#{key}.yml")
  return unless File.exist?(path)

  from_file(path)
end

.find_by_key_or_name(name) ⇒ Object



143
144
145
# File 'lib/fontist/formula.rb', line 143

def find_by_key_or_name(name)
  find_by_key(name) || find_by_name(name)
end

.find_by_name(name) ⇒ Object



154
155
156
157
158
# File 'lib/fontist/formula.rb', line 154

def find_by_name(name)
  key = name_to_key(name)

  find_by_key(key)
end

.find_fonts(font_name) ⇒ Object



121
122
123
124
125
126
127
128
129
# File 'lib/fontist/formula.rb', line 121

def find_fonts(font_name)
  formulas = Indexes::FontIndex.from_file.load_formulas(font_name)

  formulas.map do |formula|
    formula.all_fonts.select do |f|
      f.name.casecmp?(font_name)
    end
  end.flatten
end

.find_many(font_name) ⇒ Object



117
118
119
# File 'lib/fontist/formula.rb', line 117

def find_many(font_name)
  Indexes::FontIndex.from_file.load_formulas(font_name)
end

.find_styles(font_name, style_name) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/fontist/formula.rb', line 131

def find_styles(font_name, style_name)
  formulas = Indexes::FontIndex.from_file.load_formulas(font_name)

  formulas.map do |formula|
    formula.all_fonts.map do |f|
      f.styles.select do |s|
        f.name.casecmp?(font_name) && s.type.casecmp?(style_name)
      end
    end
  end.flatten
end

.from_file(path) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/fontist/formula.rb', line 173

def from_file(path)
  unless File.exist?(path)
    raise Fontist::Errors::FormulaNotFoundError,
          "Formula file not found: #{path}"
  end

  content = File.read(path)

  from_yaml(content).tap do |formula|
    formula.path = path
    formula.name = titleize(formula.key_from_path) if formula.name.nil?
  end
rescue Lutaml::Model::Error, TypeError, ArgumentError,
       Psych::BadAlias, Psych::SyntaxError => e
  Fontist.ui.error("WARN: Could not load formula #{path}: #{e.message}")
  nil
end

.name_to_key(name) ⇒ Object



160
161
162
# File 'lib/fontist/formula.rb', line 160

def name_to_key(name)
  name.downcase.gsub(" ", "_")
end

.titleize(str) ⇒ Object



191
192
193
194
195
# File 'lib/fontist/formula.rb', line 191

def titleize(str)
  str.split("/").map do |part|
    part.tr("_", " ").split.map(&:capitalize).join(" ")
  end.join("/")
end

.update_formulas_repoObject



95
96
97
# File 'lib/fontist/formula.rb', line 95

def update_formulas_repo
  Update.call
end

Instance Method Details

#all_fontsObject



365
366
367
# File 'lib/fontist/formula.rb', line 365

def all_fonts
  Array(fonts) + collection_fonts
end

#collection_fontsObject



369
370
371
372
373
374
375
376
377
378
379
# File 'lib/fontist/formula.rb', line 369

def collection_fonts
  Array(font_collections).flat_map do |c|
    c.fonts.flat_map do |f|
      f.styles.each do |s|
        s.font = c.filename
        s.source_font = c.source_filename
      end
      f
    end
  end
end

#compatible_with_current_platform?Boolean

Returns:

  • (Boolean)


240
241
242
243
244
245
246
247
# File 'lib/fontist/formula.rb', line 240

def compatible_with_current_platform?
  return true unless macos_import?

  current_macos = Utils::System.macos_version
  return true unless current_macos

  import_source.compatible_with_macos?(current_macos)
end

#compatible_with_platform?(platform = nil) ⇒ Boolean

Returns:

  • (Boolean)


255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/fontist/formula.rb', line 255

def compatible_with_platform?(platform = nil)
  target = platform || Utils::System.user_os.to_s

  # No platform restrictions = compatible with all
  return true if platforms.nil? || platforms.empty?

  # Check if platform matches - support both exact matches and prefixed matches
  platform_matches = platforms.any? do |p|
    p == target || p.start_with?("#{target}-")
  end

  return false unless platform_matches

  # For macOS platform-tagged formulas, check framework support
  if target == "macos" && macos_import?
    current_macos = Utils::System.macos_version
    return true unless current_macos

    # Check if framework exists for this macOS version
    framework = Utils::System.catalog_version_for_macos
    if framework.nil?
      raise Errors::UnsupportedMacOSVersionError.new(
        current_macos,
        MacosFrameworkMetadata.,
      )
    end

    return import_source.compatible_with_macos?(current_macos)
  end

  true
end

#downloadable?Boolean

Returns:

  • (Boolean)


220
221
222
# File 'lib/fontist/formula.rb', line 220

def downloadable?
  !resources.nil? && !resources.empty?
end

#effective_schema_versionObject

Get the effective schema version (default to 4 if not set)



205
206
207
# File 'lib/fontist/formula.rb', line 205

def effective_schema_version
  schema_version || 4
end

#file_sizeObject



347
348
349
350
351
# File 'lib/fontist/formula.rb', line 347

def file_size
  return nil if resources.nil? || resources.empty?

  resources.first.file_size
end

#font_by_name(name) ⇒ Object



353
354
355
356
357
# File 'lib/fontist/formula.rb', line 353

def font_by_name(name)
  all_fonts.find do |font|
    font.name.casecmp?(name)
  end
end

#fonts_by_name(name) ⇒ Object



359
360
361
362
363
# File 'lib/fontist/formula.rb', line 359

def fonts_by_name(name)
  all_fonts.select do |font|
    font.name.casecmp?(name)
  end
end

#google_import?Boolean

Returns:

  • (Boolean)


228
229
230
# File 'lib/fontist/formula.rb', line 228

def google_import?
  import_source.is_a?(GoogleImportSource)
end

#keyObject



321
322
323
# File 'lib/fontist/formula.rb', line 321

def key
  @key ||= key_from_path
end

#key_from_pathObject



325
326
327
328
329
330
# File 'lib/fontist/formula.rb', line 325

def key_from_path
  return "" unless @path

  escaped = Regexp.escape("#{Fontist.formulas_path}/")
  @path.sub(Regexp.new("^#{escaped}"), "").sub(/\.yml$/, "").to_s
end

#licenseObject



332
333
334
# File 'lib/fontist/formula.rb', line 332

def license
  open_license || requires_license_agreement
end

#license_required?Boolean

Returns:

  • (Boolean)


336
337
338
# File 'lib/fontist/formula.rb', line 336

def license_required?
  requires_license_agreement ? true : false
end

#licensed_for_current_platform?Boolean

Returns:

  • (Boolean)


340
341
342
343
344
345
# File 'lib/fontist/formula.rb', line 340

def licensed_for_current_platform?
  return false unless platforms && !platforms.empty?

  current_os = Utils::System.user_os.to_s
  platforms.any? { |p| p == current_os || p.start_with?("#{current_os}-") }
end

#macos_import?Boolean

Returns:

  • (Boolean)


224
225
226
# File 'lib/fontist/formula.rb', line 224

def macos_import?
  import_source.is_a?(MacosImportSource)
end

#manual?Boolean

Returns:

  • (Boolean)


216
217
218
# File 'lib/fontist/formula.rb', line 216

def manual?
  !downloadable?
end

#manual_formula?Boolean

Returns:

  • (Boolean)


236
237
238
# File 'lib/fontist/formula.rb', line 236

def manual_formula?
  import_source.nil?
end

#matching_resources(format_spec) ⇒ Object

Filter resources based on format specification



210
211
212
213
214
# File 'lib/fontist/formula.rb', line 210

def matching_resources(format_spec)
  return resources if format_spec.nil?

  FormatMatcher.new(format_spec).filter_resources(resources)
end

#platform_restriction_messageObject



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/fontist/formula.rb', line 288

def platform_restriction_message
  return nil if compatible_with_platform?

  current = Utils::System.user_os

  message = "Font '#{name}' is only available for: #{platforms.join(', ')}. "
  message += "Your current platform is: #{current}."

  if current == :macos && macos_import?
    current_version = Utils::System.macos_version
    if current_version
      message += " Your macOS version is: #{current_version}."
    end

    min_version = import_source.min_macos_version
    max_version = import_source.max_macos_version

    if min_version && max_version
      message += " This font requires macOS #{min_version} to #{max_version}."
    elsif min_version
      message += " This font requires macOS #{min_version} or later."
    elsif max_version
      message += " This font requires macOS #{max_version} or earlier."
    end
  end

  "#{message} This font cannot be installed on your system."
end

#requires_system_installation?Boolean

Returns:

  • (Boolean)


317
318
319
# File 'lib/fontist/formula.rb', line 317

def requires_system_installation?
  source == "apple_cdn" && platforms&.include?("macos")
end

#sil_import?Boolean

Returns:

  • (Boolean)


232
233
234
# File 'lib/fontist/formula.rb', line 232

def sil_import?
  import_source.is_a?(SilImportSource)
end

#sourceObject



249
250
251
252
253
# File 'lib/fontist/formula.rb', line 249

def source
  return nil if resources.empty?

  resources.first.source
end

#style_override(font) ⇒ Object



381
382
383
384
385
386
# File 'lib/fontist/formula.rb', line 381

def style_override(font)
  all_fonts
    .map(&:styles)
    .flatten
    .detect { |s| s.family_name == font }&.override || {}
end

#v5?Boolean

Check if formula uses v5 schema (multi-format support) Returns true only if schema_version is explicitly 5

Returns:

  • (Boolean)


200
201
202
# File 'lib/fontist/formula.rb', line 200

def v5?
  schema_version == 5
end