fontist 1.9.3 → 1.11.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/metanorma.yml +1 -1
  3. data/.github/workflows/rspec.yml +1 -1
  4. data/README.adoc +804 -0
  5. data/exe/fontist +24 -2
  6. data/fontist.gemspec +3 -2
  7. data/lib/fontist/cli.rb +23 -9
  8. data/lib/fontist/font.rb +8 -3
  9. data/lib/fontist/formula.rb +4 -0
  10. data/lib/fontist/google_cli.rb +29 -0
  11. data/lib/fontist/import/files/collection_file.rb +10 -1
  12. data/lib/fontist/import/formula_builder.rb +30 -5
  13. data/lib/fontist/import/google/new_fonts_fetcher.rb +44 -33
  14. data/lib/fontist/import/google.rb +18 -0
  15. data/lib/fontist/import/google_check.rb +0 -7
  16. data/lib/fontist/import/google_import.rb +26 -138
  17. data/lib/fontist/import/helpers/system_helper.rb +1 -1
  18. data/lib/fontist/import/otf/font_file.rb +17 -4
  19. data/lib/fontist/import/otf_parser.rb +2 -0
  20. data/lib/fontist/import/otf_style.rb +10 -2
  21. data/lib/fontist/import/otfinfo/template.erb +6 -0
  22. data/lib/fontist/import/recursive_extraction.rb +2 -1
  23. data/lib/fontist/index.rb +4 -28
  24. data/lib/fontist/indexes/default_family_font_index.rb +21 -0
  25. data/lib/fontist/indexes/font_index.rb +8 -13
  26. data/lib/fontist/indexes/preferred_family_font_index.rb +24 -0
  27. data/lib/fontist/system_font.rb +6 -16
  28. data/lib/fontist/system_index.rb +108 -29
  29. data/lib/fontist/update.rb +2 -2
  30. data/lib/fontist/utils/downloader.rb +14 -6
  31. data/lib/fontist/version.rb +1 -1
  32. data/lib/fontist.rb +43 -13
  33. metadata +26 -29
  34. data/.github/workflows/check_google.yml +0 -28
  35. data/README.md +0 -570
  36. data/lib/fontist/fontist_font.rb +0 -24
  37. data/lib/fontist/formula_paths.rb +0 -44
  38. data/lib/fontist/import/google/fonts_public.md +0 -10
  39. data/lib/fontist/import/google/fonts_public.pb.rb +0 -71
  40. data/lib/fontist/import/google/fonts_public.proto +0 -46
data/exe/fontist CHANGED
@@ -3,5 +3,27 @@
3
3
  require "fontist"
4
4
  require "fontist/cli"
5
5
 
6
- status_code = Fontist::CLI.start(ARGV)
7
- exit status_code.is_a?(Integer) ? status_code : 1
6
+ fontist_cli = proc {
7
+ status_code = Fontist::CLI.start(ARGV)
8
+ exit status_code.is_a?(Integer) ? status_code : 1
9
+ }
10
+
11
+ if ENV["SOCKS_PROXY"]
12
+ require "socksify"
13
+ require "uri"
14
+ begin
15
+ proxy = URI.parse(ENV["SOCKS_PROXY"])
16
+ if proxy.userinfo
17
+ user, pass = proxy.userinfo.split(":")
18
+ TCPSocket::socks_username = user
19
+ TCPSocket::socks_password = pass
20
+ end
21
+ Socksify::proxy(proxy.host, proxy.port, &fontist_cli)
22
+ rescue URI::InvalidURIError
23
+ warn "Value of ENV.SOCKS_PROXY=#{ENV['SOCKS_PROXY']} is invalid! Droping it"
24
+ ENV.delete("SOCKS_PROXY")
25
+ fontist_cli.call
26
+ end
27
+ else
28
+ fontist_cli.call
29
+ end
data/fontist.gemspec CHANGED
@@ -28,12 +28,12 @@ Gem::Specification.new do |spec|
28
28
  spec.test_files = `git ls-files -- {spec}/*`.split("\n")
29
29
 
30
30
  spec.add_runtime_dependency "down", "~> 5.0"
31
+ spec.add_runtime_dependency "extract_ttc", "~> 0.1"
31
32
  spec.add_runtime_dependency "thor", "~> 1.0.1"
