fontist 1.13.0 → 1.14.0
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.
- 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
|