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



102
103
104
105
106
107
108
# File 'lib/fontist/formula.rb', line 102

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



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

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



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

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

.find_by_font_file(font_file) ⇒ Object



167
168
169
170
171
172
173
174
# File 'lib/fontist/formula.rb', line 167

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



150
151
152
153
154
155
# File 'lib/fontist/formula.rb', line 150

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



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

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

.find_by_name(name) ⇒ Object



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

def find_by_name(name)
  key = name_to_key(name)

  find_by_key(key)
end

.find_fonts(font_name) ⇒ Object



124
125
126
127
128
129
130
131
132
# File 'lib/fontist/formula.rb', line 124

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



120
121
122
# File 'lib/fontist/formula.rb', line 120

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

.find_styles(font_name, style_name) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
# File 'lib/fontist/formula.rb', line 134

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



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

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



163
164
165
# File 'lib/fontist/formula.rb', line 163

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

.titleize(str) ⇒ Object



194
195
196
197
198
# File 'lib/fontist/formula.rb', line 194

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

.update_formulas_repoObject



98
99
100
# File 'lib/fontist/formula.rb', line 98

def update_formulas_repo
  Update.call
end

Instance Method Details

#all_fontsObject



376
377
378
# File 'lib/fontist/formula.rb', line 376

def all_fonts
  Array(fonts) + collection_fonts
end

#collection_fontsObject



380
381
382
383
384
385
386
387
388
389
390
# File 'lib/fontist/formula.rb', line 380

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)


251
252
253
254
255
256
257
258
# File 'lib/fontist/formula.rb', line 251

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)


266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/fontist/formula.rb', line 266

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)


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

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

#effective_schema_versionObject

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



208
209
210
# File 'lib/fontist/formula.rb', line 208

def effective_schema_version
  schema_version || 4
end

#file_sizeObject



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

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

  resources.first.file_size
end

#font_by_name(name) ⇒ Object



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

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

#fonts_by_name(name) ⇒ Object



370
371
372
373
374
# File 'lib/fontist/formula.rb', line 370

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

#google_import?Boolean

Returns:

  • (Boolean)


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

def google_import?
  import_source.is_a?(GoogleImportSource)
end

#keyObject



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

def key
  @key ||= key_from_path
end

#key_from_pathObject



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

def key_from_path
  return "" unless @path

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

#licenseObject



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

def license
  open_license || requires_license_agreement
end

#license_required?Boolean

Returns:

  • (Boolean)


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

def license_required?
  requires_license_agreement ? true : false
end

#licensed_for_current_platform?Boolean

Returns:

  • (Boolean)


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

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)


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

def macos_import?
  import_source.is_a?(MacosImportSource)
end

#manual?Boolean

Returns:

  • (Boolean)


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

def manual?
  !downloadable?
end

#manual_formula?Boolean

Returns:

  • (Boolean)


243
244
245
# File 'lib/fontist/formula.rb', line 243

def manual_formula?
  import_source.nil?
end

#matching_resources(format_spec) ⇒ Object

Filter resources based on format specification



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

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

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

#platform_restriction_messageObject



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/fontist/formula.rb', line 299

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)


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

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

#sil_import?Boolean

Returns:

  • (Boolean)


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

def sil_import?
  import_source.is_a?(SilImportSource)
end

#sourceObject



260
261
262
263
264
# File 'lib/fontist/formula.rb', line 260

def source
  return nil if resources.empty?

  resources.first.source
end

#style_override(font) ⇒ Object



392
393
394
395
396
397
# File 'lib/fontist/formula.rb', line 392

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)


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

def v5?
  schema_version == 5
end

#windows_fod?Boolean

Returns:

  • (Boolean)


247
248
249
# File 'lib/fontist/formula.rb', line 247

def windows_fod?
  source == "windows_fod" && platforms&.any? { |p| p == "windows" || p.start_with?("windows-") }
end

#windows_import?Boolean

Returns:

  • (Boolean)


239
240
241
# File 'lib/fontist/formula.rb', line 239

def windows_import?
  import_source.is_a?(WindowsImportSource)
end