fontist 3.0.28 → 3.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.adoc +148 -0
- data/fontist.gemspec +4 -3
- data/lib/fontist/cache/store.rb +13 -6
- data/lib/fontist/cli.rb +106 -6
- data/lib/fontist/css/google_family.rb +50 -0
- data/lib/fontist/css/local.rb +157 -0
- data/lib/fontist/css/styles.rb +79 -0
- data/lib/fontist/css.rb +110 -0
- data/lib/fontist/errors.rb +3 -0
- data/lib/fontist/font_file.rb +2 -1
- data/lib/fontist/font_finder.rb +15 -2
- data/lib/fontist/font_installer.rb +97 -94
- data/lib/fontist/font_operations/convert.rb +55 -0
- data/lib/fontist/font_operations/extract.rb +53 -0
- data/lib/fontist/font_operations/instance.rb +92 -0
- data/lib/fontist/font_operations/pack.rb +49 -0
- data/lib/fontist/font_operations/subset.rb +68 -0
- data/lib/fontist/font_operations.rb +25 -0
- data/lib/fontist/format_spec.rb +35 -0
- data/lib/fontist/formula.rb +33 -5
- data/lib/fontist/formula_picker.rb +27 -8
- data/lib/fontist/from_file_cache.rb +4 -0
- data/lib/fontist/helpers.rb +1 -1
- data/lib/fontist/import/convert_formulas.rb +1 -1
- data/lib/fontist/import/google/data_sources/base.rb +3 -3
- data/lib/fontist/import/google/data_sources/github.rb +28 -0
- data/lib/fontist/import/google/formula_builders/formula_builder_v5.rb +30 -57
- data/lib/fontist/import/google/models/font_family.rb +7 -4
- data/lib/fontist/import/google/variable_font_source.rb +105 -0
- data/lib/fontist/import/google.rb +1 -0
- data/lib/fontist/import/helpers/hash_helper.rb +1 -1
- data/lib/fontist/import/sil_importer.rb +18 -5
- data/lib/fontist/import/v4_to_v5_migrator.rb +1 -1
- data/lib/fontist/import/windows.rb +13 -2
- data/lib/fontist/import_cli.rb +19 -0
- data/lib/fontist/index_cli.rb +1 -1
- data/lib/fontist/indexes/index_mixin.rb +4 -4
- data/lib/fontist/json.rb +76 -0
- data/lib/fontist/manifest.rb +63 -0
- data/lib/fontist/resource.rb +6 -1
- data/lib/fontist/validate_cli.rb +1 -1
- data/lib/fontist/version.rb +1 -1
- data/lib/fontist/yaml_backend.rb +18 -5
- data/lib/fontist.rb +3 -0
- metadata +17 -19
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fontist
|
|
4
|
+
module Import
|
|
5
|
+
module Google
|
|
6
|
+
# The real variable font files of a family as published in the
|
|
7
|
+
# google/fonts repository (METADATA.pb `fonts` entries whose filename
|
|
8
|
+
# carries an axis list, e.g. "Roboto[wdth,wght].ttf").
|
|
9
|
+
#
|
|
10
|
+
# The Google Fonts API only serves static per-weight instances, so these
|
|
11
|
+
# files are the only genuine variable sources. Google publishes them as
|
|
12
|
+
# TTF only.
|
|
13
|
+
class VariableFontSource
|
|
14
|
+
RAW_BASE = "https://raw.githubusercontent.com/google/fonts"
|
|
15
|
+
AXIS_LIST = /\[([^\]]+)\]/
|
|
16
|
+
|
|
17
|
+
attr_reader :directory, :commit, :files, :axes
|
|
18
|
+
|
|
19
|
+
# @param directory [String] repo-relative family dir, e.g. "ofl/roboto"
|
|
20
|
+
# @param commit [String] google/fonts commit SHA the URLs are pinned to
|
|
21
|
+
# @param files [Array<Models::FontFileMetadata>] METADATA.pb fonts
|
|
22
|
+
# @param axes [Array<Models::AxisMetadata>] METADATA.pb axes
|
|
23
|
+
def self.from_metadata(metadata, directory:, commit:)
|
|
24
|
+
files = Array(metadata.fonts).select do |font|
|
|
25
|
+
font.filename.to_s.match?(AXIS_LIST)
|
|
26
|
+
end
|
|
27
|
+
return nil if files.empty? || commit.to_s.empty?
|
|
28
|
+
|
|
29
|
+
new(directory: directory, commit: commit, files: files,
|
|
30
|
+
axes: Array(metadata.axes))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def initialize(directory:, commit:, files:, axes:)
|
|
34
|
+
@directory = directory
|
|
35
|
+
@commit = commit
|
|
36
|
+
@files = files
|
|
37
|
+
@axes = axes
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def urls
|
|
41
|
+
files.map { |file| url_for(file.filename) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Axis tags in METADATA.pb order, limited to those the files carry.
|
|
45
|
+
def axis_tags
|
|
46
|
+
present = files.flat_map { |file| tags_in(file.filename) }.uniq
|
|
47
|
+
declared = axes.map(&:tag)
|
|
48
|
+
(declared & present) + (present - declared)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Resource entry for a v5 formula (TTF only).
|
|
52
|
+
def resource(family_name)
|
|
53
|
+
{
|
|
54
|
+
"source" => "google",
|
|
55
|
+
"family" => family_name,
|
|
56
|
+
"files" => urls,
|
|
57
|
+
"urls" => urls,
|
|
58
|
+
"format" => "ttf",
|
|
59
|
+
"variable_axes" => axis_tags,
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# One style per variable file, described by METADATA.pb.
|
|
64
|
+
def styles
|
|
65
|
+
files.map { |file| style_for(file) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def style_for(file)
|
|
71
|
+
names_of(file).merge(
|
|
72
|
+
"font" => file.filename,
|
|
73
|
+
"formats" => ["ttf"],
|
|
74
|
+
"variable_font" => true,
|
|
75
|
+
"variable_axes" => tags_in(file.filename),
|
|
76
|
+
"source_resource" => "ttf_variable",
|
|
77
|
+
).compact
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def names_of(file)
|
|
81
|
+
{
|
|
82
|
+
"family_name" => file.name,
|
|
83
|
+
"type" => file.style.to_s == "italic" ? "Italic" : "Regular",
|
|
84
|
+
"full_name" => file.full_name,
|
|
85
|
+
"post_script_name" => file.post_script_name,
|
|
86
|
+
"copyright" => file.copyright,
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def tags_in(filename)
|
|
91
|
+
filename[AXIS_LIST, 1].to_s.split(",")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def url_for(filename)
|
|
95
|
+
# Brackets stay literal: the installer matches style files against
|
|
96
|
+
# the URL basename, which must equal the downloaded file name.
|
|
97
|
+
encoded = filename.gsub(/[^A-Za-z0-9._,\-\[\]]/) do |char|
|
|
98
|
+
char.bytes.map { |b| format("%%%02X", b) }.join
|
|
99
|
+
end
|
|
100
|
+
"#{RAW_BASE}/#{commit}/#{directory}/#{encoded}"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -7,6 +7,7 @@ module Fontist
|
|
|
7
7
|
autoload :MetadataAdapter, "#{__dir__}/google/metadata_adapter"
|
|
8
8
|
autoload :MetadataParser, "#{__dir__}/google/metadata_parser"
|
|
9
9
|
autoload :Models, "#{__dir__}/google/models"
|
|
10
|
+
autoload :VariableFontSource, "#{__dir__}/google/variable_font_source"
|
|
10
11
|
|
|
11
12
|
DEFAULT_MAX_COUNT = 100
|
|
12
13
|
|
|
@@ -5,6 +5,9 @@ module Fontist
|
|
|
5
5
|
module Import
|
|
6
6
|
class SilImporter
|
|
7
7
|
ROOT = "https://software.sil.org/fonts/".freeze
|
|
8
|
+
CARD_LINK_SELECTOR = "a.inner-card".freeze
|
|
9
|
+
CARD_TITLE_SELECTOR = ".title-text".freeze
|
|
10
|
+
LEGACY_LINK_SELECTOR = "table.products div.title > a".freeze
|
|
8
11
|
|
|
9
12
|
def initialize(options = {})
|
|
10
13
|
@output_path = options[:output_path]
|
|
@@ -99,7 +102,7 @@ module Fontist
|
|
|
99
102
|
end
|
|
100
103
|
|
|
101
104
|
def process_single_font(link, current, total)
|
|
102
|
-
family_name = link
|
|
105
|
+
family_name = link_name(link)
|
|
103
106
|
|
|
104
107
|
# Display progress
|
|
105
108
|
ImportDisplay.progress(current, total, family_name) if @verbose
|
|
@@ -257,7 +260,8 @@ module Fontist
|
|
|
257
260
|
end
|
|
258
261
|
|
|
259
262
|
def empty_result
|
|
260
|
-
{ successful: 0, failed: 0, errors: [], duration: 0
|
|
263
|
+
{ successful: 0, failed: 0, errors: [], duration: 0,
|
|
264
|
+
no_fonts_found: true }
|
|
261
265
|
end
|
|
262
266
|
|
|
263
267
|
def build_results(duration)
|
|
@@ -274,12 +278,21 @@ module Fontist
|
|
|
274
278
|
def font_links
|
|
275
279
|
html = URI.parse(ROOT).open("User-Agent" => Fontist::Utils::UserAgent.random_user_agent).read
|
|
276
280
|
document = Nokogiri::HTML.parse(html)
|
|
277
|
-
document.css(
|
|
281
|
+
links = document.css(CARD_LINK_SELECTOR)
|
|
282
|
+
links = document.css(LEGACY_LINK_SELECTOR) if links.empty?
|
|
283
|
+
links
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Font name of a product link. Cards on the current site also carry a
|
|
287
|
+
# description, so the name is taken from the card title only.
|
|
288
|
+
def link_name(link)
|
|
289
|
+
title = link.at_css(CARD_TITLE_SELECTOR)
|
|
290
|
+
(title || link).text.strip
|
|
278
291
|
end
|
|
279
292
|
|
|
280
293
|
def filter_by_name(links)
|
|
281
294
|
links.select do |link|
|
|
282
|
-
link.
|
|
295
|
+
link_name(link).downcase.include?(@font_name.downcase)
|
|
283
296
|
end
|
|
284
297
|
end
|
|
285
298
|
|
|
@@ -342,7 +355,7 @@ module Fontist
|
|
|
342
355
|
end
|
|
343
356
|
|
|
344
357
|
def find_archive_url_by_page_link(link)
|
|
345
|
-
family_name = link
|
|
358
|
+
family_name = link_name(link)
|
|
346
359
|
|
|
347
360
|
# Skip known index pages that just link to other fonts
|
|
348
361
|
if index_page?(family_name)
|
|
@@ -95,7 +95,7 @@ module Fontist
|
|
|
95
95
|
def load_yaml_without_aliases(path)
|
|
96
96
|
data = YAML.safe_load(File.read(path), aliases: true,
|
|
97
97
|
permitted_classes: [Date])
|
|
98
|
-
JSON.parse(JSON.
|
|
98
|
+
Fontist::JSON.parse(Fontist::JSON.dump(data))
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
def file_has_yaml_aliases?(path)
|
|
@@ -53,7 +53,7 @@ module Fontist
|
|
|
53
53
|
def base_attrs(description)
|
|
54
54
|
{
|
|
55
55
|
schema_version: 5,
|
|
56
|
-
name: description,
|
|
56
|
+
name: formula_name(description),
|
|
57
57
|
description: "#{description} for Windows",
|
|
58
58
|
homepage: HOMEPAGE,
|
|
59
59
|
platforms: ["windows"],
|
|
@@ -119,7 +119,7 @@ module Fontist
|
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def formula_path(description)
|
|
122
|
-
filename = "#{
|
|
122
|
+
filename = "#{formula_key(description)}.yml"
|
|
123
123
|
formula_dir.join(filename)
|
|
124
124
|
end
|
|
125
125
|
|
|
@@ -135,6 +135,17 @@ module Fontist
|
|
|
135
135
|
end
|
|
136
136
|
end
|
|
137
137
|
|
|
138
|
+
# Formula names keep to letters, digits and spaces, so the file key the
|
|
139
|
+
# formulas repository derives from them (lowercase, spaces as
|
|
140
|
+
# underscores) needs no shell escaping in its CI.
|
|
141
|
+
def formula_name(description)
|
|
142
|
+
description.tr("-", " ").delete("^A-Za-z0-9 ").squeeze(" ").strip
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def formula_key(description)
|
|
146
|
+
formula_name(description).downcase.tr(" ", "_")
|
|
147
|
+
end
|
|
148
|
+
|
|
138
149
|
def normalize_key(name)
|
|
139
150
|
name.downcase.gsub(/[^a-z0-9]+/, "_").gsub(/^_|_$/, "")
|
|
140
151
|
end
|
data/lib/fontist/import_cli.rb
CHANGED
|
@@ -122,6 +122,24 @@ module Fontist
|
|
|
122
122
|
CLI::STATUS_UNKNOWN_ERROR
|
|
123
123
|
end
|
|
124
124
|
|
|
125
|
+
desc "windows", "Import Windows font extension packs (Features on Demand)"
|
|
126
|
+
option :output_path,
|
|
127
|
+
type: :string,
|
|
128
|
+
desc: "Output directory for generated formulas " \
|
|
129
|
+
"(default: formulas/windows)"
|
|
130
|
+
|
|
131
|
+
def windows
|
|
132
|
+
handle_class_options(options)
|
|
133
|
+
require_relative "import/windows"
|
|
134
|
+
|
|
135
|
+
Import::Windows.new(formulas_dir: options[:output_path]).call
|
|
136
|
+
|
|
137
|
+
CLI::STATUS_SUCCESS
|
|
138
|
+
rescue StandardError => e
|
|
139
|
+
Fontist.ui.error("Import error: #{e.message}")
|
|
140
|
+
CLI::STATUS_UNKNOWN_ERROR
|
|
141
|
+
end
|
|
142
|
+
|
|
125
143
|
desc "sil", "Import formulas from SIL International"
|
|
126
144
|
option :output_path,
|
|
127
145
|
type: :string,
|
|
@@ -158,6 +176,7 @@ module Fontist
|
|
|
158
176
|
)
|
|
159
177
|
|
|
160
178
|
result = importer.call
|
|
179
|
+
return CLI::STATUS_UNKNOWN_ERROR if result[:no_fonts_found]
|
|
161
180
|
|
|
162
181
|
# Report results only in non-verbose mode (verbose mode already has rich summary)
|
|
163
182
|
unless options[:verbose]
|
data/lib/fontist/index_cli.rb
CHANGED
|
@@ -103,13 +103,13 @@ module Fontist
|
|
|
103
103
|
return entries if entries
|
|
104
104
|
|
|
105
105
|
parse_sidecar_entries(sidecar_path)
|
|
106
|
-
rescue JSON::
|
|
106
|
+
rescue Fontist::JSON::ParseError, TypeError
|
|
107
107
|
nil
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
def rebuild_sidecar_entries
|
|
111
111
|
entries_hash = from_file(path).entries_hash
|
|
112
|
-
File.write("#{path}.json", JSON.dump("entries" => entries_hash))
|
|
112
|
+
File.write("#{path}.json", Fontist::JSON.dump("entries" => entries_hash))
|
|
113
113
|
entries_hash
|
|
114
114
|
end
|
|
115
115
|
|
|
@@ -135,7 +135,7 @@ module Fontist
|
|
|
135
135
|
# as read-only
|
|
136
136
|
def parse_sidecar_entries(sidecar_path)
|
|
137
137
|
mtime = File.mtime(sidecar_path)
|
|
138
|
-
entries = JSON.parse(File.read(sidecar_path))["entries"]
|
|
138
|
+
entries = Fontist::JSON.parse(File.read(sidecar_path))["entries"]
|
|
139
139
|
|
|
140
140
|
sidecar_cache_mutex.synchronize do
|
|
141
141
|
sidecar_cache[sidecar_path] = { mtime: mtime, entries: entries }
|
|
@@ -244,7 +244,7 @@ module Fontist
|
|
|
244
244
|
|
|
245
245
|
def write_lookup_sidecar(file_path)
|
|
246
246
|
File.write("#{file_path}.json",
|
|
247
|
-
JSON.dump("entries" => entries_hash))
|
|
247
|
+
Fontist::JSON.dump("entries" => entries_hash))
|
|
248
248
|
end
|
|
249
249
|
|
|
250
250
|
# key -> formula paths, in the sidecar's JSON-friendly shape
|
data/lib/fontist/json.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require "yeptris"
|
|
2
|
+
require "yeptris/json"
|
|
3
|
+
|
|
4
|
+
module Fontist
|
|
5
|
+
# JSON encoding and decoding through yeptris's native engine, replacing
|
|
6
|
+
# the json gem as fontist's JSON backend. The yeptris parser and
|
|
7
|
+
# generator produce and accept the same plain Ruby structures as the
|
|
8
|
+
# json gem (string keys, arrays, scalars), so this module is a
|
|
9
|
+
# drop-in surface; call sites rescue the module's error classes
|
|
10
|
+
# instead of JSON::ParserError and JSON::GeneratorError.
|
|
11
|
+
module JSON
|
|
12
|
+
class Error < StandardError; end
|
|
13
|
+
class ParseError < Error; end
|
|
14
|
+
class DumpError < Error; end
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def parse(source)
|
|
18
|
+
unless source.is_a?(String)
|
|
19
|
+
raise ParseError,
|
|
20
|
+
"cannot parse #{source.class}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Yeptris::JSON.load(source)
|
|
24
|
+
rescue Yeptris::Error, TypeError => e
|
|
25
|
+
raise ParseError, e.message
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def dump(object)
|
|
29
|
+
Yeptris::JSON.dump(object)
|
|
30
|
+
rescue Yeptris::Error, TypeError => e
|
|
31
|
+
raise DumpError, e.message
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Two-space-indented JSON for human-facing CLI output; yeptris's
|
|
35
|
+
# generator is compact-only, so the indentation is applied here
|
|
36
|
+
def pretty(object)
|
|
37
|
+
pretty_value(object, 0)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def pretty_value(value, depth)
|
|
43
|
+
case value
|
|
44
|
+
when Hash then pretty_object(value, depth)
|
|
45
|
+
when Array then pretty_array(value, depth)
|
|
46
|
+
else dump(value)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def pretty_object(hash, depth)
|
|
51
|
+
return "{}" if hash.empty?
|
|
52
|
+
|
|
53
|
+
inner = hash.map do |key, value|
|
|
54
|
+
"#{indent(depth + 1)}#{dump(key.to_s)}: #{pretty_value(value,
|
|
55
|
+
depth + 1)}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
"{\n#{inner.join(",\n")}\n#{indent(depth)}}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def pretty_array(array, depth)
|
|
62
|
+
return "[]" if array.empty?
|
|
63
|
+
|
|
64
|
+
inner = array.map do |value|
|
|
65
|
+
"#{indent(depth + 1)}#{pretty_value(value, depth + 1)}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
"[\n#{inner.join(",\n")}\n#{indent(depth)}]"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def indent(depth)
|
|
72
|
+
" " * depth
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/lib/fontist/manifest.rb
CHANGED
|
@@ -18,6 +18,12 @@ module Fontist
|
|
|
18
18
|
# Collection options
|
|
19
19
|
attribute :collection_index, :integer
|
|
20
20
|
|
|
21
|
+
# Font operations (subsetting, conversion failure handling)
|
|
22
|
+
attribute :subset_unicodes, :string
|
|
23
|
+
attribute :subset_text, :string
|
|
24
|
+
attribute :fallback_to_original, :boolean, default: false
|
|
25
|
+
attribute :instance, :string
|
|
26
|
+
|
|
21
27
|
# Build FormatSpec from attributes
|
|
22
28
|
def format_spec
|
|
23
29
|
FormatSpec.new(
|
|
@@ -27,6 +33,10 @@ module Fontist
|
|
|
27
33
|
transcode_path: transcode_path,
|
|
28
34
|
keep_original: keep_original,
|
|
29
35
|
collection_index: collection_index,
|
|
36
|
+
subset_unicodes: subset_unicodes,
|
|
37
|
+
subset_text: subset_text,
|
|
38
|
+
fallback_to_original: fallback_to_original,
|
|
39
|
+
instance: instance,
|
|
30
40
|
)
|
|
31
41
|
end
|
|
32
42
|
|
|
@@ -129,6 +139,57 @@ module Fontist
|
|
|
129
139
|
}
|
|
130
140
|
end
|
|
131
141
|
|
|
142
|
+
# Font options a manifest entry can carry next to its styles:
|
|
143
|
+
#
|
|
144
|
+
# Roboto:
|
|
145
|
+
# styles: [Regular, Bold]
|
|
146
|
+
# format: woff2
|
|
147
|
+
#
|
|
148
|
+
# The plain form `Roboto: [Regular, Bold]` stays valid.
|
|
149
|
+
FONT_OPTIONS = %w[format variable_axes prefer_variable transcode_path
|
|
150
|
+
keep_original collection_index subset_unicodes
|
|
151
|
+
subset_text fallback_to_original instance].freeze
|
|
152
|
+
|
|
153
|
+
def self.from_yaml(content, options = {})
|
|
154
|
+
data = YAML.safe_load(content)
|
|
155
|
+
return super unless data.is_a?(Hash) && data.values.any?(Hash)
|
|
156
|
+
|
|
157
|
+
plain, font_options = split_font_options(data)
|
|
158
|
+
super(plain.to_yaml, options).tap do |manifest|
|
|
159
|
+
manifest.apply_font_options(font_options)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def self.split_font_options(data)
|
|
164
|
+
font_options = {}
|
|
165
|
+
plain = data.to_h do |name, value|
|
|
166
|
+
next [name, value] unless value.is_a?(Hash)
|
|
167
|
+
|
|
168
|
+
font_options[name] = font_options_of(name, value)
|
|
169
|
+
[name, value["styles"] || value[:styles]]
|
|
170
|
+
end
|
|
171
|
+
[plain, font_options]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def self.font_options_of(name, value)
|
|
175
|
+
value = value.transform_keys(&:to_s)
|
|
176
|
+
unknown = value.keys - FONT_OPTIONS - ["styles"]
|
|
177
|
+
unless unknown.empty?
|
|
178
|
+
raise Fontist::Errors::ManifestCouldNotBeReadError,
|
|
179
|
+
"Unknown option(s) for '#{name}': #{unknown.join(', ')}"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
value.slice(*FONT_OPTIONS)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def apply_font_options(font_options)
|
|
186
|
+
Array(fonts).each do |font|
|
|
187
|
+
font_options.fetch(font.name, {}).each do |key, value|
|
|
188
|
+
font.public_send(:"#{key}=", value)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
132
193
|
def self.from_file(path, locations: false)
|
|
133
194
|
Fontist.ui.debug("Manifest: #{path}")
|
|
134
195
|
|
|
@@ -159,8 +220,10 @@ module Fontist
|
|
|
159
220
|
|
|
160
221
|
def self.from_hash(data, options = {})
|
|
161
222
|
locations = options.delete(:locations) || false
|
|
223
|
+
data, font_options = split_font_options(data) if data.is_a?(Hash)
|
|
162
224
|
|
|
163
225
|
model = super
|
|
226
|
+
model.apply_font_options(font_options) if font_options
|
|
164
227
|
|
|
165
228
|
# Use performance optimizations for faster manifest compilation
|
|
166
229
|
with_performance_optimizations do
|
data/lib/fontist/resource.rb
CHANGED
|
@@ -6,7 +6,12 @@ module Fontist
|
|
|
6
6
|
attribute :name, :string
|
|
7
7
|
attribute :source, :string
|
|
8
8
|
attribute :urls, :string, collection: true
|
|
9
|
-
|
|
9
|
+
# Declared scalar: the corpus writes one digest per resource almost
|
|
10
|
+
# everywhere, and consumers render it verbatim. A handful of
|
|
11
|
+
# resources pair one digest with each mirror URL and write a list;
|
|
12
|
+
# a scalar-typed attribute keeps both forms as written instead of
|
|
13
|
+
# normalizing every single digest into a one-element Array.
|
|
14
|
+
attribute :sha256, :string
|
|
10
15
|
attribute :file_size, :integer
|
|
11
16
|
attribute :family, :string
|
|
12
17
|
attribute :files, :string, collection: true
|
data/lib/fontist/validate_cli.rb
CHANGED
|
@@ -189,7 +189,7 @@ module Fontist
|
|
|
189
189
|
|
|
190
190
|
def output_json_report(report, output_file)
|
|
191
191
|
require "json"
|
|
192
|
-
json_content = JSON.
|
|
192
|
+
json_content = Fontist::JSON.pretty(report.to_hash)
|
|
193
193
|
|
|
194
194
|
if output_file
|
|
195
195
|
File.write(output_file, json_content)
|
data/lib/fontist/version.rb
CHANGED
data/lib/fontist/yaml_backend.rb
CHANGED
|
@@ -14,13 +14,26 @@ module Fontist
|
|
|
14
14
|
ENABLED_VALUE = "yeptris"
|
|
15
15
|
|
|
16
16
|
class << self
|
|
17
|
-
#
|
|
18
|
-
#
|
|
17
|
+
# Pins the YAML adapter explicitly: lutaml-model auto-selects
|
|
18
|
+
# yeptris for a format whenever the yeptris gem is in the bundle,
|
|
19
|
+
# which would silently switch YAML parsing and dumping (and its
|
|
20
|
+
# line-wrap behavior) for every model in the process. The yeptris
|
|
21
|
+
# YAML engine activates only when explicitly enabled with
|
|
22
|
+
# FONTIST_YAML=yeptris; otherwise the standard Psych engine is
|
|
23
|
+
# pinned, because the yeptris x86_64-linux and Windows builds
|
|
24
|
+
# currently mis-parse some plain scalars as single-element Arrays
|
|
25
|
+
# (leptris/yeptris#863). Flip the default once that parity fix
|
|
26
|
+
# lands.
|
|
27
|
+
#
|
|
28
|
+
# Returns the activated adapter class.
|
|
19
29
|
def activate!
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
adapter =
|
|
31
|
+
if enabled? && yeptris_available?
|
|
32
|
+
Yaml::YeptrisAdapter
|
|
33
|
+
else
|
|
34
|
+
Lutaml::Yaml::Adapter::StandardAdapter
|
|
35
|
+
end
|
|
22
36
|
|
|
23
|
-
adapter = Yaml::YeptrisAdapter
|
|
24
37
|
Lutaml::Model::AdapterResolver.set_adapter_class(:yaml, adapter)
|
|
25
38
|
|
|
26
39
|
adapter
|
data/lib/fontist.rb
CHANGED
|
@@ -17,6 +17,8 @@ require_relative "fontist/yaml_backend"
|
|
|
17
17
|
module Fontist
|
|
18
18
|
# Core classes
|
|
19
19
|
autoload :Config, "#{__dir__}/fontist/config"
|
|
20
|
+
autoload :Css, "#{__dir__}/fontist/css"
|
|
21
|
+
autoload :JSON, "#{__dir__}/fontist/json"
|
|
20
22
|
autoload :Font, "#{__dir__}/fontist/font"
|
|
21
23
|
autoload :FontCollection, "#{__dir__}/fontist/font_collection"
|
|
22
24
|
autoload :FontFile, "#{__dir__}/fontist/font_file"
|
|
@@ -25,6 +27,7 @@ module Fontist
|
|
|
25
27
|
autoload :FontPath, "#{__dir__}/fontist/font_path"
|
|
26
28
|
autoload :FontStyle, "#{__dir__}/fontist/font_style"
|
|
27
29
|
autoload :Fontconfig, "#{__dir__}/fontist/fontconfig"
|
|
30
|
+
autoload :FontOperations, "#{__dir__}/fontist/font_operations"
|
|
28
31
|
autoload :FormatSpec, "#{__dir__}/fontist/format_spec"
|
|
29
32
|
autoload :Formula, "#{__dir__}/fontist/formula"
|
|
30
33
|
autoload :FormulaCollection, "#{__dir__}/fontist/formula"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fontist
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.30
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -53,7 +53,7 @@ dependencies:
|
|
|
53
53
|
version: '0.4'
|
|
54
54
|
- - ">="
|
|
55
55
|
- !ruby/object:Gem::Version
|
|
56
|
-
version: 0.4.
|
|
56
|
+
version: 0.4.47
|
|
57
57
|
type: :runtime
|
|
58
58
|
prerelease: false
|
|
59
59
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -63,7 +63,7 @@ dependencies:
|
|
|
63
63
|
version: '0.4'
|
|
64
64
|
- - ">="
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
|
-
version: 0.4.
|
|
66
|
+
version: 0.4.47
|
|
67
67
|
- !ruby/object:Gem::Dependency
|
|
68
68
|
name: fuzzy_match
|
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -92,20 +92,6 @@ dependencies:
|
|
|
92
92
|
- - "~>"
|
|
93
93
|
- !ruby/object:Gem::Version
|
|
94
94
|
version: '6.0'
|
|
95
|
-
- !ruby/object:Gem::Dependency
|
|
96
|
-
name: json
|
|
97
|
-
requirement: !ruby/object:Gem::Requirement
|
|
98
|
-
requirements:
|
|
99
|
-
- - "~>"
|
|
100
|
-
- !ruby/object:Gem::Version
|
|
101
|
-
version: '2.0'
|
|
102
|
-
type: :runtime
|
|
103
|
-
prerelease: false
|
|
104
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
105
|
-
requirements:
|
|
106
|
-
- - "~>"
|
|
107
|
-
- !ruby/object:Gem::Version
|
|
108
|
-
version: '2.0'
|
|
109
95
|
- !ruby/object:Gem::Dependency
|
|
110
96
|
name: lutaml-model
|
|
111
97
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -154,14 +140,14 @@ dependencies:
|
|
|
154
140
|
requirements:
|
|
155
141
|
- - "~>"
|
|
156
142
|
- !ruby/object:Gem::Version
|
|
157
|
-
version: '
|
|
143
|
+
version: '10.0'
|
|
158
144
|
type: :runtime
|
|
159
145
|
prerelease: false
|
|
160
146
|
version_requirements: !ruby/object:Gem::Requirement
|
|
161
147
|
requirements:
|
|
162
148
|
- - "~>"
|
|
163
149
|
- !ruby/object:Gem::Version
|
|
164
|
-
version: '
|
|
150
|
+
version: '10.0'
|
|
165
151
|
- !ruby/object:Gem::Dependency
|
|
166
152
|
name: paint
|
|
167
153
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -357,6 +343,10 @@ files:
|
|
|
357
343
|
- lib/fontist/collection_file.rb
|
|
358
344
|
- lib/fontist/config.rb
|
|
359
345
|
- lib/fontist/config_cli.rb
|
|
346
|
+
- lib/fontist/css.rb
|
|
347
|
+
- lib/fontist/css/google_family.rb
|
|
348
|
+
- lib/fontist/css/local.rb
|
|
349
|
+
- lib/fontist/css/styles.rb
|
|
360
350
|
- lib/fontist/errors.rb
|
|
361
351
|
- lib/fontist/exclude.yml
|
|
362
352
|
- lib/fontist/extract.rb
|
|
@@ -366,6 +356,12 @@ files:
|
|
|
366
356
|
- lib/fontist/font_finder.rb
|
|
367
357
|
- lib/fontist/font_installer.rb
|
|
368
358
|
- lib/fontist/font_model.rb
|
|
359
|
+
- lib/fontist/font_operations.rb
|
|
360
|
+
- lib/fontist/font_operations/convert.rb
|
|
361
|
+
- lib/fontist/font_operations/extract.rb
|
|
362
|
+
- lib/fontist/font_operations/instance.rb
|
|
363
|
+
- lib/fontist/font_operations/pack.rb
|
|
364
|
+
- lib/fontist/font_operations/subset.rb
|
|
369
365
|
- lib/fontist/font_path.rb
|
|
370
366
|
- lib/fontist/font_style.rb
|
|
371
367
|
- lib/fontist/fontconfig.rb
|
|
@@ -414,6 +410,7 @@ files:
|
|
|
414
410
|
- lib/fontist/import/google/models/font_variant.rb
|
|
415
411
|
- lib/fontist/import/google/models/metadata.rb
|
|
416
412
|
- lib/fontist/import/google/models/source_metadata.rb
|
|
413
|
+
- lib/fontist/import/google/variable_font_source.rb
|
|
417
414
|
- lib/fontist/import/google_fonts_importer.rb
|
|
418
415
|
- lib/fontist/import/google_import.rb
|
|
419
416
|
- lib/fontist/import/helpers.rb
|
|
@@ -462,6 +459,7 @@ files:
|
|
|
462
459
|
- lib/fontist/install_locations/fontist_location.rb
|
|
463
460
|
- lib/fontist/install_locations/system_location.rb
|
|
464
461
|
- lib/fontist/install_locations/user_location.rb
|
|
462
|
+
- lib/fontist/json.rb
|
|
465
463
|
- lib/fontist/macos.rb
|
|
466
464
|
- lib/fontist/macos/catalog.rb
|
|
467
465
|
- lib/fontist/macos/catalog/asset.rb
|