fontist 1.11.1 → 1.11.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,7 @@
1
1
  require "erb"
2
2
  require_relative "google"
3
3
  require_relative "google/new_fonts_fetcher"
4
- require_relative "google/fonts_public.pb"
5
- require_relative "template_helper"
6
- require_relative "text_helper"
7
- require_relative "otf_parser"
8
- require_relative "otf_style"
4
+ require_relative "create_formula"
9
5
 
10
6
  module Fontist
11
7
  module Import
@@ -13,6 +9,7 @@ module Fontist
13
9
  def call
14
10
  fonts = new_fonts
15
11
  create_formulas(fonts)
12
+ rebuild_index
16
13
  end
17
14
 
18
15
  private
@@ -22,161 +19,48 @@ module Fontist
22
19
  end
23
20
 
24
21
  def create_formulas(fonts)
22
+ return puts("Nothing to update") if fonts.empty?
23
+
25
24
  puts "Creating formulas..."
26
25
  fonts.each do |path|
27
26
  create_formula(path)
28
27
  end
29
28
  end
30
29
 
31
- def create_formula(path)
32
- puts path
33
- metadata = fetch_metadata(path)
34
- font = build_font(metadata, path)
35
- save_formula(font)
36
- end
30
+ def create_formula(font_path)
31
+ puts font_path
37
32
 
38
- def fetch_metadata(path)
39
- protobuf = File.read(File.join(path, "METADATA.pb"))
40
- ::Google::Fonts::FamilyProto.parse_from_text(protobuf)
41
- end
33
+ path = Fontist::Import::CreateFormula.new(
34
+ url(font_path),
35
+ name: Google.metadata_name(font_path),
36
+ formula_dir: formula_dir,
37
+ skip_sha: variable_style?(font_path),
38
+ digest: Google.digest(font_path),
39
+ ).call
42
40
 
43
- def build_font(metadata, path)
44
- h = from_metadata(metadata)
45
- .merge(from_otfinfo(path))
46
- .merge(styles: styles_from_otfinfo(path, metadata.fonts))
47
- .merge(from_license(path))
48
-
49
- OpenStruct.new(h)
41
+ Fontist.ui.success("Formula has been successfully created: #{path}")
50
42
  end
51
43
 
52
- def from_metadata(metadata)
53
- copyright = metadata.fonts.first.copyright
54
-
55
- Hash.new.tap do |h|
56
- h[:fullname] = metadata.name
57
- h[:cleanname] = metadata.name.gsub(/ /, "")
58
- h[:sha256] = sha256(metadata.name) unless variable_style?(metadata)
59
- h[:copyright] = Fontist::Import::TextHelper.cleanup(copyright)
60
- end
44
+ def url(path)
45
+ name = Google.metadata_name(path)
46
+ "https://fonts.google.com/download?family=#{ERB::Util.url_encode(name)}"
61
47
  end
62
48
 
63
- def variable_style?(metadata)
64
- metadata.fonts.any? do |s|
65
- s.filename.match?(/\[(.+,)?wght\]/)
49
+ def formula_dir
50
+ @formula_dir ||= Fontist.formulas_path.join("google").tap do |path|
51
+ FileUtils.mkdir_p(path) unless File.exist?(path)
66
52
  end
67
53
  end
68
54
 
69
- def sha256(name)
70
- file = Down.download("https://fonts.google.com/download?family=#{name}",
71
- open_timeout: 10,
72
- read_timeout: 10)
73
-
74
- Digest::SHA256.file(file).to_s
75
- end
76
-
77
- def from_license(path)
78
- file = Dir.glob(File.join(path, "{OFL.txt,UFL.txt,LICENSE.txt}")).first
79
- print "warn, no license, " unless file
80
- return { license: "" } unless file
81
-
82
- { license: cleanup_text(File.read(file)) }
83
- end
84
-
85
- def cleanup_text(text)
86
- text.rstrip
87
- .gsub("\r\n", "\n")
88
- .lines
89
- .map(&:rstrip)
90
- .drop_while(&:empty?)
91
- .join("\n")
92
- end
93
-
94
- def from_otfinfo(path)
95
- font_file = Dir.glob(File.join(path, "*.ttf")).first
96
- otf = OtfParser.new(font_file).call
97
-
98
- { homepage: otf["Vendor URL"],
99
- license_url: otf["License URL"] }
100
- end
101
-
102
- def styles_from_otfinfo(path, fonts)
103
- fonts.map do |f|
104
- file_path = File.join(path, f.filename)
105
- info = OtfParser.new(file_path).call
106
- OtfStyle.new(info, file_path).call
55
+ def variable_style?(path)
56
+ fonts = Dir.glob(File.join(path, "*.{ttf,otf}"))
57
+ fonts.any? do |font|
58
+ File.basename(font).match?(/\[(.+,)?(wght|opsz)\]/)
107
59
  end
