Module: Fontisan::Woff2::Directory

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

Overview

WOFF2 Table Directory Entry

[‘Woff2::Directory`](lib/fontisan/woff2/directory.rb) 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: 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_NONE =

Transformation versions

0
TRANSFORM_GLYF_LOCA =

Applied to both glyf and loca

0
TRANSFORM_HMTX =

Applied to hmtx

0
CUSTOM_TAG_INDEX =

Custom tag indicator

0x3F

Class Method Summary collapse

Class Method Details

.decode_255_uint16(io) ⇒ Integer

Decode 255UInt16 from IO

Parameters:

  • io (IO)

    Input stream

Returns:

  • (Integer)

    Decoded value



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/fontisan/woff2/directory.rb', line 238

def self.decode_255_uint16(io)
  first = io.read(1)&.unpack1("C")
  return nil unless first

  case first
  when 0..252
    first
  when 253
    second = io.read(1)&.unpack1("C")
    253 + second
  when 254
    io.read(2)&.unpack1("n")
  when 255
    value = io.read(2)&.unpack1("n")
    value + 506
  end
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



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

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 value

Used in transformed glyf table:

  • 0-252: value itself (1 byte)

  • 253: next byte + 253 (2 bytes)

  • 254: next 2 bytes as big-endian (3 bytes)

  • 255: next 2 bytes + 506 (3 bytes)

Parameters:

  • value (Integer)

    Value to encode (0-65535)

Returns:

  • (String)

    Binary encoded data



222
223
224
225
226
227
228
229
230
231
232
# File 'lib/fontisan/woff2/directory.rb', line 222

def self.encode_255_uint16(value)
  if value < 253
    [value].pack("C")
  elsif value < 506
    [253, value - 253].pack("C*")
  elsif value < 65536
    [254].pack("C") + [value].pack("n")
  else
    [255].pack("C") + [value - 506].pack("n")
  end
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



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/fontisan/woff2/directory.rb', line 168

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