32
33
  spec.add_runtime_dependency "git", "~> 1.0"
33
34
  spec.add_runtime_dependency "ttfunk", "~> 1.6"
34
35
  spec.add_runtime_dependency "excavate", "~> 0.1"
35
36
 
36
- spec.add_development_dependency "extract_ttc", "~> 0.1"
37
37
  spec.add_development_dependency "pry"
38
38
  spec.add_development_dependency "bundler", "~> 2.0"
39
39
  spec.add_development_dependency "gem-release"
@@ -43,5 +43,6 @@ Gem::Specification.new do |spec|
43
43
  spec.add_development_dependency "rubocop", "1.5.2"
44
44
  spec.add_development_dependency "rubocop-rails"
45
45
  spec.add_development_dependency "rubocop-performance"
46
- spec.add_development_dependency "ruby-protocol-buffers", "~> 1.0"
46
+
47
+ spec.add_runtime_dependency "socksify"
47
48
  end
data/lib/fontist/cli.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "thor"
2
2
  require "fontist/repo_cli"
3
+ require "fontist/google_cli"
3
4
 
4
5
  module Fontist
5
6
  class CLI < Thor
@@ -32,6 +33,10 @@ module Fontist
32
33
  false
33
34
  end
34
35
 
36
+ class_option :preferred_family,
37
+ type: :boolean,
38
+ desc: "Use Preferred Family when available"
39
+
35
40
  desc "install FONT", "Install font"
36
41
  option :force, type: :boolean, aliases: :f,
37
42
  desc: "Install even if it's already installed in system"
@@ -39,6 +44,7 @@ module Fontist
39
44
  option :hide_licenses, type: :boolean, desc: "Hide license texts"
40
45
  option :no_progress, type: :boolean, desc: "Hide download progress"
41
46
  def install(font)