108
60
  end
109
61
 
110
- def save_formula(font)
111
- hash = formula_hash(font)
112
- path = formula_path(font.fullname)
113
- save_to_path(hash, path)
114
- end
115
-
116
- def formula_hash(font)
117
- stringify_keys(name: font.cleanname.sub(/\S/, &:upcase),
118
- description: font.fullname,
119
- homepage: font.homepage,
120
- resources: formula_resource(font),
121
- fonts: [yaml_font(font)],
122
- extract: { format: :zip },
123
- copyright: font.copyright,
124
- license_url: font.license_url,
125
- open_license: font.license)
126
- end
127
-
128
- def stringify_keys(hash)
129
- JSON.parse(hash.to_json)
130
- end
131
-
132
- def formula_resource(font)
133
- encoded_name = ERB::Util.url_encode(font.fullname)
134
- url = "https://fonts.google.com/download?family=#{encoded_name}"
135
-
136
- options = {}
137
- options[:urls] = [url]
138
- options[:sha256] = font.sha256 if font.sha256
139
-
140
- { "#{font.cleanname}.zip" => options }
141
- end
142
-
143
- def yaml_font(font)
144
- { name: font.fullname,
145
- styles: yaml_styles(font.styles) }
146
- end
147
-
148
- def yaml_styles(styles)
149
- styles.map do |s|
150
- yaml_style(s)
151
- end
152
- end
153
-
154
- def yaml_style(style)
155
- Hash.new.tap do |h|
156
- h.merge!(family_name: style.family_name,
157
- type: style.style,
158
- full_name: style.full_name)
159
- if style.preferred_family_name
160
- h[:preferred_family_name] = style.preferred_family_name
161
- end
162
- h[:preferred_type] = style.preferred_style if style.preferred_style
163
- h.merge!(style.to_h.select do |k, _|
164
- %i(post_script_name version description copyright).include?(k)
165
- end.compact)
166
- h.merge!(font: fix_variable_filename(style.filename))
167
- end
168
- end
169
-
170
- def fix_variable_filename(filename)
171
- filename.sub("[wght]", "-VariableFont_wght")
172
- end
173
-
174
- def formula_path(name)
175
- Fontist::Import::Google.formula_path(name)
176
- end
177
-
178
- def save_to_path(hash, path)
179
- File.write(path, YAML.dump(hash))
62
+ def rebuild_index
63
+ Fontist::Index.rebuild
180
64
  end
181
65
  end
182
66
  end
@@ -4,7 +4,7 @@ module Fontist
4
4
  module SystemHelper
5
5
  class << self
6
6
  def run(command)
7
- Fontist.ui.say("Run `#{command}`")
7
+ Fontist.ui.say("Run `#{command}`") if Fontist.debug?
8
8
 
9
9
  result = `#{command}`
10
10
  unless $CHILD_STATUS.to_i.zero?
@@ -13,6 +13,8 @@ module Fontist
13
13
  end
14
14
 
15
15
  def call
16
+ raise ArgumentError, "Empty path" unless @path
17
+
16
18
  text = REQUIREMENTS[:otfinfo].call(@path)
17
19
  text.split("\n")
18
20
  .select { |x| x.include?(":") }
@@ -3,7 +3,8 @@ require_relative "files/font_detector"
3
3
  module Fontist
4
4
  module Import
5
5
  class RecursiveExtraction
6
- LICENSE_PATTERN = /(ofl\.txt|ufl\.txt|licenses?\.txt|copying)$/i.freeze
6
+ LICENSE_PATTERN =
7
+ /(ofl\.txt|ufl\.txt|licenses?\.txt|license(\.md)?|copying)$/i.freeze
7
8
 
8
9
  def initialize(archive, subarchive: nil, subdir: nil)
9
10
  @archive = archive
@@ -69,6 +70,8 @@ module Fontist
69
70
 
70
71
  def extract_data(archive)
71
72
  Excavate::Archive.new(path(archive)).files(recursive_packages: true) do |path|
73
+ next unless File.file?(path)
74
+
72
75
  match_license(path)
