Class: Fontisan::Tables::Cff::CharStringBuilder

Inherits:
Object
  • Object
show all
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

Examples:

Building a CharString from outline

builder = Fontisan::Tables::Cff::CharStringBuilder.new
charstring_data = builder.build(outline, width: 500)

Direct Known Subclasses

Cff2CharStringBuilder

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

Instance Method Summary collapse

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.

Parameters:

  • operations (Array<Hash>)

    Array of operation hashes from parser

Returns:

  • (String)

    Binary CharString data



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

Parameters:

  • outline (Models::Outline)

    Universal outline object

  • width (Integer, nil) (defaults to: nil)

    Glyph width (optional)

Returns:

  • (String)

    Binary CharString data



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)

Parameters:

  • width (Integer, nil) (defaults to: nil)

    Glyph width

Returns:

  • (String)

    Binary CharString data



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

Parameters:

  • operations (Array<Hash>)

    Array of operation hashes

Returns:

  • (String)

    Binary CharString data



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