47
+ handle_class_options(options)
42
48
  Fontist::Font.install(
43
49
  font,
44
50
  force: options[:force],
@@ -53,6 +59,7 @@ module Fontist
53
59
 
54
60
  desc "uninstall/remove FONT", "Uninstall font by font or formula"
55
61
  def uninstall(font)
62
+ handle_class_options(options)
56
63
  fonts_paths = Fontist::Font.uninstall(font)
57
64
  Fontist.ui.success("These fonts are removed:")
58
65
  Fontist.ui.success(fonts_paths.join("\n"))
@@ -64,6 +71,7 @@ module Fontist
64
71
 
65
72
  desc "status [FONT]", "Show paths of FONT or all fonts"
66
73
  def status(font = nil)
74
+ handle_class_options(options)
67
75
  paths = Fontist::Font.status(font)
68
76
  return error("No font is installed.", STATUS_MISSING_FONT_ERROR) if paths.empty?
69
77
 
@@ -74,6 +82,7 @@ module Fontist
74
82
 
75
83
  desc "list [FONT]", "List installation status of FONT or fonts in fontist"
76
84
  def list(font = nil)
85
+ handle_class_options(options)
77
86
  formulas = Fontist::Font.list(font)
78
87
  print_list(formulas)
79
88
  success
@@ -83,6 +92,7 @@ module Fontist
83
92
 
84
93
  desc "update", "Update formulas"
85
94
  def update
95
+ handle_class_options(options)
86
96
  Formula.update_formulas_repo
87
97
  Fontist.ui.success("Formulas have been successfully updated.")
88
98
  success
@@ -94,6 +104,7 @@ module Fontist
94
104
  desc "manifest-locations MANIFEST",
95
105
  "Get locations of fonts from MANIFEST (yaml)"
96
106
  def manifest_locations(manifest)
107
+ handle_class_options(options)
97
108
  paths = Fontist::Manifest::Locations.from_file(manifest)
98
109
  print_yaml(paths)
99
110
  success
@@ -105,6 +116,7 @@ module Fontist
105
116
  option :accept_all_licenses, type: :boolean, aliases: "--confirm-license", desc: "Accept all license agreements"
106
117
  option :hide_licenses, type: :boolean, desc: "Hide license texts"
107
118
  def manifest_install(manifest)
119
+ handle_class_options(options)
108
120
  paths = Fontist::Manifest::Install.from_file(
109
121
  manifest,
110
122
  confirmation: options[:accept_all_licenses] ? "yes" : "no",
@@ -124,6 +136,7 @@ module Fontist
124
136
  option :subdir, desc: "Subdirectory to take fonts from, starting with the " \
125
137
  "root dir, e.g.: stixfonts-2.10/fonts/static_otf. May include `fnmatch` patterns."
126
138
  def create_formula(url)
139
+ handle_class_options(options)
127
140
  require "fontist/import/create_formula"
128
141
  name = Fontist::Import::CreateFormula.new(url, options).call
129
142
  Fontist.ui.say("#{name} formula has been successfully created")
@@ -137,22 +150,16 @@ module Fontist
137
150
  It is done automatically when formulas are updated, or private formulas
138
151
  are set up.
139
152
  LONGDESC
140
- option :main_repo, type: :boolean,
141
- desc: "Updates indexes in the main repo (for backward " \
142
- "compatibility with versions prior to 1.9)"
143
153
  def rebuild_index
144
- if options[:main_repo]
145
- Fontist::Index.rebuild_for_main_repo
146
- else
147
- Fontist::Index.rebuild
148
- end
149
-
154
+ handle_class_options(options)
155
+ Fontist::Index.rebuild
150
156
  Fontist.ui.say("Formula index has been rebuilt.")
151
157
  STATUS_SUCCESS
152
158
  end
153
159
 
154
160
  desc "import-sil", "Import formulas from SIL"
155
161
  def import_sil
162
+ handle_class_options(options)
156
163
  require "fontist/import/sil_import"
157
164
  Fontist::Import::SilImport.new.call
158
165
  end
@@ -160,8 +167,15 @@ module Fontist
160
167
  desc "repo SUBCOMMAND ...ARGS", "Manage custom repositories"
161
168
  subcommand "repo", Fontist::RepoCLI
162
169
 
170
+ desc "google SUBCOMMAND ...ARGS", "Manage Google formulas"
171
+ subcommand "google", Fontist::GoogleCLI
172
+
163
173
  private
164
174
 
175
+ def handle_class_options(options)
176
+ Fontist.preferred_family = options[:preferred_family]
177
+ end
178
+
165
179
  def success
166
180
  STATUS_SUCCESS
167
181
  end
data/lib/fontist/font.rb CHANGED
@@ -166,7 +166,7 @@ module Fontist
166
166
  end
167
167
 
168
168
  def uninstall_font
169
- paths = find_fontist_font
169
+ paths = find_fontist_paths
170
170
  return unless paths
171
171
 
172
172
  paths.each do |path|
@@ -176,8 +176,13 @@ module Fontist
176
176
  paths
177
177
  end
178
178
 
179
- def find_fontist_font
180
- Fontist::FontistFont.find(name)
179
+ def find_fontist_paths
180
+ fonts = Fontist::SystemIndex.fontist_index.find(name, nil)
181
+ return unless fonts
182
+
183
+ fonts.map do |font|
184
+ font[:path]
185
+ end
181
186
  end
182
187
 
183
188
  def installed_paths
@@ -103,6 +103,10 @@ module Fontist
103
103
  @fonts ||= Helpers.parse_to_object(hash_collection_fonts + hash_fonts)
104
104
  end
105
105
 
106
+ def digest
107
+ @data["digest"]
108
+ end
109
+
106
110
  private
107
111
 
108
112
  def default_key
@@ -0,0 +1,29 @@
1
+ module Fontist
2
+ class GoogleCLI < Thor
3
+ class_option :formulas_path, type: :string, desc: "Path to formulas"
4
+
5
+ desc "check", "Check Google fonts for updates"
6
+ def check
7
+ handle_class_options(options)
8
+ require "fontist/import/google_check"
9
+ Fontist::Import::GoogleCheck.new.call
10
+ CLI::STATUS_SUCCESS
11
+ end
12
+
13
+ desc "import", "Import Google fonts"
14
+ def import
15
+ handle_class_options(options)
16
+ require "fontist/import/google_import"
17
+ Fontist::Import::GoogleImport.new.call
18
+ CLI::STATUS_SUCCESS
19
+ end
20
+
21
+ private
22
+
23
+ def handle_class_options(options)
24
+ if options[:formulas_path]
25
+ Fontist.formulas_path = Pathname.new(options[:formulas_path])
26
+ end
27
+ end
28
+ end
29
+ end
@@ -11,7 +11,7 @@ module Fontist
11
11
  def initialize(path)
12
12
  @path = path
13
13
  @fonts = read
14
- @extension = "ttc"
14
+ @extension = detect_extension
15
15
  end
16
16
 
17
17
  def filename
@@ -46,6 +46,15 @@ module Fontist
46
46
  File.join(tmp_dir, filename)
47
47
  end
48
48
  end
49
+
50
+ def detect_extension
51
+ base_extension = "ttc"
52
+
53
+ file_extension = File.extname(File.basename(@path)).sub(/^\./, "")
54
+ return file_extension if file_extension.casecmp?(base_extension)
55
+
56
+ base_extension
57
+ end
49
58
  end
50
59
  end
51
60
  end
@@ -1,3 +1,4 @@
1
+ require "shellwords"
1
2
  require_relative "text_helper"
2
3
 
3
4
  module Fontist
@@ -5,7 +6,7 @@ module Fontist
5
6
  class FormulaBuilder
6
7
  FORMULA_ATTRIBUTES = %i[name description homepage resources
7
8
  font_collections fonts extract copyright
8
- license_url open_license].freeze
9
+ license_url open_license digest command].freeze
9
10
 
10
11
  attr_accessor :archive,
11
12
  :url,
@@ -49,7 +50,7 @@ module Fontist
49
50
  end
50
51
 
51
52
  def homepage
52
- both_fonts.first.homepage
53
+ both_fonts.map(&:homepage).compact.first
53
54
  end
54
55
 
55
56
  def resources
@@ -59,6 +60,18 @@ module Fontist
59
60
  end
60
61
 
61
62
  def resource_options
63
+ if @options[:skip_sha]
64
+ resource_options_without_sha
65
+ else
66
+ resource_options_with_sha
67
+ end
68
+ end
69
+
70
+ def resource_options_without_sha
71
+ { urls: [@url] + mirrors }
72
+ end
73
+
74
+ def resource_options_with_sha
62
75
  urls = []
63
76
  sha = []
64
77
  downloads do |url, path|
@@ -129,7 +142,7 @@ module Fontist
129
142
 
130
143
  fonts = groups.map do |name, group|
131
144
  { name: name,
132
- styles: group.map(&style_type) }
145
+ styles: styles_from_files(group, style_type) }
133
146
  end
