fontist 1.19.0 → 1.21.1

Sign up to get free protection for your applications and to get access to all the features.
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