Class: Fontisan::Tables::Cff::CharStringBuilder
- Inherits:
-
Object
- Object
- Fontisan::Tables::Cff::CharStringBuilder
- Defined in:
- lib/fontisan/tables/cff/charstring_builder.rb
Overview
Type 2 CharString builder/encoder
CharStringBuilder
encodes glyph outlines into Type 2 CharString binary format. It takes
high-level outline commands and produces the stack-based CharString
operators used in CFF fonts.
Type 2 CharString encoding:
- Numbers are encoded in various compact formats
- Operators are single or two-byte commands
- All coordinates are relative (dx, dy format)
- Current point tracking for relative calculations
Operator optimization:
- Use specialized operators (hlineto, vlineto) when possible
- Merge sequential operators of same type
- Minimize operator bytes
Reference: Adobe Type 2 CharString Format https://adobe-type-tools.github.io/font-tech-notes/pdfs/5177.Type2.pdf
Direct Known Subclasses
Constant Summary collapse
- OPERATORS =
Type 2 CharString operators (opposite of parser)
{ hstem: 1, vstem: 3, vmoveto: 4, rlineto: 5, hlineto: 6, vlineto: 7, rrcurveto: 8, callsubr: 10, return: 11, endchar: 14, hstemhm: 18, hintmask: 19, cntrmask: 20, rmoveto: 21, hmoveto: 22, vstemhm: 23, rcurveline: 24, rlinecurve: 25, vvcurveto: 26, hhcurveto: 27, shortint: 28, callgsubr: 29, vhcurveto: 30, hvcurveto: 31, }.freeze
- TWO_BYTE_OPERATORS =
Two-byte operators (12 prefix)
{ and: [12, 3], or: [12, 4], not: [12, 5], abs: [12, 9], add: [12, 10], sub: [12, 11], div: [12, 12], neg: [12, 14], eq: [12, 15], drop: [12, 18], put: [12, 20], get: [12, 21], ifelse: [12, 22], random: [12, 23], mul: [12, 24], sqrt: [12, 26], dup: [12, 27], exch: [12, 28], index: [12, 29], roll: [12, 30], hflex: [12, 34], flex: [12, 35], hflex1: [12, 36], flex1: [12, 37], }.freeze
Class Method Summary collapse
-
.build_from_operations(operations) ⇒ String
Build a CharString from operation list.
Instance Method Summary collapse
-
#build(outline, width: nil) ⇒ String
Build a CharString from an outline.
-
#build_empty(width: nil) ⇒ String
Build an empty CharString (for .notdef or empty glyphs).
-
#build_from_operations(operations) ⇒ String
Instance method for building from operations.
Class Method Details
.build_from_operations(operations) ⇒ String
Build a CharString from operation list
This method takes operations from CharStringParser and encodes them back to binary CharString format. Useful for CharString modification.
128 129 130 |
# File 'lib/fontisan/tables/cff/charstring_builder.rb', line 128 def self.build_from_operations(operations) new.build_from_operations(operations) end |
Instance Method Details
#build(outline, width: nil) ⇒ String
Build a CharString from an outline
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/fontisan/tables/cff/charstring_builder.rb', line 94 def build(outline, width: nil) @output = StringIO.new("".b) @current_x = 0.0 @current_y = 0.0 @first_move = true # Convert outline to CFF commands commands = outline.to_cff_commands # Encode width if provided (before first move) if width && !commands.empty? # Width is encoded as first operator before first move # For now, we'll add it before the first moveto encode_width(width) end # Encode each command commands.each do |cmd| encode_command(cmd) end # End character write_operator(:endchar) @output.string end |
#build_empty(width: nil) ⇒ String
Build an empty CharString (for .notdef or empty glyphs)
159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/fontisan/tables/cff/charstring_builder.rb', line 159 def build_empty(width: nil) @output = StringIO.new("".b) # Encode width if provided encode_width(width) if width # Just endchar for empty glyph write_operator(:endchar) @output.string end |
#build_from_operations(operations) ⇒ String
Instance method for building from operations
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/fontisan/tables/cff/charstring_builder.rb', line 136 def build_from_operations(operations) @output = StringIO.new("".b) operations.each do |op| # Write operands op[:operands].each { |operand| write_number(operand) } # Write operator write_operator(op[:name]) # Write hint data if present (for hintmask/cntrmask) if op[:hint_data] @output.write(op[:hint_data]) end end @output.string end |