fontist 1.6.0 → 1.8.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 +5 -5
- data/.github/workflows/release.yml +38 -0
- data/.github/workflows/rspec.yml +58 -0
- data/README.md +109 -32
- data/{bin → exe}/fontist +0 -0
- data/fontist.gemspec +4 -2
- data/lib/fontist.rb +10 -3
- data/lib/fontist/cli.rb +63 -42
- data/lib/fontist/errors.rb +14 -11
- data/lib/fontist/font.rb +25 -27
- data/lib/fontist/font_installer.rb +114 -0
- data/lib/fontist/fontist_font.rb +3 -49
- data/lib/fontist/formula.rb +89 -63
- data/lib/fontist/formula_paths.rb +43 -0
- data/lib/fontist/helpers.rb +7 -0
- 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 +7 -3
- data/lib/fontist/import/google_check.rb +1 -1
- data/lib/fontist/import/google_import.rb +3 -4
- data/lib/fontist/import/otf/font_file.rb +17 -3
- data/lib/fontist/import/otfinfo_generate.rb +1 -1
- data/lib/fontist/import/recursive_extraction.rb +74 -13
- data/lib/fontist/index.rb +72 -0
- data/lib/fontist/index_formula.rb +30 -0
- data/lib/fontist/manifest/install.rb +6 -15
- data/lib/fontist/manifest/locations.rb +59 -4
- data/lib/fontist/system_font.rb +22 -49
- data/lib/fontist/system_index.rb +92 -0
- data/lib/fontist/utils.rb +1 -0
- 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 +6 -5
- data/lib/fontist/utils/zip_extractor.rb +20 -12
- data/lib/fontist/version.rb +1 -1
- metadata +45 -20
- data/.github/workflows/macosx.yml +0 -33
- data/.github/workflows/ubuntu.yml +0 -30
- data/.github/workflows/windows.yml +0 -32
- data/bin/check_google +0 -8
- data/bin/console +0 -11
- data/bin/convert_formulas +0 -8
- data/bin/generate_otfinfo +0 -8
- data/bin/import_google +0 -8
- data/bin/rspec +0 -29
- data/bin/setup +0 -7
- data/lib/fontist/font_formula.rb +0 -130
- data/lib/fontist/formula_template.rb +0 -108
- data/lib/fontist/formulas.rb +0 -56
- data/lib/fontist/manifest/common.rb +0 -60
- data/lib/fontist/registry.rb +0 -43
@@ -0,0 +1,43 @@
|
|
1
|
+
module Fontist
|
2
|
+
class FormulaPaths
|
3
|
+
attr_reader :font_paths
|
4
|
+
|
5
|
+
def initialize(font_paths)
|
6
|
+
@font_paths = font_paths
|
7
|
+
end
|
8
|
+
|
9
|
+
def find(font, style = nil)
|
10
|
+
styles = find_styles_by_formulas(font, style)
|
11
|
+
return if styles.empty?
|
12
|
+
|
13
|
+
fonts = styles.uniq { |s| s["font"] }.flat_map do |s|
|
14
|
+
paths = search_font_paths(s["font"])
|
15
|
+
paths.map do |path|
|
16
|
+
{ full_name: s["full_name"],
|
17
|
+
path: path }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
fonts.empty? ? nil : fonts
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def find_styles_by_formulas(font, style)
|
27
|
+
if style
|
28
|
+
Formula.find_styles(font, style)
|
29
|
+
else
|
30
|
+
fonts = Formula.find_fonts(font)
|
31
|
+
return [] unless fonts
|
32
|
+
|
33
|
+
fonts.flat_map(&:styles)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def search_font_paths(filename)
|
38
|
+
font_paths.select do |path|
|
39
|
+
File.basename(path) == filename
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -8,10 +8,6 @@ require_relative "formula_builder"
|
|
8
8
|
module Fontist
|
9
9
|
module Import
|
10
10
|
class CreateFormula
|
11
|
-
FONT_PATTERN = /(\.ttf|\.otf)/i.freeze
|
12
|
-
FONT_COLLECTION_PATTERN = /\.ttc/i.freeze
|
13
|
-
LICENSE_PATTERN = /(OFL\.txt|UFL\.txt|LICENSE\.txt|COPYING)$/i.freeze
|
14
|
-
|
15
11
|
def initialize(url, options = {})
|
16
12
|
@url = url
|
17
13
|
@options = options
|
@@ -26,41 +22,30 @@ module Fontist
|
|
26
22
|
def formula
|
27
23
|
builder = FormulaBuilder.new
|
28
24
|
builder.url = @url
|
29
|
-
builder.archive =
|
30
|
-
builder.extractor = extractor
|
25
|
+
builder.archive = archive
|
26
|
+
builder.extractor = extractor
|
31
27
|
builder.options = @options
|
32
|
-
builder.font_files = font_files
|
33
|
-
builder.font_collection_files = font_collection_files
|
34
|
-
builder.license_text =
|
28
|
+
builder.font_files = extractor.font_files
|
29
|
+
builder.font_collection_files = extractor.font_collection_files
|
30
|
+
builder.license_text = extractor.license_text
|
35
31
|
builder.formula
|
36
32
|
end
|
37
33
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
def extractor
|
35
|
+
@extractor ||=
|
36
|
+
RecursiveExtraction.new(archive,
|
37
|
+
subarchive: @options[:subarchive],
|
38
|
+
subdir: @options[:subdir])
|
42
39
|
end
|
43
40
|
|
44
|
-
def
|
45
|
-
|
41
|
+
def archive
|
42
|
+
@archive ||= download(@url)
|
46
43
|
end
|
47
44
|
|
48
|
-
def
|
49
|
-
|
50
|
-
Otf::FontFile.new(path)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def font_collection_files(extractor)
|
55
|
-
extractor.extract(FONT_COLLECTION_PATTERN) do |path|
|
56
|
-
Files::CollectionFile.new(path)
|
57
|
-
end
|
58
|
-
end
|
45
|
+
def download(url)
|
46
|
+
return url if File.exist?(url)
|
59
47
|
|
60
|
-
|
61
|
-
extractor.extract(LICENSE_PATTERN) do |path|
|
62
|
-
File.read(path)
|
63
|
-
end
|
48
|
+
Fontist::Utils::Downloader.download(url, progress_bar: true).path
|
64
49
|
end
|
65
50
|
|
66
51
|
def save(hash)
|
@@ -11,10 +11,15 @@ module Fontist
|
|
11
11
|
def initialize(path)
|
12
12
|
@path = path
|
13
13
|
@fonts = read
|
14
|
+
@extension = "ttc"
|
14
15
|
end
|
15
16
|
|
16
17
|
def filename
|
17
|
-
File.basename(@path)
|
18
|
+
File.basename(@path, ".*") + "." + @extension
|
19
|
+
end
|
20
|
+
|
21
|
+
def source_filename
|
22
|
+
File.basename(@path) unless filename == File.basename(@path)
|
18
23
|
end
|
19
24
|
|
20
25
|
private
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Fontist
|
2
|
+
module Import
|
3
|
+
module Files
|
4
|
+
class FileRequirement
|
5
|
+
def initialize
|
6
|
+
`file -v`
|
7
|
+
rescue Errno::ENOENT
|
8
|
+
abort "`file` is not available. (Or is PATH not setup properly?)"
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(path)
|
12
|
+
Helpers::SystemHelper.run("file --brief '#{path}'")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative "file_requirement"
|
2
|
+
|
3
|
+
module Fontist
|
4
|
+
module Import
|
5
|
+
module Files
|
6
|
+
class FontDetector
|
7
|
+
REQUIREMENTS = { file: FileRequirement.new }.freeze
|
8
|
+
|
9
|
+
FONT_LABELS = ["OpenType font data",
|
10
|
+
"TrueType Font data"].freeze
|
11
|
+
|
12
|
+
COLLECTION_LABEL = "TrueType font collection data".freeze
|
13
|
+
|
14
|
+
FONT_EXTENSIONS = {
|
15
|
+
"OpenType font data" => "otf",
|
16
|
+
"TrueType Font data" => "ttf",
|
17
|
+
"TrueType font collection data" => "ttc",
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
def self.detect(path)
|
21
|
+
brief = file_brief(path)
|
22
|
+
|
23
|
+
if brief.start_with?(*FONT_LABELS)
|
24
|
+
:font
|
25
|
+
elsif brief.start_with?(COLLECTION_LABEL)
|
26
|
+
:collection
|
27
|
+
else
|
28
|
+
:other
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.standard_extension(path)
|
33
|
+
brief = file_brief(path)
|
34
|
+
|
35
|
+
FONT_EXTENSIONS.each do |label, extension|
|
36
|
+
return extension if brief.start_with?(label)
|
37
|
+
end
|
38
|
+
|
39
|
+
raise Errors::UnknownFontTypeError.new(path)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.file_brief(path)
|
43
|
+
REQUIREMENTS[:file].call(path)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -107,7 +107,10 @@ module Fontist
|
|
107
107
|
|
108
108
|
collections = @font_collection_files.map do |file|
|
109
109
|
fonts = fonts_from_files(file.fonts, :to_collection_style)
|
110
|
-
|
110
|
+
|
111
|
+
{ filename: file.filename,
|
112
|
+
source_filename: file.source_filename,
|
113
|
+
fonts: fonts }.compact
|
111
114
|
end
|
112
115
|
|
113
116
|
collections.sort_by do |x|
|
@@ -152,8 +155,9 @@ module Fontist
|
|
152
155
|
return
|
153
156
|
end
|
154
157
|
|
155
|
-
Fontist.ui.error("WARN: ensure it's an open license, otherwise
|
156
|
-
|
158
|
+
Fontist.ui.error("WARN: ensure it's an open license, otherwise " \
|
159
|
+
"change the 'open_license' attribute to " \
|
160
|
+
"'requires_license_agreement'")
|
157
161
|
|
158
162
|
TextHelper.cleanup(@license_text)
|
159
163
|
end
|
@@ -156,10 +156,9 @@ module Fontist
|
|
156
156
|
h.merge!(family_name: style.family_name,
|
157
157
|
type: style.style,
|
158
158
|
full_name: style.full_name)
|
159
|
-
h.merge!(style.to_h.
|
160
|
-
|
161
|
-
|
162
|
-
:copyright).compact)
|
159
|
+
h.merge!(style.to_h.select do |k, _|
|
160
|
+
%i(post_script_name version description copyright).include?(k)
|
161
|
+
end.compact)
|
163
162
|
h.merge!(font: fix_variable_filename(style.filename))
|
164
163
|
end
|
165
164
|
end
|
@@ -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
|
@@ -85,6 +95,10 @@ module Fontist
|
|
85
95
|
.map { |x| x.map { |y| Fontist::Import::TextHelper.cleanup(y) } }
|
86
96
|
.to_h
|
87
97
|
end
|
98
|
+
|
99
|
+
def detect_extension
|
100
|
+
Files::FontDetector.standard_extension(@path)
|
101
|
+
end
|
88
102
|
end
|
89
103
|
end
|
90
104
|
end
|
@@ -25,7 +25,7 @@ module Fontist
|
|
25
25
|
def font_paths(font)
|
26
26
|
formula = Fontist::Formula.find(font)
|
27
27
|
font_formula = Object.const_get(formula.installer)
|
28
|
-
font_formula.
|
28
|
+
font_formula.install(confirmation: "yes")
|
29
29
|
end
|
30
30
|
|
31
31
|
def generate_styles(paths)
|
@@ -1,28 +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, subarchive: nil)
|
12
|
+
def initialize(archive, subarchive: nil, subdir: nil)
|
11
13
|
@archive = archive
|
12
14
|
@subarchive = subarchive
|
15
|
+
@subdir = subdir
|
13
16
|
@operations = []
|
17
|
+
@font_files = []
|
18
|
+
@collection_files = []
|
14
19
|
end
|
15
20
|
|
16
21
|
def extension
|
17
22
|
File.extname(filename(@archive)).sub(/^\./, "")
|
18
23
|
end
|
19
24
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
26
38
|
end
|
27
39
|
|
28
40
|
def operations
|
@@ -50,7 +62,11 @@ module Fontist
|
|
50
62
|
|
51
63
|
def extract_recursively(archive)
|
52
64
|
path = operate_on_archive(archive)
|
53
|
-
|
65
|
+
match_files(path)
|
66
|
+
if matched?
|
67
|
+
save_operation_subdir
|
68
|
+
return path
|
69
|
+
end
|
54
70
|
|
55
71
|
next_archive = find_archive(path)
|
56
72
|
extract_recursively(next_archive)
|
@@ -84,13 +100,58 @@ module Fontist
|
|
84
100
|
@operations << { format: extractor.format }
|
85
101
|
end
|
86
102
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
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
|
+
# https://bugs.ruby-lang.org/issues/10011
|
122
|
+
base_path = Pathname.new(base_path)
|
123
|
+
|
124
|
+
relative_path = Pathname.new(path).relative_path_from(base_path).to_s
|
125
|
+
dirname = File.dirname(relative_path)
|
126
|
+
normalized_pattern = @subdir.chomp("/")
|
127
|
+
File.fnmatch?(normalized_pattern, dirname)
|
128
|
+
end
|
129
|
+
|
130
|
+
def match_font(path)
|
131
|
+
case Files::FontDetector.detect(path)
|
132
|
+
when :font
|
133
|
+
@font_files << Otf::FontFile.new(path)
|
134
|
+
when :collection
|
135
|
+
@collection_files << Files::CollectionFile.new(path)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def matched?
|
140
|
+
[@font_files, @collection_files].any? do |files|
|
141
|
+
files.size.positive?
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def save_operation_subdir
|
146
|
+
return unless @subdir
|
147
|
+
|
148
|
+
@operations.last[:options] ||= {}
|
149
|
+
@operations.last[:options][:fonts_sub_dir] = @subdir
|
90
150
|
end
|
91
151
|
|
92
152
|
def find_archive(path)
|
93
|
-
|
153
|
+
children = Dir.entries(path) - [".", ".."] # ruby 2.4 compat
|
154
|
+
paths = children.map { |file| File.join(path, file) }
|
94
155
|
by_subarchive(paths) || by_size(paths)
|
95
156
|
end
|
96
157
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require_relative "index_formula"
|
2
|
+
|
3
|
+
module Fontist
|
4
|
+
class Index
|
5
|
+
def self.from_yaml
|
6
|
+
unless File.exist?(Fontist.formula_index_path)
|
7
|
+
raise Errors::FormulaIndexNotFoundError.new("Please fetch index with `fontist update`.")
|
8
|
+
end
|
9
|
+
|
10
|
+
data = YAML.load_file(Fontist.formula_index_path)
|
11
|
+
new(data)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.rebuild
|
15
|
+
index = new
|
16
|
+
index.build
|
17
|
+
index.to_yaml
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(data = {})
|
21
|
+
@index = {}
|
22
|
+
|
23
|
+
data.each_pair do |font, paths|
|
24
|
+
paths.each do |path|
|
25
|
+
add_index_formula(font, IndexFormula.new(path))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def build
|
31
|
+
Formula.all.each do |formula|
|
32
|
+
add_formula(formula)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_formula(formula)
|
37
|
+
formula.fonts.each do |font|
|
38
|
+
add_index_formula(font.name, formula.to_index_formula)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_index_formula(font_raw, index_formula)
|
43
|
+
font = normalize_font(font_raw)
|
44
|
+
@index[font] ||= []
|
45
|
+
@index[font] << index_formula unless @index[font].include?(index_formula)
|
46
|
+
end
|
47
|
+
|
48
|
+
def load_formulas(font)
|
49
|
+
index_formulas(font).map(&:to_full)
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_yaml
|
53
|
+
File.write(Fontist.formula_index_path, YAML.dump(to_h))
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_h
|
57
|
+
@index.map do |font, index_formulas|
|
58
|
+
[font, index_formulas.map(&:to_s)]
|
59
|
+
end.to_h
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def index_formulas(font)
|
65
|
+
@index[normalize_font(font)] || []
|
66
|
+
end
|
67
|
+
|
68
|
+
def normalize_font(font)
|
69
|
+
font.downcase
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|