fontist 1.19.0 → 1.21.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-pages.yml +48 -0
  3. data/.github/workflows/tebako-pack.yml +61 -0
  4. data/.github/workflows/test-and-release.yml +2 -1
  5. data/LICENSE.txt +1 -2
  6. data/README.adoc +24 -2
  7. data/docs/.gitignore +136 -0
  8. data/docs/.vitepress/config.ts +83 -0
  9. data/docs/guide/api-ruby.md +190 -0
  10. data/docs/guide/ci.md +29 -0
  11. data/docs/guide/fontconfig.md +23 -0
  12. data/docs/guide/index.md +67 -0
  13. data/docs/guide/proxy.md +47 -0
  14. data/docs/guide/why.md +7 -0
  15. data/docs/index.md +40 -0
  16. data/docs/package-lock.json +1791 -0
  17. data/docs/package.json +17 -0
  18. data/docs/public/hero.png +0 -0
  19. data/docs/public/logo.png +0 -0
  20. data/docs/reference/index.md +143 -0
  21. data/exe/fontist +1 -2
  22. data/fontist.gemspec +3 -0
  23. data/lib/fontist/cli/class_options.rb +7 -0
  24. data/lib/fontist/cli/thor_ext.rb +79 -0
  25. data/lib/fontist/cli.rb +2 -0
  26. data/lib/fontist/config.rb +2 -1
  27. data/lib/fontist/font.rb +55 -10
  28. data/lib/fontist/font_installer.rb +22 -51
  29. data/lib/fontist/formula.rb +77 -3
  30. data/lib/fontist/formula_suggestion.rb +55 -0
  31. data/lib/fontist/helpers.rb +2 -0
  32. data/lib/fontist/import/create_formula.rb +77 -35
  33. data/lib/fontist/import/formula_builder.rb +63 -81
  34. data/lib/fontist/import/google/api.rb +25 -0
  35. data/lib/fontist/import/google/create_google_formula.rb +89 -0
  36. data/lib/fontist/import/google_import.rb +63 -32
  37. data/lib/fontist/import/recursive_extraction.rb +0 -16
  38. data/lib/fontist/manifest/locations.rb +2 -0
  39. data/lib/fontist/resources/archive_resource.rb +55 -0
  40. data/lib/fontist/resources/google_resource.rb +64 -0
  41. data/lib/fontist/style_version.rb +4 -0
  42. data/lib/fontist/utils/cache.rb +16 -0
  43. data/lib/fontist/utils/downloader.rb +9 -2
  44. data/lib/fontist/utils/ui.rb +10 -2
  45. data/lib/fontist/version.rb +1 -1
  46. data/lib/fontist.rb +13 -1
  47. metadata +67 -6
  48. data/lib/fontist/import/google/new_fonts_fetcher.rb +0 -146
  49. data/lib/fontist/import/google/skiplist.yml +0 -12
  50. data/lib/fontist/import/google_check.rb +0 -27
