fontist 1.7.3 → 1.8.5

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +38 -0
  3. data/.github/workflows/rspec.yml +58 -0
  4. data/README.md +48 -4
  5. data/{bin → exe}/fontist +0 -0
  6. data/fontist.gemspec +10 -7
  7. data/lib/fontist.rb +9 -2
  8. data/lib/fontist/cli.rb +64 -55
  9. data/lib/fontist/errors.rb +63 -12
  10. data/lib/fontist/font.rb +33 -64
  11. data/lib/fontist/font_installer.rb +118 -0
  12. data/lib/fontist/font_path.rb +29 -0
  13. data/lib/fontist/fontist_font.rb +3 -49
  14. data/lib/fontist/formula.rb +101 -35
  15. data/lib/fontist/formula_paths.rb +43 -0
  16. data/lib/fontist/helpers.rb +7 -0
  17. data/lib/fontist/import/create_formula.rb +3 -2
  18. data/lib/fontist/import/extractors.rb +4 -0
  19. data/lib/fontist/import/extractors/cpio_extractor.rb +39 -0
  20. data/lib/fontist/import/extractors/gzip_extractor.rb +27 -0
  21. data/lib/fontist/import/extractors/rpm_extractor.rb +45 -0
  22. data/lib/fontist/import/extractors/tar_extractor.rb +47 -0
  23. data/lib/fontist/import/google/skiplist.yml +3 -0
  24. data/lib/fontist/import/google_check.rb +1 -1
  25. data/lib/fontist/import/google_import.rb +3 -4
  26. data/lib/fontist/import/otfinfo_generate.rb +1 -1
  27. data/lib/fontist/import/recursive_extraction.rb +26 -8
  28. data/lib/fontist/import/sil_import.rb +99 -0
  29. data/lib/fontist/index.rb +11 -0
  30. data/lib/fontist/indexes/base_index.rb +82 -0
  31. data/lib/fontist/indexes/filename_index.rb +19 -0
  32. data/lib/fontist/indexes/font_index.rb +21 -0
  33. data/lib/fontist/indexes/index_formula.rb +36 -0
  34. data/lib/fontist/manifest/install.rb +4 -5
  35. data/lib/fontist/manifest/locations.rb +9 -1
  36. data/lib/fontist/system_font.rb +32 -62
  37. data/lib/fontist/system_index.rb +47 -5
  38. data/lib/fontist/utils.rb +5 -0
  39. data/lib/fontist/utils/cache.rb +12 -4
  40. data/lib/fontist/utils/cpio/cpio.rb +199 -0
  41. data/lib/fontist/utils/cpio_extractor.rb +47 -0
  42. data/lib/fontist/utils/exe_extractor.rb +1 -1
  43. data/lib/fontist/utils/gzip_extractor.rb +24 -0
  44. data/lib/fontist/utils/locking.rb +17 -0
  45. data/lib/fontist/utils/rpm_extractor.rb +37 -0
  46. data/lib/fontist/utils/tar_extractor.rb +61 -0
  47. data/lib/fontist/utils/zip_extractor.rb +1 -1
  48. data/lib/fontist/version.rb +1 -1
  49. metadata +74 -26
  50. data/.github/workflows/macosx.yml +0 -33
  51. data/.github/workflows/ubuntu.yml +0 -30
  52. data/.github/workflows/windows.yml +0 -32
  53. data/bin/check_google +0 -8
  54. data/bin/console +0 -11
  55. data/bin/convert_formulas +0 -8
  56. data/bin/generate_otfinfo +0 -8
  57. data/bin/import_google +0 -8
  58. data/bin/rspec +0 -29
  59. data/bin/setup +0 -7
  60. data/lib/fontist/font_formula.rb +0 -169
  61. data/lib/fontist/formula_template.rb +0 -122
  62. data/lib/fontist/formulas.rb +0 -56
  63. data/lib/fontist/registry.rb +0 -43
