Class: Fontisan::Variation::BlendApplier
- Inherits:
-
Object
- Object
- Fontisan::Variation::BlendApplier
- Defined in:
- lib/fontisan/variation/blend_applier.rb
Overview
Applies CFF2 blend operators during CharString execution
The blend operator in CFF2 CharStrings provides variation support by blending base values with deltas based on design space coordinates.
Blend Format:
v1 Δv1_axis1 Δv1_axis2 ... v2 Δv2_axis1 ... K N blend
Where:
- K = number of values to blend
- N = number of axes
- Each value has N deltas (one per axis)
The applier calculates blended values:
result = base + Σ(delta_i × scalar_i)
Reference: Adobe Technical Note #5177 (CFF2 specification)
Instance Attribute Summary collapse
-
#interpolator ⇒ Interpolator
readonly
Coordinate interpolator.
-
#scalars ⇒ Array<Float>
readonly
Current variation scalars.
Instance Method Summary collapse
-
#apply_blend(base:, deltas:, num_axes: nil) ⇒ Float
Apply blend operation.
-
#apply_blend_operands(operands, num_values, num_axes) ⇒ Array<Float>
Apply blend operator from CharString stack.
-
#apply_blends(blends, num_axes) ⇒ Array<Float>
Apply multiple blend operations.
-
#at_default? ⇒ Boolean
Check if coordinates are at default.
-
#blend_point(base_x, base_y, deltas_x, deltas_y) ⇒ Array<Float>
Get blended point coordinates.
-
#blend_to_static(blend_data) ⇒ Array<Float>
Convert blend data to static values.
-
#calculate_scalars(axes) ⇒ Array<Float>
Calculate scalars for current coordinates.
-
#initialize(interpolator, coordinates = {}) ⇒ BlendApplier
constructor
Initialize blend applier.
-
#set_coordinates(coordinates, axes) ⇒ Object
Set design space coordinates.
Constructor Details
#initialize(interpolator, coordinates = {}) ⇒ BlendApplier
Initialize blend applier
38 39 40 41 42 |
# File 'lib/fontisan/variation/blend_applier.rb', line 38 def initialize(interpolator, coordinates = {}) @interpolator = interpolator @coordinates = coordinates @scalars = [] end |
Instance Attribute Details
#interpolator ⇒ Interpolator (readonly)
Returns Coordinate interpolator.
29 30 31 |
# File 'lib/fontisan/variation/blend_applier.rb', line 29 def interpolator @interpolator end |
#scalars ⇒ Array<Float> (readonly)
Returns Current variation scalars.
32 33 34 |
# File 'lib/fontisan/variation/blend_applier.rb', line 32 def scalars @scalars end |
Instance Method Details
#apply_blend(base:, deltas:, num_axes: nil) ⇒ Float
Apply blend operation
Blends base value with deltas using variation scalars.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/fontisan/variation/blend_applier.rb', line 64 def apply_blend(base:, deltas:, num_axes: nil) # Validate delta count matches axes if num_axes && deltas.length != num_axes raise InvalidVariationDataError.new( message: "Blend delta count (#{deltas.length}) doesn't match axes (#{num_axes})", details: { delta_count: deltas.length, expected_axes: num_axes, base_value: base, }, ) end # Start with base value result = base.to_f # Apply each delta with its scalar deltas.each_with_index do |delta, index| scalar = @scalars[index] || 0.0 result += delta.to_f * scalar end result end |
#apply_blend_operands(operands, num_values, num_axes) ⇒ Array<Float>
Apply blend operator from CharString stack
Processes blend operator arguments from CharString execution.
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 |
# File 'lib/fontisan/variation/blend_applier.rb', line 115 def apply_blend_operands(operands, num_values, num_axes) # Expected operands: K * (N + 1) expected_count = num_values * (num_axes + 1) if operands.length != expected_count raise InvalidVariationDataError.new( message: "Blend operand count mismatch: expected #{expected_count}, got #{operands.length}", details: { operand_count: operands.length, expected_count: expected_count, num_values: num_values, num_axes: num_axes, }, ) end blended_values = [] num_values.times do |i| offset = i * (num_axes + 1) base = operands[offset] deltas = operands[offset + 1, num_axes] || [] blended_values << apply_blend( base: base, deltas: deltas, num_axes: num_axes, ) end blended_values end |
#apply_blends(blends, num_axes) ⇒ Array<Float>
Apply multiple blend operations
Processes multiple values with their deltas.
96 97 98 99 100 101 102 103 104 |
# File 'lib/fontisan/variation/blend_applier.rb', line 96 def apply_blends(blends, num_axes) blends.map do |blend| apply_blend( base: blend[:base], deltas: blend[:deltas], num_axes: num_axes, ) end end |
#at_default? ⇒ Boolean
Check if coordinates are at default
164 165 166 |
# File 'lib/fontisan/variation/blend_applier.rb', line 164 def at_default? @scalars.all?(&:zero?) end |
#blend_point(base_x, base_y, deltas_x, deltas_y) ⇒ Array<Float>
Get blended point coordinates
Applies blend to X and Y coordinates simultaneously.
177 178 179 180 181 182 |
# File 'lib/fontisan/variation/blend_applier.rb', line 177 def blend_point(base_x, base_y, deltas_x, deltas_y) [ apply_blend(base: base_x, deltas: deltas_x), apply_blend(base: base_y, deltas: deltas_y), ] end |
#blend_to_static(blend_data) ⇒ Array<Float>
Convert blend data to static values
For instance generation, replaces blend operators with static values.
190 191 192 193 194 |
# File 'lib/fontisan/variation/blend_applier.rb', line 190 def blend_to_static(blend_data) blend_data.flat_map do |blend_op| apply_blends(blend_op[:blends], blend_op[:num_axes]) end end |
#calculate_scalars(axes) ⇒ Array<Float>
Calculate scalars for current coordinates
Converts design space coordinates to normalized scalars [-1, 1].
154 155 156 157 158 159 |
# File 'lib/fontisan/variation/blend_applier.rb', line 154 def calculate_scalars(axes) axes.map do |axis| coord = @coordinates[axis.axis_tag] || axis.default_value @interpolator.normalize_coordinate(coord, axis.axis_tag) end end |
#set_coordinates(coordinates, axes) ⇒ Object
Set design space coordinates
Updates the variation scalars based on new coordinates.
50 51 52 53 |
# File 'lib/fontisan/variation/blend_applier.rb', line 50 def set_coordinates(coordinates, axes) @coordinates = coordinates @scalars = calculate_scalars(axes) end |