Class: Fontisan::Tables::Cff::Cff2CharStringBuilder
- Inherits:
-
CharStringBuilder
- Object
- CharStringBuilder
- Fontisan::Tables::Cff::Cff2CharStringBuilder
- Defined in:
- lib/fontisan/tables/cff/cff2_charstring_builder.rb
Overview
CFF2 CharStringBuilder — extends the CFF1 charstring with variable-font operators (vsindex, blend).
In CFF2, the hmoveto operator (22) is repurposed as vsindex (selects a VariationStore item), and a new blend operator (23) applies variation deltas to the current operands.
The blend protocol for a single coordinate:
push base_value, push delta_r0, ..., push delta_r(n-1), push 1, blend
After blend, the stack has one blended value.
For n coordinates batched together:
push base_0 + deltas_0, push base_1 + deltas_1, ..., push n, blend
After blend, the stack has n blended values.
Defined Under Namespace
Classes: MasterState
Constant Summary collapse
- OPERATORS_CFF2 =
CFF2 replaces hmoveto(22) with vsindex(22). The :hmoveto key is omitted entirely so attempts to emit it raise naturally.
OPERATORS.except(:hmoveto).merge( vsindex: 22, blend: 23, )
Constants inherited from CharStringBuilder
Fontisan::Tables::Cff::CharStringBuilder::OPERATORS, Fontisan::Tables::Cff::CharStringBuilder::TWO_BYTE_OPERATORS
Class Method Summary collapse
-
.build_variable(outline, master_outlines:, num_regions:, width: nil) ⇒ String
Build a complete variable CFF2 charstring from a default outline plus variation master outlines.
Instance Method Summary collapse
-
#build_variable_outline(outline, master_outlines, num_regions, width) ⇒ String
Build a variable charstring from a default outline + masters.
- #encode_variable_command(cmd, master_cmds) ⇒ Object
-
#encode_variable_curveto(cmd, master_cmds) ⇒ Object
---------- variable curveto ----------.
-
#encode_variable_lineto(cmd, master_cmds) ⇒ Object
---------- variable lineto ----------.
-
#encode_variable_moveto(cmd, master_cmds) ⇒ Object
---------- variable moveto ----------.
-
#encode_variable_outline(outline, master_outlines, num_regions) ⇒ Object
Process an outline with blend operators for each varying coordinate.
- #write_operator(operator) ⇒ Object
-
#write_variable_number(base_value, deltas) ⇒ Object
Emit a variable coordinate: base value + per-region deltas + blend.
- #write_vsindex(index) ⇒ Object
Methods inherited from CharStringBuilder
#build, #build_empty, build_from_operations, #build_from_operations
Class Method Details
.build_variable(outline, master_outlines:, num_regions:, width: nil) ⇒ String
Build a complete variable CFF2 charstring from a default outline plus variation master outlines.
43 44 45 46 47 |
# File 'lib/fontisan/tables/cff/cff2_charstring_builder.rb', line 43 def self.build_variable(outline, master_outlines:, num_regions:, width: nil) new.build_variable_outline(outline, master_outlines, num_regions, width) end |
Instance Method Details
#build_variable_outline(outline, master_outlines, num_regions, width) ⇒ String
Build a variable charstring from a default outline + masters. Creates the output buffer internally (same pattern as the parent's build method).
55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/fontisan/tables/cff/cff2_charstring_builder.rb', line 55 def build_variable_outline(outline, master_outlines, num_regions, width) @output = StringIO.new("".b) @first_move = true @current_x = 0.0 @current_y = 0.0 encode_width(width) if width encode_variable_outline(outline, master_outlines, num_regions) write_operator(:endchar) @output.string end |
#encode_variable_command(cmd, master_cmds) ⇒ Object
86 87 88 89 90 91 92 |
# File 'lib/fontisan/tables/cff/cff2_charstring_builder.rb', line 86 def encode_variable_command(cmd, master_cmds) case cmd[:type] when :move_to then encode_variable_moveto(cmd, master_cmds) when :line_to then encode_variable_lineto(cmd, master_cmds) when :curve_to then encode_variable_curveto(cmd, master_cmds) end end |
#encode_variable_curveto(cmd, master_cmds) ⇒ Object
---------- variable curveto ----------
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 |
# File 'lib/fontisan/tables/cff/cff2_charstring_builder.rb', line 122 def encode_variable_curveto(cmd, master_cmds) # Compute relative values for the default master dx1 = (cmd[:x1] - @current_x).round dy1 = (cmd[:y1] - @current_y).round dx2 = (cmd[:x2] - @current_x).round dy2 = (cmd[:y2] - @current_y).round dx = (cmd[:x] - @current_x).round dy = (cmd[:y] - @current_y).round base_values = [dx1, dy1, dx2, dy2, dx, dy] base_keys = %i[x1 y1 x2 y2 x y] # Compute deltas for each coordinate against each master all_deltas = base_keys.each_with_index.map do |key, vi| master_cmds.each_with_index.map do |mc, i| ms = @master_states[i] master_relative = if key.to_s.start_with?("x") (mc[key] - ms.current_x).round else (mc[key] - ms.current_y).round end master_relative - base_values[vi] end end base_values.each_with_index do |val, vi| write_variable_number(val, all_deltas[vi]) end write_operator(:rrcurveto) advance_state(cmd, master_cmds) end |
#encode_variable_lineto(cmd, master_cmds) ⇒ Object
---------- variable lineto ----------
109 110 111 112 113 114 115 116 117 118 |
# File 'lib/fontisan/tables/cff/cff2_charstring_builder.rb', line 109 def encode_variable_lineto(cmd, master_cmds) dx, dy, dx_deltas, dy_deltas = compute_variable_deltas(cmd, master_cmds) write_variable_number(dx, dx_deltas) write_variable_number(dy, dy_deltas) write_operator(:rlineto) advance_state(cmd, master_cmds) end |
#encode_variable_moveto(cmd, master_cmds) ⇒ Object
---------- variable moveto ----------
96 97 98 99 100 101 102 103 104 105 |
# File 'lib/fontisan/tables/cff/cff2_charstring_builder.rb', line 96 def encode_variable_moveto(cmd, master_cmds) dx, dy, dx_deltas, dy_deltas = compute_variable_deltas(cmd, master_cmds) write_variable_number(dx, dx_deltas) write_variable_number(dy, dy_deltas) write_operator(:rmoveto) advance_state(cmd, master_cmds) end |
#encode_variable_outline(outline, master_outlines, num_regions) ⇒ Object
Process an outline with blend operators for each varying coordinate.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/fontisan/tables/cff/cff2_charstring_builder.rb', line 69 def encode_variable_outline(outline, master_outlines, num_regions) @master_states = master_outlines.map do MasterState.new(current_x: 0, current_y: 0) end @num_regions = num_regions @has_blend = false outline.commands.each_with_index do |cmd, i| master_cmds = master_outlines.map { |mo| mo.commands[i] } encode_variable_command(cmd, master_cmds) end # Emit vsindex(0) at the start if any blend operators were used. # In a full implementation, vsindex selects the ItemVariationData # subtable; for single-subtable fonts, vsindex 0 is the default. end |
#write_operator(operator) ⇒ Object
177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/fontisan/tables/cff/cff2_charstring_builder.rb', line 177 def write_operator(operator) if OPERATORS_CFF2.key?(operator) @output.putc(OPERATORS_CFF2[operator]) elsif TWO_BYTE_OPERATORS.key?(operator) bytes = TWO_BYTE_OPERATORS[operator] @output.putc(bytes[0]) @output.putc(bytes[1]) else raise ArgumentError, "Unknown CFF2 operator: #{operator}" end end |
#write_variable_number(base_value, deltas) ⇒ Object
Emit a variable coordinate: base value + per-region deltas + blend. If all deltas are zero, emits just the base value (no blend needed).
161 162 163 164 165 166 167 168 169 170 |
# File 'lib/fontisan/tables/cff/cff2_charstring_builder.rb', line 161 def write_variable_number(base_value, deltas) return write_number(base_value) if deltas.nil? || deltas.empty? return write_number(base_value) if deltas.all?(&:zero?) @has_blend = true write_number(base_value) deltas.each { |d| write_number(d) } write_number(1) # n = 1 value to blend write_operator(:blend) end |
#write_vsindex(index) ⇒ Object
172 173 174 175 |
# File 'lib/fontisan/tables/cff/cff2_charstring_builder.rb', line 172 def write_vsindex(index) write_number(index) write_operator(:vsindex) end |