@@ -1,16 +1,67 @@
1
1
  module Fontist
2
2
  module Errors
3
- class LicensingError < StandardError; end
4
- class MissingFontError < StandardError; end
5
- class NonSupportedFontError < StandardError; end
6
- class TamperedFileError < StandardError; end
7
- class InvalidResourceError < StandardError; end
8
- class TimeoutError < StandardError; end
9
- class MissingAttributeError < StandardError; end
10
- class UnknownFontTypeError < StandardError; end
11
- class FontNotFoundError < StandardError; end
12
- class BinaryCallError < StandardError; end
13
- class ManifestCouldNotBeReadError < StandardError; end
14
- class ManifestCouldNotBeFoundError < StandardError; end
3
+ class GeneralError < StandardError; end
4
+
5
+ class BinaryCallError < GeneralError; end
6
+ class FontIndexCorrupted < GeneralError; end
7
+ class FontNotFoundError < GeneralError; end
8
+ class FormulaIndexNotFoundError < GeneralError; end
9
+ class InvalidResourceError < GeneralError; end
10
+ class LicensingError < GeneralError; end
11
+ class ManifestCouldNotBeFoundError < GeneralError; end
12
+ class ManifestCouldNotBeReadError < GeneralError; end
13
+ class MissingAttributeError < GeneralError; end
14
+ class TamperedFileError < GeneralError; end
15
+ class TimeoutError < GeneralError; end
16
+ class UnknownFontTypeError < GeneralError; end
17
+ class UnknownArchiveError < GeneralError; end
18
+
19
+ class FontError < GeneralError
20
+ attr_reader :font, :style
21
+
22
+ def initialize(msg, font = nil, style = nil)
23
+ @font = font
24
+ @style = style
25
+
26
+ super(msg)
27
+ end
28
+
29
+ def name
30
+ messages = []
31
+ messages << "Font name: '#{@font}'"
32
+ messages << "Style: '#{@style}'" if @style
33
+ messages.join("; ")
34
+ end
35
+ end
36
+
37
+ class MissingFontError < FontError
38
+ def initialize(font, style = nil)
39
+ name = prepare_name(font, style)
40
+ msg = "#{name} font is missing, please run `fontist install '#{font}'` to download the font."
41
+
42
+ super(msg, font, style)
43
+ end
44
+
45
+ private
46
+
47
+ def prepare_name(font, style)
48
+ names = []
49
+ names << "'#{font}'"
50
+ names << "'#{style}'" if style
51
+ names.join(" ")
52
+ end
53
+ end
54
+
55
+ class UnsupportedFontError < FontError
56
+ def initialize(font)
57
+ msg = <<~MSG.chomp
58
+ Font '#{font}' not found locally nor available in the Fontist formula repository.
59
+ Perhaps it is available at the latest Fontist formula repository.
60
+ You can update the formula repository using the command `fontist update` and try again.
61
+ MSG
62
+
63
+ super(msg, font)
64
+ end
65
+ end
15
66
  end
16
67
  end
data/lib/fontist/font.rb CHANGED
@@ -1,9 +1,12 @@
1
+ require "fontist/font_installer"
2
+ require "fontist/font_path"
3
+
1
4
  module Fontist
2
5
  class Font
3
6
  def initialize(options = {})
4
- @name = options.fetch(:name, nil)
5
- @confirmation = options.fetch(:confirmation, "no")
6
- @force = options.fetch(:force, false)
7
+ @name = options[:name]
8
+ @confirmation = options[:confirmation] || "no"
9
+ @force = options[:force] || false
7
10
 
8
11
  check_or_create_fontist_path!
9
12
  end
@@ -20,10 +23,6 @@ module Fontist
20
23
  new(name: name, confirmation: confirmation, force: force).install
21
24
  end
22
25
 
23
- def self.try_install(name, confirmation: "no")
24
- new(name: name, confirmation: confirmation).try_install
25
- end
26
-
27
26
  def self.uninstall(name)
