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
@@ -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