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
@@ -1,11 +1,29 @@
1
1
  module Fontist
2
2
  module Import
3
3
  module Google
4
+ def self.metadata_name(path)
5
+ metadata_path = File.join(path, "METADATA.pb")
6
+ return unless File.exists?(metadata_path)
7
+
8
+ File.foreach(metadata_path) do |line|
9
+ name = line.match(/^name: "(.+)"/)
10
+ return name[1] if name
11
+ end
12
+ end
13
+
4
14
  def self.formula_path(name)
5
15
  filename = name.downcase.gsub(" ", "_") + ".yml"
6
16
  Fontist.formulas_path.join("google", filename)
7
17
  end
8
18
 
19
+ def self.digest(path)
20
+ checksums = Dir.glob(File.join(path, "*.{ttf,otf,ttc}"))
21
+ .sort
22
+ .map { |x| Digest::SHA256.file(x).to_s }
23
+
24
+ Digest::SHA256.hexdigest(checksums.to_s)
25
+ end
26
+
9
27
  def self.style_version(text)
10
28
  return unless text
11
29
 
@@ -4,17 +4,12 @@ module Fontist
4
4
  module Import
5
5
  class GoogleCheck
6
6
  def call
7
- fetch_formulas
8
7
  fonts = new_fonts
9
8
  indicate(fonts)
10
9
  end
11
10
 
12
11
  private
13
12
 
14
- def fetch_formulas
15
- Formula.update_formulas_repo
16
- end
17
-
18
13
  def new_fonts
19
14
  Fontist::Import::Google::NewFontsFetcher.new(logging: true).call
20
15
  end
@@ -26,8 +21,6 @@ module Fontist
26
21
  new_paths.each do |path|
27
22
  puts path
28
23
  end
29
-
30
- abort
31
24
  end
32
25
  end
33
26
  end
@@ -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,157 +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
- h.merge!(style.to_h.select do |k, _|
160
- %i(post_script_name version description copyright).include?(k)
161
- end.compact)
162
- h.merge!(font: fix_variable_filename(style.filename))
163
- end
164
- end
165
-
166
- def fix_variable_filename(filename)
167
- filename.sub("[wght]", "-VariableFont_wght")
168
- end
169
-
170
- def formula_path(name)
171
- Fontist::Import::Google.formula_path(name)
172
- end
173
-
174
- def save_to_path(hash, path)
175
- File.write(path, YAML.dump(hash))
62
+ def rebuild_index
63
+ Fontist::Index.rebuild
176
64
  end
177
65
  end
178
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?
@@ -10,7 +10,8 @@ module Fontist
10
10
  otfinfo: Otfinfo::OtfinfoRequirement.new,
11
11
  }.freeze
12
12
 
13
- STYLE_ATTRIBUTES = %i[family_name type full_name post_script_name
13
+ STYLE_ATTRIBUTES = %i[family_name type preferred_family_name
14
+ preferred_type full_name post_script_name
14
15
  version description copyright font
15
16
  source_font].freeze
16
17
 
@@ -35,11 +36,19 @@ module Fontist
35
36
  end
36
37
 
37
38
  def family_name
38
- info["Preferred family"] || info["Family"]
39
+ info["Family"]
39
40
  end
40
41
 
41
42
  def type
42
- info["Preferred subfamily"] || info["Subfamily"]
43
+ info["Subfamily"]
44
+ end
45
+
46
+ def preferred_family_name
47
+ info["Preferred family"]
48
+ end
49
+
50
+ def preferred_type
51
+ info["Preferred subfamily"]
43
52
  end
44
53
 
45
54
  def full_name
@@ -97,7 +106,11 @@ module Fontist
97
106
  end
98
107
 
99
108
  def detect_extension
100
- Files::FontDetector.standard_extension(@path)
109
+ detected = Files::FontDetector.standard_extension(@path)
110
+ file_extension = File.extname(File.basename(@path)).sub(/^\./, "")
111
+ return file_extension if file_extension.casecmp?(detected)
112
+
113
+ detected
101
114
  end
102
115
  end
103
116
  end
@@ -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?(":") }
@@ -7,8 +7,8 @@ module Fontist
7
7
  end
8
8
 
9
9
  def call