28
27
  new(name: name).uninstall
29
28
  end
@@ -37,43 +36,31 @@ module Fontist
37
36
  end
38
37
 
39
38
  def find
40
- find_system_font || downloadable_font || raise(
41
- Fontist::Errors::NonSupportedFontError
42
- )
39
+ find_system_font || downloadable_font || raise_non_supported_font
43
40
  end
44
41
 
45
42
  def install
46
- (find_system_font unless @force) || download_font || raise(
47
- Fontist::Errors::NonSupportedFontError
48
- )
49
- end
50
-
51
- def try_install
52
- download_font
43
+ (find_system_font unless @force) || download_font || raise_non_supported_font
53
44
  end
54
45
 
55
46
  def uninstall
56
- uninstall_font || downloadable_font || raise(
57
- Fontist::Errors::NonSupportedFontError
58
- )
47
+ uninstall_font || downloadable_font || raise_non_supported_font
59
48
  end
60
49
 
61
50
  def status
62
- return installed_statuses unless @name
51
+ return installed_paths unless @name
63
52
 
64
- font_status || downloadable_font || raise(
65
- Fontist::Errors::NonSupportedFontError
66
- )
53
+ find_system_font || downloadable_font || raise_non_supported_font
67
54
  end
68
55
 
69
56
  def list
70
57
  return all_list unless @name
71
58
 
72
- font_list || raise(Fontist::Errors::NonSupportedFontError)
59
+ font_list || raise_non_supported_font
73
60
  end
74
61
 
75
62
  def all
76
- Fontist::Formula.all.to_h.map { |_name, formula| formula.fonts }.flatten
63
+ Fontist::Formula.all.map(&:fonts).flatten
77
64
  end
78
65
 
79
66
  private
@@ -87,9 +74,14 @@ module Fontist
87
74
  return
88
75
  end
89
76
 
77
+ print_paths(paths)
78
+ end
79
+
80
+ def print_paths(paths)
90
81
  Fontist.ui.say("Fonts found at:")
91
82
  paths.each do |path|
92
- Fontist.ui.say("- #{path}")
83
+ font_path = FontPath.new(path)
84
+ Fontist.ui.say(font_path.to_s)
93
85
  end
94
86
  end
95
87
 
@@ -101,7 +93,7 @@ module Fontist
101
93
  end
102
94
 
103
95
  def font_installer(formula)
104
- Object.const_get(formula.installer)
96
+ FontInstaller.new(formula)
105
97
  end
106
98
 
107
99
  def formula
@@ -110,20 +102,14 @@ module Fontist
110
102
 
111
103
  def downloadable_font
112
104
  if formula
113
- raise(
114
- Fontist::Errors::MissingFontError,
115
- "#{name} fonts are missing, please run " \
116
- "`fontist install '#{name}'` to " \
117
- "download the font."
118
- )
105
+ raise Fontist::Errors::MissingFontError.new(name)
119
106
  end
120
107
  end
121
108
 
122
109
  def download_font
123
110
  if formula
124
111
  check_and_confirm_required_license(formula)
125
- paths = font_installer(formula).fetch_font(name,
126
- confirmation: confirmation)
112
+ paths = font_installer(formula).install(confirmation: confirmation)
127
113
 
128
114
  Fontist.ui.say("Fonts installed at:")
129
115
  paths.each do |path|
@@ -136,7 +122,7 @@ module Fontist
136
122
  if formula.license_required && !confirmation.casecmp("yes").zero?
137
123
  @confirmation = show_license_and_ask_for_input(formula.license)
138
124
 
139
- if !confirmation.casecmp("yes").zero?
125
+ unless confirmation&.casecmp?("yes")
140
126
  raise Fontist::Errors::LicensingError.new(
141
127
  "Fontist will not download these fonts unless you accept the terms."
142
128
  )
@@ -181,39 +167,18 @@ module Fontist
181
167
  Fontist::FontistFont.find(name)
