fontist 1.13.0 → 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/metanorma.yml +3 -1
- data/README.adoc +230 -141
- data/lib/fontist/cli/class_options.rb +14 -0
- data/lib/fontist/cli.rb +33 -19
- data/lib/fontist/errors.rb +22 -0
- data/lib/fontist/font.rb +42 -7
- data/lib/fontist/fontconfig.rb +87 -0
- data/lib/fontist/fontconfig_cli.rb +29 -0
- data/lib/fontist/formula.rb +9 -2
- data/lib/fontist/helpers.rb +21 -0
- data/lib/fontist/import/create_formula.rb +22 -2
- data/lib/fontist/import/files/collection_file.rb +7 -3
- data/lib/fontist/import/formula_builder.rb +23 -7
- data/lib/fontist/import/google/skiplist.yml +1 -0
- data/lib/fontist/import/helpers/system_helper.rb +1 -9
- data/lib/fontist/import/macos/macos_license.txt +596 -0
- data/lib/fontist/import/macos.rb +25 -109
- data/lib/fontist/import/recursive_extraction.rb +11 -1
- data/lib/fontist/import/text_helper.rb +1 -1
- data/lib/fontist/import_cli.rb +20 -10
- data/lib/fontist/repo_cli.rb +6 -0
- data/lib/fontist/utils/downloader.rb +5 -8
- data/lib/fontist/utils/system.rb +8 -0
- data/lib/fontist/utils/ui.rb +33 -4
- data/lib/fontist/utils.rb +0 -3
- data/lib/fontist/version.rb +1 -1
- data/lib/fontist.rb +2 -6
- metadata +6 -6
- data/lib/fontist/google_cli.rb +0 -29
- data/lib/fontist/utils/dsl/collection_font.rb +0 -36
- data/lib/fontist/utils/dsl/font.rb +0 -38
- data/lib/fontist/utils/dsl.rb +0 -85
data/lib/fontist/errors.rb
CHANGED
@@ -4,6 +4,18 @@ module Fontist
|
|
4
4
|
|
5
5
|
class BinaryCallError < GeneralError; end
|
6
6
|
|
7
|
+
class FontconfigNotFoundError < GeneralError
|
8
|
+
def initialize
|
9
|
+
super("Could not find fontconfig.")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class FontconfigFileNotFoundError < GeneralError
|
14
|
+
def initialize
|
15
|
+
super("Fontist file could not be found in fontconfig configuration.")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
7
19
|
class FontIndexCorrupted < GeneralError; end
|
8
20
|
|
9
21
|
class FontNotFoundError < GeneralError; end
|
@@ -12,6 +24,16 @@ module Fontist
|
|
12
24
|
# it depends on this exception to automatically download formulas
|
13
25
|
class FormulaIndexNotFoundError < GeneralError; end
|
14
26
|
|
27
|
+
class FormulaNotFoundError < GeneralError
|
28
|
+
def initialize(formula)
|
29
|
+
super(<<~MSG.chomp)
|
30
|
+
Formula '#{formula}' not found locally nor available in the Fontist formula repository.
|
31
|
+
Perhaps it is available at the latest Fontist formula repository.
|
32
|
+
You can update the formula repository using the command `fontist update` and try again.
|
33
|
+
MSG
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
15
37
|
class MainRepoNotFoundError < FormulaIndexNotFoundError; end
|
16
38
|
|
17
39
|
class InvalidResourceError < GeneralError; end
|
data/lib/fontist/font.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "fontist/font_installer"
|
2
2
|
require "fontist/font_path"
|
3
3
|
require "fontist/formula_picker"
|
4
|
+
require "fontist/fontconfig"
|
4
5
|
|
5
6
|
module Fontist
|
6
7
|
class Font
|
@@ -14,6 +15,8 @@ module Fontist
|
|
14
15
|
@smallest = options[:smallest]
|
15
16
|
@newest = options[:newest]
|
16
17
|
@size_limit = options[:size_limit]
|
18
|
+
@by_formula = options[:formula]
|
19
|
+
@update_fontconfig = options[:update_fontconfig]
|
17
20
|
|
18
21
|
check_or_create_fontist_path!
|
19
22
|
end
|
@@ -48,6 +51,8 @@ module Fontist
|
|
48
51
|
end
|
49
52
|
|
50
53
|
def install
|
54
|
+
return install_formula if @by_formula
|
55
|
+
|
51
56
|
(find_system_font unless @force) || download_font || manual_font ||
|
52
57
|
raise_non_supported_font
|
53
58
|
end
|
@@ -103,6 +108,22 @@ module Fontist
|
|
103
108
|
end
|
104
109
|
end
|
105
110
|
|
111
|
+
def install_formula
|
112
|
+
download_formula || raise_formula_not_found
|
113
|
+
end
|
114
|
+
|
115
|
+
def download_formula
|
116
|
+
formula = Formula.find_by_key(@name)
|
117
|
+
return unless formula
|
118
|
+
return unless formula.downloadable?
|
119
|
+
|
120
|
+
request_formula_installation(formula)
|
121
|
+
end
|
122
|
+
|
123
|
+
def raise_formula_not_found
|
124
|
+
raise Errors::FormulaNotFoundError.new(@name)
|
125
|
+
end
|
126
|
+
|
106
127
|
def font_installer(formula)
|
107
128
|
FontInstaller.new(formula, no_progress: @no_progress)
|
108
129
|
end
|
@@ -147,14 +168,22 @@ module Fontist
|
|
147
168
|
def download_font
|
148
169
|
return if sufficient_formulas.empty?
|
149
170
|
|
150
|
-
sufficient_formulas.flat_map do |formula|
|
151
|
-
|
152
|
-
|
171
|
+
paths = sufficient_formulas.flat_map do |formula|
|
172
|
+
request_formula_installation(formula)
|
173
|
+
end
|
153
174
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
175
|
+
update_fontconfig
|
176
|
+
|
177
|
+
paths
|
178
|
+
end
|
179
|
+
|
180
|
+
def request_formula_installation(formula)
|
181
|
+
confirmation = check_and_confirm_required_license(formula)
|
182
|
+
paths = font_installer(formula).install(confirmation: confirmation)
|
183
|
+
|
184
|
+
Fontist.ui.say("Fonts installed at:")
|
185
|
+
paths.each do |path|
|
186
|
+
Fontist.ui.say("- #{path}")
|
158
187
|
end
|
159
188
|
end
|
160
189
|
|
@@ -197,6 +226,12 @@ module Fontist
|
|
197
226
|
MSG
|
198
227
|
end
|
199
228
|
|
229
|
+
def update_fontconfig
|
230
|
+
return unless @update_fontconfig
|
231
|
+
|
232
|
+
Fontconfig.update
|
233
|
+
end
|
234
|
+
|
200
235
|
def manual_font
|
201
236
|
return if manual_formulas.empty?
|
202
237
|
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Fontist
|
2
|
+
class Fontconfig
|
3
|
+
def self.update
|
4
|
+
new.update
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.remove(options = {})
|
8
|
+
new(options).remove
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def update
|
16
|
+
ensure_fontconfig_installed
|
17
|
+
create_config
|
18
|
+
regenerate_fontconfig_cache
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove
|
22
|
+
return handle_file_not_found unless config_exists?
|
23
|
+
|
24
|
+
regenerate_fontconfig_cache if fontconfig_installed?
|
25
|
+
remove_config
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def ensure_fontconfig_installed
|
31
|
+
raise Errors::FontconfigNotFoundError unless fontconfig_installed?
|
32
|
+
end
|
33
|
+
|
34
|
+
def fontconfig_installed?
|
35
|
+
Utils::System.fontconfig_installed?
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_config
|
39
|
+
return if File.exist?(config_path)
|
40
|
+
|
41
|
+
FileUtils.mkdir_p(File.dirname(config_path))
|
42
|
+
File.write(config_path, config_content)
|
43
|
+
end
|
44
|
+
|
45
|
+
def config_path
|
46
|
+
File.join(xdg_config_home, "fontconfig", "conf.d", "10-fontist.conf")
|
47
|
+
end
|
48
|
+
|
49
|
+
def xdg_config_home
|
50
|
+
ENV["XDG_CONFIG_HOME"] || File.join(Dir.home, ".config")
|
51
|
+
end
|
52
|
+
|
53
|
+
def config_content
|
54
|
+
<<~CONTENT
|
55
|
+
<?xml version='1.0'?>
|
56
|
+
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
|
57
|
+
<fontconfig>
|
58
|
+
<dir>#{Fontist.fonts_path}</dir>
|
59
|
+
</fontconfig>
|
60
|
+
CONTENT
|
61
|
+
end
|
62
|
+
|
63
|
+
def regenerate_fontconfig_cache
|
64
|
+
Helpers.run("fc-cache -f")
|
65
|
+
end
|
66
|
+
|
67
|
+
def ensure_file_exists
|
68
|
+
return if @options[:force]
|
69
|
+
|
70
|
+
raise Errors::FontconfigFileNotFoundError unless File.exist?(config_path)
|
71
|
+
end
|
72
|
+
|
73
|
+
def config_exists?
|
74
|
+
File.exist?(config_path)
|
75
|
+
end
|
76
|
+
|
77
|
+
def handle_file_not_found
|
78
|
+
return if @options[:force]
|
79
|
+
|
80
|
+
raise Errors::FontconfigFileNotFoundError
|
81
|
+
end
|
82
|
+
|
83
|
+
def remove_config
|
84
|
+
FileUtils.rm(config_path)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Fontist
|
2
|
+
class FontconfigCLI < Thor
|
3
|
+
include CLI::ClassOptions
|
4
|
+
|
5
|
+
desc "update", "Update fontconfig configuration to use fontist fonts"
|
6
|
+
def update
|
7
|
+
handle_class_options(options)
|
8
|
+
Fontconfig.update
|
9
|
+
Fontist.ui.success("Fontconfig file has been successfully updated.")
|
10
|
+
CLI::STATUS_SUCCESS
|
11
|
+
rescue Errors::FontconfigNotFoundError => e
|
12
|
+
Fontist.ui.error(e.message)
|
13
|
+
CLI::STATUS_FONTCONFIG_NOT_FOUND
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "remove", "Remove fontist file in fontconfig configuration"
|
17
|
+
option :force, type: :boolean, aliases: :f,
|
18
|
+
desc: "Proceed even if does not exist"
|
19
|
+
def remove
|
20
|
+
handle_class_options(options)
|
21
|
+
Fontconfig.remove(options)
|
22
|
+
Fontist.ui.success("Fontconfig file has been successfully removed.")
|
23
|
+
CLI::STATUS_SUCCESS
|
24
|
+
rescue Errors::FontconfigFileNotFoundError => e
|
25
|
+
Fontist.ui.error(e.message)
|
26
|
+
CLI::STATUS_FONTCONFIG_FILE_NOT_FOUND
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/fontist/formula.rb
CHANGED
@@ -45,6 +45,13 @@ module Fontist
|
|
45
45
|
end.flatten
|
46
46
|
end
|
47
47
|
|
48
|
+
def self.find_by_key(key)
|
49
|
+
path = Fontist.formulas_path.join("#{key}.yml")
|
50
|
+
return unless File.exist?(path)
|
51
|
+
|
52
|
+
new_from_file(path)
|
53
|
+
end
|
54
|
+
|
48
55
|
def self.new_from_file(path)
|
49
56
|
data = YAML.load_file(path)
|
50
57
|
new(data, path)
|
@@ -72,7 +79,7 @@ module Fontist
|
|
72
79
|
end
|
73
80
|
|
74
81
|
def key
|
75
|
-
|
82
|
+
key_from_path
|
76
83
|
end
|
77
84
|
|
78
85
|
def description
|
@@ -137,7 +144,7 @@ module Fontist
|
|
137
144
|
|
138
145
|
private
|
139
146
|
|
140
|
-
def
|
147
|
+
def key_from_path
|
141
148
|
escaped = Regexp.escape(Fontist.formulas_path.to_s + "/")
|
142
149
|
@path.sub(Regexp.new("^" + escaped), "").sub(/\.yml$/, "")
|
143
150
|
end
|
data/lib/fontist/helpers.rb
CHANGED
@@ -3,5 +3,26 @@ module Fontist
|
|
3
3
|
def self.parse_to_object(data)
|
4
4
|
JSON.parse(data.to_json, object_class: OpenStruct)
|
5
5
|
end
|
6
|
+
|
7
|
+
def self.run(command)
|
8
|
+
Fontist.ui.debug("Run `#{command}`")
|
9
|
+
|
10
|
+
result = `#{command}`
|
11
|
+
unless $CHILD_STATUS.to_i.zero?
|
12
|
+
raise Errors::BinaryCallError,
|
13
|
+
"Failed to run #{command}, status: #{$CHILD_STATUS}"
|
14
|
+
end
|
15
|
+
|
16
|
+
result
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.silence_stream(stream)
|
20
|
+
old_stream = stream.dup
|
21
|
+
stream.reopen(RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ ? "NUL:" : "/dev/null") # rubocop:disable Performance/RegexpMatch, Metrics/LineLength
|
22
|
+
stream.sync = true
|
23
|
+
yield
|
24
|
+
ensure
|
25
|
+
stream.reopen(old_stream)
|
26
|
+
end
|
6
27
|
end
|
7
28
|
end
|
@@ -55,12 +55,32 @@ module Fontist
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def save(builder)
|
58
|
-
|
59
|
-
path = @options[:formula_dir] ? File.join(@options[:formula_dir], filename) : filename
|
58
|
+
path = vacant_path
|
60
59
|
yaml = YAML.dump(Helpers::HashHelper.stringify_keys(builder.formula))
|
61
60
|
File.write(path, yaml)
|
62
61
|
path
|
63
62
|
end
|
63
|
+
|
64
|
+
def vacant_path
|
65
|
+
path = path_from_name
|
66
|
+
return path unless @options[:keep_existing] && File.exist?(path)
|
67
|
+
|
68
|
+
2.upto(9) do |i|
|
69
|
+
candidate = path.sub(/\.yml$/, "#{i}.yml")
|
70
|
+
return candidate unless File.exist?(candidate)
|
71
|
+
end
|
72
|
+
|
73
|
+
raise Errors::GeneralError, "Formula #{path} already exists."
|
74
|
+
end
|
75
|
+
|
76
|
+
def path_from_name
|
77
|
+
filename = Import.name_to_filename(builder.name)
|
78
|
+
if @options[:formula_dir]
|
79
|
+
File.join(@options[:formula_dir], filename)
|
80
|
+
else
|
81
|
+
filename
|
82
|
+
end
|
83
|
+
end
|
64
84
|
end
|
65
85
|
end
|
66
86
|
end
|
@@ -26,9 +26,9 @@ module Fontist
|
|
26
26
|
|
27
27
|
def read
|
28
28
|
switch_to_temp_dir do |tmp_dir|
|
29
|
-
extract_ttfs(tmp_dir)
|
30
|
-
Otf::FontFile.new(path)
|
31
|
-
|
29
|
+
extract_ttfs(tmp_dir)
|
30
|
+
.map { |path| Otf::FontFile.new(path) }
|
31
|
+
.reject { |font_file| hidden_or_pua_encoded?(font_file) }
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -47,6 +47,10 @@ module Fontist
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
def hidden_or_pua_encoded?(font_file)
|
51
|
+
font_file.family_name.start_with?(".")
|
52
|
+
end
|
53
|
+
|
50
54
|
def detect_extension
|
51
55
|
base_extension = "ttc"
|
52
56
|
|
@@ -4,9 +4,10 @@ require_relative "text_helper"
|
|
4
4
|
module Fontist
|
5
5
|
module Import
|
6
6
|
class FormulaBuilder
|
7
|
-
FORMULA_ATTRIBUTES = %i[description homepage resources
|
7
|
+
FORMULA_ATTRIBUTES = %i[platforms description homepage resources
|
8
8
|
font_collections fonts extract copyright
|
9
|
-
license_url
|
9
|
+
license_url requires_license_agreement
|
10
|
+
open_license digest command].freeze
|
10
11
|
|
11
12
|
attr_writer :archive,
|
12
13
|
:url,
|
@@ -28,9 +29,15 @@ module Fontist
|
|
28
29
|
def name
|
29
30
|
return @options[:name] if @options[:name]
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
common = %i[family_name type]
|
33
|
+
.map { |attr| both_fonts.map(&attr).uniq }
|
34
|
+
.map { |names| TextHelper.longest_common_prefix(names) }
|
35
|
+
.map { |prefix| prefix unless prefix == "Regular" }
|
36
|
+
.compact
|
37
|
+
.join(" ")
|
38
|
+
return common unless common.empty?
|
39
|
+
|
40
|
+
both_fonts.map(&:family_name).first
|
34
41
|
end
|
35
42
|
|
36
43
|
private
|
@@ -50,6 +57,10 @@ module Fontist
|
|
50
57
|
files
|
51
58
|
end
|
52
59
|
|
60
|
+
def platforms
|
61
|
+
@options[:platforms]
|
62
|
+
end
|
63
|
+
|
53
64
|
def description
|
54
65
|
name
|
55
66
|
end
|
@@ -175,12 +186,17 @@ module Fontist
|
|
175
186
|
both_fonts.map(&:license_url).compact.first
|
176
187
|
end
|
177
188
|
|
189
|
+
def requires_license_agreement
|
190
|
+
@options[:requires_license_agreement]
|
191
|
+
end
|
192
|
+
|
178
193
|
def open_license
|
179
|
-
unless @license_text
|
194
|
+
unless @license_text || requires_license_agreement
|
180
195
|
Fontist.ui.error("WARN: please add license manually")
|
181
|
-
return
|
182
196
|
end
|
183
197
|
|
198
|
+
return unless @license_text
|
199
|
+
|
184
200
|
Fontist.ui.error("WARN: ensure it's an open license, otherwise " \
|
185
201
|
"change the 'open_license' attribute to " \
|
186
202
|
"'requires_license_agreement'")
|
@@ -4,15 +4,7 @@ module Fontist
|
|
4
4
|
module SystemHelper
|
5
5
|
class << self
|
6
6
|
def run(command)
|
7
|
-
Fontist.
|
8
|
-
|
9
|
-
result = `#{command}`
|
10
|
-
unless $CHILD_STATUS.to_i.zero?
|
11
|
-
raise Errors::BinaryCallError,
|
12
|
-
"Failed to run #{command}, status: #{$CHILD_STATUS}"
|
13
|
-
end
|
14
|
-
|
15
|
-
result
|
7
|
+
Fontist::Helpers.run(command)
|
16
8
|
end
|
17
9
|
end
|
18
10
|
end
|