10
- style = { family_name: @info["Preferred family"] || @info["Family"],
11
- style: @info["Preferred subfamily"] || @info["Subfamily"],
10
+ style = { family_name: @info["Family"],
11
+ style: @info["Subfamily"],
12
12
  full_name: @info["Full name"],
13
13
  post_script_name: @info["PostScript name"],
14
14
  version: version(@info["Version"]),
@@ -16,6 +16,14 @@ module Fontist
16
16
  filename: File.basename(@path),
17
17
  copyright: @info["Copyright"] }
18
18
 
19
+ if @info["Preferred family"]
20
+ style[:preferred_family_name] = @info["Preferred family"]
21
+ end
22
+
23
+ if @info["Preferred subfamily"]
24
+ style[:preferred_style] = @info["Preferred subfamily"]
25
+ end
26
+
19
27
  OpenStruct.new(style)
20
28
  end
21
29
 
@@ -2,6 +2,12 @@
2
2
  {
3
3
  family_name: "<%= s.family_name %>",
4
4
  style: "<%= s.style %>",
5
+ <%- if s.preferred_family_name -%>
6
+ preferred_family_name: "<%= s.preferred_family_name %>",
7
+ <%- end -%>
8
+ <%- if s.preferred_style -%>
9
+ preferred_type: "<%= s.preferred_style %>",
10
+ <%- end -%>
5
11
  full_name: "<%= s.full_name %>",
6
12
  <%- if s.post_script_name -%>
7
13
  post_script_name: "<%= s.post_script_name %>",
@@ -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|copying)$/i.freeze
7
8
 
8
9
  def initialize(archive, subarchive: nil, subdir: nil)
9
10
  @archive = archive
data/lib/fontist/index.rb CHANGED
@@ -3,41 +3,17 @@ 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_index_path = Fontist.formulas_repo_path.join("index.yml")
23
- Fontist.formula_filename_index_path =
24
- Fontist.formulas_repo_path.join("filename_index.yml")
25
-
26
- rebuild
27
-
28
- Fontist.formula_index_path = nil
29
- Fontist.formula_filename_index_path = nil
30
- end
31
-
32
6
  def self.rebuild
33
- Fontist::Indexes::FontIndex.rebuild
7
+ Fontist::Indexes::DefaultFamilyFontIndex.rebuild
8
+ Fontist::Indexes::PreferredFamilyFontIndex.rebuild
34
9
  Fontist::Indexes::FilenameIndex.rebuild
35
10
 
36
11
  reset_cache
37
12
  end
38
13
 
39
14
  def self.reset_cache
40
- Fontist::Indexes::FontIndex.reset_cache
15
+ Fontist::Indexes::DefaultFamilyFontIndex.reset_cache
16
+ Fontist::Indexes::PreferredFamilyFontIndex.reset_cache
41
17
  Fontist::Indexes::FilenameIndex.reset_cache
42
18
  end
43
19
  end
@@ -0,0 +1,21 @@
1
+ require_relative "base_index"
2
+
3
+ module Fontist
4
+ module Indexes
5
+ class DefaultFamilyFontIndex < BaseIndex
6
+ def self.path
7
+ Fontist.formula_index_path
8
+ end
9
+
10
+ def add_formula(formula)
11
+ formula.fonts.each do |font|
12
+ add_index_formula(font.name, formula.to_index_formula)
13
+ end
14
+ end
15
+
16
+ def normalize_key(key)
17
+ key.downcase
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,21 +1,16 @@
1
- require_relative "base_index"
1
+ require_relative "default_family_font_index"
2
+ require_relative "preferred_family_font_index"
2
3
 
3
4
  module Fontist
4
5
  module Indexes
5
- class FontIndex < BaseIndex
6
- def self.path
7
- Fontist.formula_index_path
8
- end
9
-
10
- def add_formula(formula)
11
- formula.fonts.each do |font|
12
- add_index_formula(font.name, formula.to_index_formula)
6
+ class FontIndex
7
+ def self.from_yaml
8
+ if Fontist.preferred_family?
9
+ PreferredFamilyFontIndex.from_yaml
10
+ else
11
+ DefaultFamilyFontIndex.from_yaml
13
12
  end
14
13
  end
15
-
16
- def normalize_key(key)
17
- key.downcase
18
- end
19
14
  end
20
15
  end
21
16
  end