182
168
  end
183
169
 
184
- def installed_statuses
185
- installed_styles(all_formulas)
170
+ def installed_paths
171
+ print_paths(SystemFont.font_paths)
186
172
  end
187
173
 
188
174
  def all_formulas
189
- Fontist::Formula.all.to_h.values
190
- end
191
-
192
- def font_status
193
- return unless formula
194
-
195
- statuses = installed_styles([formula])
196
- statuses.empty? ? nil : statuses
197
- end
198
-
199
- def installed_styles(formulas)
200
- filter_blank(formulas) do |formula|
201
- filter_blank(formula.fonts) do |font|
202
- filter_blank(font.styles) do |style|
203
- path(style)
204
- end
205
- end
206
- end
207
- end
208
-
209
- def filter_blank(elements)
210
- elements.map { |e| [e, yield(e)] }
211
- .to_h
212
- .reject { |_k, v| v.nil? || v.empty? }
175
+ Fontist::Formula.all
213
176
  end
214
177
 
215
178
  def path(style)
216
- font_paths.grep(/#{style.font}/i).first
179
+ font_paths.detect do |path|
180
+ File.basename(path) == style.font
181
+ end
217
182
  end
218
183
 
219
184
  def font_paths
@@ -247,5 +212,9 @@ module Fontist
247
212
  def installed(style)
248
213
  path(style) ? true : false
249
214
  end
215
+
216
+ def raise_non_supported_font
217
+ raise Fontist::Errors::UnsupportedFontError.new(@name)
218
+ end
250
219
  end
251
220
  end
@@ -0,0 +1,118 @@
1
+ require "fontist/utils"
2
+
3
+ module Fontist
4
+ class FontInstaller
5
+ include Utils::ZipExtractor
6
+ include Utils::ExeExtractor
7
+ include Utils::MsiExtractor
8
+ include Utils::SevenZipExtractor
9
+ include Utils::RpmExtractor
10
+ include Utils::GzipExtractor
11
+ include Utils::CpioExtractor
12
+ include Utils::TarExtractor
13
+
14
+ def initialize(formula)
15
+ @formula = formula
16
+ end
17
+
18
+ def install(confirmation:)
19
+ if @formula.license_required && !"yes".casecmp?(confirmation)
20
+ raise(Fontist::Errors::LicensingError)
21
+ end
22
+
23
+ reinitialize
24
+ install_font
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :formula
30
+
31
+ def reinitialize
32
+ @downloaded = false
33
+ end
34
+
35
+ def install_font
36
+ fonts_paths = run_in_temp_dir { extract }
37
+ fonts_paths.empty? ? nil : fonts_paths
38
+ end
39
+
40
+ def run_in_temp_dir
41
+ Dir.mktmpdir(nil, Dir.tmpdir) do |dir|
42
+ @temp_dir = Pathname.new(dir)
43
+
44
+ result = yield
45
+
46
+ @temp_dir = nil
47
+
48
+ result
49
+ end
50
+ end
51
+
52
+ def extract
53
+ resource = @formula.resources.first
54
+
55
+ [@formula.extract].flatten.each do |operation|
56
+ resource = extract_by_operation(operation, resource)
57
+ end
58
+
59
+ fonts_paths = resource
60
+
61
+ fonts_paths
62
+ end
63
+
64
+ def extract_by_operation(operation, resource)
65
+ method = "#{operation.format}_extract"
66
+ if operation.options
67
+ send(method, resource, **operation.options.to_h)
68
+ else
69
+ send(method, resource)
70
+ end
71
+ end
72
+
73
+ def fonts_path
74
+ Fontist.fonts_path
75
+ end
76
+
77
+ def download_file(source)
78
+ url = source.urls.first
79
+ Fontist.ui.say(%(Downloading font "#{@formula.key}" from #{url}))
80
+
81
+ downloaded_file = Fontist::Utils::Downloader.download(
82
+ url,
83
+ sha: source.sha256,
84
+ file_size: source.file_size,
85
+ progress_bar: true
86
+ )
87
+
88
+ @downloaded = true
89
+ downloaded_file
90
+ end
91
+
92
+ def font_file?(filename)
93
+ source_files.include?(filename)
94
+ end
95
+
96
+ def source_files
97
+ @source_files ||= @formula.fonts.flat_map do |font|
98
+ font.styles.map do |style|
99
+ style.source_font || style.font
100
+ end
101
+ end
102
+ end
103
+
104
+ def target_filename(source_filename)
105
+ target_filenames[source_filename]
106
+ end
107
+
108
+ def target_filenames
109
+ @target_filenames ||= @formula.fonts.flat_map do |font|
110
+ font.styles.map do |style|
111
+ source = style.source_font || style.font
112
+ target = style.font
113
+ [source, target]
114
+ end
115
+ end.to_h
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,29 @@
1
+ require "fontist/indexes/filename_index"
2
+
3
+ module Fontist
4
+ class FontPath
5
+ def initialize(path)
6
+ @path = path
7
+ end
8
+
9
+ def to_s
10
+ [].tap do |s|
11
+ s << "-"
12
+ s << @path
13
+ s << "(from #{formulas.join(' or ')} formula)" if formulas.any?
14
+ end.join(" ")
15
+ end
16
+
17
+ def formulas
18
+ @formulas ||= if fontist_font?
19
+ Indexes::FilenameIndex.from_yaml.load_index_formulas(File.basename(@path)).map(&:name)
20
+ else
21
+ []
22
+ end
23
+ end
24
+
25
+ def fontist_font?
26
+ @path.start_with?(Fontist.fonts_path.to_s)
27
+ end
28
+ end
29
+ end
@@ -2,8 +2,6 @@ module Fontist
2
2
  class FontistFont
3
3
  def initialize(font_name:)
4
4
  @font_name = font_name
5
-
6
- check_and_register_font_formulas
7
5
  end
8
6
 
9
7
  def self.find(name)
@@ -11,60 +9,16 @@ module Fontist
11
9
  end
12
10
 
13
11
  def find
14
- return unless @font_name
15
-
16
- filenames = fonts_filenames
17
- return if filenames.empty?
18
-
19
- paths = font_paths.select do |path|
20
- filenames.any? { |f| File.basename(path).casecmp?(f) }
21
- end
12
+ styles = FormulaPaths.new(font_paths).find(@font_name)
13
+ return unless styles
22
14
 
23
- paths.empty? ? nil : paths
15
+ styles.map { |x| x[:path] }
24
16
  end
25
17
 
26
18
  private
27
19
 
28
- def fonts_filenames
29
- fonts.map { |font| font.styles.map(&:font) }.flatten
30
- end
31
-
32
- def fonts
33
- by_key || by_name || []
34
- end
35
-
36
- def by_key
37
- _key, formula = formulas.detect do |key, _value|
38
- key.to_s.casecmp?(@font_name)
39
- end
40
-
41
- return unless formula
42
-
43
- formula.fonts
44
- end
45
-
46
- def by_name
47
- _key, formula = formulas.detect do |_key, value|
48
- value.fonts.map(&:name).map(&:downcase).include?(@font_name.downcase)
49
- end
50
-
51
- return unless formula
52
-
53
- formula.fonts.select do |font|
54
- font.name.casecmp?(@font_name)
55
- end
56
- end
57
-
58
- def formulas
59
- @formulas ||= Fontist::Registry.instance.formulas.to_h
60
- end
61
-
62
20
  def font_paths
63
21
  Dir.glob(Fontist.fonts_path.join("**"))
64
22
  end
65
-
66
- def check_and_register_font_formulas
67
- $check_and_register_font_formulas ||= Fontist::Formulas.register_formulas
68
- end
69
23
  end
70
24
  end