fontisan 0.4.35 → 0.4.37
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +15 -3
- data/.rubocop_todo.yml +23 -11
- data/TODO.improvements/README.md +1 -1
- data/lib/fontisan/audit/check_registry.rb +26 -2
- data/lib/fontisan/audit/checks/format_round_trip_check.rb +150 -0
- data/lib/fontisan/audit/checks/head_check.rb +96 -0
- data/lib/fontisan/audit/checks/hhea_check.rb +68 -0
- data/lib/fontisan/audit/checks/hinting_check.rb +163 -0
- data/lib/fontisan/audit/checks/kern_check.rb +47 -0
- data/lib/fontisan/audit/checks/maxp_check.rb +85 -0
- data/lib/fontisan/audit/checks/name_table_check.rb +78 -0
- data/lib/fontisan/audit/checks/opentype_conformance_check.rb +105 -0
- data/lib/fontisan/audit/checks/os2_check.rb +117 -0
- data/lib/fontisan/audit/checks/post_check.rb +75 -0
- data/lib/fontisan/audit/checks/variable_font_check.rb +151 -0
- data/lib/fontisan/audit/checks/woff2_validation_check.rb +140 -0
- data/lib/fontisan/audit/checks.rb +15 -0
- data/lib/fontisan/version.rb +1 -1
- metadata +13 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fontisan
|
|
4
|
+
module Audit
|
|
5
|
+
module Checks
|
|
6
|
+
# Validates WOFF2 (Web Open Font Format 2) wrapper structure per
|
|
7
|
+
# the W3C WOFF2 spec. Only runs when the font IS a Woff2Font —
|
|
8
|
+
# plain TTF/OTF inputs are skipped.
|
|
9
|
+
#
|
|
10
|
+
# Checks:
|
|
11
|
+
#
|
|
12
|
+
# - Signature must be 0x774F4632 ('wOF2')
|
|
13
|
+
# - Flavor must be a valid SFNT version
|
|
14
|
+
# - reserved field must be 0
|
|
15
|
+
# - major_version should be 1
|
|
16
|
+
# - total_compressed_size must be positive
|
|
17
|
+
# - If metadata block is present, lengths must be consistent
|
|
18
|
+
# - num_tables must match the table directory count
|
|
19
|
+
#
|
|
20
|
+
# @see https://www.w3.org/TR/WOFF2/
|
|
21
|
+
class Woff2ValidationCheck < Check
|
|
22
|
+
WOFF2_SIGNATURE = 0x774F4632
|
|
23
|
+
VALID_FLAVORS = [
|
|
24
|
+
0x00010000, # TrueType
|
|
25
|
+
0x74727565, # 'true' (Apple TrueType)
|
|
26
|
+
0x4F54544F, # 'OTTO' (OpenType CFF)
|
|
27
|
+
0x74746366, # 'ttcf' (collection)
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
# @param font [Woff2Font, SfntFont]
|
|
31
|
+
# @return [Array<Models::ValidationReport::Issue>]
|
|
32
|
+
def self.call(font)
|
|
33
|
+
return [] unless woff2?(font)
|
|
34
|
+
|
|
35
|
+
header = font.header
|
|
36
|
+
issues = []
|
|
37
|
+
issues.concat(validate_signature(header))
|
|
38
|
+
issues.concat(validate_flavor(header))
|
|
39
|
+
issues.concat(validate_reserved(header))
|
|
40
|
+
issues.concat(validate_version(header))
|
|
41
|
+
issues.concat(validate_sizes(header))
|
|
42
|
+
issues.concat(validate_metadata(header))
|
|
43
|
+
issues
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.code
|
|
47
|
+
:woff2_validation
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.woff2?(font)
|
|
51
|
+
font.is_a?(Woff2Font)
|
|
52
|
+
end
|
|
53
|
+
private_class_method :woff2?
|
|
54
|
+
|
|
55
|
+
def self.validate_signature(header)
|
|
56
|
+
return [] if header.signature == WOFF2_SIGNATURE
|
|
57
|
+
|
|
58
|
+
[issue(severity: :error,
|
|
59
|
+
message: "WOFF2 signature is 0x#{header.signature.to_s(16)} " \
|
|
60
|
+
"but must be 0x#{WOFF2_SIGNATURE.to_s(16)} ('wOF2')",
|
|
61
|
+
location: "woff2_header.signature")]
|
|
62
|
+
end
|
|
63
|
+
private_class_method :validate_signature
|
|
64
|
+
|
|
65
|
+
def self.validate_flavor(header)
|
|
66
|
+
return [] if VALID_FLAVORS.include?(header.flavor)
|
|
67
|
+
|
|
68
|
+
[issue(severity: :error,
|
|
69
|
+
message: "WOFF2 flavor is 0x#{header.flavor.to_s(16)} " \
|
|
70
|
+
"but must be a valid SFNT version " \
|
|
71
|
+
"(0x00010000, 0x74727565, 0x4F54544F, or 0x74746366)",
|
|
72
|
+
location: "woff2_header.flavor")]
|
|
73
|
+
end
|
|
74
|
+
private_class_method :validate_flavor
|
|
75
|
+
|
|
76
|
+
def self.validate_reserved(header)
|
|
77
|
+
return [] if header.reserved.zero?
|
|
78
|
+
|
|
79
|
+
[issue(severity: :warning,
|
|
80
|
+
message: "WOFF2 reserved field is #{header.reserved} " \
|
|
81
|
+
"but must be 0 per the spec",
|
|
82
|
+
location: "woff2_header.reserved")]
|
|
83
|
+
end
|
|
84
|
+
private_class_method :validate_reserved
|
|
85
|
+
|
|
86
|
+
def self.validate_version(header)
|
|
87
|
+
issues = []
|
|
88
|
+
if header.major_version != 1
|
|
89
|
+
issues << issue(severity: :warning,
|
|
90
|
+
message: "WOFF2 major version is #{header.major_version} " \
|
|
91
|
+
"but the spec recommends 1",
|
|
92
|
+
location: "woff2_header.major_version")
|
|
93
|
+
end
|
|
94
|
+
issues
|
|
95
|
+
end
|
|
96
|
+
private_class_method :validate_version
|
|
97
|
+
|
|
98
|
+
def self.validate_sizes(header)
|
|
99
|
+
issues = []
|
|
100
|
+
if header.total_compressed_size.to_i <= 0
|
|
101
|
+
issues << issue(severity: :error,
|
|
102
|
+
message: "WOFF2 total_compressed_size is " \
|
|
103
|
+
"#{header.total_compressed_size} but must be positive",
|
|
104
|
+
location: "woff2_header.total_compressed_size")
|
|
105
|
+
end
|
|
106
|
+
if header.total_sfnt_size.to_i <= 0
|
|
107
|
+
issues << issue(severity: :error,
|
|
108
|
+
message: "WOFF2 total_sfnt_size is " \
|
|
109
|
+
"#{header.total_sfnt_size} but must be positive",
|
|
110
|
+
location: "woff2_header.total_sfnt_size")
|
|
111
|
+
end
|
|
112
|
+
issues
|
|
113
|
+
end
|
|
114
|
+
private_class_method :validate_sizes
|
|
115
|
+
|
|
116
|
+
def self.validate_metadata(header)
|
|
117
|
+
return [] unless header.meta_offset.to_i.positive?
|
|
118
|
+
|
|
119
|
+
issues = []
|
|
120
|
+
if header.meta_length.to_i <= 0
|
|
121
|
+
issues << issue(severity: :error,
|
|
122
|
+
message: "WOFF2 metadata offset is set but " \
|
|
123
|
+
"meta_length is #{header.meta_length} " \
|
|
124
|
+
"(must be positive)",
|
|
125
|
+
location: "woff2_header.meta_length")
|
|
126
|
+
end
|
|
127
|
+
if header.meta_orig_length.to_i <= 0
|
|
128
|
+
issues << issue(severity: :error,
|
|
129
|
+
message: "WOFF2 metadata offset is set but " \
|
|
130
|
+
"meta_orig_length is #{header.meta_orig_length} " \
|
|
131
|
+
"(must be positive)",
|
|
132
|
+
location: "woff2_header.meta_orig_length")
|
|
133
|
+
end
|
|
134
|
+
issues
|
|
135
|
+
end
|
|
136
|
+
private_class_method :validate_metadata
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -17,6 +17,21 @@ module Fontisan
|
|
|
17
17
|
"fontisan/audit/checks/ots_compatibility_check"
|
|
18
18
|
autoload :CollectionIntegrityCheck,
|
|
19
19
|
"fontisan/audit/checks/collection_integrity_check"
|
|
20
|
+
autoload :VariableFontCheck, "fontisan/audit/checks/variable_font_check"
|
|
21
|
+
autoload :HintingCheck, "fontisan/audit/checks/hinting_check"
|
|
22
|
+
autoload :Woff2ValidationCheck,
|
|
23
|
+
"fontisan/audit/checks/woff2_validation_check"
|
|
24
|
+
autoload :FormatRoundTripCheck,
|
|
25
|
+
"fontisan/audit/checks/format_round_trip_check"
|
|
26
|
+
autoload :OpenTypeConformanceCheck,
|
|
27
|
+
"fontisan/audit/checks/opentype_conformance_check"
|
|
28
|
+
autoload :HeadCheck, "fontisan/audit/checks/head_check"
|
|
29
|
+
autoload :HheaCheck, "fontisan/audit/checks/hhea_check"
|
|
30
|
+
autoload :MaxpCheck, "fontisan/audit/checks/maxp_check"
|
|
31
|
+
autoload :Os2Check, "fontisan/audit/checks/os2_check"
|
|
32
|
+
autoload :NameTableCheck, "fontisan/audit/checks/name_table_check"
|
|
33
|
+
autoload :PostCheck, "fontisan/audit/checks/post_check"
|
|
34
|
+
autoload :KernCheck, "fontisan/audit/checks/kern_check"
|
|
20
35
|
end
|
|
21
36
|
end
|
|
22
37
|
end
|
data/lib/fontisan/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fontisan
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.37
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -299,9 +299,21 @@ files:
|
|
|
299
299
|
- lib/fontisan/audit/checks.rb
|
|
300
300
|
- lib/fontisan/audit/checks/cmap_check.rb
|
|
301
301
|
- lib/fontisan/audit/checks/collection_integrity_check.rb
|
|
302
|
+
- lib/fontisan/audit/checks/format_round_trip_check.rb
|
|
302
303
|
- lib/fontisan/audit/checks/glyph_name_check.rb
|
|
304
|
+
- lib/fontisan/audit/checks/head_check.rb
|
|
305
|
+
- lib/fontisan/audit/checks/hhea_check.rb
|
|
306
|
+
- lib/fontisan/audit/checks/hinting_check.rb
|
|
307
|
+
- lib/fontisan/audit/checks/kern_check.rb
|
|
308
|
+
- lib/fontisan/audit/checks/maxp_check.rb
|
|
309
|
+
- lib/fontisan/audit/checks/name_table_check.rb
|
|
310
|
+
- lib/fontisan/audit/checks/opentype_conformance_check.rb
|
|
311
|
+
- lib/fontisan/audit/checks/os2_check.rb
|
|
303
312
|
- lib/fontisan/audit/checks/ots_compatibility_check.rb
|
|
313
|
+
- lib/fontisan/audit/checks/post_check.rb
|
|
304
314
|
- lib/fontisan/audit/checks/table_directory_check.rb
|
|
315
|
+
- lib/fontisan/audit/checks/variable_font_check.rb
|
|
316
|
+
- lib/fontisan/audit/checks/woff2_validation_check.rb
|
|
305
317
|
- lib/fontisan/base_collection.rb
|
|
306
318
|
- lib/fontisan/binary.rb
|
|
307
319
|
- lib/fontisan/binary/base_record.rb
|