fontist 1.19.0 → 1.20.0

Sign up to get free protection for your applications and to get access to all the features.
data/docs/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "scripts": {
3
+ "dev": "vitepress dev",
4
+ "build": "vitepress build",
5
+ "preview": "vitepress preview",
6
+ "format": "prettier -w ."
7
+ },
8
+ "type": "module",
9
+ "devDependencies": {
10
+ "@types/node": "latest",
11
+ "prettier": "^3.2.4",
12
+ "typescript": "latest",
13
+ "vitepress": "^1.0.0-rc.44",
14
+ "vue": "^3.4.15",
15
+ "vue-tsc": "^1.8.27"
16
+ }
17
+ }
Binary file
Binary file
@@ -0,0 +1,143 @@
1
+ # Fontist CLI reference
2
+
3
+ <!-- This can be converted to a folder & multiple pages at any time. -->
4
+
5
+ ## `fontist cache`
6
+
7
+ The only subcommand available on `fontist cache` is `fontist cache clear`. It clears the `~/.fontist` cache.
8
+
9
+ ```sh
10
+ fontist cache clear
11
+ ```
12
+
13
+ ```
14
+ Cache has been successfully removed.
15
+ ```
16
+
17
+ ## `fontist config`
18
+
19
+ `fontist config` lets you edit the Fontist config file from the command line instead of opening an editor. There are four subcommands available:
20
+
21
+ - `fontist config delete <key>`
22
+ - `fontist config keys`
23
+ - `fontist config set <key> <value>`
24
+ - `fontist config show`
25
+
26
+ Here's an example of these commands being used to edit the config file:
27
+
28
+ ```sh
29
+ fontist config keys
30
+ fontist config set font_path /var/myfonts
31
+ fontist config delete font_path
32
+ fontist config show
33
+ ```
34
+
35
+ ```
36
+ $ fontist config keys
37
+ Available keys:
38
+ fonts_path (default: /home/octocat/.fontist/fonts)
39
+ open_timeout (default: 10)
40
+ read_timeout (default: 10)
41
+
42
+ $ fontist config set fonts_path /var/myfonts
43
+ 'fonts_path' set to '/var/myfonts'.
44
+
45
+ $ fontist config delete fonts_path
46
+ 'fonts_path' reset to default ('/home/octocat/.fontist/fonts').
47
+
48
+ $ fontist config show
49
+ Config is empty.
50
+ ```
51
+
52
+ ### Config reference
53
+
54
+ <!-- Move this into its own '/reference/config' page if this grows a lot. -->
55
+
56
+ - **`fonts_path`:** Where to put the `.ttf` files. Defaults to `~/.fontist/fonts`
57
+
58
+ - **`open_timeout`:** Defaults to 10.
59
+
60
+ - **`read_timeout`:** Defaults to 10.
61
+
62
+ ## `fontist status [font-name]`
63
+
64
+ Prints the paths to a particular installed font or all fonts if the `font-name` is omitted. This searches **all fonts available on your system** even those not managed by Fontist.
65
+
66
+ ```sh
67
+ fontist status
68
+ ```
69
+
70
+ ```
71
+ Fonts found at:
72
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-B.ttf
73
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-BI.ttf
74
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-R.ttf
75
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-RI.ttf
76
+ - /home/octocat/.fontist/fonts/Arial.ttf (from ms_truetype formula)
77
+ - /home/octocat/.fontist/fonts/ArialBI.ttf (from ms_truetype formula)
78
+ - /home/octocat/.fontist/fonts/ArialBd.ttf (from ms_truetype formula)
79
+ - /home/octocat/.fontist/fonts/ArialI.ttf (from ms_truetype formula)
80
+ ```
81
+
82
+ Here's an example narrowed to a specific font:
83
+
84
+ ```sh
85
+ fontist status "Open Sans"
86
+ ```
87
+
88
+ ```
89
+ Fonts found at:
90
+ - /home/octocat/.fontist/fonts/OpenSans-Bold.ttf (from open_sans formula)
91
+ - /home/octocat/.fontist/fonts/OpenSans-BoldItalic.ttf (from open_sans formula)
92
+ - /home/octocat/.fontist/fonts/OpenSans-Italic.ttf (from open_sans formula)
93
+ - /home/octocat/.fontist/fonts/OpenSans-Regular.ttf (from open_sans formula)
94
+ ```
95
+
96
+ ## `fontist list [font-name]`
97
+
98
+ Lists the installation status of `font-name` or all fonts if no font name provided.
99
+
100
+ ```sh
101
+ fontist status
102
+ ```
103
+
104
+ ```
105
+ Fonts found at:
106
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-B.ttf
107
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-BI.ttf
108
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-R.ttf
109
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-RI.ttf
110
+ - /home/octocat/.fontist/fonts/Arial.ttf (from ms_truetype formula)
111
+ - /home/octocat/.fontist/fonts/ArialBI.ttf (from ms_truetype formula)
112
+ - /home/octocat/.fontist/fonts/ArialBd.ttf (from ms_truetype formula)
113
+ - /home/octocat/.fontist/fonts/ArialI.ttf (from ms_truetype formula)
114
+ ```
115
+
116
+ Here's an example getting the status of a specific font:
117
+
118
+ ```sh
119
+ fontist status "Fira Mono"
120
+ ```
121
+
122
+ ```
123
+ Font "Fira Mono" not found locally.
124
+ 'Fira Mono' font is missing, please run `fontist install 'Fira Mono'` to download the font.
125
+ ```
126
+
127
+ ## Environment variables
128
+
129
+ ### `FONTIST_PATH`
130
+
131
+ By default Fontist uses the `~/.fontist` directory to store fonts and its files. It can be changed with the `FONTIST_PATH` environment variable.
132
+
133
+ ```sh
134
+ FONTIST_PATH=/var/fontist2 fontist update
135
+ ```
136
+
137
+ ## Excluded fonts
138
+
139
+ `fontist` excludes some fonts from usage when they break other software:
140
+
141
+ - `NISC18030.ttf` (GB18030 Bitmap) - macOS [fontist/fontist#344](https://github.com/fontist/fontist/issues/344)
142
+
143
+ [📑 View the up-to-date list of known problematic fonts on GitHub](https://github.com/fontist/fontist/blob/main/lib/fontist/exclude.yml)
data/fontist.gemspec CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.add_runtime_dependency "down", "~> 5.0"
33
33
  spec.add_runtime_dependency "extract_ttc", "~> 0.1"
34
+ spec.add_runtime_dependency "fuzzy_match", "~> 2.1"
34
35
  spec.add_runtime_dependency "json", "~> 2.0"
35
36
  spec.add_runtime_dependency "nokogiri", "~> 1.0"
36
37
  spec.add_runtime_dependency "mime-types", "~> 3.0"
@@ -50,4 +51,6 @@ Gem::Specification.new do |spec|
50
51
  spec.add_development_dependency "rubocop", "~> 1.22.1"
51
52
  spec.add_development_dependency "rubocop-rails", "~> 2.9"
52
53
  spec.add_development_dependency "rubocop-performance", "~> 1.10"
54
+ spec.add_development_dependency "vcr"
55
+ spec.add_development_dependency "webmock"
53
56
  end
@@ -22,6 +22,12 @@ module Fontist
22
22
  type: :boolean,
23
23
  desc: "Avoid using cache during download"
24
24
 
25
+ base.class_option :interactive,
26
+ aliases: :i,
27
+ type: :boolean,
28
+ default: true,
29
+ desc: "Interactive mode"
30
+
25
31
  base.class_option :formulas_path,
26
32
  type: :string,
27
33
  desc: "Path to formulas"
@@ -32,6 +38,7 @@ module Fontist
32
38
  Fontist.preferred_family = options[:preferred_family]
33
39
  Fontist.log_level = log_level(options)
34
40
  Fontist.use_cache = !options[:no_cache]
41
+ Fontist.interactive = options[:interactive]
35
42
 
36
43
  if options[:formulas_path]
37
44
  Fontist.formulas_path = Pathname.new(options[:formulas_path])
data/lib/fontist/font.rb CHANGED
@@ -2,6 +2,7 @@ require "fontist/font_installer"
2
2
  require "fontist/font_path"
3
3
  require "fontist/formula_picker"
4
4
  require "fontist/fontconfig"
5
+ require "fontist/formula_suggestion"
5
6
 
6
7
  module Fontist
7
8
  class Font
@@ -109,17 +110,48 @@ module Fontist
109
110
  end
110
111
 
111
112
  def install_formula
112
- download_formula || raise_formula_not_found
113
+ download_formula || make_suggestions || raise_formula_not_found
113
114
  end
114
115
 
115
116
  def download_formula
116
- formula = Formula.find_by_key(@name)
117
+ formula = Formula.find_by_key_or_name(@name)
117
118
  return unless formula
118
119
  return unless formula.downloadable?
119
120
 
120
121
  request_formula_installation(formula)
121
122
  end
122
123
 
124
+ def make_suggestions
125
+ return unless Fontist.interactive?
126
+
127
+ suggestions = fuzzy_search_formulas
128
+ return if suggestions.empty?
129
+
130
+ choice = offer_to_choose(suggestions)
131
+ return unless choice
132
+
133
+ request_formula_installation(choice)
134
+ end
135
+
136
+ def fuzzy_search_formulas
137
+ @formula_suggestion ||= FormulaSuggestion.new
138
+ @formula_suggestion.find(@name)
139
+ end
140
+
141
+ def offer_to_choose(formulas)
142
+ Fontist.ui.say("Formula '#{@name}' not found. Did you mean?")
143
+
144
+ formulas.each_with_index do |formula, index|
145
+ Fontist.ui.say("[#{index}] #{formula.name}")
146
+ end
147
+
148
+ choice = Fontist.ui.ask("Please type number or " \
149
+ "press ENTER to skip installation:").chomp
150
+ return unless choice.to_i.to_s == choice
151
+
152
+ formulas[choice.to_i]
153
+ end
154
+
123
155
  def raise_formula_not_found
124
156
  raise Errors::FormulaNotFoundError.new(@name)
125
157
  end
@@ -193,7 +225,7 @@ module Fontist
193
225
  def check_and_confirm_required_license(formula)
194
226
  return @confirmation unless formula.license_required
195
227
 
196
- show_license(formula.license) unless @hide_licenses
228
+ show_license(formula) unless @hide_licenses
197
229
  return @confirmation if @confirmation.casecmp?("yes")
198
230
 
199
231
  confirmation = ask_for_agreement
@@ -204,8 +236,8 @@ module Fontist
204
236
  )
205
237
  end
206
238
 
207
- def show_license(license)
208
- Fontist.ui.say(license_agrement_message(license))
239
+ def show_license(formula)
240
+ Fontist.ui.say(license_agrement_message(formula))
209
241
  end
210
242
 
211
243
  def ask_for_agreement
@@ -215,20 +247,28 @@ module Fontist
215
247
  )
