Class: Fontisan::Woff2::GlyfCanonicalizer

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/woff2/glyf_canonicalizer.rb

Overview

Canonicalizes a source glyf table by padding each glyph to a 4-byte boundary, matching fontTools' WOFF2 encoder behavior.

fontTools' WOFF2 writer calls _normaliseGlyfAndLoca(padding=4) before transforming the glyf table (see Lib/fontTools/ttLib/woff2.py). Each glyph is recompiled with recalcBBoxes=False, which makes Glyph#compile return the source glyph bytes verbatim, then the glyf-table-level compile pads each glyph to the configured alignment. The result is a glyf whose bytes match the source byte-for-byte except for added padding (≤ 3 bytes per glyph).

Chrome's OpenType Sanitizer (OTS) fails to reconstruct "unpadded" glyf tables — see the long comment in WOFF2Writer.close(): https://github.com/google/woff2/issues/15 https://github.com/khaledhosny/ots/issues/60

The canonical glyf is the bytes the WOFF2 transform encodes, the bytes the decoder reconstructs, the bytes glyf.origLength reports, and the bytes the SFNT checksum is computed over. All four must agree.

Instance Method Summary collapse

Constructor Details

#initialize(glyf_data:, loca_data:, num_glyphs:, source_format:, target_format: nil) ⇒ GlyfCanonicalizer

Returns a new instance of GlyfCanonicalizer.

Parameters:

  • glyf_data (String)

    Source glyf table bytes

  • loca_data (String)

    Source loca table bytes

  • num_glyphs (Integer)

    Glyph count from maxp

  • source_format (Integer)

    0 (short loca) or 1 (long loca) — how to interpret the source loca

  • target_format (Integer) (defaults to: nil)

    0 (short loca) or 1 (long loca) — how to emit the canonical loca. Defaults to source_format.



33
34
35
36
37
38
39
40
# File 'lib/fontisan/woff2/glyf_canonicalizer.rb', line 33

def initialize(glyf_data:, loca_data:, num_glyphs:, source_format:,
               target_format: nil)
  @glyf_data = glyf_data
  @loca_data = loca_data
  @num_glyphs = num_glyphs
  @source_format = source_format
  @target_format = target_format || source_format
end

Instance Method Details

#canonicalHash{Symbol => String}

Returns { glyf:, loca: } where glyf is padded to 4-byte alignment per glyph and loca is emitted in target_format.

Returns:

  • (Hash{Symbol => String})

    { glyf:, loca: } where glyf is padded to 4-byte alignment per glyph and loca is emitted in target_format.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/fontisan/woff2/glyf_canonicalizer.rb', line 45

def canonical
  glyf = String.new(encoding: Encoding::BINARY)
  new_offsets = [0]

  @num_glyphs.times do |i|
    start_offset = source_offsets[i]
    end_offset = source_offsets[i + 1]
    if start_offset < end_offset
      glyf << @glyf_data.byteslice(start_offset, end_offset - start_offset)
    end
    remainder = glyf.bytesize % 4
    glyf << ("\x00" * (4 - remainder)) if remainder.positive?
    new_offsets << glyf.bytesize
  end

  { glyf:, loca: build_loca(new_offsets) }
end