73
76
  match_font(path) if font_directory?(path)
74
77
  end
data/lib/fontist/index.rb CHANGED
@@ -3,33 +3,6 @@ require_relative "indexes/filename_index"
3
3
 
4
4
  module Fontist
5
5
  class Index
6
- def self.rebuild_for_main_repo
7
- unless Dir.exist?(Fontist.private_formulas_path)
8
- return do_rebuild_for_main_repo_with
9
- end
10
-
11
- Dir.mktmpdir do |dir|
12
- tmp_private_path = File.join(dir, "private")
13
- FileUtils.mv(Fontist.private_formulas_path, tmp_private_path)
14
-
15
- do_rebuild_for_main_repo_with
16
-
17
- FileUtils.mv(tmp_private_path, Fontist.private_formulas_path)
18
- end
19
- end
20
-
21
- def self.do_rebuild_for_main_repo_with
22
- Fontist.formula_preferred_family_index_path =
23
- Fontist.formulas_repo_path.join("index.yml")
24
- Fontist.formula_filename_index_path =
25
- Fontist.formulas_repo_path.join("filename_index.yml")
26
-
27
- rebuild
28
-
29
- Fontist.formula_preferred_family_index_path = nil
30
- Fontist.formula_filename_index_path = nil
31
- end
32
-
33
6
  def self.rebuild
34
7
  Fontist::Indexes::DefaultFamilyFontIndex.rebuild
35
8
  Fontist::Indexes::PreferredFamilyFontIndex.rebuild
data/lib/fontist/repo.rb CHANGED
@@ -15,8 +15,20 @@ module Fontist
15
15
  raise(Errors::RepoNotFoundError, "No such repo '#{name}'.")
16
16
  end
17
17
 
18
- Git.open(path).pull
18
+ git = Git.open(path)
19
+ git.pull("origin", git.current_branch)
20
+
19
21
  Index.rebuild
22
+ rescue Git::GitExecuteError => e
23
+ raise Errors::RepoCouldNotBeUpdatedError.new(<<~MSG.chomp)
24
+ Formulas repo '#{name}' could not be updated.
25
+ Please consider reinitializing it with:
26
+ fontist remove #{name}
27
+ fontist setup #{name} REPO_URL
28
+
29
+ Git error:
30
+ #{e.message}
31
+ MSG
20
32
  end
21
33
 
22
34
  def remove(name)
@@ -1,5 +1,4 @@
1
1
  require_relative "system_index"
2
- require_relative "formula_paths"
3
2
 
4
3
  module Fontist
5
4
  class SystemFont
@@ -13,12 +12,20 @@ module Fontist
13
12
  end
14
13
 
15
14
  def self.system_font_paths
15
+ @system_font_paths ||= load_system_font_paths
16
+ end
17
+
18
+ def self.load_system_font_paths
16
19
  config_path = Fontist.system_file_path
17
20
  os = Fontist::Utils::System.user_os.to_s
18
21
  templates = YAML.load_file(config_path)["system"][os]["paths"]
19
22
  patterns = expand_paths(templates)
20
23
 
21
- Dir.glob(patterns)
24
+ Dir.glob(patterns, File::FNM_CASEFOLD)
25
+ end
26
+
27
+ def self.reset_system_font_paths_cache
28
+ @system_font_paths = nil
22
29
  end
23
30
 
24
31
  def self.expand_paths(paths)
@@ -51,7 +58,7 @@ module Fontist
51
58
  end
52
59
 
53
60
  def find_styles
54
- find_by_index || find_by_formulas
61
+ find_by_index
55
62
  end
56
63
 
57
64
  private
@@ -61,9 +68,5 @@ module Fontist
61
68
  def find_by_index
62
69
  SystemIndex.system_index.find(font, style)
63
70
  end
64
-
65
- def find_by_formulas
66
- FormulaPaths.new(self.class.font_paths).find(font, style)
67
- end
68
71
  end
69
72
  end
@@ -37,35 +37,39 @@ module Fontist
37
37
  LANGUAGE_MAC_ENGLISH = 0
38
38
  LANGUAGE_MS_ENGLISH_AMERICAN = 0x409
39
39
 
40
- attr_reader :font_paths
41
-
42
40
  def self.system_index
