class Fontist::Import::Google::FontDatabase
Database for merged font data from API sources
Generates v4 formulas (TTF static only) or v5 formulas (all formats)
Attributes
Public Class Methods
Source
# File lib/fontist/import/google/font_database.rb, line 79 def self.build(api_key:, source_path: nil) if source_path build_v4(api_key: api_key, source_path: source_path) else # Build without GitHub data ttf_data = DataSources::Ttf.new(api_key: api_key).fetch new(ttf_data: ttf_data, version: 4, source_path: nil) end end
Source
# File lib/fontist/import/google/font_database.rb, line 33 def self.build_v4(api_key:, source_path:) ttf_data = DataSources::Ttf.new(api_key: api_key).fetch # NO VF endpoint, NO WOFF2 endpoint for v4 github_data = DataSources::Github.new(source_path: source_path).fetch new( ttf_data: ttf_data, github_data: github_data, version: 4, source_path: source_path, ) end
Build database for v4 formulas (production)
V4 Requirements:
-
TTF format ONLY (no WOFF2)
-
Static fonts ONLY (exclude variable fonts)
-
OFL.txt license from GitHub repository
-
Complete metadata from Fontisan
@param api_key [String] Google Fonts API key @param source_path [String] Path to google/fonts repository @return [FontDatabase] configured database instance
Source
# File lib/fontist/import/google/font_database.rb, line 57 def self.build_v5(api_key:, source_path:) ttf_data = DataSources::Ttf.new(api_key: api_key).fetch vf_data = DataSources::Vf.new(api_key: api_key).fetch woff2_data = DataSources::Woff2.new(api_key: api_key).fetch github_data = DataSources::Github.new(source_path: source_path).fetch new( ttf_data: ttf_data, vf_data: vf_data, woff2_data: woff2_data, github_data: github_data, version: 5, source_path: source_path, ) end
Build database for v5 formulas (future)
V5 will support:
-
TTF + WOFF2 formats
-
Static + Variable fonts
-
Enhanced โprovides` attribute
-
Per-file resources
@param api_key [String] Google Fonts API key @param source_path [String] Path to google/fonts repository @return [FontDatabase] configured database instance
Source
# File lib/fontist/import/google/font_database.rb, line 97 def initialize(ttf_data:, vf_data: nil, woff2_data: nil, github_data: nil, version: 4, source_path: nil) @ttf_data = Array(ttf_data) @vf_data = Array(vf_data) @woff2_data = Array(woff2_data) @github_data_raw = Array(github_data) @version = version @source_path = source_path @ttf_files = {} @woff2_files = {} @github_index = index_github_data @github_data = @github_index # Expose indexed version @fonts = merge_data end
Initialize database with API data
@param ttf_data [Array] TTF endpoint data @param vf_data [Array, nil] VF endpoint data (optional, for v5) @param woff2_data [Array, nil] WOFF2 endpoint data (optional, for v5) @param github_data [Array, nil] GitHub repository data (optional) @param version [Integer] Formula version (4 or 5) @param source_path [String, nil] Path to google/fonts repository (optional)
Public Instance Methods
Source
# File lib/fontist/import/google/font_database.rb, line 113 def all_fonts @fonts.values end
Get all font families
Source
# File lib/fontist/import/google/font_database.rb, line 283 def build_fonts_v4(family) parsed_fonts = [] # V4: Download and parse ONLY TTF files to get complete metadata @ttf_files[family.family]&.each_value do |url| sleep(0.05) # Throttle API requests begin # Download font downloaded = Fontist::Utils::Downloader.download(url) # Parse with Fontisan metadata = Fontist::Import::FontMetadataExtractor.new(downloaded.path).extract # V4: Skip variable fonts if metadata.is_variable next end # Get filename from URL filename = url.split("/").last # Create style with complete metadata style_data = { family_name: metadata.family_name, type: metadata.subfamily_name, full_name: metadata.full_name, post_script_name: metadata.postscript_name, version: metadata.version, copyright: metadata.copyright, font: filename, } # Add preferred names if present if metadata.preferred_family_name style_data[:preferred_family_name] = metadata.preferred_family_name end if metadata.preferred_subfamily_name style_data[:preferred_type] = metadata.preferred_subfamily_name end # Add description if present if metadata.description style_data[:description] = metadata.description end parsed_fonts << style_data rescue StandardError => e warn "Warning: Failed to download/parse #{url}: #{e.message}" end end return [] if parsed_fonts.empty? # Group by subfamily (family_name from font, not API family) fonts_by_subfamily = parsed_fonts.group_by { |f| f[:family_name] } fonts_by_subfamily.map do |subfamily_name, styles| { "name" => subfamily_name, "styles" => styles.map { |s| stringify_style(s) }, } end end
Build fonts from API variant data with full metadata (v4: TTF only)
Source
# File lib/fontist/import/google/font_database.rb, line 205 def build_formula_v4(family) github_family = @github_index[family.family] # Read license from GitHub if available license_url, license_text = if github_family&.license_text [ "https://scripts.sil.org/OFL", github_family.license_text, ] else [ "https://scripts.sil.org/OFL", "SIL Open Font License v1.1", ] end # Build fonts first to get copyright fonts_data = build_fonts_v4(family) # Extract copyright from first font style, or use license_text as fallback copyright = fonts_data.dig(0, "styles", 0, "copyright") || github_family&.license_text # Use GitHub description if available description = github_family&.description || default_description(family) # Create import_source if available import_source = create_import_source(family) formula = { name: formula_name(family), description: description, homepage: default_homepage(family), resources: build_resources_v4(family), fonts: fonts_data, extract: {}, copyright: copyright, license_url: license_url, license: license_text, # Changed from open_license open_license: license_text, } # Add import_source if available formula[:import_source] = import_source if import_source formula.compact end
Build v4 formula from API data
Source
# File lib/fontist/import/google/font_database.rb, line 254 def build_resources_v4(family) files = [] # V4: Collect ONLY TTF URLs from API (no WOFF2) @ttf_files[family.family]&.each_value do |url| files << url end return nil if files.empty? # V4: Always use "ttf" format (no variable fonts in v4) format = "ttf" resource = { "source" => "google", "family" => family.family, "files" => files, "format" => format, } # Add variable_axes if present if family.variable_font? && family.axes resource["variable_axes"] = family.axes.map(&:tag) end { family.family => resource } end
Build resources from API URLs (v4: TTF only, no WOFF2)
Source
# File lib/fontist/import/google/font_database.rb, line 123 def by_category(category) all_fonts.select { |font| font.category == category } end
Filter fonts by category
Source
# File lib/fontist/import/google/font_database.rb, line 147 def categories all_fonts.map(&:category).compact.uniq.sort end
Get all unique categories
Source
# File lib/fontist/import/google/font_database.rb, line 441 def create_import_source(family) # Only create import_source if we have a commit_id commit = current_commit_id return nil unless commit Fontist::GoogleImportSource.new( commit_id: commit, api_version: "v1", last_modified: last_modified_for(family), family_id: family.family.downcase.tr(" ", "_"), ) end
Create GoogleImportSource for a font family
@param family [Models::FontFamily] Font family object @return [GoogleImportSource, nil] Import source or nil if no commit_id
Source
# File lib/fontist/import/google/font_database.rb, line 418 def current_commit_id return @commit_id if defined?(@commit_id) @commit_id = if @source_path && File.directory?(@source_path) get_git_commit(@source_path) end @commit_id end
Get current git commit from google/fonts repository
@return [String, nil] Git commit SHA or nil if not available
Source
# File lib/fontist/import/google/font_database.rb, line 386 def default_description(family) "#{family.family} font family" end
Generate default description
Source
# File lib/fontist/import/google/font_database.rb, line 391 def default_homepage(family) "https://fonts.google.com/specimen/#{family.family.gsub(/\s+/, '+')}" end
Generate default homepage
Source
# File lib/fontist/import/google/font_database.rb, line 357 def find_font_filename_for_variant(family, variant) # Try TTF first url = @ttf_files[family.family]&.[](variant) return url.split("/").last if url # Try WOFF2 url = @woff2_files[family.family]&.[](variant) return url.split("/").last if url nil end
Find filename for a variant
Source
# File lib/fontist/import/google/font_database.rb, line 118 def font_by_name(family_name) @fonts[family_name] end
Find a specific font family by name
Source
# File lib/fontist/import/google/font_database.rb, line 138 def fonts_count { total: all_fonts.count, variable: variable_fonts_only.count, static: static_fonts_only.count, } end
Get count of fonts by type
Source
# File lib/fontist/import/google/font_database.rb, line 162 def fonts_with_both_formats all_fonts.select do |font| @ttf_files.key?(font.family) && @woff2_files.key?(font.family) end end
Get fonts available in both formats
Source
# File lib/fontist/import/google/font_database.rb, line 152 def fonts_with_ttf all_fonts.select { |font| @ttf_files.key?(font.family) } end
Get fonts available in TTF format
Source
# File lib/fontist/import/google/font_database.rb, line 157 def fonts_with_woff2 all_fonts.select { |font| @woff2_files.key?(font.family) } end
Get fonts available in WOFF2 format
Source
# File lib/fontist/import/google/font_database.rb, line 381 def formula_name(family) family.family.downcase.gsub(/\s+/, "_") end
Generate formula name from family name
Source
# File lib/fontist/import/google/font_database.rb, line 432 def last_modified_for(family) # Use last_modified from API metadata if available family.last_modified || Time.now.utc.iso8601 end
Get last modified timestamp for a font family
@param family [Models::FontFamily] Font family object @return [String] ISO 8601 timestamp
Source
# File lib/fontist/import/google/font_database.rb, line 396 def save_formula(formula_hash, family_name, output_dir) FileUtils.mkdir_p(output_dir) # Google Fonts formulas always use simple filenames # (Google Fonts is a live service, always pointing to latest version) base_name = family_name.downcase.gsub(/\s+/, "_") filename = "#{base_name}.yml" path = File.join(output_dir, filename) # Use HashHelper to convert keys to strings (preserves import_source object) require_relative "../helpers/hash_helper" formula_with_string_keys = Helpers::HashHelper.stringify_keys(formula_hash) File.write(path, YAML.dump(formula_with_string_keys)) path end
Save formula to disk
Source
# File lib/fontist/import/google/font_database.rb, line 192 def save_formulas(output_dir, family_name: nil) families = family_name ? [font_by_name(family_name)] : all_fonts families = families.compact families.map do |family| formula = to_formula(family.family) next unless formula save_formula(formula, family.family, output_dir) end.compact end
Save formulas to disk
Source
# File lib/fontist/import/google/font_database.rb, line 133 def static_fonts_only all_fonts.reject(&:variable_font?) end
Get only static fonts (fonts without axes)
Source
# File lib/fontist/import/google/font_database.rb, line 350 def stringify_style(style) style.transform_keys(&:to_s).transform_values do |v| v.is_a?(Symbol) ? v.to_s : v end end
Convert style hash to string keys
Source
# File lib/fontist/import/google/font_database.rb, line 179 def to_formula(family_name) family = font_by_name(family_name) return nil unless family build_formula_v4(family) end
Generate formula for a font family
Source
# File lib/fontist/import/google/font_database.rb, line 187 def to_formulas all_fonts.map { |f| to_formula(f.family) }.compact end
Generate formulas for all fonts
Source
# File lib/fontist/import/google/font_database.rb, line 169 def ttf_files_for(family_name) @ttf_files[family_name] end
Get TTF files for a specific font family
Source
# File lib/fontist/import/google/font_database.rb, line 128 def variable_fonts_only all_fonts.select(&:variable_font?) end
Get only variable fonts (fonts with axes)
Source
# File lib/fontist/import/google/font_database.rb, line 370 def variant_to_type(variant) case variant when "regular" then "Regular" when "italic" then "Italic" when /^(\d+)italic$/ then "#{$1} Italic" when /^(\d+)$/ then variant else variant.capitalize end end
Convert API variant to style type
Source
# File lib/fontist/import/google/font_database.rb, line 174 def woff2_files_for(family_name) @woff2_files[family_name] end
Get WOFF2 files for a specific font family