prawn-svg 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +6 -6
- data/README.md +3 -3
- data/lib/prawn/svg/elements.rb +1 -0
- data/lib/prawn/svg/elements/base.rb +4 -0
- data/lib/prawn/svg/elements/gradient.rb +81 -24
- data/lib/prawn/svg/elements/image.rb +2 -2
- data/lib/prawn/svg/elements/path.rb +41 -29
- data/lib/prawn/svg/elements/root.rb +4 -1
- data/lib/prawn/svg/elements/text_component.rb +4 -2
- data/lib/prawn/svg/elements/use.rb +23 -7
- data/lib/prawn/svg/interface.rb +10 -1
- data/lib/prawn/svg/properties.rb +1 -0
- data/lib/prawn/svg/version.rb +1 -1
- data/prawn-svg.gemspec +1 -1
- data/spec/prawn/svg/elements/gradient_spec.rb +53 -2
- data/spec/prawn/svg/elements/path_spec.rb +29 -17
- data/spec/prawn/svg/elements/text_spec.rb +15 -0
- data/spec/sample_svg/radgrad01-bounding.svg +26 -0
- data/spec/sample_svg/radgrad01.svg +26 -0
- data/spec/sample_svg/svg_fill.svg +5 -0
- data/spec/sample_svg/text-decoration.svg +4 -0
- data/spec/sample_svg/use_disordered.svg +17 -0
- metadata +18 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ed78a64dc94709fc5e53f1d6f521f93e3bf41196c29dde616c6139743c9c889
|
4
|
+
data.tar.gz: 7a0a5683370d3478e8b9717069e0f4012337aaf6250106065dfd5ad9b4458a91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a58fd3f4508d486b3632f2e337f5e2fe97ef8c2baeeeda9b4f53f816ff18eb10b3e8879c98ea10c645becb29c8351a7addc6867c22abfeda77a7edf654607f44
|
7
|
+
data.tar.gz: 3bdbbc06f25405d3f51de30e81c39406f7ccf535e507bb36b43df3939d1a4931b763cb5308bde72674ab3f2ffcb55171b958f93a3b603bf5cf17a1bbd24d44b4
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -61,7 +61,7 @@ prawn-svg supports most but not all of the full SVG 1.1 specification. It curre
|
|
61
61
|
implementation of elliptical arc is a bit rough at the moment.
|
62
62
|
|
63
63
|
- `<text>`, `<tspan>` and `<tref>` with attributes `x`, `y`, `dx`, `dy`, `rotate`, 'textLength', 'lengthAdjust', and with extra properties
|
64
|
-
`text-anchor`, `font-size`, `font-family`, `font-weight`, `font-style`, `letter-spacing`
|
64
|
+
`text-anchor`, `text-decoration` (underline only), `font-size`, `font-family`, `font-weight`, `font-style`, `letter-spacing`
|
65
65
|
|
66
66
|
- <tt><svg></tt>, <tt><g></tt> and <tt><symbol></tt>
|
67
67
|
|
@@ -76,7 +76,7 @@ prawn-svg supports most but not all of the full SVG 1.1 specification. It curre
|
|
76
76
|
|
77
77
|
- `<marker>`
|
78
78
|
|
79
|
-
- `<linearGradient>`
|
79
|
+
- `<linearGradient>` and `<radialGradient>` are implemented on Prawn 2.2.0+ with attributes `gradientUnits` and `gradientTransform` (spreadMethod and stop-opacity are unimplemented.)
|
80
80
|
|
81
81
|
- `<switch>` and `<foreignObject>`, although prawn-svg cannot handle any data that is not SVG so `<foreignObject>`
|
82
82
|
tags are always ignored.
|
@@ -116,7 +116,7 @@ implemented, but `!important` is not.
|
|
116
116
|
|
117
117
|
## Not supported
|
118
118
|
|
119
|
-
prawn-svg does not support
|
119
|
+
prawn-svg does not support hyperlinks, patterns or filters.
|
120
120
|
|
121
121
|
It does not support text in the clip area, but you can clip shapes and text by any shape.
|
122
122
|
|
data/lib/prawn/svg/elements.rb
CHANGED
@@ -28,6 +28,7 @@ module Prawn::SVG::Elements
|
|
28
28
|
use: Prawn::SVG::Elements::Use,
|
29
29
|
image: Prawn::SVG::Elements::Image,
|
30
30
|
linearGradient: Prawn::SVG::Elements::Gradient,
|
31
|
+
radialGradient: Prawn::SVG::Elements::Gradient,
|
31
32
|
marker: Prawn::SVG::Elements::Marker,
|
32
33
|
style: Prawn::SVG::Elements::Ignored, # because it is pre-parsed by Document
|
33
34
|
title: Prawn::SVG::Elements::Ignored,
|
@@ -267,4 +267,8 @@ class Prawn::SVG::Elements::Base
|
|
267
267
|
element = document.elements_by_id[matches[1]] if matches
|
268
268
|
element if element && (expected_type.nil? || element.name == expected_type)
|
269
269
|
end
|
270
|
+
|
271
|
+
def href_attribute
|
272
|
+
attributes['xlink:href'] || attributes['href']
|
273
|
+
end
|
270
274
|
end
|
@@ -1,10 +1,18 @@
|
|
1
1
|
class Prawn::SVG::Elements::Gradient < Prawn::SVG::Elements::Base
|
2
|
-
|
2
|
+
attr_reader :parent_gradient
|
3
|
+
attr_reader :x1, :y1, :x2, :y2, :cx, :cy, :fx, :fy, :radius, :units, :stops, :transform_matrix
|
4
|
+
|
5
|
+
TAG_NAME_TO_TYPE = {
|
6
|
+
"linearGradient" => :linear,
|
7
|
+
"radialGradient" => :radial
|
8
|
+
}
|
3
9
|
|
4
10
|
def parse
|
5
11
|
# A gradient tag without an ID is inaccessible and can never be used
|
6
12
|
raise SkipElementQuietly if attributes['id'].nil?
|
7
13
|
|
14
|
+
@parent_gradient = document.gradients[href_attribute[1..-1]] if href_attribute && href_attribute[0] == '#'
|
15
|
+
|
8
16
|
assert_compatible_prawn_version
|
9
17
|
load_gradient_configuration
|
10
18
|
load_coordinates
|
@@ -16,31 +24,57 @@ class Prawn::SVG::Elements::Gradient < Prawn::SVG::Elements::Base
|
|
16
24
|
end
|
17
25
|
|
18
26
|
def gradient_arguments(element)
|
19
|
-
case @units
|
20
|
-
when :bounding_box
|
21
|
-
x1, y1, x2, y2 = element.bounding_box
|
22
|
-
return if y2.nil?
|
23
|
-
|
24
|
-
width = x2 - x1
|
25
|
-
height = y1 - y2
|
26
|
-
|
27
|
-
from = [x1 + width * @x1, y1 - height * @y1]
|
28
|
-
to = [x1 + width * @x2, y1 - height * @y2]
|
29
|
-
|
30
|
-
when :user_space
|
31
|
-
from = [@x1, @y1]
|
32
|
-
to = [@x2, @y2]
|
33
|
-
end
|
34
|
-
|
35
27
|
# Passing in a transformation matrix to the apply_transformations option is supported
|
36
28
|
# by a monkey patch installed by prawn-svg. Prawn only sees this as a truthy variable.
|
37
29
|
#
|
38
30
|
# See Prawn::SVG::Extensions::AdditionalGradientTransforms for details.
|
39
|
-
|
31
|
+
base_arguments = {stops: stops, apply_transformations: transform_matrix || true}
|
32
|
+
|
33
|
+
arguments = specific_gradient_arguments(element)
|
34
|
+
arguments.merge(base_arguments) if arguments
|
40
35
|
end
|
41
36
|
|
42
37
|
private
|
43
38
|
|
39
|
+
def specific_gradient_arguments(element)
|
40
|
+
if units == :bounding_box
|
41
|
+
bounding_x1, bounding_y1, bounding_x2, bounding_y2 = element.bounding_box
|
42
|
+
return if bounding_y2.nil?
|
43
|
+
|
44
|
+
width = bounding_x2 - bounding_x1
|
45
|
+
height = bounding_y1 - bounding_y2
|
46
|
+
end
|
47
|
+
|
48
|
+
case [type, units]
|
49
|
+
when [:linear, :bounding_box]
|
50
|
+
from = [bounding_x1 + width * x1, bounding_y1 - height * y1]
|
51
|
+
to = [bounding_x1 + width * x2, bounding_y1 - height * y2]
|
52
|
+
|
53
|
+
{from: from, to: to}
|
54
|
+
|
55
|
+
when [:linear, :user_space]
|
56
|
+
{from: [x1, y1], to: [x2, y2]}
|
57
|
+
|
58
|
+
when [:radial, :bounding_box]
|
59
|
+
center = [bounding_x1 + width * cx, bounding_y1 - height * cy]
|
60
|
+
focus = [bounding_x1 + width * fx, bounding_y1 - height * fy]
|
61
|
+
|
62
|
+
# Note: Chrome, at least, implements radial bounding box radiuses as
|
63
|
+
# having separate X and Y components, so in bounding box mode their
|
64
|
+
# gradients come out as ovals instead of circles. PDF radial shading
|
65
|
+
# doesn't have the option to do this, and it's confusing why the
|
66
|
+
# Chrome user space gradients don't apply the same logic anyway.
|
67
|
+
hypot = Math.sqrt(width * width + height * height)
|
68
|
+
{from: focus, r1: 0, to: center, r2: radius * hypot}
|
69
|
+
|
70
|
+
when [:radial, :user_space]
|
71
|
+
{from: [fx, fy], r1: 0, to: [cx, cy], r2: radius}
|
72
|
+
|
73
|
+
else
|
74
|
+
raise "unexpected type/unit system"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
44
78
|
def type
|
45
79
|
TAG_NAME_TO_TYPE.fetch(name)
|
46
80
|
end
|
@@ -64,18 +98,35 @@ class Prawn::SVG::Elements::Gradient < Prawn::SVG::Elements::Base
|
|
64
98
|
end
|
65
99
|
|
66
100
|
def load_coordinates
|
67
|
-
case
|
68
|
-
when :bounding_box
|
101
|
+
case [type, units]
|
102
|
+
when [:linear, :bounding_box]
|
69
103
|
@x1 = parse_zero_to_one(attributes["x1"], 0)
|
70
104
|
@y1 = parse_zero_to_one(attributes["y1"], 0)
|
71
105
|
@x2 = parse_zero_to_one(attributes["x2"], 1)
|
72
106
|
@y2 = parse_zero_to_one(attributes["y2"], 0)
|
73
107
|
|
74
|
-
when :user_space
|
108
|
+
when [:linear, :user_space]
|
75
109
|
@x1 = x(attributes["x1"])
|
76
110
|
@y1 = y(attributes["y1"])
|
77
111
|
@x2 = x(attributes["x2"])
|
78
112
|
@y2 = y(attributes["y2"])
|
113
|
+
|
114
|
+
when [:radial, :bounding_box]
|
115
|
+
@cx = parse_zero_to_one(attributes["cx"], 0.5)
|
116
|
+
@cy = parse_zero_to_one(attributes["cy"], 0.5)
|
117
|
+
@fx = parse_zero_to_one(attributes["fx"], cx)
|
118
|
+
@fy = parse_zero_to_one(attributes["fy"], cy)
|
119
|
+
@radius = parse_zero_to_one(attributes["r"], 0.5)
|
120
|
+
|
121
|
+
when [:radial, :user_space]
|
122
|
+
@cx = x(attributes["cx"] || '50%')
|
123
|
+
@cy = y(attributes["cy"] || '50%')
|
124
|
+
@fx = x(attributes["fx"] || attributes["cx"])
|
125
|
+
@fy = y(attributes["fy"] || attributes["cy"])
|
126
|
+
@radius = pixels(attributes["r"] || '50%')
|
127
|
+
|
128
|
+
else
|
129
|
+
raise "unexpected type/unit system"
|
79
130
|
end
|
80
131
|
end
|
81
132
|
|
@@ -101,10 +152,16 @@ class Prawn::SVG::Elements::Gradient < Prawn::SVG::Elements::Base
|
|
101
152
|
end
|
102
153
|
end
|
103
154
|
|
104
|
-
|
155
|
+
if stops.empty?
|
156
|
+
if parent_gradient.nil? || parent_gradient.stops.empty?
|
157
|
+
raise SkipElementError, "gradient does not have any valid stops"
|
158
|
+
end
|
105
159
|
|
106
|
-
|
107
|
-
|
160
|
+
@stops = parent_gradient.stops
|
161
|
+
else
|
162
|
+
stops.unshift([0, stops.first.last]) if stops.first.first > 0
|
163
|
+
stops.push([1, stops.last.last]) if stops.last.first < 1
|
164
|
+
end
|
108
165
|
end
|
109
166
|
|
110
167
|
def parse_zero_to_one(string, default = 0)
|
@@ -15,9 +15,9 @@ class Prawn::SVG::Elements::Image < Prawn::SVG::Elements::Base
|
|
15
15
|
|
16
16
|
raise SkipElementQuietly if state.computed_properties.display == "none"
|
17
17
|
|
18
|
-
@url =
|
18
|
+
@url = href_attribute
|
19
19
|
if @url.nil?
|
20
|
-
raise SkipElementError, "image tag must have an xlink:href"
|
20
|
+
raise SkipElementError, "image tag must have an href or xlink:href"
|
21
21
|
end
|
22
22
|
|
23
23
|
x = x(attributes['x'] || 0)
|
@@ -4,10 +4,31 @@ class Prawn::SVG::Elements::Path < Prawn::SVG::Elements::Base
|
|
4
4
|
|
5
5
|
INSIDE_SPACE_REGEXP = /[ \t\r\n,]*/
|
6
6
|
OUTSIDE_SPACE_REGEXP = /[ \t\r\n]*/
|
7
|
-
INSIDE_REGEXP = /#{INSIDE_SPACE_REGEXP}([+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:(?<=[0-9])[eE][+-]?[0-9]+)?)/
|
8
|
-
|
7
|
+
INSIDE_REGEXP = /#{INSIDE_SPACE_REGEXP}(?>([+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:(?<=[0-9])[eE][+-]?[0-9]+)?))/
|
8
|
+
FLAG_REGEXP = /#{INSIDE_SPACE_REGEXP}([01])/
|
9
9
|
COMMAND_REGEXP = /^#{OUTSIDE_SPACE_REGEXP}([A-Za-z])((?:#{INSIDE_REGEXP})*)#{OUTSIDE_SPACE_REGEXP}/
|
10
10
|
|
11
|
+
A_PARAMETERS_REGEXP = /^#{INSIDE_REGEXP}#{INSIDE_REGEXP}#{INSIDE_REGEXP}#{FLAG_REGEXP}#{FLAG_REGEXP}#{INSIDE_REGEXP}#{INSIDE_REGEXP}/
|
12
|
+
ONE_PARAMETER_REGEXP = /^#{INSIDE_REGEXP}/
|
13
|
+
TWO_PARAMETER_REGEXP = /^#{INSIDE_REGEXP}#{INSIDE_REGEXP}/
|
14
|
+
FOUR_PARAMETER_REGEXP = /^#{INSIDE_REGEXP}#{INSIDE_REGEXP}#{INSIDE_REGEXP}#{INSIDE_REGEXP}/
|
15
|
+
SIX_PARAMETER_REGEXP = /^#{INSIDE_REGEXP}#{INSIDE_REGEXP}#{INSIDE_REGEXP}#{INSIDE_REGEXP}#{INSIDE_REGEXP}#{INSIDE_REGEXP}/
|
16
|
+
|
17
|
+
COMMAND_MATCH_MAP = {
|
18
|
+
'A' => A_PARAMETERS_REGEXP,
|
19
|
+
'C' => SIX_PARAMETER_REGEXP,
|
20
|
+
'H' => ONE_PARAMETER_REGEXP,
|
21
|
+
'L' => TWO_PARAMETER_REGEXP,
|
22
|
+
'M' => TWO_PARAMETER_REGEXP,
|
23
|
+
'Q' => FOUR_PARAMETER_REGEXP,
|
24
|
+
'S' => FOUR_PARAMETER_REGEXP,
|
25
|
+
'T' => TWO_PARAMETER_REGEXP,
|
26
|
+
'V' => ONE_PARAMETER_REGEXP,
|
27
|
+
'Z' => //,
|
28
|
+
}
|
29
|
+
|
30
|
+
PARAMETERLESS_COMMANDS = COMMAND_MATCH_MAP.select { |_, v| v == // }.map(&:first)
|
31
|
+
|
11
32
|
FLOAT_ERROR_DELTA = 1e-10
|
12
33
|
|
13
34
|
attr_reader :commands
|
@@ -23,14 +44,13 @@ class Prawn::SVG::Elements::Path < Prawn::SVG::Elements::Base
|
|
23
44
|
matched_commands = match_all(data, COMMAND_REGEXP)
|
24
45
|
raise SkipElementError, "Invalid/unsupported syntax for SVG path data" if matched_commands.nil?
|
25
46
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
47
|
+
matched_commands.each do |(command, parameters)|
|
48
|
+
regexp = COMMAND_MATCH_MAP[command.upcase] or break
|
49
|
+
matched_values = match_all(parameters, regexp) or break
|
50
|
+
values = matched_values.map { |value| value.map(&:to_f) }
|
51
|
+
break if values.empty? && !PARAMETERLESS_COMMANDS.include?(command.upcase)
|
52
|
+
|
53
|
+
parse_path_command(command, values)
|
34
54
|
end
|
35
55
|
end
|
36
56
|
|
@@ -49,8 +69,7 @@ class Prawn::SVG::Elements::Path < Prawn::SVG::Elements::Base
|
|
49
69
|
|
50
70
|
case upcase_command
|
51
71
|
when 'M' # moveto
|
52
|
-
x = values.shift
|
53
|
-
y = values.shift or throw :invalid_command
|
72
|
+
x, y = values.shift
|
54
73
|
|
55
74
|
if relative && @last_point
|
56
75
|
x += @last_point.first
|
@@ -69,8 +88,7 @@ class Prawn::SVG::Elements::Path < Prawn::SVG::Elements::Base
|
|
69
88
|
|
70
89
|
when 'L' # lineto
|
71
90
|
while values.any?
|
72
|
-
x = values.shift
|
73
|
-
y = values.shift or throw :invalid_command
|
91
|
+
x, y = values.shift
|
74
92
|
if relative && @last_point
|
75
93
|
x += @last_point.first
|
76
94
|
y += @last_point.last
|
@@ -81,22 +99,21 @@ class Prawn::SVG::Elements::Path < Prawn::SVG::Elements::Base
|
|
81
99
|
|
82
100
|
when 'H' # horizontal lineto
|
83
101
|
while values.any?
|
84
|
-
x = values.shift
|
102
|
+
x = values.shift.first
|
85
103
|
x += @last_point.first if relative && @last_point
|
86
104
|
push_command Prawn::SVG::Pathable::Line.new([x, @last_point.last])
|
87
105
|
end
|
88
106
|
|
89
107
|
when 'V' # vertical lineto
|
90
108
|
while values.any?
|
91
|
-
y = values.shift
|
109
|
+
y = values.shift.first
|
92
110
|
y += @last_point.last if relative && @last_point
|
93
111
|
push_command Prawn::SVG::Pathable::Line.new([@last_point.first, y])
|
94
112
|
end
|
95
113
|
|
96
114
|
when 'C' # curveto
|
97
115
|
while values.any?
|
98
|
-
x1, y1, x2, y2, x, y = values.shift
|
99
|
-
throw :invalid_command unless y
|
116
|
+
x1, y1, x2, y2, x, y = values.shift
|
100
117
|
|
101
118
|
if relative && @last_point
|
102
119
|
x += @last_point.first
|
@@ -113,8 +130,7 @@ class Prawn::SVG::Elements::Path < Prawn::SVG::Elements::Base
|
|
113
130
|
|
114
131
|
when 'S' # shorthand/smooth curveto
|
115
132
|
while values.any?
|
116
|
-
x2, y2, x, y = values.shift
|
117
|
-
throw :invalid_command unless y
|
133
|
+
x2, y2, x, y = values.shift
|
118
134
|
|
119
135
|
if relative && @last_point
|
120
136
|
x += @last_point.first
|
@@ -137,13 +153,11 @@ class Prawn::SVG::Elements::Path < Prawn::SVG::Elements::Base
|
|
137
153
|
when 'Q', 'T' # quadratic curveto
|
138
154
|
while values.any?
|
139
155
|
if shorthand = upcase_command == 'T'
|
140
|
-
x, y = values.shift
|
156
|
+
x, y = values.shift
|
141
157
|
else
|
142
|
-
x1, y1, x, y = values.shift
|
158
|
+
x1, y1, x, y = values.shift
|
143
159
|
end
|
144
160
|
|
145
|
-
throw :invalid_command unless y
|
146
|
-
|
147
161
|
if relative && @last_point
|
148
162
|
x += @last_point.first
|
149
163
|
x1 += @last_point.first if x1
|
@@ -175,8 +189,7 @@ class Prawn::SVG::Elements::Path < Prawn::SVG::Elements::Base
|
|
175
189
|
return unless @last_point
|
176
190
|
|
177
191
|
while values.any?
|
178
|
-
rx, ry, phi, fa, fs, x2, y2 = values.shift
|
179
|
-
throw :invalid_command unless y2
|
192
|
+
rx, ry, phi, fa, fs, x2, y2 = values.shift
|
180
193
|
|
181
194
|
x1, y1 = @last_point
|
182
195
|
|
@@ -277,9 +290,8 @@ class Prawn::SVG::Elements::Path < Prawn::SVG::Elements::Base
|
|
277
290
|
def match_all(string, regexp) # regexp must start with ^
|
278
291
|
result = []
|
279
292
|
while string != ""
|
280
|
-
matches = string.match(regexp)
|
281
|
-
result << matches
|
282
|
-
return if matches.nil?
|
293
|
+
matches = string.match(regexp) or return
|
294
|
+
result << matches.captures
|
283
295
|
string = matches.post_match
|
284
296
|
end
|
285
297
|
result
|
@@ -8,7 +8,10 @@ class Prawn::SVG::Elements::Root < Prawn::SVG::Elements::Base
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def apply
|
11
|
-
|
11
|
+
if [nil, 'inherit', 'none', 'currentColor'].include?(properties.fill)
|
12
|
+
add_call 'fill_color', '000000'
|
13
|
+
end
|
14
|
+
|
12
15
|
add_call 'transformation_matrix', @document.sizing.x_scale, 0, 0, @document.sizing.y_scale, 0, 0
|
13
16
|
add_call 'transformation_matrix', 1, 0, 0, 1, -@document.sizing.x_offset, @document.sizing.y_offset
|
14
17
|
end
|
@@ -46,9 +46,11 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
46
46
|
opts = {
|
47
47
|
size: computed_properties.numerical_font_size,
|
48
48
|
style: font && font.subfamily,
|
49
|
-
text_anchor: computed_properties.text_anchor
|
49
|
+
text_anchor: computed_properties.text_anchor,
|
50
50
|
}
|
51
51
|
|
52
|
+
opts[:decoration] = computed_properties.text_decoration unless computed_properties.text_decoration == 'none'
|
53
|
+
|
52
54
|
if state.text.parent
|
53
55
|
add_call_and_enter 'character_spacing', state.text.spacing unless state.text.spacing == state.text.parent.spacing
|
54
56
|
add_call_and_enter 'text_rendering_mode', state.text.mode unless state.text.mode == state.text.parent.mode
|
@@ -183,7 +185,7 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
183
185
|
end
|
184
186
|
|
185
187
|
def find_referenced_element
|
186
|
-
href =
|
188
|
+
href = href_attribute
|
187
189
|
|
188
190
|
if href && href[0..0] == '#'
|
189
191
|
element = document.elements_by_id[href[1..-1]]
|
@@ -1,19 +1,35 @@
|
|
1
1
|
class Prawn::SVG::Elements::Use < Prawn::SVG::Elements::Base
|
2
|
-
attr_reader :
|
2
|
+
attr_reader :referenced_element_class
|
3
|
+
attr_reader :referenced_element_source
|
3
4
|
|
4
5
|
def parse
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
href = href_attribute
|
7
|
+
if href.nil?
|
8
|
+
raise SkipElementError, "use tag must have an href or xlink:href"
|
9
|
+
end
|
8
10
|
|
9
11
|
if href[0..0] != '#'
|
10
12
|
raise SkipElementError, "use tag has an href that is not a reference to an id; this is not supported"
|
11
13
|
end
|
12
14
|
|
13
15
|
id = href[1..-1]
|
14
|
-
|
16
|
+
referenced_element = @document.elements_by_id[id]
|
17
|
+
|
18
|
+
if referenced_element
|
19
|
+
@referenced_element_class = referenced_element.class
|
20
|
+
@referenced_element_source = referenced_element.source
|
21
|
+
else
|
22
|
+
# Perhaps the element is defined further down in the document. This is not recommended but still valid SVG,
|
23
|
+
# so we'll support it with an exception case that's not particularly performant.
|
24
|
+
raw_element = REXML::XPath.match(@document.root, %(//*[@id="#{id.gsub('"', '\"')}"])).first
|
25
|
+
|
26
|
+
if raw_element
|
27
|
+
@referenced_element_class = Prawn::SVG::Elements::TAG_CLASS_MAPPING[raw_element.name.to_sym]
|
28
|
+
@referenced_element_source = raw_element
|
29
|
+
end
|
30
|
+
end
|
15
31
|
|
16
|
-
if
|
32
|
+
if referenced_element_class.nil?
|
17
33
|
raise SkipElementError, "no tag with ID '#{id}' was found, referenced by use tag"
|
18
34
|
end
|
19
35
|
|
@@ -36,7 +52,7 @@ class Prawn::SVG::Elements::Use < Prawn::SVG::Elements::Base
|
|
36
52
|
def process_child_elements
|
37
53
|
add_call "save"
|
38
54
|
|
39
|
-
child =
|
55
|
+
child = referenced_element_class.new(document, referenced_element_source, calls, state.dup)
|
40
56
|
child.process
|
41
57
|
|
42
58
|
add_call "restore"
|
data/lib/prawn/svg/interface.rb
CHANGED
@@ -119,7 +119,7 @@ module Prawn
|
|
119
119
|
|
120
120
|
if skip
|
121
121
|
# the call has been overridden
|
122
|
-
elsif children.empty?
|
122
|
+
elsif children.empty? && call != 'transparent' # some prawn calls complain if they aren't supplied a block
|
123
123
|
prawn.send(call, *arguments)
|
124
124
|
else
|
125
125
|
prawn.send(call, *arguments, &proc_creator(prawn, children))
|
@@ -172,6 +172,15 @@ module Prawn
|
|
172
172
|
@cursor = [at[0] + width, at[1]]
|
173
173
|
end
|
174
174
|
|
175
|
+
decoration = options.delete(:decoration)
|
176
|
+
if decoration == 'underline'
|
177
|
+
prawn.save_graphics_state do
|
178
|
+
prawn.line_width 1
|
179
|
+
prawn.line [at[0], at[1] - 1.25], [at[0] + width, at[1] - 1.25]
|
180
|
+
prawn.stroke
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
175
184
|
when 'transformation_matrix'
|
176
185
|
left = prawn.bounds.absolute_left
|
177
186
|
top = prawn.bounds.absolute_top
|
data/lib/prawn/svg/properties.rb
CHANGED
@@ -37,6 +37,7 @@ class Prawn::SVG::Properties
|
|
37
37
|
"stroke-opacity" => Config.new("1", true),
|
38
38
|
"stroke-width" => Config.new("1", true),
|
39
39
|
"text-anchor" => Config.new("start", true, %w(inherit start middle end), true),
|
40
|
+
'text-decoration' => Config.new('none', true, %w(inherit none underline), true),
|
40
41
|
}.freeze
|
41
42
|
|
42
43
|
PROPERTIES.each do |name, value|
|
data/lib/prawn/svg/version.rb
CHANGED
data/prawn-svg.gemspec
CHANGED
@@ -22,5 +22,5 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.add_runtime_dependency "prawn", ">= 0.11.1", "< 3"
|
23
23
|
gem.add_runtime_dependency "css_parser", "~> 1.6"
|
24
24
|
gem.add_development_dependency "rspec", "~> 3.0"
|
25
|
-
gem.add_development_dependency "rake", "~>
|
25
|
+
gem.add_development_dependency "rake", "~> 13.0"
|
26
26
|
end
|
@@ -9,7 +9,7 @@ describe Prawn::SVG::Elements::Gradient do
|
|
9
9
|
element.process
|
10
10
|
end
|
11
11
|
|
12
|
-
describe "object bounding box" do
|
12
|
+
describe "object bounding box with linear gradient" do
|
13
13
|
let(:svg) do
|
14
14
|
<<-SVG
|
15
15
|
<linearGradient id="flag" x1="0" x2="0.2" y1="0" y2="1">
|
@@ -40,7 +40,35 @@ describe Prawn::SVG::Elements::Gradient do
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
describe "
|
43
|
+
describe "object bounding box with radial gradient" do
|
44
|
+
let(:svg) do
|
45
|
+
<<-SVG
|
46
|
+
<radialGradient id="flag" cx="0" cy="0.2" fx="0.5" r="0.8">
|
47
|
+
<stop offset="25%" stop-color="red"/>
|
48
|
+
<stop offset="50%" stop-color="white"/>
|
49
|
+
<stop offset="75%" stop-color="blue"/>
|
50
|
+
</radialGradient>
|
51
|
+
SVG
|
52
|
+
end
|
53
|
+
|
54
|
+
it "is stored in the document gradients table" do
|
55
|
+
expect(document.gradients["flag"]).to eq element
|
56
|
+
end
|
57
|
+
|
58
|
+
it "returns correct gradient arguments for an element" do
|
59
|
+
arguments = element.gradient_arguments(double(bounding_box: [100, 100, 200, 0]))
|
60
|
+
expect(arguments).to eq(
|
61
|
+
from: [150, 80],
|
62
|
+
to: [100, 80],
|
63
|
+
r1: 0,
|
64
|
+
r2: Math.sqrt((0.8 * 100) ** 2 + (0.8 * 100) ** 2),
|
65
|
+
stops: [[0, "ff0000"], [0.25, "ff0000"], [0.5, "ffffff"], [0.75, "0000ff"], [1, "0000ff"]],
|
66
|
+
apply_transformations: true,
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "user space on use with linear gradient" do
|
44
72
|
let(:svg) do
|
45
73
|
<<-SVG
|
46
74
|
<linearGradient id="flag" gradientUnits="userSpaceOnUse" x1="100" y1="500" x2="200" y2="600">
|
@@ -61,6 +89,29 @@ describe Prawn::SVG::Elements::Gradient do
|
|
61
89
|
end
|
62
90
|
end
|
63
91
|
|
92
|
+
describe "user space on use with radial gradient" do
|
93
|
+
let(:svg) do
|
94
|
+
<<-SVG
|
95
|
+
<radialGradient id="flag" gradientUnits="userSpaceOnUse" fx="100" fy="500" cx="200" cy="600" r="150">
|
96
|
+
<stop offset="0" stop-color="red"/>
|
97
|
+
<stop offset="1" stop-color="blue"/>
|
98
|
+
</radialGradient>
|
99
|
+
SVG
|
100
|
+
end
|
101
|
+
|
102
|
+
it "returns correct gradient arguments for an element" do
|
103
|
+
arguments = element.gradient_arguments(double)
|
104
|
+
expect(arguments).to eq(
|
105
|
+
from: [100.0, 100.0],
|
106
|
+
to: [200.0, 0.0],
|
107
|
+
r1: 0,
|
108
|
+
r2: 150,
|
109
|
+
stops: [[0, "ff0000"], [1, "0000ff"]],
|
110
|
+
apply_transformations: true,
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
64
115
|
context "when gradientTransform is specified" do
|
65
116
|
let(:svg) do
|
66
117
|
<<-SVG
|
@@ -11,22 +11,22 @@ describe Prawn::SVG::Elements::Path do
|
|
11
11
|
|
12
12
|
describe "command parsing" do
|
13
13
|
context "with a valid path" do
|
14
|
-
let(:d) { "
|
14
|
+
let(:d) { "m12.34 -56.78 1 2M4 5 12-34 -.5.7+3 2.3e3 4e4 4e+4 L31,-2e-5L 6,7 Z ZZa50 50 0 100 100" }
|
15
15
|
|
16
16
|
it "correctly parses" do
|
17
17
|
calls = []
|
18
|
-
path.
|
18
|
+
allow(path).to receive(:parse_path_command) {|*args| calls << args}
|
19
19
|
path.parse
|
20
20
|
|
21
|
-
calls.
|
22
|
-
["
|
23
|
-
["
|
24
|
-
["
|
25
|
-
["
|
26
|
-
["
|
27
|
-
["
|
28
|
-
["
|
29
|
-
["
|
21
|
+
expect(calls).to eq [
|
22
|
+
["m", [[12.34, -56.78], [1, 2]]],
|
23
|
+
["M", [[4, 5], [12, -34], [-0.5, 0.7], [3, 2.3e3], [4e4, 4e4]]],
|
24
|
+
["L", [[31, -2e-5]]],
|
25
|
+
["L", [[6, 7]]],
|
26
|
+
["Z", []],
|
27
|
+
["Z", []],
|
28
|
+
["Z", []],
|
29
|
+
["a", [[50, 50, 0, 1, 0, 0, 100]]],
|
30
30
|
]
|
31
31
|
end
|
32
32
|
end
|
@@ -36,12 +36,12 @@ describe Prawn::SVG::Elements::Path do
|
|
36
36
|
|
37
37
|
it "treats subsequent points to m/M command as relative/absolute depending on command" do
|
38
38
|
[
|
39
|
-
["M", [1,2,3,4]],
|
40
|
-
["L", [3,4]],
|
41
|
-
["m", [5,6,7,8]],
|
42
|
-
["l", [7,8]]
|
39
|
+
["M", [[1,2],[3,4]]],
|
40
|
+
["L", [[3,4]]],
|
41
|
+
["m", [[5,6],[7,8]]],
|
42
|
+
["l", [[7,8]]]
|
43
43
|
].each do |args|
|
44
|
-
path.
|
44
|
+
expect(path).to receive(:parse_path_command).with(*args).and_call_original
|
45
45
|
end
|
46
46
|
|
47
47
|
path.parse
|
@@ -52,7 +52,7 @@ describe Prawn::SVG::Elements::Path do
|
|
52
52
|
let(:d) { "" }
|
53
53
|
|
54
54
|
it "correctly parses" do
|
55
|
-
path.
|
55
|
+
expect(path).not_to receive(:run_path_command)
|
56
56
|
path.parse
|
57
57
|
end
|
58
58
|
end
|
@@ -292,5 +292,17 @@ describe Prawn::SVG::Elements::Path do
|
|
292
292
|
]
|
293
293
|
end
|
294
294
|
end
|
295
|
+
|
296
|
+
context "with highly-compressed flags" do
|
297
|
+
let(:d) { "M100,100a50 50 0 100 100" }
|
298
|
+
|
299
|
+
it "correctly parses them" do
|
300
|
+
expect(subject).to eq [
|
301
|
+
Prawn::SVG::Elements::Path::Move.new([100.0, 100.0]),
|
302
|
+
Prawn::SVG::Elements::Path::Curve.new([50.0, 150.0], [72.57081148225681, 100.0], [50.0, 122.57081148225681]),
|
303
|
+
Prawn::SVG::Elements::Path::Curve.new([99.99999999999999, 200.0], [50.0, 177.42918851774317], [72.5708114822568, 200.0])
|
304
|
+
]
|
305
|
+
end
|
306
|
+
end
|
295
307
|
end
|
296
308
|
end
|
@@ -88,6 +88,21 @@ Even more
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
+
describe "underline" do
|
92
|
+
let(:svg) { '<text text-decoration="underline">underlined</text>' }
|
93
|
+
|
94
|
+
it "marks the element to be underlined" do
|
95
|
+
element.process
|
96
|
+
|
97
|
+
expect(element.base_calls).to eq [
|
98
|
+
["text_group", [], [
|
99
|
+
["font", ["Helvetica", {:style=>:normal}], []],
|
100
|
+
["draw_text", ["underlined", default_style.merge(decoration: 'underline')], []]
|
101
|
+
]]
|
102
|
+
]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
91
106
|
describe "fill/stroke modes" do
|
92
107
|
context "with a stroke and no fill" do
|
93
108
|
let(:svg) { '<text stroke="red" fill="none">stroked</text>' }
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<?xml version="1.0" standalone="no"?>
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
3
|
+
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
4
|
+
<svg width="8cm" height="4cm" viewBox="0 0 800 400" version="1.1"
|
5
|
+
xmlns="http://www.w3.org/2000/svg">
|
6
|
+
<desc>Example radgrad01 - fill a rectangle by referencing a
|
7
|
+
radial gradient paint server</desc>
|
8
|
+
<g>
|
9
|
+
<defs>
|
10
|
+
<radialGradient id="MyGradient"
|
11
|
+
cx="0.5" cy="0.5" r="0.2" fx="0.5" fy="0.5">
|
12
|
+
<stop offset="0%" stop-color="red" />
|
13
|
+
<stop offset="50%" stop-color="blue" />
|
14
|
+
<stop offset="100%" stop-color="red" />
|
15
|
+
</radialGradient>
|
16
|
+
</defs>
|
17
|
+
|
18
|
+
<!-- Outline the drawing area in blue -->
|
19
|
+
<rect fill="none" stroke="blue"
|
20
|
+
x="1" y="1" width="798" height="398"/>
|
21
|
+
|
22
|
+
<!-- The rectangle is filled using a radial gradient paint server -->
|
23
|
+
<rect fill="url(#MyGradient)" stroke="black" stroke-width="5"
|
24
|
+
x="100" y="100" width="600" height="200"/>
|
25
|
+
</g>
|
26
|
+
</svg>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<?xml version="1.0" standalone="no"?>
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
3
|
+
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
4
|
+
<svg width="8cm" height="4cm" viewBox="0 0 800 400" version="1.1"
|
5
|
+
xmlns="http://www.w3.org/2000/svg">
|
6
|
+
<desc>Example radgrad01 - fill a rectangle by referencing a
|
7
|
+
radial gradient paint server</desc>
|
8
|
+
<g>
|
9
|
+
<defs>
|
10
|
+
<radialGradient id="MyGradient" gradientUnits="userSpaceOnUse"
|
11
|
+
cx="400" cy="200" r="50%" fx="200" fy="200">
|
12
|
+
<stop offset="0%" stop-color="red" />
|
13
|
+
<stop offset="50%" stop-color="blue" />
|
14
|
+
<stop offset="100%" stop-color="red" />
|
15
|
+
</radialGradient>
|
16
|
+
</defs>
|
17
|
+
|
18
|
+
<!-- Outline the drawing area in blue -->
|
19
|
+
<rect fill="none" stroke="blue"
|
20
|
+
x="1" y="1" width="798" height="398"/>
|
21
|
+
|
22
|
+
<!-- The rectangle is filled using a radial gradient paint server -->
|
23
|
+
<rect fill="url(#MyGradient)" stroke="black" stroke-width="5"
|
24
|
+
x="100" y="100" width="600" height="200"/>
|
25
|
+
</g>
|
26
|
+
</svg>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<?xml version="1.0" standalone="no"?>
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
3
|
+
<svg width="12cm" height="5.25cm" viewBox="0 0 1200 400" xmlns="http://www.w3.org/2000/svg" version="1.1" fill="red">
|
4
|
+
<text style="font-size: 40pt;">This is red text.</text>
|
5
|
+
</svg>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0" standalone="no"?>
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
3
|
+
<svg width="12cm" height="4cm" viewBox="0 0 1200 400" xmlns="http://www.w3.org/2000/svg" xmlns:xlink='http://www.w3.org/1999/xlink' version="1.1">
|
4
|
+
<use xlink:href="#two-rects"/>
|
5
|
+
<use xlink:href="#two-rects" x="90" y="90"/>
|
6
|
+
<use xlink:href="#two-rects" x="180" y="180"/>
|
7
|
+
|
8
|
+
<use xlink:href="#circle" x="0" y="0" style="opacity: 0.5;" transform="scale(1.3)"/>
|
9
|
+
<use xlink:href="#circle" x="0" y="0" style="opacity: 0.2;" transform="scale(1.7)"/>
|
10
|
+
|
11
|
+
<circle id="circle" cx="600" cy="200" r="100" fill="red" stroke="blue" stroke-width="10" />
|
12
|
+
|
13
|
+
<symbol id="two-rects">
|
14
|
+
<rect x="1" y="1" width="1198" height="398" fill="none" stroke="blue" stroke-width="2"/>
|
15
|
+
<rect x="400" y="100" width="400" height="200" fill="yellow" stroke="navy" stroke-width="10" />
|
16
|
+
</symbol>
|
17
|
+
</svg>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prawn-svg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.31.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roger Nesbitt
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prawn
|
@@ -64,14 +64,14 @@ dependencies:
|
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version: '
|
67
|
+
version: '13.0'
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: '
|
74
|
+
version: '13.0'
|
75
75
|
description: This gem allows you to render SVG directly into a PDF using the 'prawn'
|
76
76
|
gem. Since PDF is vector-based, you'll get nice scaled graphics if you use SVG
|
77
77
|
instead of an image.
|
@@ -213,6 +213,8 @@ files:
|
|
213
213
|
- spec/sample_svg/polyline01.svg
|
214
214
|
- spec/sample_svg/preserve-space.svg
|
215
215
|
- spec/sample_svg/quad01.svg
|
216
|
+
- spec/sample_svg/radgrad01-bounding.svg
|
217
|
+
- spec/sample_svg/radgrad01.svg
|
216
218
|
- spec/sample_svg/rect01.svg
|
217
219
|
- spec/sample_svg/rect02.svg
|
218
220
|
- spec/sample_svg/rotate_scale.svg
|
@@ -221,6 +223,8 @@ files:
|
|
221
223
|
- spec/sample_svg/subfamilies.svg
|
222
224
|
- spec/sample_svg/subviewports.svg
|
223
225
|
- spec/sample_svg/subviewports2.svg
|
226
|
+
- spec/sample_svg/svg_fill.svg
|
227
|
+
- spec/sample_svg/text-decoration.svg
|
224
228
|
- spec/sample_svg/text_entities.svg
|
225
229
|
- spec/sample_svg/text_stroke.svg
|
226
230
|
- spec/sample_svg/transform.svg
|
@@ -234,6 +238,7 @@ files:
|
|
234
238
|
- spec/sample_svg/tspan05.svg
|
235
239
|
- spec/sample_svg/tspan91.svg
|
236
240
|
- spec/sample_svg/use.svg
|
241
|
+
- spec/sample_svg/use_disordered.svg
|
237
242
|
- spec/sample_svg/viewbox.svg
|
238
243
|
- spec/sample_svg/viewport.svg
|
239
244
|
- spec/sample_svg/warning-radioactive.svg
|
@@ -243,7 +248,7 @@ homepage: http://github.com/mogest/prawn-svg
|
|
243
248
|
licenses:
|
244
249
|
- MIT
|
245
250
|
metadata: {}
|
246
|
-
post_install_message:
|
251
|
+
post_install_message:
|
247
252
|
rdoc_options: []
|
248
253
|
require_paths:
|
249
254
|
- lib
|
@@ -258,8 +263,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
258
263
|
- !ruby/object:Gem::Version
|
259
264
|
version: '0'
|
260
265
|
requirements: []
|
261
|
-
rubygems_version: 3.0.
|
262
|
-
signing_key:
|
266
|
+
rubygems_version: 3.0.3
|
267
|
+
signing_key:
|
263
268
|
specification_version: 4
|
264
269
|
summary: SVG renderer for Prawn PDF library
|
265
270
|
test_files:
|
@@ -335,6 +340,8 @@ test_files:
|
|
335
340
|
- spec/sample_svg/polyline01.svg
|
336
341
|
- spec/sample_svg/preserve-space.svg
|
337
342
|
- spec/sample_svg/quad01.svg
|
343
|
+
- spec/sample_svg/radgrad01-bounding.svg
|
344
|
+
- spec/sample_svg/radgrad01.svg
|
338
345
|
- spec/sample_svg/rect01.svg
|
339
346
|
- spec/sample_svg/rect02.svg
|
340
347
|
- spec/sample_svg/rotate_scale.svg
|
@@ -343,6 +350,8 @@ test_files:
|
|
343
350
|
- spec/sample_svg/subfamilies.svg
|
344
351
|
- spec/sample_svg/subviewports.svg
|
345
352
|
- spec/sample_svg/subviewports2.svg
|
353
|
+
- spec/sample_svg/svg_fill.svg
|
354
|
+
- spec/sample_svg/text-decoration.svg
|
346
355
|
- spec/sample_svg/text_entities.svg
|
347
356
|
- spec/sample_svg/text_stroke.svg
|
348
357
|
- spec/sample_svg/transform.svg
|
@@ -356,6 +365,7 @@ test_files:
|
|
356
365
|
- spec/sample_svg/tspan05.svg
|
357
366
|
- spec/sample_svg/tspan91.svg
|
358
367
|
- spec/sample_svg/use.svg
|
368
|
+
- spec/sample_svg/use_disordered.svg
|
359
369
|
- spec/sample_svg/viewbox.svg
|
360
370
|
- spec/sample_svg/viewport.svg
|
361
371
|
- spec/sample_svg/warning-radioactive.svg
|