216
248
  end
217
249
 
218
- def license_agrement_message(license)
250
+ def license_agrement_message(formula)
251
+ human_name = human_name(formula)
252
+
219
253
  <<~MSG
220
- FONT LICENSE ACCEPTANCE REQUIRED FOR "#{name}":
254
+ FONT LICENSE ACCEPTANCE REQUIRED FOR "#{human_name}":
221
255
 
222
256
  Fontist can install this font if you accept its licensing conditions.
223
257
 
224
- FONT LICENSE BEGIN ("#{name}")
258
+ FONT LICENSE BEGIN ("#{human_name}")
225
259
  -----------------------------------------------------------------------
226
- #{license}
260
+ #{formula.license}
227
261
  -----------------------------------------------------------------------
228
- FONT LICENSE END ("#{name}")
262
+ FONT LICENSE END ("#{human_name}")
229
263
  MSG
230
264
  end
231
265
 
266
+ def human_name(formula)
267
+ return formula.name if @by_formula
268
+
269
+ formula.font_by_name(@name).name
270
+ end
271
+
232
272
  def update_fontconfig
233
273
  return unless @update_fontconfig
234
274
 
@@ -5,6 +5,11 @@ require "git"
5
5
 
6
6
  module Fontist
7
7
  class Formula
8
+ NAMESPACES = {
9
+ "sil" => "SIL",
10
+ "macos" => "macOS",
11
+ }.freeze
12
+
8
13
  def self.update_formulas_repo
