Module: Postsvg::Visitors::PsVisitor::Font

Included in:
Postsvg::Visitors::PsVisitor
Defined in:
lib/postsvg/visitors/ps_visitor/font.rb

Overview

Font and text operator handlers. Each pops from the RUNTIME stack (not the AST) so chained font setup works:

/Helvetica findfont 18 scalefont setfont

pushes a FontRef after findfont, scales it after scalefont, installs it after setfont.

Defined Under Namespace

Classes: FontRef

Instance Method Summary collapse

Instance Method Details

#visit_charpath(_op, _ctx) ⇒ Object



87
88
89
# File 'lib/postsvg/visitors/ps_visitor/font.rb', line 87

def visit_charpath(_op, _ctx)
  @builder.comment("charpath: requires font metrics")
end

#visit_findfont(op, _ctx) ⇒ Object



23
24
25
26
27
28
29
30
31
# File 'lib/postsvg/visitors/ps_visitor/font.rb', line 23

def visit_findfont(op, _ctx)
  name =
    case op.name
    when Model::Literals::Name then op.name.value
    when Model::Literals::StringLiteral then op.name.value
    else op.name.to_s
    end
  @stack << FontRef.new(name: name, size: nil)
end

#visit_scalefont(op, _ctx) ⇒ Object



33
34
35
36
37
38
# File 'lib/postsvg/visitors/ps_visitor/font.rb', line 33

def visit_scalefont(op, _ctx)
  size = op.size.to_i || op.size.to_f
  font = @stack.pop
  base = font.is_a?(FontRef) ? font : FontRef.new(name: "Helvetica", size: nil)
  @stack << FontRef.new(name: base.name, size: size.to_f)
end

#visit_setfont(_op, _ctx) ⇒ Object



40
41
42
43
44
45
# File 'lib/postsvg/visitors/ps_visitor/font.rb', line 40

def visit_setfont(_op, _ctx)
  font = @stack.pop
  ref = font.is_a?(FontRef) ? font : FontRef.new(name: "Helvetica", size: 12.0)
  @graphics.update(font_name: ref.name || "Helvetica",
                   font_size: ref.size || 12.0)
end

#visit_show(_op, _ctx) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/postsvg/visitors/ps_visitor/font.rb', line 47

def visit_show(_op, _ctx)
  text = @stack.pop
  text_str =
    case text
    when Model::Literals::StringLiteral then text.value
    when String then text
    else text.to_s
    end
  pos = @graphics.current.last_text_position
  return unless pos

  ctm = @graphics.current.ctm
  screen_pos = ctm.apply_point(pos[:x], pos[:y])
  color = @graphics.current.fill_color
  # SVG y is inverted relative to PS — the Y-flip wrapper
  # group already flips the whole body, so we emit a
  # scale(1 -1) per <text> to undo the flip for text only.
  @builder.text(
    content: text_str,
    x: screen_pos[:x],
    y: -screen_pos[:y],
    font_family: @graphics.current.font_name,
    font_size: @graphics.current.font_size,
    color: color || Postsvg::Color::BLACK,
    transform: "scale(1 -1)",
  )
  @path.reset
  @graphics.update(last_text_position: nil)
end

#visit_stringwidth(_op, _ctx) ⇒ Object



81
82
83
84
85
# File 'lib/postsvg/visitors/ps_visitor/font.rb', line 81

def visit_stringwidth(_op, _ctx)
  # Without real font metrics, push zero advance.
  @stack << 0
  @stack << 0
end

#visit_xyshow(_op, _ctx) ⇒ Object



77
78
79
# File 'lib/postsvg/visitors/ps_visitor/font.rb', line 77

def visit_xyshow(_op, _ctx)
  @builder.comment("xyshow: not yet implemented")
end