fontist 1.7.2 → 1.8.4
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/release.yml +38 -0
- data/.github/workflows/rspec.yml +58 -0
- data/README.md +77 -14
- data/{bin → exe}/fontist +0 -0
- data/fontist.gemspec +10 -7
- data/lib/fontist.rb +5 -2
- data/lib/fontist/cli.rb +65 -41
- data/lib/fontist/errors.rb +63 -12
- data/lib/fontist/font.rb +23 -37
- data/lib/fontist/font_installer.rb +118 -0
- data/lib/fontist/fontist_font.rb +3 -49
- data/lib/fontist/formula.rb +101 -35
- data/lib/fontist/formula_paths.rb +43 -0
- data/lib/fontist/helpers.rb +7 -0
- data/lib/fontist/import/create_formula.rb +3 -2
- data/lib/fontist/import/extractors.rb +4 -0
- data/lib/fontist/import/extractors/cpio_extractor.rb +39 -0
- data/lib/fontist/import/extractors/gzip_extractor.rb +27 -0
- data/lib/fontist/import/extractors/rpm_extractor.rb +45 -0
- data/lib/fontist/import/extractors/tar_extractor.rb +47 -0
- data/lib/fontist/import/google/skiplist.yml +3 -0
- data/lib/fontist/import/google_check.rb +1 -1
- data/lib/fontist/import/google_import.rb +3 -4
- data/lib/fontist/import/otfinfo_generate.rb +1 -1
- data/lib/fontist/import/recursive_extraction.rb +26 -8
- data/lib/fontist/import/sil_import.rb +99 -0
- data/lib/fontist/index.rb +72 -0
- data/lib/fontist/index_formula.rb +30 -0
- data/lib/fontist/manifest/install.rb +4 -9
- data/lib/fontist/manifest/locations.rb +28 -20
- data/lib/fontist/system_font.rb +32 -62
- data/lib/fontist/system_index.rb +47 -5
- data/lib/fontist/utils.rb +5 -0
- data/lib/fontist/utils/cache.rb +12 -4
- data/lib/fontist/utils/cpio/cpio.rb +199 -0
- data/lib/fontist/utils/cpio_extractor.rb +47 -0
- data/lib/fontist/utils/exe_extractor.rb +1 -1
- data/lib/fontist/utils/gzip_extractor.rb +24 -0
- data/lib/fontist/utils/locking.rb +17 -0
- data/lib/fontist/utils/rpm_extractor.rb +37 -0
- data/lib/fontist/utils/tar_extractor.rb +61 -0
- data/lib/fontist/utils/zip_extractor.rb +1 -1
- data/lib/fontist/version.rb +1 -1
- metadata +68 -24
- 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 -169
- data/lib/fontist/formula_template.rb +0 -122
- data/lib/fontist/formulas.rb +0 -56
- 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
|
@@ -50,9 +50,10 @@ module Fontist
|
|
50
50
|
|
51
51
|
def save(hash)
|
52
52
|
filename = Import.name_to_filename(hash[:name])
|
53
|
+
path = @options[:formula_dir] ? File.join(@options[:formula_dir], filename) : filename
|
53
54
|
yaml = YAML.dump(Helpers::HashHelper.stringify_keys(hash))
|
54
|
-
File.write(
|
55
|
-
|
55
|
+
File.write(path, yaml)
|
56
|
+
path
|
56
57
|
end
|
57
58
|
end
|
58
59
|
end
|
@@ -3,3 +3,7 @@ require_relative "extractors/zip_extractor"
|
|
3
3
|
require_relative "extractors/ole_extractor"
|
4
4
|
require_relative "extractors/seven_zip_extractor"
|
5
5
|
require_relative "extractors/cab_extractor"
|
6
|
+
require_relative "extractors/rpm_extractor"
|
7
|
+
require_relative "extractors/gzip_extractor"
|
8
|
+
require_relative "extractors/cpio_extractor"
|
9
|
+
require_relative "extractors/tar_extractor"
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Fontist
|
2
|
+
module Import
|
3
|
+
module Extractors
|
4
|
+
class CpioExtractor < Extractor
|
5
|
+
def extract
|
6
|
+
dir = Dir.mktmpdir
|
7
|
+
extract_cpio(@archive, dir)
|
8
|
+
dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def format
|
12
|
+
"cpio"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def extract_cpio(archive, dir)
|
18
|
+
archive_file = File.open(archive, "rb")
|
19
|
+
|
20
|
+
reader_class.new(archive_file).each do |entry, file|
|
21
|
+
path = File.join(dir, entry.name)
|
22
|
+
if entry.directory?
|
23
|
+
FileUtils.mkdir_p(path)
|
24
|
+
else
|
25
|
+
File.write(path, file.read)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def reader_class
|
31
|
+
@reader_class ||= begin
|
32
|
+
require "fontist/utils/cpio/cpio"
|
33
|
+
CPIO::ASCIIReader
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Fontist
|
2
|
+
module Import
|
3
|
+
module Extractors
|
4
|
+
class GzipExtractor < Extractor
|
5
|
+
def extract
|
6
|
+
dir = Dir.mktmpdir
|
7
|
+
extract_gzip(@archive, dir)
|
8
|
+
dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def format
|
12
|
+
"gzip"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def extract_gzip(archive, dir)
|
18
|
+
Zlib::GzipReader.open(archive) do |gz|
|
19
|
+
basename = File.basename(archive, ".*")
|
20
|
+
path = File.join(dir, basename)
|
21
|
+
File.write(path, gz.read)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Fontist
|
2
|
+
module Import
|
3
|
+
module Extractors
|
4
|
+
class RpmExtractor < Extractor
|
5
|
+
def extract
|
6
|
+
dir = Dir.mktmpdir
|
7
|
+
extract_rpm(@archive, dir)
|
8
|
+
dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def format
|
12
|
+
"rpm"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def extract_rpm(archive, dir)
|
18
|
+
file = File.open(archive, "rb")
|
19
|
+
rpm = rpm_class.new(file)
|
20
|
+
content = rpm.payload.read
|
21
|
+
path = target_path(archive, rpm.tags, dir)
|
22
|
+
|
23
|
+
File.write(path, content)
|
24
|
+
ensure
|
25
|
+
file.close
|
26
|
+
end
|
27
|
+
|
28
|
+
def rpm_class
|
29
|
+
@rpm_class ||= begin
|
30
|
+
require "arr-pm"
|
31
|
+
RPM::File
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def target_path(archive, tags, dir)
|
36
|
+
archive_format = tags[:payloadformat]
|
37
|
+
compression_format = tags[:payloadcompressor] == "gzip" ? "gz" : tags[:payloadcompressor]
|
38
|
+
basename = File.basename(archive, ".*")
|
39
|
+
filename = basename + "." + archive_format + "." + compression_format
|
40
|
+
File.join(dir, filename)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Fontist
|
2
|
+
module Import
|
3
|
+
module Extractors
|
4
|
+
class TarExtractor < Extractor
|
5
|
+
def extract
|
6
|
+
dir = Dir.mktmpdir
|
7
|
+
extract_tar(@archive, dir)
|
8
|
+
dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def format
|
12
|
+
"tar"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def extract_tar(archive, dir)
|
18
|
+
archive_file = File.open(archive, "rb")
|
19
|
+
reader_class.new(archive_file) do |tar|
|
20
|
+
tar.each do |tarfile|
|
21
|
+
save_tar_file(tarfile, dir)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def reader_class
|
27
|
+
@reader_class ||= begin
|
28
|
+
require "rubygems/package"
|
29
|
+
Gem::Package::TarReader
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def save_tar_file(file, dir)
|
34
|
+
path = File.join(dir, file.full_name)
|
35
|
+
|
36
|
+
if file.directory?
|
37
|
+
FileUtils.mkdir_p(path)
|
38
|
+
else
|
39
|
+
File.open(path, "wb") do |f|
|
40
|
+
f.print(file.read)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
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
|
@@ -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)
|
@@ -7,7 +7,7 @@ module Fontist
|
|
7
7
|
class RecursiveExtraction
|
8
8
|
FONTS_PATTERN = "**/*.{ttf,otf,ttc}".freeze
|
9
9
|
ARCHIVE_EXTENSIONS = %w[zip msi exe cab].freeze
|
10
|
-
LICENSE_PATTERN = /(
|
10
|
+
LICENSE_PATTERN = /(ofl\.txt|ufl\.txt|licenses?\.txt|copying)$/i.freeze
|
11
11
|
|
12
12
|
def initialize(archive, subarchive: nil, subdir: nil)
|
13
13
|
@archive = archive
|
@@ -19,7 +19,7 @@ module Fontist
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def extension
|
22
|
-
|
22
|
+
fetch_extension(@archive)
|
23
23
|
end
|
24
24
|
|
25
25
|
def font_files
|
@@ -44,6 +44,10 @@ module Fontist
|
|
44
44
|
|
45
45
|
private
|
46
46
|
|
47
|
+
def fetch_extension(file)
|
48
|
+
File.extname(filename(file)).sub(/^\./, "")
|
49
|
+
end
|
50
|
+
|
47
51
|
def filename(file)
|
48
52
|
if file.respond_to?(:original_filename)
|
49
53
|
file.original_filename
|
@@ -82,16 +86,26 @@ module Fontist
|
|
82
86
|
|
83
87
|
# rubocop:disable Metrics/MethodLength
|
84
88
|
def choose_extractor(archive)
|
85
|
-
case
|
86
|
-
when
|
89
|
+
case fetch_extension(archive).downcase
|
90
|
+
when "msi"
|
87
91
|
Extractors::OleExtractor.new(archive)
|
88
|
-
when
|
92
|
+
when "cab"
|
89
93
|
Extractors::CabExtractor.new(archive)
|
90
|
-
when
|
94
|
+
when "exe"
|
91
95
|
extractor = Extractors::SevenZipExtractor.new(archive)
|
92
96
|
extractor.try ? extractor : Extractors::CabExtractor.new(archive)
|
93
|
-
|
97
|
+
when "zip"
|
94
98
|
Extractors::ZipExtractor.new(archive)
|
99
|
+
when "rpm"
|
100
|
+
Extractors::RpmExtractor.new(archive)
|
101
|
+
when "gz"
|
102
|
+
Extractors::GzipExtractor.new(archive)
|
103
|
+
when "cpio"
|
104
|
+
Extractors::CpioExtractor.new(archive)
|
105
|
+
when "tar"
|
106
|
+
Extractors::TarExtractor.new(archive)
|
107
|
+
else
|
108
|
+
raise Errors::UnknownArchiveError, "Could not unarchive `#{filename(archive)}`."
|
95
109
|
end
|
96
110
|
end
|
97
111
|
# rubocop:enable Metrics/MethodLength
|
@@ -118,6 +132,9 @@ module Fontist
|
|
118
132
|
def font_directory?(path, base_path)
|
119
133
|
return true unless @subdir
|
120
134
|
|
135
|
+
# https://bugs.ruby-lang.org/issues/10011
|
136
|
+
base_path = Pathname.new(base_path)
|
137
|
+
|
121
138
|
relative_path = Pathname.new(path).relative_path_from(base_path).to_s
|
122
139
|
dirname = File.dirname(relative_path)
|
123
140
|
normalized_pattern = @subdir.chomp("/")
|
@@ -147,7 +164,8 @@ module Fontist
|
|
147
164
|
end
|
148
165
|
|
149
166
|
def find_archive(path)
|
150
|
-
|
167
|
+
children = Dir.entries(path) - [".", ".."] # ruby 2.4 compat
|
168
|
+
paths = children.map { |file| File.join(path, file) }
|
151
169
|
by_subarchive(paths) || by_size(paths)
|
152
170
|
end
|
153
171
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
require "fontist/import/create_formula"
|
3
|
+
|
4
|
+
module Fontist
|
5
|
+
module Import
|
6
|
+
class SilImport
|
7
|
+
ROOT = "https://software.sil.org/fonts/".freeze
|
8
|
+
|
9
|
+
def call
|
10
|
+
links = font_links
|
11
|
+
Fontist.ui.success("Found #{links.size} links.")
|
12
|
+
|
13
|
+
paths = []
|
14
|
+
links.each do |link|
|
15
|
+
path = create_formula_by_page_link(link)
|
16
|
+
paths << path if path
|
17
|
+
end
|
18
|
+
|
19
|
+
Fontist::Index.rebuild
|
20
|
+
|
21
|
+
Fontist.ui.success("Created #{paths.size} formulas.")
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def font_links
|
27
|
+
html = URI.parse(ROOT).open.read
|
28
|
+
document = Nokogiri::HTML.parse(html)
|
29
|
+
document.css("table.products div.title > a")
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_formula_by_page_link(link)
|
33
|
+
url = find_archive_url_by_page_link(link)
|
34
|
+
return unless url
|
35
|
+
|
36
|
+
create_formula_by_archive_url(url)
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_formula_by_archive_url(url)
|
40
|
+
path = Fontist::Import::CreateFormula.new(url, formula_dir: formula_dir).call
|
41
|
+
Fontist.ui.success("Formula has been successfully created: #{path}")
|
42
|
+
|
43
|
+
path
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_archive_url_by_page_link(link)
|
47
|
+
Fontist.ui.print("Searching for an archive of #{link.content}... ")
|
48
|
+
|
49
|
+
page_uri = URI.join(ROOT, link[:href])
|
50
|
+
archive_uri = find_archive_url_by_page_uri(page_uri)
|
51
|
+
unless archive_uri
|
52
|
+
Fontist.ui.error("NOT FOUND")
|
53
|
+
return
|
54
|
+
end
|
55
|
+
|
56
|
+
Fontist.ui.success("DONE")
|
57
|
+
|
58
|
+
archive_uri.to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_archive_url_by_page_uri(uri)
|
62
|
+
response = uri.open
|
63
|
+
current_url = response.base_uri
|
64
|
+
html = response.read
|
65
|
+
document = Nokogiri::HTML.parse(html)
|
66
|
+
link = find_archive_link(document)
|
67
|
+
return URI.join(current_url, link[:href]) if link
|
68
|
+
|
69
|
+
page_link = find_download_page(document)
|
70
|
+
return unless page_link
|
71
|
+
|
72
|
+
page_uri = URI.join(current_url, page_link[:href])
|
73
|
+
find_archive_url_by_page_uri(page_uri)
|
74
|
+
end
|
75
|
+
|
76
|
+
def find_archive_link(document)
|
77
|
+
links = document.css("a.btn-download")
|
78
|
+
download_links = links.select { |tag| tag.content.include?("DOWNLOAD CURRENT VERSION") }
|
79
|
+
return download_links.first unless download_links.empty?
|
80
|
+
|
81
|
+
links = document.css("a")
|
82
|
+
download_links = links.select { |tag| tag.content.match?(/Download.*\.zip/) }
|
83
|
+
download_links.first
|
84
|
+
end
|
85
|
+
|
86
|
+
def find_download_page(document)
|
87
|
+
links = document.css("a.btn-download")
|
88
|
+
page_links = links.select { |tag| tag.content == "DOWNLOADS" }
|
89
|
+
page_links.first
|
90
|
+
end
|
91
|
+
|
92
|
+
def formula_dir
|
93
|
+
@formula_dir ||= Fontist.formulas_path.join("sil").tap do |path|
|
94
|
+
FileUtils.mkdir_p(path) unless File.exist?(path)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|