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.
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fontisan
4
- VERSION = "0.4.35"
4
+ VERSION = "0.4.37"
5
5
  end
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.35
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