Class: Fontisan::Variation::DeltaParser

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/variation/delta_parser.rb

Overview

Parses variation deltas from gvar tuple data

The gvar table stores deltas in various compression formats to minimize file size. This parser handles all delta formats and decompresses them into usable point delta arrays.

Delta Formats:

  • DELTAS_ARE_ZERO: All deltas are zero (no data stored)

  • DELTAS_ARE_WORDS: Deltas stored as signed 16-bit words

  • DELTAS_ARE_BYTES: Deltas stored as signed 8-bit bytes

  • Point number runs: Compressed sequences of affected points

Reference: OpenType specification, gvar table delta encoding

Examples:

Parsing delta data

parser = Fontisan::Variation::DeltaParser.new
deltas = parser.parse(tuple_data, point_count)
# Returns: [{ x: 10, y: 5 }, { x: -3, y: 2 }, ...]

Constant Summary collapse

DELTAS_ARE_ZERO =

Delta format flags (from tuple variation header flags)

0x80
DELTAS_ARE_WORDS =
0x40
POINTS_ARE_WORDS =

Point number flags

0x80
POINT_RUN_COUNT_MASK =
0x7F

Instance Method Summary collapse

Instance Method Details

#parse(data, point_count, private_points: false, shared_points: nil) ⇒ Array<Hash>

Parse delta data from tuple variation

Parameters:

  • data (String)

    Binary delta data

  • point_count (Integer)

    Total number of points in glyph

  • private_points (Boolean) (defaults to: false)

    Whether tuple has private point numbers

  • shared_points (Array<Integer>, nil) (defaults to: nil)

    Shared point numbers if applicable

Returns:

  • (Array<Hash>)

    Array of point deltas { x:, y: }

Raises:



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/fontisan/variation/delta_parser.rb', line 42

def parse(data, point_count, private_points: false, shared_points: nil)
  return zero_deltas(point_count) if data.nil? || data.empty?

  io = StringIO.new(data)
  io.set_encoding(Encoding::BINARY)

  # Parse point numbers if present
  points = if private_points
             parse_point_numbers(io)
           elsif shared_points
             shared_points
           else
             # All points affected
             (0...point_count).to_a
           end

  # Determine delta format from first byte (if present)
  format_byte = io.getbyte
  return zero_deltas(point_count) if format_byte.nil?

  io.pos -= 1 # Put byte back

  # Parse X deltas
  x_deltas = parse_delta_array(io, points.length)

  # Parse Y deltas
  y_deltas = parse_delta_array(io, points.length)

  # Build full delta array (zero for untouched points)
  build_full_deltas(points, x_deltas, y_deltas, point_count)
rescue StandardError => e
  raise VariationDataCorruptedError.new(
    message: "Failed to parse delta data: #{e.message}",
    details: {
      point_count: point_count,
      private_points: private_points,
      error_class: e.class.name,
    },
  )
end

#parse_with_flags(data, point_count, flags) ⇒ Array<Hash>

Parse delta data with explicit format flag

Parameters:

  • data (String)

    Binary delta data

  • point_count (Integer)

    Total number of points

  • flags (Integer)

    Tuple variation flags

Returns:

  • (Array<Hash>)

    Array of point deltas



89
90
91
92
93
94
95
# File 'lib/fontisan/variation/delta_parser.rb', line 89

def parse_with_flags(data, point_count, flags)
  if (flags & DELTAS_ARE_ZERO).zero?
    parse(data, point_count)
  else
    zero_deltas(point_count)
  end
end