fontist 1.5.0 → 1.7.2
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/README.md +46 -18
- data/bin/fontist +0 -1
- data/fontist.gemspec +1 -0
- data/lib/fontist.rb +5 -1
- data/lib/fontist/cli.rb +22 -10
- data/lib/fontist/errors.rb +1 -0
- data/lib/fontist/font.rb +17 -3
- data/lib/fontist/font_formula.rb +58 -16
- data/lib/fontist/formula.rb +13 -53
- data/lib/fontist/formula_template.rb +16 -2
- data/lib/fontist/import/create_formula.rb +15 -30
- data/lib/fontist/import/files/collection_file.rb +6 -1
- data/lib/fontist/import/files/file_requirement.rb +17 -0
- data/lib/fontist/import/files/font_detector.rb +48 -0
- data/lib/fontist/import/formula_builder.rb +14 -4
- data/lib/fontist/import/google/skiplist.yml +2 -0
- data/lib/fontist/import/otf/font_file.rb +20 -4
- data/lib/fontist/import/recursive_extraction.rb +99 -15
- data/lib/fontist/manifest/install.rb +5 -9
- data/lib/fontist/manifest/locations.rb +51 -4
- data/lib/fontist/system_font.rb +44 -44
- data/lib/fontist/system_index.rb +92 -0
- data/lib/fontist/utils.rb +1 -0
- data/lib/fontist/utils/cache.rb +27 -8
- data/lib/fontist/utils/downloader.rb +54 -10
- data/lib/fontist/utils/dsl.rb +4 -0
- data/lib/fontist/utils/dsl/collection_font.rb +36 -0
- data/lib/fontist/utils/dsl/font.rb +2 -1
- data/lib/fontist/utils/exe_extractor.rb +19 -9
- data/lib/fontist/utils/ui.rb +4 -0
- data/lib/fontist/utils/zip_extractor.rb +20 -11
- data/lib/fontist/version.rb +1 -1
- metadata +20 -3
- data/lib/fontist/manifest/common.rb +0 -60
@@ -1,8 +1,8 @@
|
|
1
|
-
require_relative "
|
1
|
+
require_relative "locations"
|
2
2
|
|
3
3
|
module Fontist
|
4
4
|
module Manifest
|
5
|
-
class Install <
|
5
|
+
class Install < Locations
|
6
6
|
def initialize(manifest, confirmation: "no")
|
7
7
|
@manifest = manifest
|
8
8
|
@confirmation = confirmation
|
@@ -15,15 +15,11 @@ module Fontist
|
|
15
15
|
private
|
16
16
|
|
17
17
|
def file_paths(font, style)
|
18
|
-
paths =
|
19
|
-
return paths unless paths.empty?
|
18
|
+
paths = super
|
19
|
+
return paths unless paths["paths"].empty?
|
20
20
|
|
21
21
|
install_font(font)
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
def find_installed_font(font, style)
|
26
|
-
Fontist::SystemFont.find_with_style(font, style)
|
22
|
+
super
|
27
23
|
end
|
28
24
|
|
29
25
|
def install_font(font)
|
@@ -1,12 +1,59 @@
|
|
1
|
-
require_relative "common"
|
2
|
-
|
3
1
|
module Fontist
|
4
2
|
module Manifest
|
5
|
-
class Locations
|
3
|
+
class Locations
|
4
|
+
def initialize(manifest)
|
5
|
+
@manifest = manifest
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.call(manifest)
|
9
|
+
new(manifest).call
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
font_names.zip(font_paths).to_h
|
14
|
+
end
|
15
|
+
|
6
16
|
private
|
7
17
|
|
18
|
+
def font_names
|
19
|
+
fonts.keys
|
20
|
+
end
|
21
|
+
|
22
|
+
def fonts
|
23
|
+
@fonts ||= begin
|
24
|
+
unless File.exist?(@manifest)
|
25
|
+
raise Fontist::Errors::ManifestCouldNotBeFoundError
|
26
|
+
end
|
27
|
+
|
28
|
+
fonts = YAML.load_file(@manifest)
|
29
|
+
unless fonts.is_a?(Hash)
|
30
|
+
raise Fontist::Errors::ManifestCouldNotBeReadError
|
31
|
+
end
|
32
|
+
|
33
|
+
fonts
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def font_paths
|
38
|
+
fonts.map do |font, styles|
|
39
|
+
styles_to_ary = [styles].flatten
|
40
|
+
style_paths_map(font, styles_to_ary)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def style_paths_map(font, names)
|
45
|
+
paths = style_paths(font, names)
|
46
|
+
names.zip(paths).to_h
|
47
|
+
end
|
48
|
+
|
49
|
+
def style_paths(font, names)
|
50
|
+
names.map do |style|
|
51
|
+
file_paths(font, style)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
8
55
|
def file_paths(font, style)
|
9
|
-
Fontist::SystemFont.
|
56
|
+
Fontist::SystemFont.find_with_name(font, style).transform_keys(&:to_s)
|
10
57
|
end
|
11
58
|
end
|
12
59
|
end
|
data/lib/fontist/system_font.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative "system_index"
|
2
|
+
|
1
3
|
module Fontist
|
2
4
|
class SystemFont
|
3
5
|
def initialize(font:, style: nil, sources: nil)
|
@@ -10,22 +12,23 @@ module Fontist
|
|
10
12
|
new(font: font, sources: sources).find
|
11
13
|
end
|
12
14
|
|
13
|
-
def self.
|
14
|
-
new(font: font, style: style).
|
15
|
+
def self.find_with_name(font, style)
|
16
|
+
new(font: font, style: style).find_with_name
|
15
17
|
end
|
16
18
|
|
17
19
|
def find
|
18
|
-
|
19
|
-
|
20
|
+
styles = find_styles
|
21
|
+
return unless styles
|
20
22
|
|
21
|
-
|
23
|
+
styles.map { |x| x[:path] }
|
22
24
|
end
|
23
25
|
|
24
|
-
def
|
25
|
-
|
26
|
-
return paths unless
|
26
|
+
def find_with_name
|
27
|
+
styles = find_styles
|
28
|
+
return { full_name: nil, paths: [] } unless styles
|
27
29
|
|
28
|
-
|
30
|
+
{ full_name: styles.first[:full_name],
|
31
|
+
paths: styles.map { |x| x[:path] } }
|
29
32
|
end
|
30
33
|
|
31
34
|
private
|
@@ -42,25 +45,6 @@ module Fontist
|
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
45
|
-
def grep_font_paths(font, style = nil)
|
46
|
-
pattern = prepare_pattern(font, style)
|
47
|
-
|
48
|
-
paths = font_paths.map { |path| [File.basename(path), path] }.to_h
|
49
|
-
files = paths.keys
|
50
|
-
matched = files.grep(pattern)
|
51
|
-
paths.values_at(*matched).compact
|
52
|
-
end
|
53
|
-
|
54
|
-
def prepare_pattern(font, style = nil)
|
55
|
-
style = nil if style&.casecmp?("regular")
|
56
|
-
|
57
|
-
s = [font, style].compact.map { |x| Regexp.quote(x) }
|
58
|
-
.join(".*")
|
59
|
-
.gsub("\\ ", "\s?") # space independent
|
60
|
-
|
61
|
-
Regexp.new(s, Regexp::IGNORECASE)
|
62
|
-
end
|
63
|
-
|
64
48
|
def font_paths
|
65
49
|
@font_paths ||= Dir.glob((
|
66
50
|
user_sources +
|
@@ -69,11 +53,6 @@ module Fontist
|
|
69
53
|
).flatten.uniq)
|
70
54
|
end
|
71
55
|
|
72
|
-
def lookup_using_font_name
|
73
|
-
font_names = map_name_to_valid_font_names || []
|
74
|
-
font_paths.grep(/#{font_names.join("|")}/i) unless font_names.empty?
|
75
|
-
end
|
76
|
-
|
77
56
|
def fontist_fonts_path
|
78
57
|
@fontist_fonts_path ||= Fontist.fonts_path
|
79
58
|
end
|
@@ -82,24 +61,45 @@ module Fontist
|
|
82
61
|
Fontist::Utils::System.user_os
|
83
62
|
end
|
84
63
|
|
85
|
-
def map_name_to_valid_font_names
|
86
|
-
fonts = Formula.find_fonts(font)
|
87
|
-
fonts.map { |font| font.styles.map(&:font) }.flatten if fonts
|
88
|
-
end
|
89
|
-
|
90
64
|
def system_path_file
|
91
65
|
File.open(Fontist.system_file_path)
|
92
66
|
end
|
93
67
|
|
94
68
|
def default_sources
|
95
|
-
@default_sources ||= YAML.
|
69
|
+
@default_sources ||= YAML.safe_load(system_path_file)["system"][user_os.to_s]
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_styles
|
73
|
+
find_by_index || find_by_formulas
|
96
74
|
end
|
97
75
|
|
98
|
-
def
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
76
|
+
def find_by_index
|
77
|
+
SystemIndex.new(font_paths).find(font, style)
|
78
|
+
end
|
79
|
+
|
80
|
+
def find_by_formulas
|
81
|
+
styles = find_styles_by_formulas(font, style)
|
82
|
+
return if styles.empty?
|
83
|
+
|
84
|
+
fonts = styles.uniq { |s| s["font"] }.flat_map do |s|
|
85
|
+
paths = search_font_paths(s["font"])
|
86
|
+
paths.map do |path|
|
87
|
+
{ full_name: s["full_name"],
|
88
|
+
path: path }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
fonts.empty? ? nil : fonts
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_styles_by_formulas(font, style)
|
96
|
+
if style
|
97
|
+
Formula.find_styles(font, style)
|
98
|
+
else
|
99
|
+
fonts = Formula.find_fonts(font)
|
100
|
+
return [] unless fonts
|
101
|
+
|
102
|
+
fonts.flat_map(&:styles)
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "ttfunk"
|
2
|
+
|
3
|
+
module Fontist
|
4
|
+
class SystemIndex
|
5
|
+
attr_reader :font_paths
|
6
|
+
|
7
|
+
def initialize(font_paths)
|
8
|
+
@font_paths = font_paths
|
9
|
+
end
|
10
|
+
|
11
|
+
def find(font, style)
|
12
|
+
fonts = system_index.select do |file|
|
13
|
+
file[:family_name].casecmp?(font) &&
|
14
|
+
(style.nil? || file[:type].casecmp?(style))
|
15
|
+
end
|
16
|
+
|
17
|
+
fonts.empty? ? nil : fonts
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def system_index
|
23
|
+
@system_index ||= build_system_index
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_system_index
|
27
|
+
previous_index = load_system_index
|
28
|
+
updated_index = detect_paths(font_paths, previous_index)
|
29
|
+
updated_index.tap do |index|
|
30
|
+
save_index(index)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def load_system_index
|
35
|
+
index = File.exist?(Fontist.system_index_path) ? YAML.load_file(Fontist.system_index_path) : []
|
36
|
+
index.group_by { |x| x[:path] }
|
37
|
+
end
|
38
|
+
|
39
|
+
def detect_paths(paths, indexed)
|
40
|
+
paths.flat_map do |path|
|
41
|
+
next indexed[path] if indexed[path]
|
42
|
+
|
43
|
+
detect_fonts(path)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def detect_fonts(path)
|
48
|
+
case File.extname(path).delete_prefix(".").downcase
|
49
|
+
when "ttf", "otf"
|
50
|
+
detect_file_font(path)
|
51
|
+
when "ttc"
|
52
|
+
detect_collection_fonts(path)
|
53
|
+
else
|
54
|
+
raise Errors::UnknownFontTypeError.new(path)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def detect_file_font(path)
|
59
|
+
file = TTFunk::File.open(path)
|
60
|
+
parse_font(file, path)
|
61
|
+
end
|
62
|
+
|
63
|
+
def detect_collection_fonts(path)
|
64
|
+
TTFunk::Collection.open(path) do |collection|
|
65
|
+
collection.map do |file|
|
66
|
+
parse_font(file, path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_font(file, path)
|
72
|
+
x = file.name
|
73
|
+
|
74
|
+
{
|
75
|
+
path: path,
|
76
|
+
full_name: parse_text(x.font_name.first),
|
77
|
+
family_name: parse_text(x.preferred_family.first || x.font_family.first),
|
78
|
+
type: parse_text(x.preferred_subfamily.first || x.font_subfamily.first),
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def parse_text(text)
|
83
|
+
text.gsub(/[^[:print:]]/, "").to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
def save_index(index)
|
87
|
+
dir = File.dirname(Fontist.system_index_path)
|
88
|
+
FileUtils.mkdir_p(dir) unless File.exist?(dir)
|
89
|
+
File.write(Fontist.system_index_path, YAML.dump(index))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/fontist/utils.rb
CHANGED
@@ -2,6 +2,7 @@ require "fontist/utils/ui"
|
|
2
2
|
require "fontist/utils/system"
|
3
3
|
require "fontist/utils/dsl"
|
4
4
|
require "fontist/utils/dsl/font"
|
5
|
+
require "fontist/utils/dsl/collection_font"
|
5
6
|
require "fontist/utils/downloader"
|
6
7
|
require "fontist/utils/zip_extractor"
|
7
8
|
require "fontist/utils/exe_extractor"
|
data/lib/fontist/utils/cache.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
module Fontist
|
2
2
|
module Utils
|
3
3
|
class Cache
|
4
|
-
def fetch(key)
|
4
|
+
def fetch(key, bar: nil)
|
5
5
|
map = load_cache
|
6
|
-
|
6
|
+
if cache_exist?(map[key])
|
7
|
+
print_bar(bar, map[key]) if bar
|
8
|
+
|
9
|
+
return downloaded_file(map[key])
|
10
|
+
end
|
7
11
|
|
8
12
|
generated_file = yield
|
9
|
-
path = save_cache(generated_file, key
|
13
|
+
path = save_cache(generated_file, key)
|
10
14
|
|
11
|
-
|
15
|
+
downloaded_file(path)
|
12
16
|
end
|
13
17
|
|
14
18
|
private
|
@@ -21,18 +25,33 @@ module Fontist
|
|
21
25
|
cache_map_path.exist? ? YAML.load_file(cache_map_path) : {}
|
22
26
|
end
|
23
27
|
|
24
|
-
def
|
25
|
-
File.new(
|
28
|
+
def downloaded_file(path)
|
29
|
+
File.new(downloaded_path(path))
|
26
30
|
end
|
27
31
|
|
28
32
|
def cache_exist?(path)
|
29
|
-
path && File.exist?(
|
33
|
+
path && File.exist?(downloaded_path(path))
|
34
|
+
end
|
35
|
+
|
36
|
+
def downloaded_path(path)
|
37
|
+
Fontist.downloads_path.join(path)
|
30
38
|
end
|
31
39
|
|
32
|
-
def
|
40
|
+
def print_bar(bar, path)
|
41
|
+
File.size(downloaded_path(path)).tap do |size|
|
42
|
+
bar.total = size
|
43
|
+
bar.increment(size)
|
44
|
+
bar.finish("cache")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def save_cache(generated_file, key)
|
33
49
|
path = move_to_downloads(generated_file)
|
50
|
+
|
51
|
+
map = load_cache
|
34
52
|
map[key] = path
|
35
53
|
File.write(cache_map_path, YAML.dump(map))
|
54
|
+
|
36
55
|
path
|
37
56
|
end
|
38
57
|
|
@@ -7,13 +7,15 @@ module Fontist
|
|
7
7
|
# TODO: If the first mirror fails, try the second one
|
8
8
|
@file = file
|
9
9
|
@sha = [sha].flatten.compact
|
10
|
-
@progress_bar = set_progress_bar(progress_bar)
|
11
10
|
@file_size = (file_size || default_file_size).to_i
|
11
|
+
@progress_bar = set_progress_bar(progress_bar)
|
12
12
|
@cache = Cache.new
|
13
13
|
end
|
14
14
|
|
15
15
|
def download
|
16
|
-
file = @cache.fetch(@file
|
16
|
+
file = @cache.fetch(@file, bar: @progress_bar) do
|
17
|
+
download_file
|
18
|
+
end
|
17
19
|
|
18
20
|
if !sha.empty? && !sha.include?(Digest::SHA256.file(file).to_s)
|
19
21
|
raise(Fontist::Errors::TamperedFileError.new(
|
@@ -46,25 +48,27 @@ module Fontist
|
|
46
48
|
end
|
47
49
|
|
48
50
|
def set_progress_bar(progress_bar)
|
49
|
-
ENV.fetch("TEST_ENV", "") === "CI"
|
51
|
+
if ENV.fetch("TEST_ENV", "") === "CI" || progress_bar
|
52
|
+
ProgressBar.new(@file_size)
|
53
|
+
else
|
54
|
+
NullProgressBar.new
|
55
|
+
end
|
50
56
|
end
|
51
57
|
|
52
58
|
def download_file
|
53
|
-
bar = ProgressBar.new(file_size / byte_to_megabyte)
|
54
|
-
|
55
59
|
file = Down.download(
|
56
60
|
@file,
|
57
61
|
open_timeout: 10,
|
58
62
|
read_timeout: 10,
|
59
63
|
content_length_proc: ->(content_length) {
|
60
|
-
|
64
|
+
@progress_bar.total = content_length if content_length
|
61
65
|
},
|
62
66
|
progress_proc: -> (progress) {
|
63
|
-
|
67
|
+
@progress_bar.increment(progress)
|
64
68
|
}
|
65
69
|
)
|
66
70
|
|
67
|
-
|
71
|
+
@progress_bar.finish
|
68
72
|
|
69
73
|
file
|
70
74
|
rescue Down::NotFound
|
@@ -72,6 +76,20 @@ module Fontist
|
|
72
76
|
end
|
73
77
|
end
|
74
78
|
|
79
|
+
class NullProgressBar
|
80
|
+
def total=(_)
|
81
|
+
# do nothing
|
82
|
+
end
|
83
|
+
|
84
|
+
def increment(_)
|
85
|
+
# do nothing
|
86
|
+
end
|
87
|
+
|
88
|
+
def finish(_ = nil)
|
89
|
+
# do nothing
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
75
93
|
class ProgressBar
|
76
94
|
def initialize(total)
|
77
95
|
@counter = 1
|
@@ -83,9 +101,35 @@ module Fontist
|
|
83
101
|
end
|
84
102
|
|
85
103
|
def increment(progress)
|
86
|
-
complete = sprintf("%#.2f%%", ((@counter.to_f / @total.to_f) * 100))
|
87
|
-
print "\r\e[0KDownloads: #{@counter}MB/#{@total}MB (#{complete})"
|
88
104
|
@counter = progress
|
105
|
+
Fontist.ui.print "\r\e[0KDownloads: #{counter_mb}MB/#{total_mb}MB " \
|
106
|
+
"(#{completeness})"
|
107
|
+
end
|
108
|
+
|
109
|
+
def finish(message = nil)
|
110
|
+
if message
|
111
|
+
Fontist.ui.print " (#{message})\n"
|
112
|
+
else
|
113
|
+
Fontist.ui.print "\n"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def completeness
|
120
|
+
sprintf("%#.2f%%", (@counter.fdiv(@total) * 100)) # rubocop:disable Style/FormatStringToken, Metrics/LineLength
|
121
|
+
end
|
122
|
+
|
123
|
+
def counter_mb
|
124
|
+
@counter / byte_to_megabyte
|
125
|
+
end
|
126
|
+
|
127
|
+
def total_mb
|
128
|
+
@total / byte_to_megabyte
|
129
|
+
end
|
130
|
+
|
131
|
+
def byte_to_megabyte
|
132
|
+
@byte_to_megabyte ||= 1024 * 1024
|
89
133
|
end
|
90
134
|
end
|
91
135
|
end
|