fontist 1.7.3 → 1.8.5

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