9
14
  Update.call
10
15
  end
@@ -15,6 +20,12 @@ module Fontist
15
20
  end
16
21
  end
17
22
 
23
+ def self.all_keys
24
+ Dir[Fontist.formulas_path.join("**/*.yml").to_s].map do |path|
25
+ path.sub("#{Fontist.formulas_path}/", "").sub(".yml", "")
26
+ end
27
+ end
28
+
18
29
  def self.find(font_name)
19
30
  Indexes::FontIndex.from_yaml.load_formulas(font_name).first
20
31
  end
@@ -45,6 +56,10 @@ module Fontist
45
56
  end.flatten
46
57
  end
47
58
 
59
+ def self.find_by_key_or_name(name)
60
+ find_by_key(name) || find_by_name(name)
61
+ end
62
+
48
63
  def self.find_by_key(key)
49
64
  path = Fontist.formulas_path.join("#{key}.yml")
50
65
  return unless File.exist?(path)
@@ -52,6 +67,16 @@ module Fontist
52
67
  new_from_file(path)
53
68
  end
54
69
 
70
+ def self.find_by_name(name)
71
+ key = name_to_key(name)
72
+
73
+ find_by_key(key)
74
+ end
75
+
76
+ def self.name_to_key(name)
77
+ name.downcase.gsub(" ", "_")
78
+ end
79
+
55
80
  def self.find_by_font_file(font_file)