43
- if Fontist.preferred_family?
44
- new(Fontist.system_preferred_family_index_path,
45
- SystemFont.font_paths,
46
- PreferredFamily.new)
47
- else
48
- new(Fontist.system_index_path,
49
- SystemFont.font_paths,
50
- DefaultFamily.new)
51
- end
41
+ path = if Fontist.preferred_family?
42
+ Fontist.system_preferred_family_index_path
43
+ else
44
+ Fontist.system_index_path
45
+ end
46
+
47
+ @system_index ||= {}
48
+ @system_index[Fontist.preferred_family?] ||= {}
49
+ @system_index[Fontist.preferred_family?][path] ||=
50
+ new(path, -> { SystemFont.font_paths }, family)
52
51
  end
53
52
 
54
53
  def self.fontist_index
55
- if Fontist.preferred_family?
56
- new(Fontist.fontist_preferred_family_index_path,
57
- SystemFont.fontist_font_paths,
58
- PreferredFamily.new)
59
- else
60
- new(Fontist.fontist_index_path,
61
- SystemFont.fontist_font_paths,
62
- DefaultFamily.new)
63
- end
54
+ path = if Fontist.preferred_family?
55
+ Fontist.fontist_preferred_family_index_path
56
+ else
57
+ Fontist.fontist_index_path
58
+ end
59
+
60
+ @fontist_index ||= {}
61
+ @fontist_index[Fontist.preferred_family?] ||= {}
62
+ @fontist_index[Fontist.preferred_family?][path] ||=
63
+ new(path, -> { SystemFont.fontist_font_paths }, family)
64
64
  end
65
65
 
66
- def initialize(index_path, font_paths, family)
66
+ def self.family
67
+ Fontist.preferred_family? ? PreferredFamily.new : DefaultFamily.new
68
+ end
69
+
70
+ def initialize(index_path, font_paths_fetcher, family)
67
71
  @index_path = index_path
68
- @font_paths = font_paths
72
+ @font_paths_fetcher = font_paths_fetcher
69
73
  @family = family
70
74
  end
71
75
 
@@ -85,7 +89,18 @@ module Fontist
85
89
  private
86
90
 
87
91
  def index
88
- @index ||= build_index
92
+ return @index unless index_changed?
93
+
94
+ @index = build_index
95
+ end
96
+
97
+ def index_changed?
98
+ @index.nil? ||
99
+ @index.map { |x| x[:path] }.uniq.sort != font_paths.sort
100
+ end
101
+
102
+ def font_paths
103
+ @font_paths_fetcher.call
89
104
  end
90
105
 
91
106
  def build_index
@@ -1,9 +1,9 @@
1
1
  module Fontist
2
2
  class Update
3
- BRANCH = "v2".freeze
3
+ VERSION = "v2".freeze
4
4
 
5
5
  def self.call
6
- new(BRANCH).call
6
+ new(VERSION).call
7
7
  end
8
8
 
9
9
  def initialize(branch = "main")
@@ -41,36 +41,11 @@ module Fontist
41
41
  end
42
42
 
43
43
  def update_private_repos
44
- private_repos.each do |path|
45
- update_repo(path)
44
+ Repo.list.each do |name|
45
+ Repo.update(name)
46
46
  end
47
47
  end
48
48
 
49
- def update_repo(path)
50
- Git.open(path).pull
51
- rescue Git::GitExecuteError => e
52
- name = repo_name(path)
53
- raise Errors::RepoCouldNotBeUpdatedError.new(<<~MSG.chomp)
54
- Formulas repo '#{name}' could not be updated.
55
- Please consider reinitializing it with:
56
- fontist remove #{name}
57
- fontist setup #{name} REPO_URL
58
-
59
- Git error:
60
- #{e.message}
61
- MSG
62
- end
63
-
64
- def private_repos
65
- Dir.glob(Fontist.private_formulas_path.join("*")).select do |path|
66
- File.directory?(path)
67
- end
68
- end
69
-
70
- def repo_name(path)
71
- File.basename(path)
72
- end
73
-
74
49
  def rebuild_index
75
50
  Index.rebuild
76
51
  end
@@ -15,7 +15,7 @@ module Fontist
15
15
  @file = file
16
16
  @sha = [sha].flatten.compact
17
17
  @file_size = file_size.to_i if file_size
18
- @progress_bar = set_progress_bar(progress_bar)
18
+ @progress_bar = progress_bar
19
19
  @cache = Cache.new
20
20
  end
21
21
 
@@ -54,35 +54,48 @@ module Fontist
54
54
  options[:download_path] || Fontist.root_path.join("tmp")
55
55
  end
56
56
 