@@ -0,0 +1,55 @@
1
+ module Fontist
2
+ module Resources
3
+ class ArchiveResource
4
+ def initialize(resource, options = {})
5
+ @resource = resource
6
+ @options = options
7
+ end
8
+
9
+ def files(_source_names, &block)
10
+ excavate.files(recursive_packages: true, &block)
11
+ end
12
+
13
+ private
14
+
15
+ def excavate
16
+ Excavate::Archive.new(archive.path)
17
+ end
18
+
19
+ def archive
20
+ download_file(@resource)
21
+ end
22
+
23
+ def download_file(source)
24
+ errors = []
25
+ source.urls.each do |request|
26
+ result = try_download_file(request, source)
27
+ return result unless result.is_a?(Errors::InvalidResourceError)
28
+
29
+ errors << result
30
+ end
31
+
32
+ raise Errors::InvalidResourceError, errors.join(" ")
33
+ end
34
+
35
+ def try_download_file(request, source)
36
+ info_log(request)
37
+
38
+ Fontist::Utils::Downloader.download(
39
+ request,
40
+ sha: source.sha256,
41
+ file_size: source.file_size,
42
+ progress_bar: !@options[:no_progress],
43
+ )
44
+ rescue Errors::InvalidResourceError => e
45
+ Fontist.ui.say(e.message)
46
+ e
47
+ end
48
+
49
+ def info_log(request)
50
+ url = request.respond_to?(:url) ? request.url : request
51
+ Fontist.ui.say(%(Downloading from #{url}))
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,64 @@
1
+ module Fontist
2
+ module Resources
3
+ class GoogleResource
4
+ def initialize(resource, options = {})
5
+ @resource = resource
6
+ @options = options
7
+ end
8
+
9
+ def files(source_names)
10
+ cached_paths = download_fonts(source_names)
11
+
12
+ cached_paths.map do |path|
13
+ Dir.mktmpdir do |dir|
14
+ FileUtils.cp(path, dir)
15
+
16
+ yield File.join(dir, File.basename(path))
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def download_fonts(source_names)
24
+ urls = font_urls(source_names)
25
+
26
+ urls.map do |url|
27
+ download(url)
28
+ end
29
+ end
30
+
31
+ def font_urls(source_names)
32
+ @resource.files.select do |url|
33
+ source_names.include?(path_to_source_file(url))
34
+ end
35
+ end
36
+
37
+ def path_to_source_file(path)
38
+ format_filename(File.basename(path))
39
+ end
40
+
41
+ # TODO: remove duplication, another in Cache
42
+ def format_filename(filename)
43
+ return filename unless filename.length > 255
44
+
45
+ ext = File.extname(filename)
46
+ target_size = 255 - ext.length
47
+ cut_filename = filename.slice(0, target_size)
48
+ "#{cut_filename}#{ext}"
49
+ end
50
+
51
+ def download(url)
52
+ Fontist.ui.say(%(Downloading from #{url}))
53
+
54
+ file = Utils::Downloader.download(
55
+ url,
56
+ use_content_length: false,
57
+ progress_bar: !@options[:no_progress],
58
+ )
59
+
60
+ file.path
61
+ end
62
+ end
63
+ end
64
+ end
@@ -4,6 +4,10 @@ module Fontist
4
4
  @text = text
5
5
  end
6
6
 
7
+ def to_s
8
+ value.join(" . ")
9
+ end
10
+
7
11
  def value
8
12
  @value ||= numbers || default_value
9
13
  end
@@ -1,6 +1,8 @@
1
1
  module Fontist
2
2
  module Utils
3
3
  class Cache
4
+ MAX_FILENAME_SIZE = 255
5
+
4
6
  include Locking
5
7
 
6
8
  def self.lock_path(path)
@@ -110,6 +112,11 @@ module Fontist
110
112
  end
111
113
 
112
114
  def filename(source)
115
+ filename = response_to_filename(source)
116
+ format_filename(filename)
117
+ end
118
+
119
+ def response_to_filename(source)
113
120
  if File.extname(source.original_filename).empty? && source.content_type
114
121
  require "mime/types"
115
122
  ext = MIME::Types[source.content_type].first&.preferred_extension
@@ -119,6 +126,15 @@ module Fontist
119
126
  source.original_filename
120
127
  end
121
128
 
129
+ def format_filename(filename)
130
+ return filename unless filename.length > MAX_FILENAME_SIZE
131
+
132
+ ext = File.extname(filename)
133
+ target_size = MAX_FILENAME_SIZE - ext.length
134
+ cut_filename = filename.slice(0, target_size)
135
+ "#{cut_filename}#{ext}"
136
+ end
137
+
122
138
  def move(source_file, target_path)
123
139
  # Windows requires file descriptors to be closed before files are moved
124
140
  source_file.close
@@ -10,12 +10,17 @@ module Fontist
10
10
  ruby2_keywords :download if respond_to?(:ruby2_keywords, true)
11
11
  end
12
12
 
13
- def initialize(file, file_size: nil, sha: nil, progress_bar: nil)
13
+ def initialize(file,
14
+ file_size: nil,
15
+ sha: nil,
16
+ progress_bar: nil,
17
+ use_content_length: true)
14
18
  # TODO: If the first mirror fails, try the second one
15
19
  @file = file
16
20
  @sha = [sha].flatten.compact
17
21
  @file_size = file_size.to_i if file_size
18
22
  @progress_bar = progress_bar
23
+ @use_content_length = use_content_length
19
24
  @cache = Cache.new
20
25
  end
21
26
 
@@ -85,7 +90,9 @@ module Fontist
85
90
  max_redirects: 10,
86
91
  headers: headers,
87
92
  content_length_proc: ->(content_length) {
88
- progress_bar.total = content_length if content_length
93
+ if @use_content_length && content_length
94
+ progress_bar.total = content_length
95
+ end
89
96
  },
90
97
  progress_proc: -> (progress) {
91
98
  progress_bar.increment(progress)
@@ -16,7 +16,15 @@ module Fontist
16
16
  end
17
17
 
18
18
  def self.level
19
- @level || default_level
19
+ @level ||= env_level || default_level
20
+ end
21
+
22
+ def self.env_level
23
+ ENV["FONTIST_LOG"]&.to_sym
24
+ end
25
+
26
+ def self.debug?
27
+ log_levels.include?(:debug)
20
28
  end
21
29
 
22
30
  def self.default_level
@@ -50,7 +58,7 @@ module Fontist
50
58
  end
51
59
 
52
60
  def self.debug(message)
53
- new.say(message) if log_levels.include?(:debug)
61
+ new.say(message) if debug?
54
62
  end
55
63
 
56
64
  def self.log_levels
@@ -1,3 +1,3 @@
1
1
  module Fontist
2
- VERSION = "1.19.0".freeze
2
+ VERSION = "1.21.1".freeze
3
3
  end
data/lib/fontist.rb CHANGED
@@ -49,7 +49,7 @@ module Fontist
49
49
  end
50
50
 
51
51
  def self.formulas_version
52
- "v3"
52
+ "v4"
53
53
  end
54
54
 
55
55
  def self.formulas_repo_url
@@ -151,4 +151,16 @@ 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
162
+
163
+ def self.google_fonts_key
164
+ ENV["GOOGLE_FONTS_API_KEY"] || config[:google_fonts_key]
165
+ end
154
166
  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.21.1
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-05-31 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,12 +357,27 @@ 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
319
377
  - lib/fontist/cache_cli.rb
320
378
  - lib/fontist/cli.rb
321
379
  - lib/fontist/cli/class_options.rb
380
+ - lib/fontist/cli/thor_ext.rb
322
381
  - lib/fontist/collection_file.rb
323
382
  - lib/fontist/config.rb
324
383
  - lib/fontist/config_cli.rb
@@ -332,6 +391,7 @@ files:
332
391
  - lib/fontist/fontconfig_cli.rb
333
392
  - lib/fontist/formula.rb
334
393
  - lib/fontist/formula_picker.rb
394
+ - lib/fontist/formula_suggestion.rb
335
395
  - lib/fontist/helpers.rb
336
396
  - lib/fontist/import.rb
337
397
  - lib/fontist/import/convert_formulas.rb
@@ -342,9 +402,8 @@ files:
342
402
  - lib/fontist/import/formula_builder.rb
343
403
  - lib/fontist/import/formula_serializer.rb
344
404
  - lib/fontist/import/google.rb
345
- - lib/fontist/import/google/new_fonts_fetcher.rb
346
- - lib/fontist/import/google/skiplist.yml
347
- - lib/fontist/import/google_check.rb
405
+ - lib/fontist/import/google/api.rb
406
+ - lib/fontist/import/google/create_google_formula.rb
348
407
  - lib/fontist/import/google_import.rb
349
408
  - lib/fontist/import/helpers/hash_helper.rb
350
409
  - lib/fontist/import/helpers/system_helper.rb
@@ -374,6 +433,8 @@ files:
374
433
  - lib/fontist/manifest/locations.rb
375
434
  - lib/fontist/repo.rb
376
435
  - lib/fontist/repo_cli.rb
436
+ - lib/fontist/resources/archive_resource.rb
437
+ - lib/fontist/resources/google_resource.rb
377
438
  - lib/fontist/style_version.rb
378
439
  - lib/fontist/system.yml
379
440
  - lib/fontist/system_font.rb
@@ -409,7 +470,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
409
470
  - !ruby/object:Gem::Version
410
471
  version: '0'
411
472
  requirements: []
412
- rubygems_version: 3.3.26
473
+ rubygems_version: 3.3.27
413
474
  signing_key:
414
475
  specification_version: 4
415
476
  summary: Install openly-licensed fonts on Windows, Linux and Mac!
@@ -1,146 +0,0 @@
1
- require_relative "../google"
2
- require_relative "../otf_parser"
3
-
4
- module Fontist
5
- module Import
6
- module Google
7
- class NewFontsFetcher
8
- REPO_PATH = Fontist.fontist_path.join("google", "fonts")
9
- REPO_URL = "https://github.com/google/fonts.git".freeze
10
- SKIPLIST_PATH = File.expand_path("skiplist.yml", __dir__)
11
-
12
- def initialize(logging: false, limit: nil)
13
- @logging = logging
14
- @limit = limit
15
- end
16
-
17
- def call
18
- update_repo
19
- fetch_new_paths
20
- end
21
-
22
- private
23
-
24
- def update_repo
25
- if Dir.exist?(REPO_PATH)
26
- `cd #{REPO_PATH} && git pull`
27
- else
28
- FileUtils.mkdir_p(File.dirname(REPO_PATH))
29
- `git clone --depth 1 #{REPO_URL} #{REPO_PATH}`
30
- end
31
- end
32
-
33
- def fetch_new_paths
34
- new_paths = []
35
-
36
- fetch_fonts_paths.each do |path|
37
- new = log_font(path) do
38
- new?(path)
39
- end
40
-
41
- next unless new
42
-
43
- new_paths << path
44
- return new_paths if @limit && new_paths.size >= @limit
45
- end
46
-
47
- new_paths
48
- end
49
-
50
- def fetch_fonts_paths
51
- Dir[File.join(REPO_PATH, "apache", "*"),
52
- File.join(REPO_PATH, "ofl", "*"),
53
- File.join(REPO_PATH, "ufl", "*")].sort
54
- end
55
-
56
- def log_font(path)
57
- return yield unless @logging
58
-
59
- print "#{path}, "
60
- new = yield
61
- puts(new ? "new" : "skipped")
62
- new
63
- end
64
-
65
- def new?(path)
66
- metadata_name = Google.metadata_name(path)
67
- return unless metadata_name
68
- return if in_skiplist?(metadata_name)
69
- return if up_to_date?(metadata_name, path)
70
- return unless downloadable?(metadata_name)
71
-
72
- true
73
- end
74
-
75
- def in_skiplist?(name)
76
- @skiplist ||= YAML.safe_load(File.open(SKIPLIST_PATH))
77
- @skiplist.include?(name)
78
- end
79
-
80
- def up_to_date?(metadata_name, path)
81
- formula = formula(metadata_name)
82
- return false unless formula
83
-
84
- repo_digest_up_to_date?(formula, path) ||
85
- fonts_up_to_date?(formula, path)
86
- end
87
-
88
- def repo_digest_up_to_date?(formula, path)
89
- return unless formula.digest
90
-
91
- formula.digest == Google.digest(path)
92
- end
93
-
94
- def fonts_up_to_date?(formula, path)
95
- styles = formula_styles(formula)
96
- repo_fonts(path).all? do |font|
97
- style = styles.find { |s| s.font == repo_to_archive_name(font) }
98
- return false unless style
99
-
100
- otfinfo_version(font) == style.version
101
- end
102
- end
103
-
104
- def formula_styles(formula)
105
- formula.fonts.map(&:styles).flatten
106
- end
107
-
108
- def repo_fonts(path)
109
- Dir.glob(File.join(path, "*.{ttf,otf}"))
110
- end
111
-
112
- def repo_to_archive_name(font_path)
113
- File.basename(font_path)
114
- .sub("[wght]", "-VariableFont_wght")
115
- .sub("[opsz]", "-Regular-VariableFont_opsz")
116
- end
117
-
118
- def formula(font_name)
119
- path = Fontist::Import::Google.formula_path(font_name)
120
- Formula.new_from_file(path) if File.exist?(path)
121
- end
122
-
123
- def otfinfo_version(path)
124
- info = OtfParser.new(path).call
125
- Fontist::Import::Google.style_version(info["Version"])
126
- end
127
-
128
- def downloadable?(name)
129
- retries ||= 0
130
- retries += 1
131
- Down.open("https://fonts.google.com/download?family=#{name}")
132
- true
133
- rescue Down::NotFound
134
- false
135
- rescue Down::ClientError => e
136
- raise unless e.message == "403 Forbidden"
137
-
138
- false
139
- rescue Down::TimeoutError
140
- retry unless retries >= 3
141
- false
142
- end
143
- end
144
- end
145
- end
146
- end
@@ -1,12 +0,0 @@
1
- - Montserrat
2
- - Montserrat Alternates
3
- - Overpass
4
- - Overpass Mono
5
- - Lato
6
- - Open Sans
7
- - Work Sans
8
- - Fira Code
9
- - Andika
10
- - Harmattan
11
- - Padauk
12
- - STIX Two Math
@@ -1,27 +0,0 @@
1
- require_relative "google/new_fonts_fetcher"
2
-
3
- module Fontist
4
- module Import
5
- class GoogleCheck
6
- def call
7
- fonts = new_fonts
8
- indicate(fonts)
9
- end
10
-
11
- private
12
-
13
- def new_fonts
14
- Fontist::Import::Google::NewFontsFetcher.new(logging: true).call
15
- end
16
-
17
- def indicate(new_paths)
18
- return if new_paths.empty?
19
-
20
- puts "New fonts are available in:"
21
- new_paths.each do |path|
22
- puts path
23
- end
24
- end
25
- end
26
- end
27
- end