fontist 1.21.4 → 2.0.1
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/.github/workflows/rake.yml +4 -0
- data/Gemfile +11 -0
- data/README.adoc +6 -6
- data/docs/guide/api-ruby.md +8 -9
- data/fontist.gemspec +14 -24
- data/formula_filename_index.yml +6210 -0
- data/formula_index.yml +2568 -0
- data/lib/fontist/cli.rb +14 -14
- data/lib/fontist/config.rb +75 -14
- data/lib/fontist/errors.rb +2 -0
- data/lib/fontist/extract.rb +25 -0
- data/lib/fontist/font.rb +6 -8
- data/lib/fontist/font_collection.rb +16 -0
- data/lib/fontist/font_installer.rb +4 -4
- data/lib/fontist/font_model.rb +15 -0
- data/lib/fontist/font_path.rb +1 -1
- data/lib/fontist/font_style.rb +37 -0
- data/lib/fontist/formula.rb +169 -112
- data/lib/fontist/import/formula_serializer.rb +4 -4
- data/lib/fontist/import/google_import.rb +1 -1
- data/lib/fontist/index.rb +47 -8
- data/lib/fontist/indexes/default_family_font_index.rb +28 -9
- data/lib/fontist/indexes/filename_index.rb +45 -8
- data/lib/fontist/indexes/font_index.rb +3 -3
- data/lib/fontist/indexes/formula_key_to_path.rb +35 -0
- data/lib/fontist/indexes/index_mixin.rb +109 -0
- data/lib/fontist/indexes/preferred_family_font_index.rb +28 -9
- data/lib/fontist/manifest.rb +144 -2
- data/lib/fontist/manifest_request.rb +64 -0
- data/lib/fontist/manifest_response.rb +66 -0
- data/lib/fontist/repo.rb +1 -1
- data/lib/fontist/system_font.rb +7 -25
- data/lib/fontist/system_index.rb +137 -126
- data/lib/fontist/utils/cache.rb +54 -4
- data/lib/fontist/version.rb +1 -1
- data/lib/fontist.rb +33 -13
- metadata +16 -136
- data/lib/fontist/indexes/base_index.rb +0 -92
- data/lib/fontist/indexes/index_formula.rb +0 -36
- data/lib/fontist/manifest/install.rb +0 -35
- data/lib/fontist/manifest/locations.rb +0 -84
data/lib/fontist/system_index.rb
CHANGED
|
@@ -2,157 +2,142 @@ require_relative "font_file"
|
|
|
2
2
|
require_relative "collection_file"
|
|
3
3
|
|
|
4
4
|
module Fontist
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
# {:path=>"/Library/Fonts/Arial Unicode.ttf",
|
|
6
|
+
# :full_name=>"Arial Unicode MS",
|
|
7
|
+
# :family_name=>"Arial Unicode MS",
|
|
8
|
+
# :preferred_family_name=>"Arial",
|
|
9
|
+
# :preferred_subfamily=>"Regular",
|
|
10
|
+
# :subfamily=>"Regular"},
|
|
11
|
+
class SystemIndexFont < Lutaml::Model::Serializable
|
|
12
|
+
attribute :path, :string
|
|
13
|
+
attribute :full_name, :string
|
|
14
|
+
attribute :family_name, :string
|
|
15
|
+
attribute :preferred_family_name, :string
|
|
16
|
+
attribute :preferred_subfamily, :string
|
|
17
|
+
attribute :subfamily, :string
|
|
18
|
+
alias :type :subfamily
|
|
19
|
+
|
|
20
|
+
key_value do
|
|
21
|
+
map "path", to: :path
|
|
22
|
+
map "full_name", to: :full_name
|
|
23
|
+
map "family_name", to: :family_name
|
|
24
|
+
map "type", to: :subfamily
|
|
25
|
+
map "preferred_family_name", to: :preferred_family_name
|
|
26
|
+
map "preferred_subfamily", to: :preferred_subfamily
|
|
27
|
+
end
|
|
28
|
+
end
|
|
12
29
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
30
|
+
class SystemIndexFontCollection < Lutaml::Model::Collection
|
|
31
|
+
instances :fonts, SystemIndexFont
|
|
32
|
+
attr_accessor :path, :paths_loader
|
|
16
33
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
end
|
|
34
|
+
key_value do
|
|
35
|
+
map_instances to: :fonts
|
|
20
36
|
end
|
|
21
37
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def type(name)
|
|
28
|
-
name.preferred_subfamily || name.subfamily
|
|
29
|
-
end
|
|
38
|
+
def set_path(path)
|
|
39
|
+
@path = path
|
|
40
|
+
end
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
dict.transform_keys! { |k| mapping[k] }
|
|
34
|
-
end
|
|
42
|
+
def set_path_loader(paths_loader)
|
|
43
|
+
@paths_loader = paths_loader
|
|
35
44
|
end
|
|
36
45
|
|
|
37
|
-
|
|
46
|
+
def self.from_file(path:, paths_loader:)
|
|
47
|
+
# If the file does not exist, return a new collection
|
|
48
|
+
return new.set_content(path, paths_loader) unless File.exist?(path)
|
|
38
49
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
Fontist.system_preferred_family_index_path
|
|
42
|
-
else
|
|
43
|
-
Fontist.system_index_path
|
|
44
|
-
end
|
|
50
|
+
from_yaml(File.read(path)).set_content(path, paths_loader)
|
|
51
|
+
end
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
def set_content(path, paths_loader)
|
|
54
|
+
tap do |content|
|
|
55
|
+
content.set_path(path)
|
|
56
|
+
content.set_path_loader(paths_loader)
|
|
57
|
+
end
|
|
50
58
|
end
|
|
51
59
|
|
|
52
|
-
|
|
53
|
-
path = if Fontist.preferred_family?
|
|
54
|
-
Fontist.fontist_preferred_family_index_path
|
|
55
|
-
else
|
|
56
|
-
Fontist.fontist_index_path
|
|
57
|
-
end
|
|
60
|
+
ALLOWED_KEYS = %i[path full_name family_name type].freeze
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
new(path, -> { SystemFont.fontist_font_paths }, family)
|
|
63
|
-
end
|
|
62
|
+
# Check if the content has all required keys
|
|
63
|
+
def check_index
|
|
64
|
+
Fontist.formulas_repo_path_exists!
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
Array(fonts).each do |font|
|
|
67
|
+
missing_keys = ALLOWED_KEYS.reject do |key|
|
|
68
|
+
font.send(key)
|
|
69
|
+
end
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
raise_font_index_corrupted(font, missing_keys) if missing_keys.any?
|
|
72
|
+
end
|
|
71
73
|
end
|
|
72
74
|
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
@family = family
|
|
75
|
+
def to_file(path)
|
|
76
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
77
|
+
File.write(path, to_yaml)
|
|
77
78
|
end
|
|
78
79
|
|
|
79
80
|
def find(font, style)
|
|
80
|
-
|
|
81
|
-
file
|
|
82
|
-
(style.nil? || file
|
|
81
|
+
found_fonts = index.select do |file|
|
|
82
|
+
file.family_name.casecmp?(font) &&
|
|
83
|
+
(style.nil? || file.type.casecmp?(style))
|
|
83
84
|
end
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
found_fonts.empty? ? nil : found_fonts
|
|
86
87
|
end
|
|
87
88
|
|
|
88
|
-
def rebuild
|
|
89
|
-
build_index
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
private
|
|
93
|
-
|
|
94
89
|
def index
|
|
95
|
-
return
|
|
90
|
+
return fonts unless index_changed?
|
|
96
91
|
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
build
|
|
93
|
+
check_index
|
|
99
94
|
|
|
100
|
-
|
|
101
|
-
@index.nil? ||
|
|
102
|
-
@index.map { |x| x[:path] }.uniq.sort != font_paths.sort
|
|
95
|
+
fonts
|
|
103
96
|
end
|
|
104
97
|
|
|
105
|
-
def
|
|
106
|
-
@
|
|
98
|
+
def index_changed?
|
|
99
|
+
fonts.nil? || fonts.empty? || font_paths != (@paths_loader&.call || []).sort.uniq
|
|
107
100
|
end
|
|
108
101
|
|
|
109
|
-
def
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
def update
|
|
103
|
+
tap do |col|
|
|
104
|
+
col.fonts = detect_paths(@paths_loader&.call || [])
|
|
112
105
|
end
|
|
113
106
|
end
|
|
114
107
|
|
|
115
|
-
def
|
|
116
|
-
Utils::Cache.lock_path(@index_path)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def do_build_index
|
|
108
|
+
def build(forced: false)
|
|
120
109
|
previous_index = load_index
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
110
|
+
updated_fonts = update
|
|
111
|
+
if forced || changed?(updated_fonts, previous_index.fonts || [])
|
|
112
|
+
to_file(@path)
|
|
124
113
|
end
|
|
114
|
+
|
|
115
|
+
self
|
|
125
116
|
end
|
|
126
117
|
|
|
127
|
-
def
|
|
128
|
-
|
|
118
|
+
def rebuild
|
|
119
|
+
build(forced: true)
|
|
129
120
|
end
|
|
130
121
|
|
|
122
|
+
private
|
|
123
|
+
|
|
131
124
|
def load_index
|
|
132
|
-
index =
|
|
133
|
-
check_index
|
|
125
|
+
index = self.class.from_file(path: @path, paths_loader: @paths_loader)
|
|
126
|
+
index.check_index
|
|
134
127
|
index
|
|
135
128
|
end
|
|
136
129
|
|
|
137
|
-
def
|
|
138
|
-
|
|
139
|
-
missing_keys = ALLOWED_KEYS - item.keys
|
|
140
|
-
unless missing_keys.empty?
|
|
141
|
-
raise(Errors::FontIndexCorrupted, <<~MSG.chomp)
|
|
142
|
-
Font index is corrupted.
|
|
143
|
-
Item #{item.inspect} misses required attributes: #{missing_keys.join(', ')}.
|
|
144
|
-
You can remove the index file (#{@index_path}) and try again.
|
|
145
|
-
MSG
|
|
146
|
-
end
|
|
147
|
-
end
|
|
130
|
+
def font_paths
|
|
131
|
+
fonts.map(&:path).uniq.sort
|
|
148
132
|
end
|
|
149
133
|
|
|
150
|
-
def
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
paths.flat_map do |path|
|
|
154
|
-
next by_path[path] if by_path[path]
|
|
134
|
+
def changed?(this_fonts, that_fonts)
|
|
135
|
+
this_fonts.map(&:path).uniq.sort != that_fonts.map(&:path).uniq.sort
|
|
136
|
+
end
|
|
155
137
|
|
|
138
|
+
def detect_paths(paths)
|
|
139
|
+
# paths are file paths to font files
|
|
140
|
+
paths.sort.uniq.flat_map do |path|
|
|
156
141
|
detect_fonts(path)
|
|
157
142
|
end.compact
|
|
158
143
|
end
|
|
@@ -169,6 +154,10 @@ module Fontist
|
|
|
169
154
|
excluded_fonts.include?(File.basename(path))
|
|
170
155
|
end
|
|
171
156
|
|
|
157
|
+
def excluded_fonts
|
|
158
|
+
@excluded_fonts ||= YAML.load_file(Fontist.excluded_fonts_path)
|
|
159
|
+
end
|
|
160
|
+
|
|
172
161
|
def gather_fonts(path)
|
|
173
162
|
case File.extname(path).gsub(/^\./, "").downcase
|
|
174
163
|
when "ttf", "otf"
|
|
@@ -189,42 +178,64 @@ module Fontist
|
|
|
189
178
|
end
|
|
190
179
|
|
|
191
180
|
def detect_file_font(path)
|
|
192
|
-
|
|
181
|
+
font_file = FontFile.from_path(path)
|
|
193
182
|
|
|
194
|
-
parse_font(
|
|
183
|
+
parse_font(font_file, path)
|
|
195
184
|
end
|
|
196
185
|
|
|
197
186
|
def detect_collection_fonts(path)
|
|
198
187
|
CollectionFile.from_path(path) do |collection|
|
|
199
|
-
collection.map do |
|
|
200
|
-
parse_font(
|
|
188
|
+
collection.map do |font_file|
|
|
189
|
+
parse_font(font_file, path)
|
|
201
190
|
end
|
|
202
191
|
end
|
|
203
192
|
end
|
|
204
193
|
|
|
205
|
-
def parse_font(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
{
|
|
194
|
+
def parse_font(font_file, path)
|
|
195
|
+
SystemIndexFont.new(
|
|
209
196
|
path: path,
|
|
210
|
-
full_name:
|
|
211
|
-
family_name:
|
|
212
|
-
|
|
213
|
-
|
|
197
|
+
full_name: font_file.full_name,
|
|
198
|
+
family_name: font_file.family,
|
|
199
|
+
subfamily: font_file.subfamily,
|
|
200
|
+
preferred_family_name: font_file.preferred_family,
|
|
201
|
+
preferred_subfamily_name: font_file.preferred_subfamily,
|
|
202
|
+
)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def raise_font_index_corrupted(font, missing_keys)
|
|
206
|
+
raise(Errors::FontIndexCorrupted, <<~MSG.chomp)
|
|
207
|
+
Font index is corrupted.
|
|
208
|
+
Item #{font.inspect} misses required attributes: #{missing_keys.join(', ')}.
|
|
209
|
+
You can remove the index file (#{@path}) and try again.
|
|
210
|
+
MSG
|
|
214
211
|
end
|
|
212
|
+
end
|
|
215
213
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
&.style_override(font_name)&.to_h || {}
|
|
214
|
+
class SystemIndex
|
|
215
|
+
include Utils::Locking
|
|
219
216
|
|
|
220
|
-
|
|
221
|
-
|
|
217
|
+
def self.system_index
|
|
218
|
+
@system_index = SystemIndexFontCollection.from_file(
|
|
219
|
+
path: Fontist.system_index_path,
|
|
220
|
+
paths_loader: -> { SystemFont.font_paths },
|
|
221
|
+
)
|
|
222
222
|
end
|
|
223
223
|
|
|
224
|
-
def
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
224
|
+
def self.fontist_index
|
|
225
|
+
@fontist_index = SystemIndexFontCollection.from_file(
|
|
226
|
+
path: Fontist.fontist_index_path,
|
|
227
|
+
paths_loader: -> { SystemFont.fontist_font_paths },
|
|
228
|
+
)
|
|
228
229
|
end
|
|
230
|
+
|
|
231
|
+
# def build_index
|
|
232
|
+
# lock(lock_path) do
|
|
233
|
+
# do_build_index
|
|
234
|
+
# end
|
|
235
|
+
# end
|
|
236
|
+
|
|
237
|
+
# def lock_path
|
|
238
|
+
# Utils::Cache.lock_path(@index_path)
|
|
239
|
+
# end
|
|
229
240
|
end
|
|
230
241
|
end
|
data/lib/fontist/utils/cache.rb
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
|
+
require "lutaml/model"
|
|
2
|
+
|
|
1
3
|
module Fontist
|
|
2
4
|
module Utils
|
|
5
|
+
class CacheIndexItem < Lutaml::Model::Serializable
|
|
6
|
+
attribute :url, :string
|
|
7
|
+
attribute :name, :string
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class CacheIndex < Lutaml::Model::Serializable
|
|
11
|
+
attribute :items, CacheIndexItem, collection: true, default: []
|
|
12
|
+
|
|
13
|
+
key_value do
|
|
14
|
+
map to: :items, root_mappings: {
|
|
15
|
+
url: :key,
|
|
16
|
+
name: :value,
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.from_file(path)
|
|
21
|
+
return new unless File.exist?(path)
|
|
22
|
+
|
|
23
|
+
content = File.read(path)
|
|
24
|
+
|
|
25
|
+
return new if content.strip.empty? || content.strip == "---"
|
|
26
|
+
|
|
27
|
+
from_yaml(content) || {}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_file(path)
|
|
31
|
+
File.write(path, to_yaml)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def [](key)
|
|
35
|
+
Array(items).find { |i| i.url == key }&.name
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def []=(key, value)
|
|
39
|
+
item = Array(items).find { |i| i.url == key }
|
|
40
|
+
if item
|
|
41
|
+
item.name = value
|
|
42
|
+
else
|
|
43
|
+
items << CacheIndexItem.new(url: key, name: value)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def delete(key)
|
|
48
|
+
item = Array(items).find { |i| i.url == key }
|
|
49
|
+
items.delete(item) if item
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
3
53
|
class Cache
|
|
4
54
|
MAX_FILENAME_SIZE = 255
|
|
5
55
|
|
|
@@ -34,7 +84,7 @@ module Fontist
|
|
|
34
84
|
return unless map[key]
|
|
35
85
|
|
|
36
86
|
value = map.delete(key)
|
|
37
|
-
|
|
87
|
+
map.to_file(cache_map_path)
|
|
38
88
|
value
|
|
39
89
|
end
|
|
40
90
|
end
|
|
@@ -43,7 +93,7 @@ module Fontist
|
|
|
43
93
|
lock(lock_path) do
|
|
44
94
|
map = load_cache
|
|
45
95
|
map[key] = value
|
|
46
|
-
|
|
96
|
+
map.to_file(cache_map_path)
|
|
47
97
|
end
|
|
48
98
|
end
|
|
49
99
|
|
|
@@ -54,7 +104,7 @@ module Fontist
|
|
|
54
104
|
end
|
|
55
105
|
|
|
56
106
|
def load_cache
|
|
57
|
-
|
|
107
|
+
CacheIndex.from_file(cache_map_path)
|
|
58
108
|
end
|
|
59
109
|
|
|
60
110
|
def downloaded_file(path)
|
|
@@ -83,7 +133,7 @@ module Fontist
|
|
|
83
133
|
lock(lock_path) do
|
|
84
134
|
map = load_cache
|
|
85
135
|
map[key] = path
|
|
86
|
-
|
|
136
|
+
map.to_file(cache_map_path)
|
|
87
137
|
end
|
|
88
138
|
|
|
89
139
|
path
|
data/lib/fontist/version.rb
CHANGED
data/lib/fontist.rb
CHANGED
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
require "down"
|
|
2
2
|
require "digest"
|
|
3
|
-
require "json"
|
|
4
|
-
require "yaml"
|
|
5
3
|
require "singleton"
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
require "fontist/repo"
|
|
11
|
-
require "fontist/font"
|
|
12
|
-
require "fontist/formula"
|
|
13
|
-
require "fontist/system_font"
|
|
14
|
-
require "fontist/manifest"
|
|
15
|
-
require "fontist/helpers"
|
|
16
|
-
require "fontist/config"
|
|
5
|
+
require_relative "fontist/errors"
|
|
6
|
+
require_relative "fontist/version"
|
|
17
7
|
|
|
18
8
|
module Fontist
|
|
19
9
|
def self.ui
|
|
@@ -141,7 +131,7 @@ module Fontist
|
|
|
141
131
|
end
|
|
142
132
|
|
|
143
133
|
def self.use_cache?
|
|
144
|
-
instance_variable_defined?(
|
|
134
|
+
instance_variable_defined?(:@use_cache) ? @use_cache : true
|
|
145
135
|
end
|
|
146
136
|
|
|
147
137
|
def self.use_cache=(bool)
|
|
@@ -163,4 +153,34 @@ module Fontist
|
|
|
163
153
|
def self.google_fonts_key
|
|
164
154
|
ENV["GOOGLE_FONTS_API_KEY"] || config[:google_fonts_key]
|
|
165
155
|
end
|
|
156
|
+
|
|
157
|
+
def self.formulas_repo_path_exists!
|
|
158
|
+
return true if Dir.exist?(Fontist.formulas_repo_path)
|
|
159
|
+
|
|
160
|
+
raise Errors::MainRepoNotFoundError.new(
|
|
161
|
+
"Please fetch formulas with `fontist update`.",
|
|
162
|
+
)
|
|
163
|
+
end
|
|
166
164
|
end
|
|
165
|
+
|
|
166
|
+
require_relative "fontist/repo"
|
|
167
|
+
require_relative "fontist/font"
|
|
168
|
+
require_relative "fontist/formula"
|
|
169
|
+
require_relative "fontist/system_font"
|
|
170
|
+
require_relative "fontist/manifest"
|
|
171
|
+
require_relative "fontist/manifest_response"
|
|
172
|
+
require_relative "fontist/manifest_request"
|
|
173
|
+
require_relative "fontist/helpers"
|
|
174
|
+
require_relative "fontist/config"
|
|
175
|
+
require_relative "fontist/update"
|
|
176
|
+
require_relative "fontist/index"
|
|
177
|
+
require_relative "fontist/indexes/font_index"
|
|
178
|
+
require_relative "fontist/indexes/filename_index"
|
|
179
|
+
require_relative "fontist/cli"
|
|
180
|
+
require_relative "fontist/font_installer"
|
|
181
|
+
require_relative "fontist/fontconfig"
|
|
182
|
+
require_relative "fontist/formula_picker"
|
|
183
|
+
require_relative "fontist/formula_suggestion"
|
|
184
|
+
require_relative "fontist/extract"
|
|
185
|
+
require_relative "fontist/font_style"
|
|
186
|
+
require_relative "fontist/font_collection"
|