Module: Fontisan::Woff2::Directory

Defined in:
lib/fontisan/woff2/directory.rb

Overview

WOFF2 Table Directory Entry

Woff2::Directory represents a single table entry in the WOFF2 table directory. Unlike WOFF, WOFF2 uses variable-length encoding for sizes and supports table transformations for better compression.

Each entry contains:

  • flags (1 byte): Contains tag index and transformation version
  • tag (0 or 4 bytes): Table tag (omitted if using known tag index)
  • origLength (UIntBase128): Original uncompressed table length
  • transformLength (UIntBase128, optional): Transformed data length

Flags byte structure:

  • Bits 0-5: Table tag index (0-62 = known tags, 63 = custom tag)
  • Bits 6-7: Transformation version

Reference: https://www.w3.org/TR/WOFF2/#table_dir_format

Examples:

Create entry for known table

entry = Directory::Entry.new
entry.tag = "glyf"
entry.orig_length = 12000
entry.flags = entry.calculate_flags

Create entry for custom table

entry = Directory::Entry.new
entry.tag = "CUST"
entry.orig_length = 5000
entry.flags = 0x3F # Custom tag indicator

Defined Under Namespace

Classes: Entry

Constant Summary collapse

KNOWN_TAGS =

Known table tags with assigned indices (0-62) Index 63 (0x3F) indicates a custom tag follows

[
  "cmap", "head", "hhea", "hmtx", "maxp", "name", "OS/2", "post",
  "cvt ", "fpgm", "glyf", "loca", "prep", "CFF ", "VORG", "EBDT",
  "EBLC", "gasp", "hdmx", "kern", "LTSH", "PCLT", "VDMX", "vhea",
  "vmtx", "BASE", "GDEF", "GPOS", "GSUB", "EBSC", "JSTF", "MATH",
  "CBDT", "CBLC", "COLR", "CPAL", "SVG ", "sbix", "acnt", "avar",
  "bdat", "bloc", "bsln", "cvar", "fdsc", "feat", "fmtx", "fvar",
  "gvar", "hsty", "just", "lcar", "mort", "morx", "opbd", "prop",
  "trak", "Zapf", "Silf", "Glat", "Gloc", "Feat", "Sill"
].freeze
TRANSFORM_NULL =

Transformation versions per WOFF2 spec section 5.1, 5.3, 5.4.

glyf/loca:

- version 0: transformed format (spec section 5.1 / 5.3)
- version 3: null transform — original bytes passed to brotli as-is
(Chrome's OTS does not accept version 3 for glyf/loca; encoders
that want to interoperate with browsers MUST use version 0).

hmtx:

- version 0: null transform (default)
- version 1: transformed format (spec section 5.4)

All other tables use version 0 for the null transform.

3
TRANSFORM_NONE =

glyf/loca null transform

TRANSFORM_NULL
TRANSFORM_GLYF_LOCA =

deprecated alias kept for older callers

0
TRANSFORM_HMTX =

glyf/loca transformed format

1
TRANSFORM_HMTX_NULL =

hmtx transformed format

0
CUSTOM_TAG_INDEX =

Custom tag indicator

0x3F

Class Method Summary collapse

Class Method Details

.decode_255_uint16(io) ⇒ Integer

Decode 255UInt16 from IO. Delegates to UInt255.

Parameters:

  • io (IO)

    Input stream

Returns:

  • (Integer)

    Decoded value



250
251
252
# File 'lib/fontisan/woff2/directory.rb', line 250

def self.decode_255_uint16(io)
  UInt255.decode(io)
end

.decode_uint_base128(io) ⇒ Integer

Decode UIntBase128 from IO

Parameters:

  • io (IO)

    Input stream

Returns:

  • (Integer)

    Decoded value

Raises:

  • (Error)

    If encoding is invalid



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/fontisan/woff2/directory.rb', line 220

def self.decode_uint_base128(io)
  result = 0
  5.times do
    byte = io.read(1)&.unpack1("C")
    return nil unless byte

    # Check if high bit is set (continuation)
    if (byte & 0x80).zero?
      return (result << 7) | byte
    else
      result = (result << 7) | (byte & 0x7F)
    end
  end

  # If we're here, encoding is invalid (> 5 bytes)
  raise Fontisan::Error, "Invalid UIntBase128 encoding"
end

.encode_255_uint16(value) ⇒ String

Encode 255UInt16. Delegates to UInt255 (single source of truth).

Parameters:

  • value (Integer)

    Value to encode (0-65535)

Returns:

  • (String)

    Binary encoded data



242
243
244
# File 'lib/fontisan/woff2/directory.rb', line 242

def self.encode_255_uint16(value)
  UInt255.encode(value)
end

.encode_uint_base128(value) ⇒ String

Encode an integer as UIntBase128

Variable-length encoding where:

  • If value < 128, use 1 byte
  • Otherwise, use high bit to indicate continuation

Parameters:

  • value (Integer)

    Value to encode

Returns:

  • (String)

    Binary encoded data



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/fontisan/woff2/directory.rb', line 194

def self.encode_uint_base128(value)
  return [value].pack("C") if value < 128

  bytes = []
  v = value

  # Build bytes from least to most significant
  loop do
    bytes.unshift(v & 0x7F)
    v >>= 7
    break if v.zero?
  end

  # Set high bit on all but last byte
  (0...bytes.length - 1).each do |i|
    bytes[i] |= 0x80
  end

  bytes.pack("C*")
end