fontist 1.9.3 → 1.11.2

Sign up to get free protection for your applications and to get access to all the features.
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