57
- def set_progress_bar(progress_bar)
58
- if progress_bar
57
+ def download_file
58
+ tries = tries ? tries + 1 : 1
59
+ do_download_file
60
+ rescue Down::Error => e
61
+ retry if tries < 3
62
+
63
+ raise Fontist::Errors::InvalidResourceError,
64
+ "Invalid URL: #{@file}. Error: #{e.inspect}."
65
+ end
66
+
67
+ def do_download_file
68
+ progress_bar = create_progress_bar
69
+ file = do_download_file_with_progress_bar(progress_bar)
70
+ progress_bar.finish
71
+ file
72
+ end
73
+
74
+ def create_progress_bar
75
+ if @progress_bar
59
76
  ProgressBar.new(@file_size)
60
77
  else
61
78
  NullProgressBar.new(@file_size)
62
79
  end
63
80
  end
64
81
 
65
- def download_file
66
- file = Down.download(
82
+ # rubocop:disable Metrics/MethodLength
83
+ def do_download_file_with_progress_bar(progress_bar)
84
+ Down.download(
67
85
  url,
68
86
  open_timeout: 10,
69
87
  read_timeout: 10,
70
88
  max_redirects: 10,
71
89
  headers: headers,
72
90
  content_length_proc: ->(content_length) {
73
- @progress_bar.total = content_length if content_length
91
+ progress_bar.total = content_length if content_length
74
92
  },
75
93
  progress_proc: -> (progress) {
76
- @progress_bar.increment(progress)
94
+ progress_bar.increment(progress)
77
95
  }
78
96
  )
79
-
80
- @progress_bar.finish
81
-
82
- file
83
- rescue Down::NotFound
84
- raise(Fontist::Errors::InvalidResourceError.new("Invalid URL: #{@file}"))
85
97
  end
98
+ # rubocop:enable Metrics/MethodLength
86
99
 
87
100
  def url
88
101
  @file.respond_to?(:url) ? @file.url : @file
@@ -1,3 +1,3 @@
1
1
  module Fontist
2
- VERSION = "1.11.1".freeze
2
+ VERSION = "1.11.6".freeze
3
3
  end
data/lib/fontist.rb CHANGED
@@ -40,7 +40,11 @@ module Fontist
40
40
  end
41
41
 
42
42
  def self.formulas_repo_path
43
- Fontist.fontist_path.join("formulas")
43
+ Fontist.fontist_version_path.join("formulas")
44
+ end
45
+
46
+ def self.fontist_version_path
47
+ Fontist.fontist_path.join("versions", Update::VERSION)
44
48
  end
45
49
 
46
50
  def self.formulas_repo_url
@@ -48,7 +52,11 @@ module Fontist
48
52
  end
49
53
 
50
54
  def self.formulas_path
51
- Fontist.formulas_repo_path.join("Formulas")
55
+ @formulas_path || Fontist.formulas_repo_path.join("Formulas")
56
+ end
57
+
58
+ def self.formulas_path=(path)
59
+ @formulas_path = path
52
60
  end
53
61
 
54
62
  def self.private_formulas_path
@@ -84,25 +92,15 @@ module Fontist
84
92
  end
85
93
 
86
94
  def self.formula_preferred_family_index_path
87
- @formula_preferred_family_index_path ||
88
- Fontist.formula_index_dir.join("formula_index.preferred_family.yml")
89
- end
90
-
91
- def self.formula_preferred_family_index_path=(path)
92
- @formula_preferred_family_index_path = path
95
+ Fontist.formula_index_dir.join("formula_index.preferred_family.yml")
93
96
  end
94
97
 
95
98
  def self.formula_filename_index_path
96
- @formula_filename_index_path ||
97
- Fontist.formula_index_dir.join("filename_index.yml")
98
- end
99
-
100
- def self.formula_filename_index_path=(path)
101
- @formula_filename_index_path = path
99
+ Fontist.formula_index_dir.join("filename_index.yml")
102
100
  end
103
101
 
104
102
  def self.formula_index_dir
105
- Fontist.fontist_path
103
+ Fontist.fontist_version_path
106
104
  end
107
105
 
108
106
  def self.preferred_family?
@@ -112,4 +110,12 @@ module Fontist
112
110
  def self.preferred_family=(bool)
113
111
  @preferred_family = bool
114
112
  end
113
+
114
+ def self.debug?
115
+ @debug || false
116
+ end
117
+
118
+ def self.debug=(bool)
119
+ @debug = bool
120
+ end
115
121
  end