fontist 1.4.0 → 1.7.1
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 +176 -6
- data/bin/fontist +1 -2
- data/fontist.gemspec +2 -0
- data/lib/fontist.rb +5 -0
- data/lib/fontist/cli.rb +59 -9
- data/lib/fontist/errors.rb +3 -0
- data/lib/fontist/font.rb +29 -6
- data/lib/fontist/font_formula.rb +35 -4
- data/lib/fontist/formula.rb +16 -1
- data/lib/fontist/formula_template.rb +41 -22
- data/lib/fontist/import/create_formula.rb +15 -30
- data/lib/fontist/import/files/collection_file.rb +11 -7
- 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 +57 -6
- data/lib/fontist/import/google/skiplist.yml +2 -0
- data/lib/fontist/import/helpers/system_helper.rb +4 -1
- data/lib/fontist/import/otf/font_file.rb +20 -4
- data/lib/fontist/import/recursive_extraction.rb +99 -15
- data/lib/fontist/manifest.rb +2 -0
- data/lib/fontist/manifest/install.rb +32 -0
- data/lib/fontist/manifest/locations.rb +60 -0
- data/lib/fontist/system_font.rb +72 -6
- 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 +37 -3
- data/bin/stripttc +0 -0
@@ -4,7 +4,10 @@ module Fontist
|
|
4
4
|
module SystemHelper
|
5
5
|
class << self
|
6
6
|
def run(command)
|
7
|
-
|
7
|
+
unless ENV.fetch("TEST_ENV", "") === "CI"
|
8
|
+
Fontist.ui.say("Run `#{command}`")
|
9
|
+
end
|
10
|
+
|
8
11
|
result = `#{command}`
|
9
12
|
unless $CHILD_STATUS.to_i.zero?
|
10
13
|
raise Errors::BinaryCallError,
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative "../otfinfo/otfinfo_requirement"
|
2
2
|
require_relative "../text_helper"
|
3
|
+
require_relative "../files/font_detector"
|
3
4
|
|
4
5
|
module Fontist
|
5
6
|
module Import
|
@@ -10,14 +11,19 @@ module Fontist
|
|
10
11
|
}.freeze
|
11
12
|
|
12
13
|
STYLE_ATTRIBUTES = %i[family_name type full_name post_script_name
|
13
|
-
version description copyright font
|
14
|
-
|
14
|
+
version description copyright font
|
15
|
+
source_font].freeze
|
16
|
+
|
17
|
+
COLLECTION_ATTRIBUTES = STYLE_ATTRIBUTES.reject do |a|
|
18
|
+
%i[font source_font].include?(a)
|
19
|
+
end
|
15
20
|
|
16
21
|
attr_reader :path
|
17
22
|
|
18
23
|
def initialize(path)
|
19
24
|
@path = path
|
20
25
|
@info = read
|
26
|
+
@extension = detect_extension
|
21
27
|
end
|
22
28
|
|
23
29
|
def to_style
|
@@ -55,7 +61,11 @@ module Fontist
|
|
55
61
|
end
|
56
62
|
|
57
63
|
def font
|
58
|
-
File.basename(@path)
|
64
|
+
File.basename(@path, ".*") + "." + @extension
|
65
|
+
end
|
66
|
+
|
67
|
+
def source_font
|
68
|
+
File.basename(@path) unless font == File.basename(@path)
|
59
69
|
end
|
60
70
|
|
61
71
|
def copyright
|
@@ -77,12 +87,18 @@ module Fontist
|
|
77
87
|
def read
|
78
88
|
text = REQUIREMENTS[:otfinfo].call(@path)
|
79
89
|
|
80
|
-
text
|
90
|
+
text
|
91
|
+
.encode("UTF-8", invalid: :replace, replace: "")
|
92
|
+
.split("\n")
|
81
93
|
.select { |x| x.include?(":") }
|
82
94
|
.map { |x| x.split(":", 2) }
|
83
95
|
.map { |x| x.map { |y| Fontist::Import::TextHelper.cleanup(y) } }
|
84
96
|
.to_h
|
85
97
|
end
|
98
|
+
|
99
|
+
def detect_extension
|
100
|
+
Files::FontDetector.standard_extension(@path)
|
101
|
+
end
|
86
102
|
end
|
87
103
|
end
|
88
104
|
end
|
@@ -1,27 +1,40 @@
|
|
1
1
|
require "find"
|
2
2
|
require_relative "extractors"
|
3
|
+
require_relative "files/font_detector"
|
3
4
|
|
4
5
|
module Fontist
|
5
6
|
module Import
|
6
7
|
class RecursiveExtraction
|
7
|
-
|
8
|
+
FONTS_PATTERN = "**/*.{ttf,otf,ttc}".freeze
|
8
9
|
ARCHIVE_EXTENSIONS = %w[zip msi exe cab].freeze
|
10
|
+
LICENSE_PATTERN = /(OFL\.txt|UFL\.txt|LICENSE\.txt|COPYING)$/i.freeze
|
9
11
|
|
10
|
-
def initialize(archive)
|
12
|
+
def initialize(archive, subarchive: nil, subdir: nil)
|
11
13
|
@archive = archive
|
14
|
+
@subarchive = subarchive
|
15
|
+
@subdir = subdir
|
12
16
|
@operations = []
|
17
|
+
@font_files = []
|
18
|
+
@collection_files = []
|
13
19
|
end
|
14
20
|
|
15
21
|
def extension
|
16
22
|
File.extname(filename(@archive)).sub(/^\./, "")
|
17
23
|
end
|
18
24
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
+
def font_files
|
26
|
+
ensure_extracted
|
27
|
+
@font_files
|
28
|
+
end
|
29
|
+
|
30
|
+
def font_collection_files
|
31
|
+
ensure_extracted
|
32
|
+
@collection_files
|
33
|
+
end
|
34
|
+
|
35
|
+
def license_text
|
36
|
+
ensure_extracted
|
37
|
+
@license_text
|
25
38
|
end
|
26
39
|
|
27
40
|
def operations
|
@@ -49,7 +62,11 @@ module Fontist
|
|
49
62
|
|
50
63
|
def extract_recursively(archive)
|
51
64
|
path = operate_on_archive(archive)
|
52
|
-
|
65
|
+
match_files(path)
|
66
|
+
if matched?
|
67
|
+
save_operation_subdir
|
68
|
+
return path
|
69
|
+
end
|
53
70
|
|
54
71
|
next_archive = find_archive(path)
|
55
72
|
extract_recursively(next_archive)
|
@@ -57,6 +74,8 @@ module Fontist
|
|
57
74
|
|
58
75
|
def operate_on_archive(archive)
|
59
76
|
extractor = choose_extractor(archive)
|
77
|
+
Fontist.ui.say("Extracting #{archive} with #{extractor.class.name}")
|
78
|
+
|
60
79
|
save_operation(extractor)
|
61
80
|
extractor.extract
|
62
81
|
end
|
@@ -81,15 +100,80 @@ module Fontist
|
|
81
100
|
@operations << { format: extractor.format }
|
82
101
|
end
|
83
102
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
103
|
+
def match_files(dir_path)
|
104
|
+
Find.find(dir_path) do |entry_path| # rubocop:disable Style/CollectionMethods
|
105
|
+
match_license(entry_path)
|
106
|
+
match_font(entry_path) if font_directory?(entry_path, dir_path)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def match_license(path)
|
111
|
+
@license_text ||= File.read(path) if license?(path)
|
112
|
+
end
|
113
|
+
|
114
|
+
def license?(file)
|
115
|
+
file.match?(LICENSE_PATTERN)
|
116
|
+
end
|
117
|
+
|
118
|
+
def font_directory?(path, base_path)
|
119
|
+
return true unless @subdir
|
120
|
+
|
121
|
+
relative_path = Pathname.new(path).relative_path_from(base_path).to_s
|
122
|
+
dirname = File.dirname(relative_path)
|
123
|
+
normalized_pattern = @subdir.chomp("/")
|
124
|
+
File.fnmatch?(normalized_pattern, dirname)
|
125
|
+
end
|
126
|
+
|
127
|
+
def match_font(path)
|
128
|
+
case Files::FontDetector.detect(path)
|
129
|
+
when :font
|
130
|
+
@font_files << Otf::FontFile.new(path)
|
131
|
+
when :collection
|
132
|
+
@collection_files << Files::CollectionFile.new(path)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def matched?
|
137
|
+
[@font_files, @collection_files].any? do |files|
|
138
|
+
files.size.positive?
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def save_operation_subdir
|
143
|
+
return unless @subdir
|
144
|
+
|
145
|
+
@operations.last[:options] ||= {}
|
146
|
+
@operations.last[:options][:fonts_sub_dir] = @subdir
|
87
147
|
end
|
88
148
|
|
89
149
|
def find_archive(path)
|
90
|
-
Dir.children(path)
|
91
|
-
|
92
|
-
|
150
|
+
paths = Dir.children(path).map { |file| File.join(path, file) }
|
151
|
+
by_subarchive(paths) || by_size(paths)
|
152
|
+
end
|
153
|
+
|
154
|
+
def by_subarchive(paths)
|
155
|
+
return unless @subarchive
|
156
|
+
|
157
|
+
path_found = paths.detect do |path|
|
158
|
+
@subarchive == File.basename(path)
|
159
|
+
end
|
160
|
+
|
161
|
+
return unless path_found
|
162
|
+
|
163
|
+
save_operation_subarchive(path_found)
|
164
|
+
|
165
|
+
path_found
|
166
|
+
end
|
167
|
+
|
168
|
+
def save_operation_subarchive(path)
|
169
|
+
@operations.last[:options] ||= {}
|
170
|
+
@operations.last[:options][:subarchive] = File.basename(path)
|
171
|
+
end
|
172
|
+
|
173
|
+
def by_size(paths)
|
174
|
+
paths.max_by do |path|
|
175
|
+
[file_type(path), File.size(path)]
|
176
|
+
end
|
93
177
|
end
|
94
178
|
|
95
179
|
def file_type(file_path)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative "locations"
|
2
|
+
|
3
|
+
module Fontist
|
4
|
+
module Manifest
|
5
|
+
class Install < Locations
|
6
|
+
def initialize(manifest, confirmation: "no")
|
7
|
+
@manifest = manifest
|
8
|
+
@confirmation = confirmation
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.call(manifest, confirmation: "no")
|
12
|
+
new(manifest, confirmation: confirmation).call
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def file_paths(font, style)
|
18
|
+
paths = super
|
19
|
+
return paths unless paths["paths"].empty?
|
20
|
+
|
21
|
+
install_font(font)
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def install_font(font)
|
26
|
+
Fontist::Font.try_install(font, confirmation: @confirmation)
|
27
|
+
rescue Fontist::Errors::LicensingError
|
28
|
+
[] # try to install other fonts
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Fontist
|
2
|
+
module Manifest
|
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
|
+
|
16
|
+
private
|
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
|
+
|
55
|
+
def file_paths(font, style)
|
56
|
+
Fontist::SystemFont.find_with_name(font, style).transform_keys(&:to_s)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/fontist/system_font.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
require_relative "system_index"
|
2
|
+
|
1
3
|
module Fontist
|
2
4
|
class SystemFont
|
3
|
-
def initialize(font:, sources: nil)
|
5
|
+
def initialize(font:, style: nil, sources: nil)
|
4
6
|
@font = font
|
7
|
+
@style = style
|
5
8
|
@user_sources = sources || []
|
6
9
|
end
|
7
10
|
|
@@ -9,6 +12,10 @@ module Fontist
|
|
9
12
|
new(font: font, sources: sources).find
|
10
13
|
end
|
11
14
|
|
15
|
+
def self.find_with_name(font, style)
|
16
|
+
new(font: font, style: style).find_with_name
|
17
|
+
end
|
18
|
+
|
12
19
|
def find
|
13
20
|
paths = grep_font_paths(font)
|
14
21
|
paths = lookup_using_font_name || [] if paths.empty?
|
@@ -16,9 +23,17 @@ module Fontist
|
|
16
23
|
paths.empty? ? nil : paths
|
17
24
|
end
|
18
25
|
|
26
|
+
def find_with_name
|
27
|
+
styles = find_styles
|
28
|
+
return { full_name: nil, paths: [] } unless styles
|
29
|
+
|
30
|
+
{ full_name: styles.first[:full_name],
|
31
|
+
paths: styles.map { |x| x[:path] } }
|
32
|
+
end
|
33
|
+
|
19
34
|
private
|
20
35
|
|
21
|
-
attr_reader :font, :user_sources
|
36
|
+
attr_reader :font, :style, :user_sources
|
22
37
|
|
23
38
|
def normalize_default_paths
|
24
39
|
@normalize_default_paths ||= default_sources["paths"].map do |path|
|
@@ -30,15 +45,27 @@ module Fontist
|
|
30
45
|
end
|
31
46
|
end
|
32
47
|
|
33
|
-
def grep_font_paths(font)
|
48
|
+
def grep_font_paths(font, style = nil)
|
49
|
+
pattern = prepare_pattern(font, style)
|
50
|
+
|
34
51
|
paths = font_paths.map { |path| [File.basename(path), path] }.to_h
|
35
52
|
files = paths.keys
|
36
|
-
matched = files.grep(
|
53
|
+
matched = files.grep(pattern)
|
37
54
|
paths.values_at(*matched).compact
|
38
55
|
end
|
39
56
|
|
57
|
+
def prepare_pattern(font, style = nil)
|
58
|
+
style = nil if style&.casecmp?("regular")
|
59
|
+
|
60
|
+
s = [font, style].compact.map { |x| Regexp.quote(x) }
|
61
|
+
.join(".*")
|
62
|
+
.gsub("\\ ", "\s?") # space independent
|
63
|
+
|
64
|
+
Regexp.new(s, Regexp::IGNORECASE)
|
65
|
+
end
|
66
|
+
|
40
67
|
def font_paths
|
41
|
-
Dir.glob((
|
68
|
+
@font_paths ||= Dir.glob((
|
42
69
|
user_sources +
|
43
70
|
normalize_default_paths +
|
44
71
|
[fontist_fonts_path.join("**")]
|
@@ -54,7 +81,6 @@ module Fontist
|
|
54
81
|
@fontist_fonts_path ||= Fontist.fonts_path
|
55
82
|
end
|
56
83
|
|
57
|
-
|
58
84
|
def user_os
|
59
85
|
Fontist::Utils::System.user_os
|
60
86
|
end
|
@@ -71,5 +97,45 @@ module Fontist
|
|
71
97
|
def default_sources
|
72
98
|
@default_sources ||= YAML.load(system_path_file)["system"][user_os.to_s]
|
73
99
|
end
|
100
|
+
|
101
|
+
def find_styles
|
102
|
+
find_by_index || find_by_formulas
|
103
|
+
end
|
104
|
+
|
105
|
+
def find_by_index
|
106
|
+
SystemIndex.new(font_paths).find(font, style)
|
107
|
+
end
|
108
|
+
|
109
|
+
def find_by_formulas
|
110
|
+
styles = find_styles_by_formulas(font, style)
|
111
|
+
return if styles.empty?
|
112
|
+
|
113
|
+
fonts = styles.uniq { |s| s["font"] }.flat_map do |s|
|
114
|
+
paths = search_font_paths(s["font"])
|
115
|
+
paths.map do |path|
|
116
|
+
{ full_name: s["full_name"],
|
117
|
+
path: path }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
fonts.empty? ? nil : fonts
|
122
|
+
end
|
123
|
+
|
124
|
+
def find_styles_by_formulas(font, style)
|
125
|
+
if style
|
126
|
+
Formula.find_styles(font, style)
|
127
|
+
else
|
128
|
+
fonts = Formula.find_fonts(font)
|
129
|
+
return [] unless fonts
|
130
|
+
|
131
|
+
fonts.flat_map(&:styles)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def search_font_paths(filename)
|
136
|
+
font_paths.select do |path|
|
137
|
+
File.basename(path) == filename
|
138
|
+
end
|
139
|
+
end
|
74
140
|
end
|
75
141
|
end
|