Class: Fontisan::Tables::Cff2::OperandStack
- Inherits:
-
Object
- Object
- Fontisan::Tables::Cff2::OperandStack
- Defined in:
- lib/fontisan/tables/cff2/operand_stack.rb
Overview
Operand stack manager for CFF2 CharStrings
This class manages the operand stack for CFF2 CharStrings, with special handling for blend operations that mix base values and deltas.
In CFF2, the blend operator takes operands in the format:
[base1, delta1_axis1, delta1_axis2, ..., base2, delta2_axis1, ..., K, N]
Where:
- K = number of values to blend
- N = number of variation axes
The stack manager separates base values from deltas and applies blend operations to produce final values based on variation coordinates.
Instance Attribute Summary collapse
-
#blend_values ⇒ Array<Hash>
readonly
Blend values (base + deltas).
-
#num_axes ⇒ Integer
readonly
Number of variation axes.
-
#stack ⇒ Array<Numeric>
readonly
The operand stack.
Instance Method Summary collapse
-
#apply_blend(scalars = []) ⇒ Array<Float>
Apply blend operation.
-
#blend_history ⇒ Array<Hash>
Get blend value history.
-
#clear ⇒ Object
Clear the stack.
-
#empty? ⇒ Boolean
Check if stack is empty.
-
#extract_blend_data ⇒ Hash
Extract blend data without applying.
-
#initialize(num_axes: 0) ⇒ OperandStack
constructor
Initialize operand stack.
-
#inspect ⇒ String
Get string representation for debugging.
-
#peek ⇒ Numeric?
Get the top value without popping.
-
#pop ⇒ Numeric?
Pop a value from the stack.
-
#pop_many(count) ⇒ Array<Numeric>
Pop multiple values from the stack.
-
#push(*values) ⇒ Object
Push a value onto the stack.
-
#reset_blend_history ⇒ Object
Reset blend history.
-
#shift ⇒ Numeric?
Shift a value from the front of the stack.
-
#size ⇒ Integer
Get stack size.
-
#to_a ⇒ Array<Numeric>
Get all values on the stack.
Constructor Details
#initialize(num_axes: 0) ⇒ OperandStack
Initialize operand stack
40 41 42 43 44 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 40 def initialize(num_axes: 0) @stack = [] @num_axes = num_axes @blend_values = [] end |
Instance Attribute Details
#blend_values ⇒ Array<Hash> (readonly)
Returns Blend values (base + deltas).
35 36 37 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 35 def blend_values @blend_values end |
#num_axes ⇒ Integer (readonly)
Returns Number of variation axes.
32 33 34 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 32 def num_axes @num_axes end |
#stack ⇒ Array<Numeric> (readonly)
Returns The operand stack.
29 30 31 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 29 def stack @stack end |
Instance Method Details
#apply_blend(scalars = []) ⇒ Array<Float>
Apply blend operation
This pops K * (N + 1) + 2 operands from the stack, where:
- K = number of values to blend
- N = number of axes
- Last 2 values are K and N themselves
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 113 def apply_blend(scalars = []) # Pop N and K n = pop.to_i k = pop.to_i # Validate required_operands = k * (n + 1) if size < required_operands warn "Blend requires #{required_operands} operands, got #{size}" clear return [] end # Extract operands (base + deltas for each value) blend_operands = pop_many(required_operands).reverse # Process each value to blend blended_values = [] k.times do |i| offset = i * (n + 1) base = blend_operands[offset] deltas = blend_operands[offset + 1, n] || [] # Apply blend: result = base + sum(delta[i] * scalar[i]) blended = base.to_f deltas.each_with_index do |delta, axis_index| scalar = scalars[axis_index] || 0.0 blended += delta.to_f * scalar end # Store blend info for debugging/inspection @blend_values << { base: base, deltas: deltas, blended: blended, } blended_values << blended end # Push blended values back onto stack push(*blended_values) blended_values end |
#blend_history ⇒ Array<Hash>
Get blend value history
221 222 223 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 221 def blend_history @blend_values.dup end |
#clear ⇒ Object
Clear the stack
99 100 101 102 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 99 def clear @stack.clear @blend_values.clear end |
#empty? ⇒ Boolean
Check if stack is empty
94 95 96 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 94 def empty? @stack.empty? end |
#extract_blend_data ⇒ Hash
Extract blend data without applying
This is used when we need to store blend operations for later application with specific coordinates.
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 165 def extract_blend_data # Pop N and K n = pop.to_i k = pop.to_i # Validate required_operands = k * (n + 1) if size < required_operands warn "Blend requires #{required_operands} operands, got #{size}" clear return nil end # Extract operands blend_operands = pop_many(required_operands).reverse # Parse into base + deltas structure blends = [] k.times do |i| offset = i * (n + 1) base = blend_operands[offset] deltas = blend_operands[offset + 1, n] || [] blends << { base: base, deltas: deltas, } # Push base value back (will be blended later) push(base) end { num_values: k, num_axes: n, blends: blends, } end |
#inspect ⇒ String
Get string representation for debugging
214 215 216 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 214 def inspect "#<OperandStack size=#{size} values=#{@stack.inspect}>" end |
#peek ⇒ Numeric?
Get the top value without popping
80 81 82 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 80 def peek @stack.last end |
#pop ⇒ Numeric?
Pop a value from the stack
56 57 58 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 56 def pop @stack.pop end |
#pop_many(count) ⇒ Array<Numeric>
Pop multiple values from the stack
64 65 66 67 68 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 64 def pop_many(count) return [] if count <= 0 || @stack.empty? @stack.pop(count) end |
#push(*values) ⇒ Object
Push a value onto the stack
49 50 51 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 49 def push(*values) @stack.concat(values) end |
#reset_blend_history ⇒ Object
Reset blend history
226 227 228 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 226 def reset_blend_history @blend_values.clear end |
#shift ⇒ Numeric?
Shift a value from the front of the stack
73 74 75 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 73 def shift @stack.shift end |
#size ⇒ Integer
Get stack size
87 88 89 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 87 def size @stack.size end |
#to_a ⇒ Array<Numeric>
Get all values on the stack
207 208 209 |
# File 'lib/fontisan/tables/cff2/operand_stack.rb', line 207 def to_a @stack.dup end |