class TTFunk::Table::Cff::Charstring

Constants

CODE_MAP
FLEX_CODE_MAP

Attributes

glyph_id[R]
raw[R]

Public Class Methods

new(glyph_id, top_dict, font_dict, raw) click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 43
def initialize(glyph_id, top_dict, font_dict, raw)
  @glyph_id = glyph_id
  @top_dict = top_dict
  @font_dict = font_dict
  @raw = raw

  default_width_x = (@font_dict || @top_dict).private_dict.default_width_x

  @nominal_width_x = (@font_dict || @top_dict).private_dict.nominal_width_x

  @subrs = (@font_dict || @top_dict).private_dict.subr_index
  @gsubrs = @top_dict.cff.global_subr_index
  @subrs_bias = @subrs.bias if @subrs
  @gsubrs_bias = @gsubrs.bias if @gsubrs

  @stack = []
  @data = raw.bytes
  @index = 0
  @n_stems = 0
  @have_width = false
  @open = false
  @width = default_width_x
  @x = 0
  @y = 0
end

Public Instance Methods

encode() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 94
def encode
  raw
end
glyph() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 77
def glyph
  @glyph ||=
    begin
      horizontal_metrics = @top_dict.file.horizontal_metrics.for(glyph_id)
      Glyf::PathBased.new(path, horizontal_metrics)
    end
end
path() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 69
def path
  @path || begin
    @path = Path.new
    parse!
    @path
  end
end
render(x: 0, y: 0, font_size: 72) click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 85
def render(x: 0, y: 0, font_size: 72)
  path.render(
    x: x,
    y: y,
    font_size: font_size,
    units_per_em: @top_dict.file.header.units_per_em
  )
end

Private Instance Methods

add_contour(x, y) click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 169
def add_contour(x, y)
  if @open
    @path.close_path
  end

  @path.move_to(x, y)
  @open = true
end
callgsubr() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 434
def callgsubr
  code_index = @stack.pop + @gsubrs_bias
  subr_code = @gsubrs[code_index].bytes
  @data[@index, 0] = subr_code
end
callsubr() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 222
def callsubr
  code_index = @stack.pop + @subrs_bias
  subr_code = @subrs[code_index].bytes
  @data[@index, 0] = subr_code
end
cntrmask() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 329
def cntrmask
  stem

  # skip past hinting data, no need to worry about it since we're not
  # very concerned about rendering glyphs
  read_bytes((@n_stems + 7) >> 3)
end
endchar() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 309
def endchar
  if !@stack.empty? && !@have_width
    @width = @stack.shift + @nominal_width_x
    @have_width = true
  end

  if @open
    @path.close_path
    @open = false
  end
end
flex() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 234
def flex
  c1x = @x + @stack.shift # dx1
  c1y = @y + @stack.shift # dy1
  c2x = c1x + @stack.shift # dx2
  c2y = c1y + @stack.shift # dy2
  jpx = c2x + @stack.shift # dx3
  jpy = c2y + @stack.shift # dy3
  c3x = jpx + @stack.shift # dx4
  c3y = jpy + @stack.shift # dy4
  c4x = c3x + @stack.shift # dx5
  c4y = c3y + @stack.shift # dy5
  @x = c4x + @stack.shift # dx6
  @y = c4y + @stack.shift # dy6
  @stack.shift # flex depth

  @path.curve_to(c1x, c1y, c2x, c2y, jpx, jpy)
  @path.curve_to(c3x, c3y, c4x, c4y, @x, @y)
end
flex1() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 287
def flex1
  c1x = @x + @stack.shift # dx1
  c1y = @y + @stack.shift # dy1
  c2x = c1x + @stack.shift # dx2
  c2y = c1y + @stack.shift # dy2
  jpx = c2x + @stack.shift # dx3
  jpy = c2y + @stack.shift # dy3
  c3x = jpx + @stack.shift # dx4
  c3y = jpy + @stack.shift # dy4
  c4x = c3x + @stack.shift # dx5
  c4y = c3y + @stack.shift # dy5

  if (c4x - @x).abs > (c4y - @y).abs
    @x = c4x + @stack.shift
  else
    @y = c4y + @stack.shift
  end

  @path.curve_to(c1x, c1y, c2x, c2y, jpx, jpy)
  @path.curve_to(c3x, c3y, c4x, c4y, @x, @y)
end
flex_select() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 228
def flex_select
  flex_code = @data[@index]
  @index += 1
  __send__(FLEX_CODE_MAP[flex_code])
