fontist 1.0.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/check_google.yml +28 -0
- data/.github/workflows/macosx.yml +3 -0
- data/.github/workflows/ubuntu.yml +1 -1
- data/.github/workflows/windows.yml +1 -1
- data/.gitignore +6 -0
- data/.hound.yml +2 -0
- data/.rubocop.yml +22 -0
- data/README.md +54 -0
- data/bin/check_google +8 -0
- data/bin/convert_formulas +8 -0
- data/bin/fontist +8 -0
- data/bin/generate_otfinfo +8 -0
- data/bin/import_google +8 -0
- data/bin/stripttc +0 -0
- data/fontist.gemspec +15 -1
- data/lib/fontist.rb +22 -15
- data/lib/fontist/cli.rb +118 -0
- data/lib/fontist/errors.rb +5 -1
- data/lib/fontist/font.rb +153 -3
- data/lib/fontist/font_formula.rb +17 -6
- data/lib/fontist/fontist_font.rb +70 -0
- data/lib/fontist/formula.rb +11 -8
- data/lib/fontist/formula_template.rb +103 -0
- data/lib/fontist/formulas.rb +41 -0
- data/lib/fontist/import.rb +9 -0
- data/lib/fontist/import/convert_formulas.rb +65 -0
- data/lib/fontist/import/create_formula.rb +74 -0
- data/lib/fontist/import/extractors.rb +5 -0
- data/lib/fontist/import/extractors/cab_extractor.rb +37 -0
- data/lib/fontist/import/extractors/extractor.rb +19 -0
- data/lib/fontist/import/extractors/ole_extractor.rb +41 -0
- data/lib/fontist/import/extractors/seven_zip_extractor.rb +44 -0
- data/lib/fontist/import/extractors/zip_extractor.rb +31 -0
- data/lib/fontist/import/files/collection_file.rb +48 -0
- data/lib/fontist/import/formula_builder.rb +115 -0
- data/lib/fontist/import/formula_serializer.rb +133 -0
- data/lib/fontist/import/google.rb +16 -0
- data/lib/fontist/import/google/fonts_public.md +10 -0
- data/lib/fontist/import/google/fonts_public.pb.rb +71 -0
- data/lib/fontist/import/google/fonts_public.proto +46 -0
- data/lib/fontist/import/google/new_fonts_fetcher.rb +121 -0
- data/lib/fontist/import/google/skiplist.yml +6 -0
- data/lib/fontist/import/google_check.rb +34 -0
- data/lib/fontist/import/google_import.rb +180 -0
- data/lib/fontist/import/helpers/hash_helper.rb +13 -0
- data/lib/fontist/import/helpers/system_helper.rb +20 -0
- data/lib/fontist/import/otf/font_file.rb +89 -0
- data/lib/fontist/import/otf_parser.rb +25 -0
- data/lib/fontist/import/otf_style.rb +29 -0
- data/lib/fontist/import/otfinfo/otfinfo_requirement.rb +22 -0
- data/lib/fontist/import/otfinfo/template.erb +20 -0
- data/lib/fontist/import/otfinfo_generate.rb +45 -0
- data/lib/fontist/import/recursive_extraction.rb +101 -0
- data/lib/fontist/import/template_helper.rb +19 -0
- data/lib/fontist/import/text_helper.rb +30 -0
- data/lib/fontist/registry.rb +1 -0
- data/lib/fontist/system.yml +5 -1
- data/lib/fontist/system_font.rb +23 -20
- data/lib/fontist/utils.rb +6 -0
- data/lib/fontist/utils/cache.rb +69 -0
- data/lib/fontist/utils/downloader.rb +92 -0
- data/lib/fontist/utils/dsl.rb +8 -0
- data/lib/fontist/utils/dsl/font.rb +37 -0
- data/lib/fontist/utils/exe_extractor.rb +12 -19
- data/lib/fontist/utils/msi_extractor.rb +31 -0
- data/lib/fontist/utils/seven_zip_extractor.rb +41 -0
- data/lib/fontist/utils/system.rb +23 -0
- data/lib/fontist/utils/ui.rb +23 -0
- data/lib/fontist/utils/zip_extractor.rb +9 -4
- data/lib/fontist/version.rb +1 -1
- metadata +170 -50
- data/lib/fontist/downloader.rb +0 -70
- data/lib/fontist/formulas/andale_font.rb +0 -79
- data/lib/fontist/formulas/arial_black_font.rb +0 -78
- data/lib/fontist/formulas/cleartype_fonts.rb +0 -225
- data/lib/fontist/formulas/comic_font.rb +0 -77
- data/lib/fontist/formulas/courier_font.rb +0 -80
- data/lib/fontist/formulas/euphemia_font.rb +0 -85
- data/lib/fontist/formulas/georgia_font.rb +0 -79
- data/lib/fontist/formulas/impact_font.rb +0 -77
- data/lib/fontist/formulas/montserrat_font.rb +0 -132
- data/lib/fontist/formulas/ms_truetype_fonts.rb +0 -124
- data/lib/fontist/formulas/open_sans_fonts.rb +0 -263
- data/lib/fontist/formulas/overpass_font.rb +0 -73
- data/lib/fontist/formulas/source_fonts.rb +0 -109
- data/lib/fontist/formulas/stix_fonts.rb +0 -108
- data/lib/fontist/formulas/tahoma_font.rb +0 -147
- data/lib/fontist/formulas/webding_font.rb +0 -77
- data/spec/fontist/downloader_spec.rb +0 -35
- data/spec/fontist/font_formula_spec.rb +0 -67
- data/spec/fontist/font_spec.rb +0 -87
- data/spec/fontist/formula_spec.rb +0 -67
- data/spec/fontist/formulas/andale_font_spec.rb +0 -29
- data/spec/fontist/formulas/arial_black_font_spec.rb +0 -29
- data/spec/fontist/formulas/cleartype_fonts_spec.rb +0 -38
- data/spec/fontist/formulas/comic_font_spec.rb +0 -29
- data/spec/fontist/formulas/courier_font_spec.rb +0 -29
- data/spec/fontist/formulas/euphemia_font_spec.rb +0 -29
- data/spec/fontist/formulas/georgia_font_spec.rb +0 -29
- data/spec/fontist/formulas/impact_font_spec.rb +0 -29
- data/spec/fontist/formulas/montserrat_font_spec.rb +0 -29
- data/spec/fontist/formulas/ms_truetype_fonts_spec.rb +0 -29
- data/spec/fontist/formulas/open_sans_fonts_spec.rb +0 -29
- data/spec/fontist/formulas/overpass_font_spec.rb +0 -29
- data/spec/fontist/formulas/source_fonts_spec.rb +0 -31
- data/spec/fontist/formulas/stix_fonts_spec.rb +0 -29
- data/spec/fontist/formulas/tahoma_font_spec.rb +0 -29
- data/spec/fontist/formulas/webding_font_spec.rb +0 -29
- data/spec/fontist/registry_spec.rb +0 -47
- data/spec/fontist/system_font_spec.rb +0 -39
- data/spec/fontist_spec.rb +0 -5
- data/spec/spec_helper.rb +0 -22
- data/spec/support/fontist_helper.rb +0 -10
@@ -0,0 +1,133 @@
|
|
1
|
+
module Fontist
|
2
|
+
module Import
|
3
|
+
class FormulaSerializer
|
4
|
+
def initialize(formula, code)
|
5
|
+
@formula = formula
|
6
|
+
@code = code
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
to_h.compact
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def to_h
|
16
|
+
info_attributes.merge(fonts_attributes).merge(license_attributes)
|
17
|
+
end
|
18
|
+
|
19
|
+
def info_attributes
|
20
|
+
{ key: @formula.key,
|
21
|
+
name: formula_name(@formula),
|
22
|
+
description: @formula.description,
|
23
|
+
homepage: @formula.homepage }
|
24
|
+
end
|
25
|
+
|
26
|
+
def fonts_attributes
|
27
|
+
{ display_progress_bar: formula_progress_bar(@formula),
|
28
|
+
resources: @formula.resources,
|
29
|
+
font_collections: font_collections(@formula.fonts),
|
30
|
+
fonts: standalone_fonts(@formula.fonts),
|
31
|
+
extract: extract_type(@code) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def license_attributes
|
35
|
+
{ copyright: @formula.copyright,
|
36
|
+
license_url: @formula.license_url,
|
37
|
+
requires_license_agreement: requires_license_agreement(@formula),
|
38
|
+
open_license: open_license(@formula) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def formula_name(formula)
|
42
|
+
formula.class.name.match(/Formulas::(.+)Fonts?/)[1]
|
43
|
+
end
|
44
|
+
|
45
|
+
def formula_progress_bar(formula)
|
46
|
+
formula.options&.dig(:progress_bar)
|
47
|
+
end
|
48
|
+
|
49
|
+
def font_collections(fonts)
|
50
|
+
collections = collections(fonts)
|
51
|
+
return if collections.empty?
|
52
|
+
|
53
|
+
collections.map do |filename, coll_fonts|
|
54
|
+
{ filename: filename,
|
55
|
+
fonts: collection_fonts(coll_fonts) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def collections(fonts)
|
60
|
+
fonts.select { |f| collection_font?(f) }
|
61
|
+
.group_by { |f| f[:styles].first[:font] }
|
62
|
+
end
|
63
|
+
|
64
|
+
def collection_font?(font)
|
65
|
+
font[:styles].first[:font].end_with?(".ttc", ".TTC")
|
66
|
+
end
|
67
|
+
|
68
|
+
def collection_fonts(fonts)
|
69
|
+
fonts.map do |f|
|
70
|
+
{ name: f[:name], styles: collection_font_styles(f[:styles]) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def collection_font_styles(styles)
|
75
|
+
styles.map do |s|
|
76
|
+
collection_font_style(s)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def collection_font_style(style)
|
81
|
+
Hash.new.tap do |h|
|
82
|
+
h[:type] = style[:type]
|
83
|
+
|
84
|
+
if style[:collection]
|
85
|
+
h[:full_name] = collection_full_name(style[:collection])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def collection_full_name(collection_label)
|
91
|
+
if collection_label.is_a?(Hash)
|
92
|
+
collection_label.values_at(:name, :style).join(" ")
|
93
|
+
else
|
94
|
+
collection_label
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def standalone_fonts(fonts)
|
99
|
+
standalone = fonts.reject do |f|
|
100
|
+
collection_font?(f)
|
101
|
+
end
|
102
|
+
|
103
|
+
standalone.empty? ? nil : standalone
|
104
|
+
end
|
105
|
+
|
106
|
+
# rubocop:disable Metrics/MethodLength, Metrics/LineLength
|
107
|
+
def extract_type(code)
|
108
|
+
case code
|
109
|
+
when /def extract.+exe_extract.+cab_extract.+ppviewer\.cab/m
|
110
|
+
[{ format: :exe }, { format: :cab, file: "ppviewer.cab" }]
|
111
|
+
when /def extract.+cab_extract/m
|
112
|
+
{ format: :cab }
|
113
|
+
when /def extract.+(zip_extract|unzip).+fonts_sub_dir: "(.+?)"/m
|
114
|
+
dir = code.match(/def extract.+(zip_extract|unzip).+fonts_sub_dir: "(.+?)"/m)[2]
|
115
|
+
{ format: :zip, options: { fonts_sub_dir: dir } }
|
116
|
+
when /def extract.+(zip_extract|unzip)/m
|
117
|
+
{ format: :zip }
|
118
|
+
else
|
119
|
+
raise NotImplementedError, "Please implement an extract format"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
# rubocop:enable Metrics/MethodLength, Metrics/LineLength
|
123
|
+
|
124
|
+
def requires_license_agreement(formula)
|
125
|
+
formula.license if formula.license && formula.license_required
|
126
|
+
end
|
127
|
+
|
128
|
+
def open_license(formula)
|
129
|
+
formula.license if formula.license && !formula.license_required
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Fontist
|
2
|
+
module Import
|
3
|
+
module Google
|
4
|
+
def self.formula_path(name)
|
5
|
+
filename = name.downcase.gsub(" ", "_") + ".yml"
|
6
|
+
Fontist.formulas_path.join("google", filename)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.style_version(text)
|
10
|
+
return unless text
|
11
|
+
|
12
|
+
text.gsub("Version ", "")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# How to work with Protobuf
|
2
|
+
|
3
|
+
Protobuf file could be downloaded from
|
4
|
+
https://raw.githubusercontent.com/googlefonts/gftools/master/Lib/gftools/fonts_public.proto
|
5
|
+
|
6
|
+
To compile Protobuf to Ruby use
|
7
|
+
|
8
|
+
```sh
|
9
|
+
ruby-protoc lib/fontist/import/google/fonts_public.proto
|
10
|
+
```
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
+
|
4
|
+
require 'protocol_buffers'
|
5
|
+
|
6
|
+
module Google
|
7
|
+
module Fonts
|
8
|
+
# forward declarations
|
9
|
+
class FamilyProto < ::ProtocolBuffers::Message; end
|
10
|
+
class FontProto < ::ProtocolBuffers::Message; end
|
11
|
+
class AxisProto < ::ProtocolBuffers::Message; end
|
12
|
+
class SourceProto < ::ProtocolBuffers::Message; end
|
13
|
+
|
14
|
+
class FamilyProto < ::ProtocolBuffers::Message
|
15
|
+
# forward declarations
|
16
|
+
class ApiDefaultOverridesEntry < ::ProtocolBuffers::Message; end
|
17
|
+
|
18
|
+
set_fully_qualified_name "google.fonts.FamilyProto"
|
19
|
+
|
20
|
+
# nested messages
|
21
|
+
class ApiDefaultOverridesEntry < ::ProtocolBuffers::Message
|
22
|
+
set_fully_qualified_name "google.fonts.FamilyProto.ApiDefaultOverridesEntry"
|
23
|
+
|
24
|
+
optional :string, :key, 1
|
25
|
+
optional :float, :value, 2
|
26
|
+
end
|
27
|
+
|
28
|
+
required :string, :name, 1
|
29
|
+
required :string, :designer, 2
|
30
|
+
required :string, :license, 3
|
31
|
+
required :string, :category, 4
|
32
|
+
required :string, :date_added, 5
|
33
|
+
repeated ::Google::Fonts::FontProto, :fonts, 6
|
34
|
+
repeated :string, :aliases, 7
|
35
|
+
repeated :string, :subsets, 8
|
36
|
+
optional :string, :ttf_autohint_args, 9
|
37
|
+
repeated ::Google::Fonts::AxisProto, :axes, 10
|
38
|
+
repeated ::Google::Fonts::FamilyProto::ApiDefaultOverridesEntry, :api_default_overrides, 11
|
39
|
+
optional ::Google::Fonts::SourceProto, :source, 12
|
40
|
+
end
|
41
|
+
|
42
|
+
class FontProto < ::ProtocolBuffers::Message
|
43
|
+
set_fully_qualified_name "google.fonts.FontProto"
|
44
|
+
|
45
|
+
required :string, :name, 1
|
46
|
+
required :string, :style, 2
|
47
|
+
required :int32, :weight, 3
|
48
|
+
required :string, :filename, 4
|
49
|
+
required :string, :post_script_name, 5
|
50
|
+
required :string, :full_name, 6
|
51
|
+
optional :string, :copyright, 7
|
52
|
+
end
|
53
|
+
|
54
|
+
class AxisProto < ::ProtocolBuffers::Message
|
55
|
+
set_fully_qualified_name "google.fonts.AxisProto"
|
56
|
+
|
57
|
+
optional :string, :tag, 1
|
58
|
+
optional :float, :min_value, 2
|
59
|
+
optional :float, :default_value, 3
|
60
|
+
optional :float, :max_value, 4
|
61
|
+
end
|
62
|
+
|
63
|
+
class SourceProto < ::ProtocolBuffers::Message
|
64
|
+
set_fully_qualified_name "google.fonts.SourceProto"
|
65
|
+
|
66
|
+
optional :string, :repository_url, 1
|
67
|
+
optional :string, :commit, 2
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
syntax = "proto2";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Open Source'd font metadata proto formats.
|
5
|
+
*/
|
6
|
+
package google.fonts;
|
7
|
+
|
8
|
+
option java_package = "com.google.fonts.proto";
|
9
|
+
option java_outer_classname = "FontsPublic";
|
10
|
+
|
11
|
+
message FamilyProto {
|
12
|
+
required string name = 1;
|
13
|
+
required string designer = 2;
|
14
|
+
required string license = 3;
|
15
|
+
required string category = 4;
|
16
|
+
required string date_added = 5;
|
17
|
+
repeated FontProto fonts = 6;
|
18
|
+
repeated string aliases = 7;
|
19
|
+
repeated string subsets = 8;
|
20
|
+
optional string ttf_autohint_args = 9;
|
21
|
+
repeated AxisProto axes = 10;
|
22
|
+
map<string, float> api_default_overrides = 11;
|
23
|
+
optional SourceProto source = 12;
|
24
|
+
};
|
25
|
+
|
26
|
+
message FontProto {
|
27
|
+
required string name = 1;
|
28
|
+
required string style = 2;
|
29
|
+
required int32 weight = 3;
|
30
|
+
required string filename = 4;
|
31
|
+
required string post_script_name = 5;
|
32
|
+
required string full_name = 6;
|
33
|
+
optional string copyright = 7;
|
34
|
+
};
|
35
|
+
|
36
|
+
message AxisProto {
|
37
|
+
optional string tag = 1;
|
38
|
+
optional float min_value = 2;
|
39
|
+
optional float default_value = 3;
|
40
|
+
optional float max_value = 4;
|
41
|
+
};
|
42
|
+
|
43
|
+
message SourceProto {
|
44
|
+
optional string repository_url = 1;
|
45
|
+
optional string commit = 2;
|
46
|
+
}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require_relative "fonts_public.pb"
|
2
|
+
require_relative "../google"
|
3
|
+
require_relative "../otf_parser"
|
4
|
+
|
5
|
+
module Fontist
|
6
|
+
module Import
|
7
|
+
module Google
|
8
|
+
class NewFontsFetcher
|
9
|
+
REPO_PATH = Fontist.root_path.join("tmp", "fonts")
|
10
|
+
REPO_URL = "https://github.com/google/fonts.git".freeze
|
11
|
+
SKIPLIST_PATH = File.expand_path("skiplist.yml", __dir__)
|
12
|
+
|
13
|
+
def initialize(logging: false)
|
14
|
+
@logging = logging
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
update_repo
|
19
|
+
fetch_new_paths
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def update_repo
|
25
|
+
if Dir.exist?(REPO_PATH)
|
26
|
+
`cd #{REPO_PATH} && git pull`
|
27
|
+
else
|
28
|
+
`git clone #{REPO_URL} #{REPO_PATH}`
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def fetch_new_paths
|
33
|
+
fetch_fonts_paths.select do |path|
|
34
|
+
log_font(path) do
|
35
|
+
new?(path)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def fetch_fonts_paths
|
41
|
+
Dir[File.join(REPO_PATH, "apache", "*"),
|
42
|
+
File.join(REPO_PATH, "ofl", "*"),
|
43
|
+
File.join(REPO_PATH, "ufl", "*")].sort
|
44
|
+
end
|
45
|
+
|
46
|
+
def log_font(path)
|
47
|
+
return yield unless @logging
|
48
|
+
|
49
|
+
print "#{path}, "
|
50
|
+
new = yield
|
51
|
+
puts(new ? "new" : "skipped")
|
52
|
+
new
|
53
|
+
end
|
54
|
+
|
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)
|
73
|
+
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
def in_skiplist?(name)
|
78
|
+
@skiplist ||= YAML.safe_load(File.open(SKIPLIST_PATH))
|
79
|
+
@skiplist.include?(name)
|
80
|
+
end
|
81
|
+
|
82
|
+
def up_to_date?(metadata, path)
|
83
|
+
formula = formula(metadata.name)
|
84
|
+
return false unless formula
|
85
|
+
|
86
|
+
styles = formula.fonts.map(&:styles).flatten
|
87
|
+
|
88
|
+
styles.all? do |style|
|
89
|
+
style.version == otfinfo_version(font_path(style.font, path))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def formula(font_name)
|
94
|
+
klass = font_name.gsub(/ /, "").sub(/\S/, &:upcase)
|
95
|
+
@formulas ||= Fontist::Formula.all
|
96
|
+
@formulas["Fontist::Formulas::#{klass}Font"]
|
97
|
+
end
|
98
|
+
|
99
|
+
def font_path(filename, directory)
|
100
|
+
File.join(directory, fix_variable_filename(filename))
|
101
|
+
end
|
102
|
+
|
103
|
+
def fix_variable_filename(filename)
|
104
|
+
filename.sub("-VariableFont_wght", "[wght]")
|
105
|
+
end
|
106
|
+
|
107
|
+
def otfinfo_version(path)
|
108
|
+
info = OtfParser.new(path).call
|
109
|
+
Fontist::Import::Google.style_version(info["Version"])
|
110
|
+
end
|
111
|
+
|
112
|
+
def downloadable?(name)
|
113
|
+
Down.open("https://fonts.google.com/download?family=#{name}")
|
114
|
+
true
|
115
|
+
rescue Down::NotFound
|
116
|
+
false
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative "google/new_fonts_fetcher"
|
2
|
+
|
3
|
+
module Fontist
|
4
|
+
module Import
|
5
|
+
class GoogleCheck
|
6
|
+
def call
|
7
|
+
fetch_formulas
|
8
|
+
fonts = new_fonts
|
9
|
+
indicate(fonts)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def fetch_formulas
|
15
|
+
Formulas.fetch_formulas
|
16
|
+
end
|
17
|
+
|
18
|
+
def new_fonts
|
19
|
+
Fontist::Import::Google::NewFontsFetcher.new(logging: true).call
|
20
|
+
end
|
21
|
+
|
22
|
+
def indicate(new_paths)
|
23
|
+
return if new_paths.empty?
|
24
|
+
|
25
|
+
puts "New fonts are available in:"
|
26
|
+
new_paths.each do |path|
|
27
|
+
puts path
|
28
|
+
end
|
29
|
+
|
30
|
+
abort
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require "erb"
|
2
|
+
require_relative "google"
|
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"
|
9
|
+
|
10
|
+
module Fontist
|
11
|
+
module Import
|
12
|
+
class GoogleImport
|
13
|
+
def call
|
14
|
+
fonts = new_fonts
|
15
|
+
create_formulas(fonts)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def new_fonts
|
21
|
+
Fontist::Import::Google::NewFontsFetcher.new(logging: true).call
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_formulas(fonts)
|
25
|
+
puts "Creating formulas..."
|
26
|
+
fonts.each do |path|
|
27
|
+
create_formula(path)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
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
|
37
|
+
|
38
|
+
def fetch_metadata(path)
|
39
|
+
protobuf = File.read(File.join(path, "METADATA.pb"))
|
40
|
+
::Google::Fonts::FamilyProto.parse_from_text(protobuf)
|
41
|
+
end
|
42
|
+
|
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)
|
50
|
+
end
|
51
|
+
|
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
|
61
|
+
end
|
62
|
+
|
63
|
+
def variable_style?(metadata)
|
64
|
+
metadata.fonts.any? do |s|
|
65
|
+
s.filename.match?(/\[(.+,)?wght\]/)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
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
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
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.slice(:post_script_name,
|
160
|
+
:version,
|
161
|
+
:description,
|
162
|
+
:copyright).compact)
|
163
|
+
h.merge!(font: fix_variable_filename(style.filename))
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def fix_variable_filename(filename)
|
168
|
+
filename.sub("[wght]", "-VariableFont_wght")
|
169
|
+
end
|
170
|
+
|
171
|
+
def formula_path(name)
|
172
|
+
Fontist::Import::Google.formula_path(name)
|
173
|
+
end
|
174
|
+
|
175
|
+
def save_to_path(hash, path)
|
176
|
+
File.write(path, YAML.dump(hash))
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|