Class: SVG

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby-svg.rb,
lib/ruby-svg/version.rb

Defined Under Namespace

Classes: Error

Constant Summary collapse

VERSION =
"0.1.1"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tag, opts = {}, &block) ⇒ SVG



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/ruby-svg.rb', line 35

def initialize(tag, opts={}, &block)
  @attrs = opts[:attrs] || {}
  if tag == :svg
    # Use `self.` since it will auto set the `attr` as an ivar
    self.fill = :transparent
    self.stroke = :black
  end
  @tag = tag
  @filename = (opts[:filename] || :svg).to_s.gsub(/\.svg$/, "")
  @content = opts[:content]
  @minx, @miny, @width, @height = opts[:minx] || 0, opts[:miny] || 0, opts[:width] || 100, opts[:height] || 100
  @items = []

  block&.call(self)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

Allows setting any attrs directly from the instance



29
30
31
32
33
# File 'lib/ruby-svg.rb', line 29

def method_missing(method, *args, &block)
  super unless method.to_s.end_with?("=")

  @attrs[method.to_s[0..-2].to_sym] = args.first
end

Instance Attribute Details

#attrsObject

Returns the value of attribute attrs.



8
9
10
# File 'lib/ruby-svg.rb', line 8

def attrs
  @attrs
end

#filenameObject

Returns the value of attribute filename.



8
9
10
# File 'lib/ruby-svg.rb', line 8

def filename
  @filename
end

#fillObject

Returns the value of attribute fill.



8
9
10
# File 'lib/ruby-svg.rb', line 8

def fill
  @fill
end

#heightObject

Returns the value of attribute height.



8
9
10
# File 'lib/ruby-svg.rb', line 8

def height
  @height
end

#itemsObject

Returns the value of attribute items.



8
9
10
# File 'lib/ruby-svg.rb', line 8

def items
  @items
end

#minxObject

Returns the value of attribute minx.



8
9
10
# File 'lib/ruby-svg.rb', line 8

def minx
  @minx
end

#minyObject

Returns the value of attribute miny.



8
9
10
# File 'lib/ruby-svg.rb', line 8

def miny
  @miny
end

#strokeObject

Returns the value of attribute stroke.



8
9
10
# File 'lib/ruby-svg.rb', line 8

def stroke
  @stroke
end

#tagObject

Returns the value of attribute tag.



8
9
10
# File 'lib/ruby-svg.rb', line 8

def tag
  @tag
end

#widthObject

Returns the value of attribute width.



8
9
10
# File 'lib/ruby-svg.rb', line 8

def width
  @width
end

Class Method Details

.open(tag = :svg, opts = {}, &block) ⇒ Object



14
15
16
# File 'lib/ruby-svg.rb', line 14

def self.open(tag=:svg, opts={}, &block)
  new(tag, opts, &block).open
end

.register(method_name, &block) ⇒ Object



22
23
24
25
26
# File 'lib/ruby-svg.rb', line 22

def self.register(method_name, &block)
  define_method(method_name) do |*attrs|
    block.call(self, *attrs)
  end
end

.write(tag = :svg, opts = {}, &block) ⇒ Object



18
19
20
# File 'lib/ruby-svg.rb', line 18

def self.write(tag=:svg, opts={}, &block)
  new(tag, opts, &block).write
end

Instance Method Details

#circle(x, y, r, **attrs, &block) ⇒ Object



64
65
66
# File 'lib/ruby-svg.rb', line 64

def circle(x, y, r, **attrs, &block)
  @items << SVG.new(:circle, attrs: attrs.merge(cx: x, cy: y, r: r), &block)
end

#ellipse(cx, cy, rx, ry, **attrs, &block) ⇒ Object



70
71
72
# File 'lib/ruby-svg.rb', line 70

def ellipse(cx, cy, rx, ry, **attrs, &block)
  @items << SVG.new(:ellipse, attrs: attrs.merge(cx: cx, cy: cy, rx: rx, ry: ry), &block)
end

#g(**attrs, &block) ⇒ Object



51
52
53
# File 'lib/ruby-svg.rb', line 51

def g(**attrs, &block)
  @items << SVG.new(:g, attrs: attrs, &block)
end

#html_tag(tag, n, **attrs, &block) ⇒ Object

<animate attributeName=“|” attributeType=“” begin=“0” end=“” from=“” to=“” dur=“” repeatCount=“” fill=“”/>



98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/ruby-svg.rb', line 98