56
81
  key = Indexes::FilenameIndex
57
82
  .from_yaml
@@ -69,7 +94,7 @@ module Fontist
69
94
 
70
95
  def initialize(data, path)
71
96
  @data = data
72
- @path = path
97
+ @path = real_path(path)
73
98
  end
74
99
 
75
100
  def to_index_formula
@@ -89,7 +114,13 @@ module Fontist
89
114
  end
90
115
 
91
116
  def key
92
- key_from_path
117
+ @key ||= {}
118
+ @key[@path] ||= key_from_path
119
+ end
120
+
121
+ def name
122
+ @name ||= {}
123
+ @name[key] ||= namespace.empty? ? base_name : "#{namespace}/#{base_name}"
93
124
  end
94
125
 
95
126
  def description
@@ -142,6 +173,12 @@ module Fontist
142
173
  @data["instructions"]
143
174
  end
144
175
 
176
+ def font_by_name(name)
177
+ fonts.find do |font|
178
+ font.name.casecmp?(name)
179
+ end
180
+ end
181
+
145
182
  def fonts_by_name(name)
146
183
  fonts.select do |font|
147
184
  font.name.casecmp?(name)
@@ -166,9 +203,40 @@ module Fontist
166
203
 
167
204
  private
168
205
 
206
+ def real_path(path)
207
+ Dir.glob(path).first
208
+ end
209
+
169
210
  def key_from_path
170
211
  escaped = Regexp.escape("#{Fontist.formulas_path}/")
171
- @path.sub(Regexp.new("^#{escaped}"), "").sub(/\.yml$/, "")
212
+ @path.sub(Regexp.new("^#{escaped}"), "").sub(/\.yml$/, "").to_s
213
+ end
214
+
215
+ def namespace
216
+ namespace_from_mappings || namespace_from_key
217
+ end
218
+
219
+ def namespace_from_mappings
220
+ parts = key.split("/")
221
+ namespace_from_key = parts.take(parts.size - 1).join("/")
222
+ NAMESPACES[namespace_from_key]
223
+ end
224
+
225
+ def namespace_from_key
226
+ parts = key.downcase.gsub("_", " ").split("/")
227
+ parts.take(parts.size - 1).map do |namespace|
228
+ namespace.split.map(&:capitalize).join(" ")
229
+ end.join("/")
230
+ end
231
+
232
+ def base_name
233
+ @data["name"] || base_name_from_key
234
+ end
235
+
236
+ def base_name_from_key
237
+ key.split("/").last
238
+ .downcase.gsub("_", " ")
239
+ .split.map(&:capitalize).join(" ")
172
240
  end
173
241
 
174
242
  def fonts_by_family