end
hflex() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 253
def hflex
  c1x = @x + @stack.shift # dx1
  c1y = @y # dy1
  c2x = c1x + @stack.shift # dx2
  c2y = c1y + @stack.shift # dy2
  jpx = c2x + @stack.shift # dx3
  jpy = c2y # dy3
  c3x = jpx + stack.shift # dx4
  c3y = c2y # dy4
  c4x = c3x + stack.shift # dx5
  c4y = @y # dy5
  @x = c4x + stack.shift # dx6

  @path.curve_to(c1x, c1y, c2x, c2y, jpx, jpy)
  @path.curve_to(c3x, c3y, c4x, c4y, @x, @y)
end
hflex1() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 270
def hflex1
  c1x = @x + @stack.shift # dx1
  c1y = @y + @stack.shift # dy1
  c2x = c1x + @stack.shift # dx2
  c2y = c1y + @stack.shift # dy2
  jpx = c2x + @stack.shift # dx3
  jpy = c2y # dy3
  c3x = jpx + @stack.shift # dx4
  c3y = c2y # dy4
  c4x = c3x + @stack.shift # dx5
  c4y = c3y + @stack.shift # dy5
  @x = c4x + @stack.shift # dx6

  @path.curve_to(c1x, c1y, c2x, c2y, jpx, jpy)
  @path.curve_to(c3x, c3y, c4x, c4y, @x, @y)
end
hhcurveto() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 412
def hhcurveto
  if @stack.size % 2 == 1
    @y += @stack.shift
  end

  until @stack.empty?
    c1x = @x + @stack.shift
    c1y = @y
    c2x = c1x + @stack.shift
    c2y = c1y + @stack.shift
    @x = c2x + @stack.shift
    @y = c2y
    @path.curve_to(c1x, c1y, c2x, c2y, @x, @y)
  end
end
hintmask() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 325
def hintmask
  cntrmask
end
hlineto() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 186
def hlineto
  until @stack.empty?
    @x += @stack.shift
    @path.line_to(@x, @y)

    break if @stack.empty?

    @y += @stack.shift
    @path.line_to(@x, @y)
  end
end
hmoveto() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 348
def hmoveto
  if @stack.size > 1 && !@have_width
    @width = @stack.shift + @nominal_width_x
    @have_width = true
  end

  @x += @stack.pop
  add_contour(@x, @y)
end
hstem() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 135
def hstem
  stem
end
hstemhm() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 321
def hstemhm
  stem
end
hvcurveto() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 462
def hvcurveto
  until @stack.empty?
    c1x = @x + @stack.shift
    c1y = @y
    c2x = c1x + @stack.shift
    c2y = c1y + @stack.shift
    @y = c2y + @stack.shift
    @x = c2x + (@stack.size == 1 ? @stack.shift : 0)
    @path.curve_to(c1x, c1y, c2x, c2y, @x, @y)

    break if @stack.empty?

    c1x = @x
    c1y = @y + @stack.shift
    c2x = c1x + @stack.shift
    c2y = c1y + @stack.shift
    @x = c2x + @stack.shift
    @y = c2y + (@stack.size == 1 ? @stack.shift : 0)
    @path.curve_to(c1x, c1y, c2x, c2y, @x, @y)
  end
end
parse!() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 100
def parse!
  until @index >= @data.size
    code = @data[@index]
    @index += 1

    # return from callgsubr - do nothing since we inline subrs
    next if code == 11

    if code >= 32 && code <= 246
      @stack << code - 139
    elsif (m = CODE_MAP[code])
      __send__(m)
    elsif code >= 247 && code <= 250
      b0 = code
      b1 = @data[@index]
      @index += 1
      @stack << (b0 - 247) * 256 + b1 + 108
    elsif code >= 251 && code <= 254
      b0 = code
      b1 = @data[@index]
      @index += 1
      @stack << -(b0 - 251) * 256 - b1 - 108
    else
      b1, b2, b3, b4 = read_bytes(4)
      @stack << ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) / 65_536
    end
  end
end
rcurveline() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 362
def rcurveline
  while @stack.size > 2
    c1x = @x + @stack.shift
    c1y = @y + @stack.shift
    c2x = c1x + @stack.shift
    c2y = c1y + @stack.shift
    @x = c2x + @stack.shift
    @y = c2y + @stack.shift
    @path.curve_to(c1x, c1y, c2x, c2y, @x, @y)
  end

  @x += @stack.shift
  @y += @stack.shift
  @path.line_to(@x, @y)