def html_tag(tag, n, **attrs, &block)
  # TODO: Need to escape attr vals, in case they have quotes in them
  attr_str = attrs.map { |k,v| "#{k}=\"#{v}\"" }.join(" ")
  content = block&.call
  no_content = content.gsub(/\s/, "").length == 0
  # content.gsub(/\s/, "").length == 0 ? "" : "\n  #{content}\n"
  if no_content
    "<#{tag} #{attr_str} />"
  else
    "<#{tag} #{attr_str}>\n#{content}\n#{"  "*n}</#{tag}>"
  end
end

#item(tag, **attrs, &block) ⇒ Object



93
94
95
# File 'lib/ruby-svg.rb', line 93

def item(tag, **attrs, &block)
  @items << SVG.new(tag, attrs: attr, &block)
end

#line(x1, y1, x2, y2, **attrs, &block) ⇒ Object



67
68
69
# File 'lib/ruby-svg.rb', line 67

def line(x1, y1, x2, y2, **attrs, &block)
  @items << SVG.new(:line, attrs: attrs.merge(x1: x1, y1: y1, x2: x2, y2: y2), &block)
end

#openObject



136
137
138
139
140
# File 'lib/ruby-svg.rb', line 136

def open
  write
  default_browser = `defaults read com.apple.LaunchServices/com.apple.launchservices.secure LSHandlers | sed -n -e "/LSHandlerURLScheme = https;/{x;p;d;}" -e 's/.*=[^"]"\\(.*\\)";/\\1/g' -e x`.strip
  `open -b #{default_browser} '#{@filename}.svg' && sleep 2 && rm '#{@filename}.svg'`
end

#path(d, **attrs, &block) ⇒ Object



54
55
56
# File 'lib/ruby-svg.rb', line 54

def path(d, **attrs, &block)
  @items << SVG.new(:path, attrs: attrs.merge(d: d), &block)
end

#polygon(*points, **attrs, &block) ⇒ Object



90
91
92
# File 'lib/ruby-svg.rb', line 90

def polygon(*points, **attrs, &block)
  @items << SVG.new(:polygon, attrs: attrs.merge(points: points), &block)
end

#polyline(*points, **attrs, &block) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/ruby-svg.rb', line 73

def polyline(*points, **attrs, &block)
  block_called = false
  points = points.presence || block.call.then { |block_points|
    next block_points if block_points.is_a?(String)
    next unless block_points.is_a?(Array)

    block_points.filter_map { |block_point|
      case block_point
      when String then block_point
      when Array then block_point.join(",")
      when Hash then "#{block_point[:x]},#{block_point[:y]}"
      end
    }.join(" ")
  }&.tap { block_called = true }
  block = nil if block_called
  @items << SVG.new(:polyline, attrs: attrs.merge(points: points), &block)
end

#rect(x, y, width, height, **attrs, &block) ⇒ Object



61
62
63
# File 'lib/ruby-svg.rb', line 61

def rect(x, y, width, height, **attrs, &block)
  @items << SVG.new(:rect, attrs: attrs.merge(x: x, y: y, width: width, height: height), &block)
end

#save_as(filename) ⇒ Object



128
129
130
# File 'lib/ruby-svg.rb', line 128

def save_as(filename)
  write(filename)
end

#text(str, x, y, **attrs, &block) ⇒ Object



57
58
59
60
# File 'lib/ruby-svg.rb', line 57

def text(str, x, y, **attrs, &block) # https://developer.mozilla.org/en-US/docs/Web/SVG/Element/text
  escaped = str.to_s.gsub("<", "&lt;").gsub(">", "&gt;")
  @items << SVG.new(:text, attrs: attrs.merge(x: x, y: y), content: escaped, &block)
end

#to_svg(n = 0) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/ruby-svg.rb', line 111

def to_svg(n=0)
  if @tag == :svg
    @attrs = {
      xmlns: "http://www.w3.org/2000/svg",
      viewBox: [@minx, @miny, @width, @height].join(" "),
    }.merge(@attrs)
  end
  @attrs = @attrs.transform_keys { |sym| sym.to_s.split("_").join("-") }

  html_tag(@tag, n, **@attrs) {
    [@content, *items.map { |i| i.to_svg(n+1) }].filter_map { |i|
      next if i.to_s.gsub(/\s/, "").length == 0
      "#{"  "*(n+1)}#{i}"
    }.join("\n")
  }
end

#write(filename = @filename) ⇒ Object



132
133
134
# File 'lib/ruby-svg.rb', line 132

def write(filename=@filename)
  File.open("#{@filename}.svg", "w") { |file| file.write(to_svg) }
end