prawn-svg 0.24.0 → 0.25.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -9
- data/lib/prawn/svg/attributes.rb +1 -1
- data/lib/prawn/svg/attributes/space.rb +10 -0
- data/lib/prawn/svg/attributes/stroke.rb +2 -2
- data/lib/prawn/svg/attributes/transform.rb +2 -2
- data/lib/prawn/svg/calculators/document_sizing.rb +4 -5
- data/lib/prawn/svg/calculators/pixels.rb +27 -3
- data/lib/prawn/svg/document.rb +0 -18
- data/lib/prawn/svg/elements.rb +2 -2
- data/lib/prawn/svg/elements/base.rb +14 -2
- data/lib/prawn/svg/elements/circle.rb +1 -1
- data/lib/prawn/svg/elements/depth_first_base.rb +52 -0
- data/lib/prawn/svg/elements/ellipse.rb +2 -2
- data/lib/prawn/svg/elements/image.rb +2 -2
- data/lib/prawn/svg/elements/line.rb +4 -4
- data/lib/prawn/svg/elements/marker.rb +2 -2
- data/lib/prawn/svg/elements/rect.rb +3 -3
- data/lib/prawn/svg/elements/root.rb +5 -1
- data/lib/prawn/svg/elements/text.rb +47 -85
- data/lib/prawn/svg/elements/text_component.rb +186 -0
- data/lib/prawn/svg/elements/use.rb +1 -1
- data/lib/prawn/svg/elements/viewport.rb +28 -0
- data/lib/prawn/svg/interface.rb +20 -12
- data/lib/prawn/svg/pathable.rb +18 -2
- data/lib/prawn/svg/state.rb +3 -2
- data/lib/prawn/svg/version.rb +1 -1
- data/spec/prawn/svg/attributes/transform_spec.rb +2 -2
- data/spec/prawn/svg/calculators/pixels_spec.rb +72 -0
- data/spec/prawn/svg/document_spec.rb +0 -28
- data/spec/prawn/svg/elements/base_spec.rb +1 -1
- data/spec/prawn/svg/elements/gradient_spec.rb +1 -1
- data/spec/prawn/svg/elements/line_spec.rb +1 -1
- data/spec/prawn/svg/elements/marker_spec.rb +4 -0
- data/spec/prawn/svg/elements/text_spec.rb +104 -15
- data/spec/sample_svg/markers_degenerate_cp.svg +19 -0
- data/spec/sample_svg/offset_viewport.svg +8 -0
- data/spec/sample_svg/subviewports.svg +173 -0
- data/spec/sample_svg/subviewports2.svg +16 -0
- data/spec/sample_svg/tref01.svg +21 -0
- data/spec/sample_svg/tspan05.svg +40 -0
- data/spec/sample_svg/tspan91.svg +34 -0
- data/spec/spec_helper.rb +6 -0
- metadata +22 -2
@@ -0,0 +1,186 @@
|
|
1
|
+
class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
2
|
+
attr_reader :commands
|
3
|
+
|
4
|
+
Printable = Struct.new(:element, :text, :leading_space?, :trailing_space?)
|
5
|
+
PositionsList = Struct.new(:x, :y, :dx, :dy, :rotation, :parent)
|
6
|
+
|
7
|
+
def parse
|
8
|
+
state.text.x = attributes['x'].split(COMMA_WSP_REGEXP).collect {|n| x(n)} if attributes['x']
|
9
|
+
state.text.y = attributes['y'].split(COMMA_WSP_REGEXP).collect {|n| y(n)} if attributes['y']
|
10
|
+
state.text.dx = attributes['dx'].split(COMMA_WSP_REGEXP).collect {|n| x_pixels(n)} if attributes['dx']
|
11
|
+
state.text.dy = attributes['dy'].split(COMMA_WSP_REGEXP).collect {|n| y_pixels(n)} if attributes['dy']
|
12
|
+
state.text.rotation = attributes['rotate'].split(COMMA_WSP_REGEXP).collect(&:to_f) if attributes['rotate']
|
13
|
+
|
14
|
+
@commands = []
|
15
|
+
|
16
|
+
text_children.each do |child|
|
17
|
+
if child.node_type == :text
|
18
|
+
append_text(child)
|
19
|
+
else
|
20
|
+
case child.name
|
21
|
+
when 'tspan', 'tref'
|
22
|
+
append_child(child)
|
23
|
+
else
|
24
|
+
warnings << "Unknown tag '#{child.name}' inside text tag; ignoring"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def apply
|
31
|
+
raise SkipElementQuietly if computed_properties.display == "none"
|
32
|
+
|
33
|
+
font = select_font
|
34
|
+
apply_font(font) if font
|
35
|
+
|
36
|
+
# text_anchor isn't a Prawn option; we have to do some math to support it
|
37
|
+
# and so we handle this in Prawn::SVG::Interface#rewrite_call_arguments
|
38
|
+
opts = {
|
39
|
+
size: computed_properties.numerical_font_size,
|
40
|
+
style: font && font.subfamily,
|
41
|
+
text_anchor: computed_properties.text_anchor
|
42
|
+
}
|
43
|
+
|
44
|
+
spacing = computed_properties.letter_spacing
|
45
|
+
spacing = spacing == 'normal' ? 0 : pixels(spacing)
|
46
|
+
|
47
|
+
add_call_and_enter 'character_spacing', spacing
|
48
|
+
|
49
|
+
@commands.each do |command|
|
50
|
+
case command
|
51
|
+
when Printable
|
52
|
+
apply_text(command.text, opts)
|
53
|
+
when self.class
|
54
|
+
add_call 'save'
|
55
|
+
command.apply_step(calls)
|
56
|
+
add_call 'restore'
|
57
|
+
else
|
58
|
+
raise
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# It's possible there was no text to render. In that case, add a 'noop' so
|
63
|
+
# character_spacing doesn't blow up when it finds it doesn't have a block to execute.
|
64
|
+
add_call 'noop' if calls.empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def append_text(child)
|
70
|
+
if state.preserve_space
|
71
|
+
text = child.value.tr("\n\t", ' ')
|
72
|
+
else
|
73
|
+
text = child.value.tr("\n", '').tr("\t", ' ')
|
74
|
+
leading = text[0] == ' '
|
75
|
+
trailing = text[-1] == ' '
|
76
|
+
text = text.strip.gsub(/ {2,}/, ' ')
|
77
|
+
end
|
78
|
+
|
79
|
+
@commands << Printable.new(self, text, leading, trailing)
|
80
|
+
end
|
81
|
+
|
82
|
+
def append_child(child)
|
83
|
+
new_state = state.dup
|
84
|
+
new_state.text = PositionsList.new([], [], [], [], [], state.text)
|
85
|
+
|
86
|
+
element = self.class.new(document, child, calls, new_state)
|
87
|
+
@commands << element
|
88
|
+
element.parse_step
|
89
|
+
end
|
90
|
+
|
91
|
+
def apply_text(text, opts)
|
92
|
+
while text != ""
|
93
|
+
x = y = dx = dy = rotate = nil
|
94
|
+
remaining = rotation_remaining = false
|
95
|
+
|
96
|
+
list = state.text
|
97
|
+
while list
|
98
|
+
shifted = list.x.shift
|
99
|
+
x ||= shifted
|
100
|
+
shifted = list.y.shift
|
101
|
+
y ||= shifted
|
102
|
+
shifted = list.dx.shift
|
103
|
+
dx ||= shifted
|
104
|
+
shifted = list.dy.shift
|
105
|
+
dy ||= shifted
|
106
|
+
|
107
|
+
shifted = list.rotation.length > 1 ? list.rotation.shift : list.rotation.first
|
108
|
+
if shifted && rotate.nil?
|
109
|
+
rotate = shifted
|
110
|
+
remaining ||= list.rotation != [0]
|
111
|
+
end
|
112
|
+
|
113
|
+
remaining ||= list.x.any? || list.y.any? || list.dx.any? || list.dy.any? || (rotate && rotate != 0)
|
114
|
+
rotation_remaining ||= list.rotation.length > 1
|
115
|
+
list = list.parent
|
116
|
+
end
|
117
|
+
|
118
|
+
opts[:at] = [x || :relative, y || :relative]
|
119
|
+
opts[:offset] = [dx || 0, dy || 0]
|
120
|
+
|
121
|
+
if rotate && rotate != 0
|
122
|
+
opts[:rotate] = -rotate
|
123
|
+
else
|
124
|
+
opts.delete(:rotate)
|
125
|
+
end
|
126
|
+
|
127
|
+
if remaining
|
128
|
+
add_call 'draw_text', text[0..0], opts.dup
|
129
|
+
text = text[1..-1]
|
130
|
+
else
|
131
|
+
add_call 'draw_text', text, opts.dup
|
132
|
+
|
133
|
+
# we can get to this path with rotations still pending
|
134
|
+
# solve this by shifting them out by the number of
|
135
|
+
# characters we've just drawn
|
136
|
+
shift = text.length - 1
|
137
|
+
if rotation_remaining && shift > 0
|
138
|
+
list = state.text
|
139
|
+
while list
|
140
|
+
count = [shift, list.rotation.length - 1].min
|
141
|
+
list.rotation.shift(count) if count > 0
|
142
|
+
list = list.parent
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
break
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def text_children
|
152
|
+
if name == 'tref'
|
153
|
+
reference = find_referenced_element
|
154
|
+
reference ? reference.source.children : []
|
155
|
+
else
|
156
|
+
source.children
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def find_referenced_element
|
161
|
+
href = attributes['xlink:href']
|
162
|
+
|
163
|
+
if href && href[0..0] == '#'
|
164
|
+
element = document.elements_by_id[href[1..-1]]
|
165
|
+
element if element.name == 'text'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def select_font
|
170
|
+
font_families = [computed_properties.font_family, document.fallback_font_name]
|
171
|
+
font_style = :italic if computed_properties.font_style == 'italic'
|
172
|
+
font_weight = Prawn::SVG::Font.weight_for_css_font_weight(computed_properties.font_weight)
|
173
|
+
|
174
|
+
font_families.compact.each do |name|
|
175
|
+
font = document.font_registry.load(name, font_weight, font_style)
|
176
|
+
return font if font
|
177
|
+
end
|
178
|
+
|
179
|
+
warnings << "Font family '#{computed_properties.font_family}' style '#{computed_properties.font_style}' is not a known font, and the fallback font could not be found."
|
180
|
+
nil
|
181
|
+
end
|
182
|
+
|
183
|
+
def apply_font(font)
|
184
|
+
add_call 'font', font.name, style: font.subfamily
|
185
|
+
end
|
186
|
+
end
|
@@ -21,7 +21,7 @@ class Prawn::SVG::Elements::Use < Prawn::SVG::Elements::Base
|
|
21
21
|
|
22
22
|
def apply
|
23
23
|
if @x || @y
|
24
|
-
add_call_and_enter "translate",
|
24
|
+
add_call_and_enter "translate", x_pixels(@x || 0), -y_pixels(@y || 0)
|
25
25
|
end
|
26
26
|
|
27
27
|
add_calls_from_element @definition_element
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Prawn::SVG::Elements::Viewport < Prawn::SVG::Elements::Base
|
2
|
+
def parse
|
3
|
+
parent_dimensions = [state.viewport_sizing.viewport_width, state.viewport_sizing.viewport_height]
|
4
|
+
|
5
|
+
@sizing = Prawn::SVG::Calculators::DocumentSizing.new(parent_dimensions, attributes)
|
6
|
+
@sizing.calculate
|
7
|
+
|
8
|
+
@x = x_pixels(attributes['x'] || 0)
|
9
|
+
@y = y_pixels(attributes['y'] || 0)
|
10
|
+
|
11
|
+
state.viewport_sizing = @sizing
|
12
|
+
end
|
13
|
+
|
14
|
+
def apply
|
15
|
+
if @x != 0 || @y != 0
|
16
|
+
add_call 'transformation_matrix', 1, 0, 0, 1, @x, -@y
|
17
|
+
end
|
18
|
+
|
19
|
+
add_call 'rectangle', [0, y(0)], @sizing.output_width, @sizing.output_height
|
20
|
+
add_call 'clip'
|
21
|
+
add_call 'transformation_matrix', @sizing.x_scale, 0, 0, @sizing.y_scale, 0, 0
|
22
|
+
add_call 'transformation_matrix', 1, 0, 0, 1, -@sizing.x_offset, @sizing.y_offset
|
23
|
+
end
|
24
|
+
|
25
|
+
def container?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
data/lib/prawn/svg/interface.rb
CHANGED
@@ -118,28 +118,36 @@ module Prawn
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def rewrite_call_arguments(prawn, call, arguments)
|
121
|
-
if call == 'relative_draw_text'
|
122
|
-
call.replace "draw_text"
|
123
|
-
arguments.last[:at][0] = @relative_text_position if @relative_text_position
|
124
|
-
end
|
125
|
-
|
126
121
|
case call
|
127
122
|
when 'text_group'
|
128
|
-
@
|
123
|
+
@cursor = [0, document.sizing.output_height]
|
129
124
|
yield
|
130
125
|
|
131
126
|
when 'draw_text'
|
132
127
|
text, options = arguments
|
133
128
|
|
134
|
-
|
129
|
+
at = options.fetch(:at)
|
130
|
+
|
131
|
+
at[0] = @cursor[0] if at[0] == :relative
|
132
|
+
at[1] = @cursor[1] if at[1] == :relative
|
135
133
|
|
136
|
-
if
|
137
|
-
|
138
|
-
|
134
|
+
if offset = options.delete(:offset)
|
135
|
+
at[0] += offset[0]
|
136
|
+
at[1] -= offset[1]
|
139
137
|
end
|
140
138
|
|
141
|
-
|
142
|
-
|
139
|
+
width = prawn.width_of(text, options.merge(kerning: true))
|
140
|
+
|
141
|
+
case options.delete(:text_anchor)
|
142
|
+
when 'middle'
|
143
|
+
at[0] -= width / 2
|
144
|
+
@cursor = [at[0] + width / 2, at[1]]
|
145
|
+
when 'end'
|
146
|
+
at[0] -= width
|
147
|
+
@cursor = at.dup
|
148
|
+
else
|
149
|
+
@cursor = [at[0] + width, at[1]]
|
150
|
+
end
|
143
151
|
|
144
152
|
when 'transformation_matrix'
|
145
153
|
left = prawn.bounds.absolute_left
|
data/lib/prawn/svg/pathable.rb
CHANGED
@@ -60,8 +60,12 @@ module Prawn::SVG::Pathable
|
|
60
60
|
angle = Math.atan2(command.destination[1] - last_point[1], command.destination[0] - last_point[0]) * 180.0 / Math::PI
|
61
61
|
[angle, angle]
|
62
62
|
when Curve
|
63
|
-
|
64
|
-
|
63
|
+
point = select_non_equal_point(last_point, command.point1, command.point2, command.destination)
|
64
|
+
start = Math.atan2(point[1] - last_point[1], point[0] - last_point[0]) * 180.0 / Math::PI
|
65
|
+
|
66
|
+
point = select_non_equal_point(command.destination, command.point2, command.point1, last_point)
|
67
|
+
stop = Math.atan2(command.destination[1] - point[1], command.destination[0] - point[0]) * 180.0 / Math::PI
|
68
|
+
|
65
69
|
[start, stop]
|
66
70
|
else
|
67
71
|
raise NotImplementedError, "Unknown path command type"
|
@@ -127,4 +131,16 @@ module Prawn::SVG::Pathable
|
|
127
131
|
def translate(point)
|
128
132
|
[point[0].to_f, document.sizing.output_height - point[1].to_f]
|
129
133
|
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def select_non_equal_point(base, point_a, point_b, point_c)
|
138
|
+
if point_a != base
|
139
|
+
point_a
|
140
|
+
elsif point_b != base
|
141
|
+
point_b
|
142
|
+
else
|
143
|
+
point_c
|
144
|
+
end
|
145
|
+
end
|
130
146
|
end
|
data/lib/prawn/svg/state.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
class Prawn::SVG::State
|
2
2
|
attr_accessor :disable_drawing,
|
3
|
-
:
|
3
|
+
:text, :preserve_space,
|
4
4
|
:fill_opacity, :stroke_opacity, :stroke_width,
|
5
|
-
:computed_properties
|
5
|
+
:computed_properties,
|
6
|
+
:viewport_sizing
|
6
7
|
|
7
8
|
def initialize
|
8
9
|
@stroke_width = 1
|
data/lib/prawn/svg/version.rb
CHANGED
@@ -20,8 +20,8 @@ describe Prawn::SVG::Attributes::Transform do
|
|
20
20
|
describe "translate" do
|
21
21
|
it "handles a missing y argument" do
|
22
22
|
expect(element).to receive(:add_call_and_enter).with('translate', -5.5, 0)
|
23
|
-
expect(element).to receive(:
|
24
|
-
expect(element).to receive(:
|
23
|
+
expect(element).to receive(:x_pixels).with(-5.5).and_return(-5.5)
|
24
|
+
expect(element).to receive(:y_pixels).with(0.0).and_return(0.0)
|
25
25
|
|
26
26
|
element.attributes['transform'] = 'translate(-5.5)'
|
27
27
|
subject
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Prawn::SVG::Calculators::Pixels do
|
4
|
+
class TestPixelsCalculator
|
5
|
+
include Prawn::SVG::Calculators::Pixels
|
6
|
+
|
7
|
+
[:x, :y, :pixels, :x_pixels, :y_pixels].each { |method| public method }
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:viewport_sizing) do
|
11
|
+
instance_double(Prawn::SVG::Calculators::DocumentSizing, viewport_width: 600, viewport_height: 400, viewport_diagonal: 500, :requested_width= => nil, :requested_height= => nil)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:document_sizing) do
|
15
|
+
instance_double(Prawn::SVG::Calculators::DocumentSizing, output_height: 800)
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:state) { instance_double(Prawn::SVG::State, viewport_sizing: viewport_sizing) }
|
19
|
+
let(:document) { instance_double(Prawn::SVG::Document, sizing: document_sizing) }
|
20
|
+
|
21
|
+
subject { TestPixelsCalculator.new }
|
22
|
+
|
23
|
+
before do
|
24
|
+
allow(subject).to receive(:state).and_return(state)
|
25
|
+
allow(subject).to receive(:document).and_return(document)
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#pixels" do
|
29
|
+
it "converts a variety of measurement units to points" do
|
30
|
+
expect(subject.pixels(32)).to eq 32.0
|
31
|
+
expect(subject.pixels(32.0)).to eq 32.0
|
32
|
+
expect(subject.pixels("32")).to eq 32.0
|
33
|
+
expect(subject.pixels("32unknown")).to eq 32.0
|
34
|
+
expect(subject.pixels("32pt")).to eq 32.0
|
35
|
+
expect(subject.pixels("32in")).to eq 32.0 * 72
|
36
|
+
expect(subject.pixels("32ft")).to eq 32.0 * 72 * 12
|
37
|
+
expect(subject.pixels("32pc")).to eq 32.0 * 15
|
38
|
+
expect(subject.pixels("32mm")).to be_within(0.0001).of(32 * 72 * 0.0393700787)
|
39
|
+
expect(subject.pixels("32cm")).to be_within(0.0001).of(32 * 72 * 0.393700787)
|
40
|
+
expect(subject.pixels("32m")).to be_within(0.0001).of(32 * 72 * 39.3700787)
|
41
|
+
expect(subject.pixels("50%")).to eq 250
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#x_pixels" do
|
46
|
+
it "uses the viewport width for percentages" do
|
47
|
+
expect(subject.x_pixels("50")).to eq 50
|
48
|
+
expect(subject.x_pixels("50%")).to eq 300
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#y_pixels" do
|
53
|
+
it "uses the viewport height for percentages" do
|
54
|
+
expect(subject.y_pixels("50")).to eq 50
|
55
|
+
expect(subject.y_pixels("50%")).to eq 200
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#x" do
|
60
|
+
it "performs the same as #x_pixels" do
|
61
|
+
expect(subject.x("50")).to eq 50
|
62
|
+
expect(subject.x("50%")).to eq 300
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#y" do
|
67
|
+
it "performs the same as #y_pixels but subtracts the pixels from the page height" do
|
68
|
+
expect(subject.y("50")).to eq 800 - 50
|
69
|
+
expect(subject.y("50%")).to eq 800 - 200
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -25,32 +25,4 @@ describe Prawn::SVG::Document do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
29
|
-
describe "#points" do
|
30
|
-
before do
|
31
|
-
sizing = instance_double(Prawn::SVG::Calculators::DocumentSizing, viewport_width: 600, viewport_height: 400, viewport_diagonal: 500, :requested_width= => nil, :requested_height= => nil)
|
32
|
-
expect(sizing).to receive(:calculate)
|
33
|
-
expect(Prawn::SVG::Calculators::DocumentSizing).to receive(:new).and_return(sizing)
|
34
|
-
end
|
35
|
-
|
36
|
-
let(:document) { Prawn::SVG::Document.new("<svg></svg>", [100, 100], {}) }
|
37
|
-
|
38
|
-
it "converts a variety of measurement units to points" do
|
39
|
-
document.send(:points, 32).should == 32.0
|
40
|
-
document.send(:points, 32.0).should == 32.0
|
41
|
-
document.send(:points, "32").should == 32.0
|
42
|
-
document.send(:points, "32unknown").should == 32.0
|
43
|
-
document.send(:points, "32pt").should == 32.0
|
44
|
-
document.send(:points, "32in").should == 32.0 * 72
|
45
|
-
document.send(:points, "32ft").should == 32.0 * 72 * 12
|
46
|
-
document.send(:points, "32pc").should == 32.0 * 15
|
47
|
-
document.send(:points, "32mm").should be_within(0.0001).of(32 * 72 * 0.0393700787)
|
48
|
-
document.send(:points, "32cm").should be_within(0.0001).of(32 * 72 * 0.393700787)
|
49
|
-
document.send(:points, "32m").should be_within(0.0001).of(32 * 72 * 39.3700787)
|
50
|
-
|
51
|
-
document.send(:points, "50%").should == 250
|
52
|
-
document.send(:points, "50%", :x).should == 300
|
53
|
-
document.send(:points, "50%", :y).should == 200
|
54
|
-
end
|
55
|
-
end
|
56
28
|
end
|
@@ -4,7 +4,7 @@ describe Prawn::SVG::Elements::Base do
|
|
4
4
|
let(:svg) { "<svg></svg>" }
|
5
5
|
let(:document) { Prawn::SVG::Document.new(svg, [800, 600], {}, font_registry: Prawn::SVG::FontRegistry.new("Helvetica" => {:normal => nil})) }
|
6
6
|
let(:parent_calls) { [] }
|
7
|
-
let(:element) { Prawn::SVG::Elements::Base.new(document, document.root, parent_calls,
|
7
|
+
let(:element) { Prawn::SVG::Elements::Base.new(document, document.root, parent_calls, fake_state) }
|
8
8
|
|
9
9
|
describe "#initialize" do
|
10
10
|
let(:svg) { '<something id="hello"/>' }
|