@@ -0,0 +1,55 @@
1
+ require "fuzzy_match"
2
+
3
+ module Fontist
4
+ class FormulaSuggestion
5
+ MINIMUM_REQUIRED_SCORE = 0.6
6
+
7
+ def initialize
8
+ @fuzzy_match = prepare_search_engine
9
+ end
10
+
11
+ def find(name)
12
+ @fuzzy_match.find_all_with_score(normalize(name))
13
+ .tap { |res| Fontist.ui.debug(prettify_result(res)) }
14
+ .select { |_key, score, _| score >= MINIMUM_REQUIRED_SCORE }
15
+ .take(10)
16
+ .map(&:first)
17
+ .map { |x| Formula.find_by_key_or_name(x) }
18
+ .select(&:downloadable?)
19
+ end
20
+
21
+ private
22
+
23
+ def normalize(name)
24
+ name.gsub(" ", "_")
25
+ end
26
+
27
+ def prepare_search_engine
28
+ dict = Formula.all_keys
29
+ stop_words = namespaces(dict).map { |ns| /^#{Regexp.escape(ns)}/i }
30
+
31
+ FuzzyMatch.new(dict, stop_words: stop_words)
32
+ end
33
+
34
+ def namespaces(keys)
35
+ keys.map do |key|
36
+ parts = key.split("/")
37
+ parts.size
38
+ parts.take(parts.size - 1).join("/")
39
+ end.uniq
40
+ end
41
+
42
+ def prettify_result(result)
43
+ list = result.map do |key, dice, leve|
44
+ sprintf(
45
+ "%<dice>.3f %<leve>.3f %<key>s",
46
+ dice: dice,
47
+ leve: leve,
48
+ key: key,
49
+ )
50
+ end
51
+
52
+ "FuzzyMatch:\n#{list.join("\n")}"
53
+ end
54
+ end
55
+ end
@@ -1,3 +1,3 @@
1
1
  module Fontist
2
- VERSION = "1.19.0".freeze
2
+ VERSION = "1.20.0".freeze
3
3
  end
data/lib/fontist.rb CHANGED
@@ -151,4 +151,12 @@ module Fontist
151
151
  def self.log_level=(level)
152
152
  Fontist.ui.level = level
153
153
  end
154
+
155
+ def self.interactive?
156
+ @interactive || false
157
+ end
158
+
159
+ def self.interactive=(bool)
160
+ @interactive = bool
161
+ end
154
162
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fontist
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.19.0
4
+ version: 1.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-22 00:00:00.000000000 Z
11
+ date: 2024-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: down
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: fuzzy_match
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.1'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: json
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -296,6 +310,34 @@ dependencies:
296
310
  - - "~>"
297
311
  - !ruby/object:Gem::Version
298
312
  version: '1.10'
313
+ - !ruby/object:Gem::Dependency
314
+ name: vcr
315
+ requirement: !ruby/object:Gem::Requirement
316
+ requirements:
317
+ - - ">="
318
+ - !ruby/object:Gem::Version
319
+ version: '0'
320
+ type: :development
321
+ prerelease: false
322
+ version_requirements: !ruby/object:Gem::Requirement
323
+ requirements:
324
+ - - ">="
325
+ - !ruby/object:Gem::Version
326
+ version: '0'
327
+ - !ruby/object:Gem::Dependency
328
+ name: webmock
329
+ requirement: !ruby/object:Gem::Requirement
330
+ requirements:
331
+ - - ">="
332
+ - !ruby/object:Gem::Version
333
+ version: '0'
334
+ type: :development
335
+ prerelease: false
336
+ version_requirements: !ruby/object:Gem::Requirement
337
+ requirements:
338
+ - - ">="
339
+ - !ruby/object:Gem::Version
340
+ version: '0'
299
341
  description: Install openly-licensed fonts on Windows, Linux and Mac!
300
342
  email:
301
343
  - open.source@ribose.com
@@ -304,6 +346,8 @@ executables:
304
346
  extensions: []
305
347
  extra_rdoc_files: []
306
348
  files:
349
+ - ".github/workflows/deploy-pages.yml"
350
+ - ".github/workflows/tebako-pack.yml"
307
351
  - ".github/workflows/test-and-release.yml"
308
352
  - ".gitignore"
309
353
  - ".hound.yml"
@@ -313,6 +357,20 @@ files:
313
357
  - LICENSE.txt
314
358
  - README.adoc
315
359
  - Rakefile
360
+ - docs/.gitignore
361
+ - docs/.vitepress/config.ts
362
+ - docs/guide/api-ruby.md
363
+ - docs/guide/ci.md
364
+ - docs/guide/fontconfig.md
365
+ - docs/guide/index.md
366
+ - docs/guide/proxy.md
367
+ - docs/guide/why.md
368
+ - docs/index.md
369
+ - docs/package-lock.json
370
+ - docs/package.json
371
+ - docs/public/hero.png
372
+ - docs/public/logo.png
373
+ - docs/reference/index.md
316
374
  - exe/fontist
317
375
  - fontist.gemspec
318
376
  - lib/fontist.rb
@@ -332,6 +390,7 @@ files:
332
390
  - lib/fontist/fontconfig_cli.rb
333
391
  - lib/fontist/formula.rb
334
392
  - lib/fontist/formula_picker.rb
393
+ - lib/fontist/formula_suggestion.rb
335
394
  - lib/fontist/helpers.rb
336
395
  - lib/fontist/import.rb
337
396
  - lib/fontist/import/convert_formulas.rb