end
read_bytes(length) click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 129
def read_bytes(length)
  bytes = @data[@index, length]
  @index += length
  bytes
end
rlinecurve() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 378
def rlinecurve
  while @stack.size > 6
    @x += @stack.shift
    @y += @stack.shift
    @path.line_to(@x, @y)
  end

  c1x = @x + @stack.shift
  c1y = @y + @stack.shift
  c2x = c1x + @stack.shift
  c2y = c1y + @stack.shift
  @x = c2x + @stack.shift
  @y = c2y + @stack.shift

  @path.curve_to(c1x, c1y, c2x, c2y, @x, @y)
end
rlineto() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 178
def rlineto
  until @stack.empty?
    @x += @stack.shift
    @y += @stack.shift
    @path.line_to(@x, @y)
  end
end
rmoveto() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 337
def rmoveto
  if @stack.size > 2 && !@have_width
    @width = @stack.shift + @nominal_width_x
    @have_width = true
  end

  @y += @stack.pop
  @x += @stack.pop
  add_contour(@x, @y)
end
rrcurveto() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 210
def rrcurveto
  until @stack.empty?
    c1x = @x + @stack.shift
    c1y = @y + @stack.shift
    c2x = c1x + @stack.shift
    c2y = c1y + @stack.shift
    @x = c2x + @stack.shift
    @y = c2y + @stack.shift
    @path.curve_to(c1x, c1y, c2x, c2y, @x, @y)
  end
end
shortint() click to toggle source

rubocop:enable Style/EvenOdd

# File lib/ttfunk/table/cff/charstring.rb, line 429
def shortint
  b1, b2 = read_bytes(2)
  @stack << (((b1 << 24) | (b2 << 16)) >> 16)
end
stem() click to toggle source

rubocop:disable Style/EvenOdd

# File lib/ttfunk/table/cff/charstring.rb, line 144
def stem
  # The number of stem operators on the stack is always even.
  # If the value is uneven, that means a width is specified.
  has_width_arg = @stack.size % 2 == 1

  if has_width_arg && !@have_width
    @width = @stack.shift + @nominal_width_x
  end

  @n_stems += @stack.length >> 1
  @stack.clear
  @have_width = true
end
vhcurveto() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 440
def vhcurveto
  until @stack.empty?
    c1x = @x
    c1y = @y + @stack.shift
    c2x = c1x + @stack.shift
    c2y = c1y + @stack.shift
    @x = c2x + @stack.shift
    @y = c2y + (@stack.size == 1 ? @stack.shift : 0)
    @path.curve_to(c1x, c1y, c2x, c2y, @x, @y)

    break if @stack.empty?

    c1x = @x + @stack.shift
    c1y = @y
    c2x = c1x + @stack.shift
    c2y = c1y + @stack.shift
    @y = c2y + @stack.shift
    @x = c2x + (@stack.size == 1 ? @stack.shift : 0)
    @path.curve_to(c1x, c1y, c2x, c2y, @x, @y)
  end
end
vlineto() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 198
def vlineto
  until @stack.empty?
    @y += @stack.shift
    @path.line_to(@x, @y)

    break if @stack.empty?

    @x += @stack.shift
    @path.line_to(@x, @y)
  end
end
vmoveto() click to toggle source

rubocop:enable Style/EvenOdd

# File lib/ttfunk/table/cff/charstring.rb, line 159
def vmoveto
  if @stack.size > 1 && !@have_width
    @width = @stack.shift + @nominal_width_x
    @have_width = true
  end

  @y += @stack.pop
  add_contour(@x, @y)
end
vstem() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 139
def vstem
  stem
end
vstemhm() click to toggle source
# File lib/ttfunk/table/cff/charstring.rb, line 358
def vstemhm
  stem
end
vvcurveto() click to toggle source

rubocop:disable Style/EvenOdd

# File lib/ttfunk/table/cff/charstring.rb, line 396
def vvcurveto
  if @stack.size % 2 == 1
    @x += @stack.shift
  end

  until @stack.empty?
    c1x = @x
    c1y = @y + @stack.shift
    c2x = c1x + @stack.shift
    c2y = c1y + @stack.shift
    @x = c2x
    @y = c2y + @stack.shift
    @path.curve_to(c1x, c1y, c2x, c2y, @x, @y)
  end
end