134
147
 
135
148
  fonts.sort_by do |x|
@@ -137,16 +150,20 @@ module Fontist
137
150
  end
138
151
  end
139
152
 
153
+ def styles_from_files(files, style_type)
154
+ files.map(&style_type).sort_by { |x| x[:type] }
155
+ end
156
+
140
157
  def extract
141
158
  @extractor.operations
142
159
  end
143
160
 
144
161
  def copyright
145
- both_fonts.first.copyright
162
+ both_fonts.map(&:copyright).compact.first
146
163
  end
147
164
 
148
165
  def license_url
149
- both_fonts.first.license_url
166
+ both_fonts.map(&:license_url).compact.first
150
167
  end
151
168
 
152
169
  def open_license
@@ -161,6 +178,14 @@ module Fontist
161
178
 
162
179
  TextHelper.cleanup(@license_text)
163
180
  end
181
+
182
+ def digest
183
+ @options[:digest]
184
+ end
185
+
186
+ def command
187
+ Shellwords.shelljoin(ARGV)
188
+ end
164
189
  end
165
190
  end
166
191
  end
@@ -1,4 +1,3 @@
1
- require_relative "fonts_public.pb"
2
1
  require_relative "../google"
3
2
  require_relative "../otf_parser"
4
3
 
@@ -6,7 +5,7 @@ module Fontist
6
5
  module Import
7
6
  module Google
8
7
  class NewFontsFetcher
9
- REPO_PATH = Fontist.root_path.join("tmp", "fonts")
8
+ REPO_PATH = Fontist.fontist_path.join("google", "fonts")
10
9
  REPO_URL = "https://github.com/google/fonts.git".freeze
11
10
  SKIPLIST_PATH = File.expand_path("skiplist.yml", __dir__)
12
11
 
@@ -25,7 +24,8 @@ module Fontist
25
24
  if Dir.exist?(REPO_PATH)
