Class: Fontisan::Woff2::EncoderRules
- Inherits:
-
Object
- Object
- Fontisan::Woff2::EncoderRules
- Defined in:
- lib/fontisan/woff2/encoder_rules.rb
Overview
Applies WOFF2 spec "encoder MUST" rules to a table collection before the table directory is built and brotli compression is applied.
Each spec-mandated transform lives here so the encoder stays a thin orchestrator and new rules are MECE/discoverable instead of sprinkled as inline conditionals across Woff2Encoder.
Reference: W3C WOFF2 spec, section 5 ("Recommended table directory").
Constant Summary collapse
- EXCLUDED_TABLES =
Tables that MUST NOT appear in WOFF2 output.
Per spec section 5: "The compliant WOFF2 encoder MUST remove the DSIG table from an input font data, prior to applying transformations and entropy coding steps." Chrome's OpenType Sanitizer (OTS) rejects WOFF2 files that still contain DSIG.
%w[DSIG].freeze
- TRANSFORMED_TABLES =
Tables that MUST be transformed (when present) per WOFF2 spec section 5.3: glyf and loca are paired — both are either transformed (version 0) or null-transformed (version 3) together. Browsers (Chrome OTS) only accept the transformed form.
%w[glyf loca].freeze
Class Method Summary collapse
-
.apply!(table_data) ⇒ Hash{String => String}
Apply all WOFF2 encoder rules to
table_datain place. -
.exclude_tables!(table_data) ⇒ Object
Drop spec-excluded tables from the collection.
-
.excluded?(tag) ⇒ Boolean
Whether
tagis dropped by WOFF2 spec rules. -
.mark_lossless_modifying!(table_data) ⇒ Object
Set head.flags bit 11 to indicate the font was losslessly modified.
-
.set_head_index_to_loc_format!(table_data, format_code) ⇒ Object
Set head.indexToLocFormat to match the chosen output loca format.
-
.touch_head_modified!(table_data) ⇒ Object
Set head.modified to the current time.
Class Method Details
.apply!(table_data) ⇒ Hash{String => String}
Apply all WOFF2 encoder rules to table_data in place.
33 34 35 36 37 38 |
# File 'lib/fontisan/woff2/encoder_rules.rb', line 33 def self.apply!(table_data) exclude_tables!(table_data) (table_data) touch_head_modified!(table_data) table_data end |
.exclude_tables!(table_data) ⇒ Object
Drop spec-excluded tables from the collection.
46 47 48 |
# File 'lib/fontisan/woff2/encoder_rules.rb', line 46 def self.exclude_tables!(table_data) EXCLUDED_TABLES.each { |tag| table_data.delete(tag) } end |
.excluded?(tag) ⇒ Boolean
Whether tag is dropped by WOFF2 spec rules.
41 42 43 |
# File 'lib/fontisan/woff2/encoder_rules.rb', line 41 def self.excluded?(tag) EXCLUDED_TABLES.include?(tag) end |
.mark_lossless_modifying!(table_data) ⇒ Object
Set head.flags bit 11 to indicate the font was losslessly modified.
Per spec section 5: "The WOFF 2.0 encoders MUST also set bit 11 of the 'flags' field of the head table ... to indicate that a recreated font file was subjected to lossless modifying transform."
Uses the model-driven Head BinData record so the bits are set via a named attribute on a typed object, not via raw byte slicing.
58 59 60 61 62 63 64 |
# File 'lib/fontisan/woff2/encoder_rules.rb', line 58 def self.(table_data) return unless table_data.key?("head") head = Tables::Head.read(table_data["head"]) head.flags |= Tables::Head::FLAG_LOSSLESS_MODIFYING table_data["head"] = head.to_binary_s end |
.set_head_index_to_loc_format!(table_data, format_code) ⇒ Object
Set head.indexToLocFormat to match the chosen output loca format.
fontTools' WOFF2 encoder re-picks the loca format (preferring short when glyf fits). The reconstructed SFNT must carry the matching indexToLocFormat in head, or Chrome's OTS rejects the file with "Failed to convert WOFF 2.0 font to SFNT".
75 76 77 78 79 80 81 |
# File 'lib/fontisan/woff2/encoder_rules.rb', line 75 def self.set_head_index_to_loc_format!(table_data, format_code) return unless table_data.key?("head") head = Tables::Head.read(table_data["head"]) head.index_to_loc_format = format_code table_data["head"] = head.to_binary_s end |
.touch_head_modified!(table_data) ⇒ Object
Set head.modified to the current time.
Chrome's OTS rejects WOFF2 fonts whose head.modified is not meaningfully later than head.created. Source fonts frequently carry modified == created (or modified within seconds of it), which Chrome rejects. Setting modified to the actual current wall-clock time matches fontTools' behaviour (it sets head.modified = timestampNow() on every save) and gives Chrome a delta large enough to accept.
Must be called BEFORE checksum recompute so the modified bytes are included in the checksum.
97 98 99 100 101 102 103 |
# File 'lib/fontisan/woff2/encoder_rules.rb', line 97 def self.touch_head_modified!(table_data) return unless table_data.key?("head") head = Tables::Head.read(table_data["head"]) head.modified_raw = Tables::Head.now_longdatetime table_data["head"] = head.to_binary_s end |