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



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

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

  FormulaCollection.new(formulas)
end

.all_keysObject



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

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



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

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

.find_by_font_file(font_file) ⇒ Object



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

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



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

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



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

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

.find_by_name(name) ⇒ Object



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

def find_by_name(name)
  key = name_to_key(name)

  find_by_key(key)
end

.find_fonts(font_name) ⇒ Object



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

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



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

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

.find_styles(font_name, style_name) ⇒ Object



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

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



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

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



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

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

.titleize(str) ⇒ Object



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

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

.update_formulas_repoObject



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

def update_formulas_repo
  Update.call
end

Instance Method Details

#all_fontsObject



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

def all_fonts
  Array(fonts) + collection_fonts
end

#collection_fontsObject



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

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)


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

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)


254
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
# File 'lib/fontist/formula.rb', line 254

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)


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

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

#effective_schema_versionObject

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



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

def effective_schema_version
  schema_version || 4
end

#file_sizeObject



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

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

  resources.first.file_size
end

#font_by_name(name) ⇒ Object



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

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

#fonts_by_name(name) ⇒ Object



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

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

#google_import?Boolean

Returns:

  • (Boolean)


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

def google_import?
  import_source.is_a?(GoogleImportSource)
end

#keyObject



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

def key
  @key ||= key_from_path
end

#key_from_pathObject



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

def key_from_path
  return "" unless @path

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

#licenseObject



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

def license
  open_license || requires_license_agreement
end

#license_required?Boolean

Returns:

  • (Boolean)


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

def license_required?
  requires_license_agreement ? true : false
end

#licensed_for_current_platform?Boolean

Returns:

  • (Boolean)


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

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)


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

def macos_import?
  import_source.is_a?(MacosImportSource)
end

#manual?Boolean

Returns:

  • (Boolean)


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

def manual?
  !downloadable?
end

#manual_formula?Boolean

Returns:

  • (Boolean)


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

def manual_formula?
  import_source.nil?
end

#matching_resources(format_spec) ⇒ Object

Filter resources based on format specification



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

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

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

#platform_restriction_messageObject



287
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
# File 'lib/fontist/formula.rb', line 287

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)


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

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

#sil_import?Boolean

Returns:

  • (Boolean)


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

def sil_import?
  import_source.is_a?(SilImportSource)
end

#sourceObject



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

def source
  return nil if resources.empty?

  resources.first.source
end

#style_override(font) ⇒ Object



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

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)


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

def v5?
  schema_version == 5
end