26
25
  `cd #{REPO_PATH} && git pull`
27
26
  else
28
- `git clone #{REPO_URL} #{REPO_PATH}`
27
+ FileUtils.mkdir_p(File.dirname(REPO_PATH))
28
+ `git clone --depth 1 #{REPO_URL} #{REPO_PATH}`
29
29
  end
30
30
  end
31
31
 
@@ -53,23 +53,11 @@ module Fontist
53
53
  end
54
54
 
55
55
  def new?(path)
56
- metadata = fetch_metadata(path)
57
- return unless metadata
58
-
59
- font_new?(metadata, path)
60
- end
61
-
62
- def fetch_metadata(path)
63
- metadata_path = File.join(path, "METADATA.pb")
64
- return unless File.exists?(metadata_path)
65
-
66
- ::Google::Fonts::FamilyProto.parse_from_text(File.read(metadata_path))
67
- end
68
-
69
- def font_new?(metadata, path)
70
- return if in_skiplist?(metadata.name)
71
- return if up_to_date?(metadata, path)
72
- return unless downloadable?(metadata.name)
56
+ metadata_name = Google.metadata_name(path)
57
+ return unless metadata_name
58
+ return if in_skiplist?(metadata_name)
59
+ return if up_to_date?(metadata_name, path)
60
+ return unless downloadable?(metadata_name)
73
61
 
74
62
  true
75
63
  end
@@ -79,29 +67,47 @@ module Fontist
79
67
  @skiplist.include?(name)
80
68
  end
81
69
 
82
- def up_to_date?(metadata, path)
83
- formula = formula(metadata.name)
70
+ def up_to_date?(metadata_name, path)
71
+ formula = formula(metadata_name)
84
72
  return false unless formula
85
73
 
86
- styles = formula.fonts.map(&:styles).flatten
74
+ repo_digest_up_to_date?(formula, path) ||
75
+ fonts_up_to_date?(formula, path)
76
+ end
77
+
78
+ def repo_digest_up_to_date?(formula, path)
79
+ return unless formula.digest
87
80
 
88
- styles.all? do |style|
89
- style.version == otfinfo_version(font_path(style.font, path))
81
+ formula.digest == Google.digest(path)
82
+ end
83
+
84
+ def fonts_up_to_date?(formula, path)
85
+ styles = formula_styles(formula)
86
+ repo_fonts(path).all? do |font|
87
+ style = styles.find { |s| s.font == repo_to_archive_name(font) }
88
+ return false unless style
89
+
90
+ otfinfo_version(font) == style.version
90
91
  end
91
92
  end
92
93
 
93
- def formula(font_name)
94
- klass = font_name.gsub(/ /, "").sub(/\S/, &:upcase)
95
- @formulas ||= Fontist::Formula.all
96
- @formulas["Fontist::Formulas::#{klass}Font"]
94
+ def formula_styles(formula)
95
+ formula.fonts.map(&:styles).flatten
97
96
  end
98
97
 
99
- def font_path(filename, directory)
100
- File.join(directory, fix_variable_filename(filename))
98
+ def repo_fonts(path)
99
+ Dir.glob(File.join(path, "*.{ttf,otf}"))
101
100
  end
102
101
 
103
- def fix_variable_filename(filename)
104
- filename.sub("-VariableFont_wght", "[wght]")
102
+ def repo_to_archive_name(font_path)
103
+ File.basename(font_path)
104
+ .sub("[wght]", "-VariableFont_wght")
105
+ .sub("[opsz]", "-Regular-VariableFont_opsz")
106
+ end
107
+
108
+ def formula(font_name)
109
+ path = Fontist::Import::Google.formula_path(font_name)
110
+ Formula.new_from_file(path) if File.exist?(path)
105
111
  end
106
112
 
107
113
  def otfinfo_version(path)
@@ -110,10 +116,15 @@ module Fontist
110
116
  end
111
117
 
112
118
  def downloadable?(name)
119
+ retries ||= 0
120
+ retries += 1
113
121
  Down.open("https://fonts.google.com/download?family=#{name}")
114
122
  true
115
123
  rescue Down::NotFound
116
124
  false
125
+ rescue Down::TimeoutError
126
+ retry unless retries >= 3
127
+ false
117
128
  end
118
129
  end
119
130
  end