prawn-svg 0.32.0 → 0.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +20 -3
- data/README.md +6 -4
- data/lib/prawn/svg/attributes/stroke.rb +5 -0
- data/lib/prawn/svg/color.rb +46 -22
- data/lib/prawn/svg/css/selector_parser.rb +11 -11
- data/lib/prawn/svg/css/stylesheets.rb +4 -4
- data/lib/prawn/svg/document.rb +15 -2
- data/lib/prawn/svg/elements/base.rb +10 -11
- data/lib/prawn/svg/elements/gradient.rb +2 -2
- data/lib/prawn/svg/elements/image.rb +6 -7
- data/lib/prawn/svg/elements/path.rb +0 -2
- data/lib/prawn/svg/elements/root.rb +1 -1
- data/lib/prawn/svg/elements/text_component.rb +8 -3
- data/lib/prawn/svg/gradients.rb +48 -0
- data/lib/prawn/svg/interface.rb +8 -1
- data/lib/prawn/svg/properties.rb +2 -0
- data/lib/prawn/svg/version.rb +1 -1
- data/lib/prawn-svg.rb +1 -0
- data/prawn-svg.gemspec +17 -16
- data/spec/prawn/svg/color_spec.rb +29 -16
- data/spec/prawn/svg/document_spec.rb +17 -0
- data/spec/prawn/svg/elements/marker_spec.rb +1 -1
- data/spec/prawn/svg/elements/root_spec.rb +28 -0
- data/spec/prawn/svg/elements/text_spec.rb +27 -0
- data/spec/prawn/svg/interface_spec.rb +10 -2
- data/spec/sample_svg/cmyk.svg +5 -0
- data/spec/sample_svg/gradients-cmyk.svg +40 -0
- data/spec/sample_svg/ordering.svg +12 -0
- metadata +49 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9f71b82858f8ee91e1ccaf2903c2e2a0d49108b1532a81d15c2275362a0d136
|
4
|
+
data.tar.gz: f3336c6a727df24e6a5877e4d26cf030eabbb1c9af9852f95c35c5bfe5128995
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94f1a140e837ab37b088cc659bc8ef63a3e89778539236441f43e7b4885b9028cce1cb83c9e9636b00ff2ac740770ec8d853c123aa42b0764fb1264405269e76
|
7
|
+
data.tar.gz: 4d02f2e14875214bb5460ace33925f9ab17302b39f46e0a37e0e5521e63b0fdd2f23f6018c8e048bf2bce7d148548aa86b5b9967dca827faddccfa23e8e09b13
|
data/.github/workflows/test.yml
CHANGED
@@ -1,14 +1,31 @@
|
|
1
1
|
name: test
|
2
2
|
on: [push, pull_request]
|
3
3
|
jobs:
|
4
|
-
|
4
|
+
rake_new:
|
5
5
|
runs-on: ubuntu-latest
|
6
6
|
strategy:
|
7
7
|
fail-fast: false
|
8
8
|
matrix:
|
9
|
-
ruby: [2.
|
9
|
+
ruby: [2.6, 2.7, '3.0', 3.1, 3.2]
|
10
10
|
steps:
|
11
|
-
- uses: actions/checkout@
|
11
|
+
- uses: actions/checkout@v4
|
12
|
+
- name: Set up Ruby
|
13
|
+
uses: ruby/setup-ruby@v1
|
14
|
+
with:
|
15
|
+
bundler-cache: true
|
16
|
+
ruby-version: ${{ matrix.ruby }}
|
17
|
+
- name: Run tests
|
18
|
+
run: bundle exec rake
|
19
|
+
env:
|
20
|
+
RUBYOPT: "--enable-frozen-string-literal"
|
21
|
+
rake_old:
|
22
|
+
runs-on: ubuntu-latest
|
23
|
+
strategy:
|
24
|
+
fail-fast: false
|
25
|
+
matrix:
|
26
|
+
ruby: [2.5]
|
27
|
+
steps:
|
28
|
+
- uses: actions/checkout@v4
|
12
29
|
- name: Set up Ruby
|
13
30
|
uses: ruby/setup-ruby@v1
|
14
31
|
with:
|
data/README.md
CHANGED
@@ -34,6 +34,7 @@ Option | Data type | Description
|
|
34
34
|
:enable_file_requests_with_root | string | If not nil, prawn-svg will serve `file:` URLs from your local disk if the file is located under the specified directory. It is very dangerous to specify the root path ("/") if you're not fully in control of your input SVG. Defaults to `nil` (off).
|
35
35
|
:cache_images | boolean | If true, prawn-svg will cache the result of all URL requests. Defaults to false.
|
36
36
|
:fallback_font_name | string | A font name which will override the default fallback font of Times-Roman. If this value is set to <tt>nil</tt>, prawn-svg will ignore a request for an unknown font and log a warning.
|
37
|
+
:color_mode | :rgb, :cmyk | Output color mode. Defaults to :rgb.
|
37
38
|
|
38
39
|
## Examples
|
39
40
|
|
@@ -61,7 +62,7 @@ prawn-svg supports most but not all of the full SVG 1.1 specification. It curre
|
|
61
62
|
implementation of elliptical arc is a bit rough at the moment.
|
62
63
|
|
63
64
|
- `<text>`, `<tspan>` and `<tref>` with attributes `x`, `y`, `dx`, `dy`, `rotate`, 'textLength', 'lengthAdjust', and with extra properties
|
64
|
-
`text-anchor`, `text-decoration` (underline only), `font-size`, `font-family`, `font-weight`, `font-style`, `letter-spacing`
|
65
|
+
`text-anchor`, `text-decoration` (underline only), `font-size`, `font-family`, `font-weight`, `font-style`, `letter-spacing`, `dominant-baseline` (middle only)
|
65
66
|
|
66
67
|
- <tt><svg></tt>, <tt><g></tt> and <tt><symbol></tt>
|
67
68
|
|
@@ -69,7 +70,7 @@ prawn-svg supports most but not all of the full SVG 1.1 specification. It curre
|
|
69
70
|
|
70
71
|
- <tt><style></tt> (see CSS section below)
|
71
72
|
|
72
|
-
-
|
73
|
+
- `<image>` referencing a JPEG or PNG image, with `http:`, `https:`, `data:image/jpeg;base64`, `data:image/png;base64` and `file:` schemes
|
73
74
|
(`file:` is disabled by default for security reasons, see Options section above)
|
74
75
|
|
75
76
|
- <tt><clipPath></tt>
|
@@ -93,7 +94,8 @@ prawn-svg supports most but not all of the full SVG 1.1 specification. It curre
|
|
93
94
|
|
94
95
|
- transform methods: `translate`, `translateX`, `translateY`, `rotate`, `scale`, `skewX`, `skewY`, `matrix`
|
95
96
|
|
96
|
-
- colors: HTML standard names, <tt>#xxx</tt>, <tt>#xxxxxx</tt>, <tt>rgb(1, 2, 3)</tt>, <tt>rgb(1%, 2%, 3%)</tt
|
97
|
+
- colors: HTML standard names, <tt>#xxx</tt>, <tt>#xxxxxx</tt>, <tt>rgb(1, 2, 3)</tt>, <tt>rgb(1%, 2%, 3%)</tt>,
|
98
|
+
and also the non-standard `device-cmyk(1, 2, 3, 4)` for CMYK colors
|
97
99
|
|
98
100
|
- measurements specified in <tt>pt</tt>, <tt>cm</tt>, <tt>dm</tt>, <tt>ft</tt>, <tt>in</tt>, <tt>m</tt>, <tt>mm</tt>, <tt>yd</tt>, <tt>pc</tt>, <tt>%</tt>
|
99
101
|
|
@@ -116,7 +118,7 @@ implemented, but `!important` is not.
|
|
116
118
|
|
117
119
|
## Not supported
|
118
120
|
|
119
|
-
prawn-svg does not support hyperlinks, patterns or filters.
|
121
|
+
prawn-svg does not support hyperlinks, patterns, masks or filters.
|
120
122
|
|
121
123
|
It does not support text in the clip area, but you can clip shapes and text by any shape.
|
122
124
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Prawn::SVG::Attributes::Stroke
|
2
2
|
CAP_STYLE_TRANSLATIONS = {"butt" => :butt, "round" => :round, "square" => :projecting_square}
|
3
|
+
JOIN_STYLE_TRANSLATIONS = {"miter" => :miter, "round" => :round, "bevel" => :bevel}
|
3
4
|
|
4
5
|
def parse_stroke_attributes_and_call
|
5
6
|
if width_string = properties.stroke_width
|
@@ -11,6 +12,10 @@ module Prawn::SVG::Attributes::Stroke
|
|
11
12
|
if (linecap = properties.stroke_linecap) && linecap != 'inherit'
|
12
13
|
add_call('cap_style', CAP_STYLE_TRANSLATIONS.fetch(linecap, :butt))
|
13
14
|
end
|
15
|
+
|
16
|
+
if (linejoin = properties.stroke_linejoin) && linejoin != 'inherit'
|
17
|
+
add_call('join_style', JOIN_STYLE_TRANSLATIONS.fetch(linejoin, :miter))
|
18
|
+
end
|
14
19
|
|
15
20
|
if dasharray = properties.stroke_dasharray
|
16
21
|
case dasharray
|
data/lib/prawn/svg/color.rb
CHANGED
@@ -1,17 +1,9 @@
|
|
1
1
|
class Prawn::SVG::Color
|
2
|
-
|
3
|
-
|
2
|
+
RGB = Struct.new(:value)
|
3
|
+
CMYK = Struct.new(:value)
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
def ==(other)
|
10
|
-
value == other.value
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
DEFAULT_COLOR = Hex.new("000000")
|
5
|
+
RGB_DEFAULT_COLOR = RGB.new("000000")
|
6
|
+
CMYK_DEFAULT_COLOR = CMYK.new([0, 0, 0, 100])
|
15
7
|
|
16
8
|
HTML_COLORS = {
|
17
9
|
'aliceblue' => 'f0f8ff',
|
@@ -163,24 +155,25 @@ class Prawn::SVG::Color
|
|
163
155
|
'yellowgreen' => '9acd32'
|
164
156
|
}.freeze
|
165
157
|
|
166
|
-
|
167
|
-
RGB_REGEXP = /\Argb\(#{
|
158
|
+
VALUE_REGEXP = "\s*(-?[0-9.]+%?)\s*"
|
159
|
+
RGB_REGEXP = /\Argb\(#{VALUE_REGEXP},#{VALUE_REGEXP},#{VALUE_REGEXP}\)\z/i
|
160
|
+
CMYK_REGEXP = /\Adevice-cmyk\(#{VALUE_REGEXP},#{VALUE_REGEXP},#{VALUE_REGEXP},#{VALUE_REGEXP}\)\z/i
|
168
161
|
URL_REGEXP = /\Aurl\(([^)]*)\)\z/i
|
169
162
|
|
170
|
-
def self.parse(color_string, gradients = nil)
|
163
|
+
def self.parse(color_string, gradients = nil, color_mode = :rgb)
|
171
164
|
url_specified = false
|
172
165
|
|
173
166
|
components = color_string.to_s.strip.scan(/([^(\s]+(\([^)]*\))?)/)
|
174
167
|
|
175
168
|
result = components.map do |color, *_|
|
176
169
|
if m = color.match(/\A#([0-9a-f])([0-9a-f])([0-9a-f])\z/i)
|
177
|
-
|
170
|
+
RGB.new("#{m[1] * 2}#{m[2] * 2}#{m[3] * 2}")
|
178
171
|
|
179
172
|
elsif color.match(/\A#[0-9a-f]{6}\z/i)
|
180
|
-
|
173
|
+
RGB.new(color[1..6])
|
181
174
|
|
182
175
|
elsif hex = HTML_COLORS[color.downcase]
|
183
|
-
|
176
|
+
hex_color(hex, color_mode)
|
184
177
|
|
185
178
|
elsif m = color.match(RGB_REGEXP)
|
186
179
|
hex = (1..3).collect do |n|
|
@@ -189,7 +182,16 @@ class Prawn::SVG::Color
|
|
189
182
|
"%02x" % clamp(value.round, 0, 255)
|
190
183
|
end.join
|
191
184
|
|
192
|
-
|
185
|
+
RGB.new(hex)
|
186
|
+
|
187
|
+
elsif m = color.match(CMYK_REGEXP)
|
188
|
+
cmyk = (1..4).collect do |n|
|
189
|
+
value = m[n].to_f
|
190
|
+
value *= 100 unless m[n][-1..-1] == '%'
|
191
|
+
clamp(value, 0, 100)
|
192
|
+
end
|
193
|
+
|
194
|
+
CMYK.new(cmyk)
|
193
195
|
|
194
196
|
elsif matches = color.match(URL_REGEXP)
|
195
197
|
url_specified = true
|
@@ -203,18 +205,40 @@ class Prawn::SVG::Color
|
|
203
205
|
# Generally, we default to black if the colour was unparseable.
|
204
206
|
# http://www.w3.org/TR/SVG/painting.html section 11.2 says if a URL was
|
205
207
|
# supplied without a fallback, that's an error.
|
206
|
-
result <<
|
208
|
+
result << default_color(color_mode) unless url_specified
|
207
209
|
|
208
210
|
result.compact
|
209
211
|
end
|
210
212
|
|
211
|
-
def self.
|
212
|
-
result = parse(color).detect {|result| result.is_a?(
|
213
|
+
def self.css_color_to_prawn_color(color)
|
214
|
+
result = parse(color).detect {|result| result.is_a?(RGB) || result.is_a?(CMYK)}
|
213
215
|
result.value if result
|
214
216
|
end
|
215
217
|
|
216
218
|
protected
|
219
|
+
|
217
220
|
def self.clamp(value, min_value, max_value)
|
218
221
|
[[value, min_value].max, max_value].min
|
219
222
|
end
|
223
|
+
|
224
|
+
def self.default_color(color_mode)
|
225
|
+
color_mode == :cmyk ? CMYK_DEFAULT_COLOR : RGB_DEFAULT_COLOR
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.hex_color(hex, color_mode)
|
229
|
+
if color_mode == :cmyk
|
230
|
+
r, g, b = [hex[0..1], hex[2..3], hex[4..5]].map { |h| h.to_i(16) / 255.0 }
|
231
|
+
k = 1 - [r, g, b].max
|
232
|
+
if k == 1
|
233
|
+
CMYK.new([0, 0, 0, 100])
|
234
|
+
else
|
235
|
+
c = (1 - r - k) / (1 - k)
|
236
|
+
m = (1 - g - k) / (1 - k)
|
237
|
+
y = (1 - b - k) / (1 - k)
|
238
|
+
CMYK.new([c, m, y, k].map { |v| (v * 100).round })
|
239
|
+
end
|
240
|
+
else
|
241
|
+
RGB.new(hex)
|
242
|
+
end
|
243
|
+
end
|
220
244
|
end
|
@@ -10,7 +10,7 @@ module Prawn::SVG::CSS
|
|
10
10
|
case token
|
11
11
|
when Modifier
|
12
12
|
part = token.type
|
13
|
-
result.last[part] ||= part == :name ? "" : []
|
13
|
+
result.last[part] ||= part == :name ? +"" : []
|
14
14
|
when Identifier
|
15
15
|
return unless part
|
16
16
|
result.last[part] << token.name
|
@@ -48,7 +48,7 @@ module Prawn::SVG::CSS
|
|
48
48
|
case attribute
|
49
49
|
when :pre_key
|
50
50
|
if VALID_CSS_IDENTIFIER_CHAR.match(char)
|
51
|
-
result.last.key = char
|
51
|
+
result.last.key = String.new(char)
|
52
52
|
attribute = :key
|
53
53
|
elsif char != " " && char != "\t"
|
54
54
|
return
|
@@ -60,7 +60,7 @@ module Prawn::SVG::CSS
|
|
60
60
|
elsif char == "]"
|
61
61
|
attribute = nil
|
62
62
|
elsif "=*~^|$".include?(char)
|
63
|
-
result.last.operator = char
|
63
|
+
result.last.operator = String.new(char)
|
64
64
|
attribute = :operator
|
65
65
|
elsif char == " " || char == "\t"
|
66
66
|
attribute = :pre_operator
|
@@ -70,7 +70,7 @@ module Prawn::SVG::CSS
|
|
70
70
|
|
71
71
|
when :pre_operator
|
72
72
|
if "=*~^|$".include?(char)
|
73
|
-
result.last.operator = char
|
73
|
+
result.last.operator = String.new(char)
|
74
74
|
attribute = :operator
|
75
75
|
elsif char != " " && char != "\t"
|
76
76
|
return
|
@@ -82,25 +82,25 @@ module Prawn::SVG::CSS
|
|
82
82
|
elsif char == " " || char == "\t"
|
83
83
|
attribute = :pre_value
|
84
84
|
elsif char == '"' || char == "'"
|
85
|
-
result.last.value = ''
|
86
|
-
attribute = char
|
85
|
+
result.last.value = +''
|
86
|
+
attribute = String.new(char)
|
87
87
|
else
|
88
|
-
result.last.value = char
|
88
|
+
result.last.value = String.new(char)
|
89
89
|
attribute = :value
|
90
90
|
end
|
91
91
|
|
92
92
|
when :pre_value
|
93
93
|
if char == '"' || char == "'"
|
94
|
-
result.last.value = ''
|
95
|
-
attribute = char
|
94
|
+
result.last.value = +''
|
95
|
+
attribute = String.new(char)
|
96
96
|
elsif char != " " && char != "\t"
|
97
|
-
result.last.value = char
|
97
|
+
result.last.value = String.new(char)
|
98
98
|
attribute = :value
|
99
99
|
end
|
100
100
|
|
101
101
|
when :value
|
102
102
|
if char == "]"
|
103
|
-
result.last.value = result.last.value.rstrip
|
103
|
+
result.last.value = String.new(result.last.value.rstrip)
|
104
104
|
attribute = nil
|
105
105
|
else
|
106
106
|
result.last.value << char
|
@@ -69,14 +69,14 @@ module Prawn::SVG::CSS
|
|
69
69
|
|
70
70
|
result = case element[:combinator]
|
71
71
|
when :child
|
72
|
-
"/"
|
72
|
+
+"/"
|
73
73
|
when :adjacent
|
74
74
|
pseudo_classes << 'first-child'
|
75
|
-
"/following-sibling::"
|
75
|
+
+"/following-sibling::"
|
76
76
|
when :siblings
|
77
|
-
"/following-sibling::"
|
77
|
+
+"/following-sibling::"
|
78
78
|
else
|
79
|
-
"//"
|
79
|
+
+"//"
|
80
80
|
end
|
81
81
|
|
82
82
|
positions = []
|
data/lib/prawn/svg/document.rb
CHANGED
@@ -13,7 +13,8 @@ class Prawn::SVG::Document
|
|
13
13
|
:font_registry,
|
14
14
|
:url_loader,
|
15
15
|
:elements_by_id, :gradients,
|
16
|
-
:element_styles
|
16
|
+
:element_styles,
|
17
|
+
:color_mode
|
17
18
|
|
18
19
|
def initialize(data, bounds, options, font_registry: nil, css_parser: CssParser::Parser.new)
|
19
20
|
@root = REXML::Document.new(data).root
|
@@ -29,9 +30,10 @@ class Prawn::SVG::Document
|
|
29
30
|
@warnings = []
|
30
31
|
@options = options
|
31
32
|
@elements_by_id = {}
|
32
|
-
@gradients =
|
33
|
+
@gradients = Prawn::SVG::Gradients.new(self)
|
33
34
|
@fallback_font_name = options.fetch(:fallback_font_name, DEFAULT_FALLBACK_FONT_NAME)
|
34
35
|
@font_registry = font_registry
|
36
|
+
@color_mode = load_color_mode
|
35
37
|
|
36
38
|
@url_loader = Prawn::SVG::UrlLoader.new(
|
37
39
|
enable_cache: options[:cache_images],
|
@@ -52,4 +54,15 @@ class Prawn::SVG::Document
|
|
52
54
|
sizing.requested_height = requested_height
|
53
55
|
sizing.calculate
|
54
56
|
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def load_color_mode
|
61
|
+
case @options[:color_mode]
|
62
|
+
when nil, :rgb then :rgb
|
63
|
+
when :cmyk then :cmyk
|
64
|
+
else
|
65
|
+
raise ArgumentError, ":color_mode must be set to :rgb (default) or :cmyk"
|
66
|
+
end
|
67
|
+
end
|
55
68
|
end
|
@@ -189,11 +189,11 @@ class Prawn::SVG::Elements::Base
|
|
189
189
|
color = computed_properties.color
|
190
190
|
end
|
191
191
|
|
192
|
-
results = Prawn::SVG::Color.parse(color, document.gradients)
|
192
|
+
results = Prawn::SVG::Color.parse(color, document.gradients, document.color_mode)
|
193
193
|
|
194
194
|
success = results.detect do |result|
|
195
195
|
case result
|
196
|
-
when Prawn::SVG::Color::
|
196
|
+
when Prawn::SVG::Color::RGB, Prawn::SVG::Color::CMYK
|
197
197
|
add_call "#{type}_color", result.value
|
198
198
|
true
|
199
199
|
when Prawn::SVG::Elements::Gradient
|
@@ -237,16 +237,15 @@ class Prawn::SVG::Elements::Base
|
|
237
237
|
|
238
238
|
def parse_css_declarations(declarations)
|
239
239
|
# copied from css_parser
|
240
|
-
declarations
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
240
|
+
declarations
|
241
|
+
.gsub(/(^[\s]*)|([\s]*$)/, '')
|
242
|
+
.split(/[\;$]+/m)
|
243
|
+
.each_with_object({}) do |decs, output|
|
244
|
+
if matches = decs.match(/\s*(.[^:]*)\s*\:\s*(.[^;]*)\s*(;|\Z)/i)
|
245
|
+
property, value, _ = matches.captures
|
246
|
+
output[property.downcase] = value
|
247
|
+
end
|
247
248
|
end
|
248
|
-
end
|
249
|
-
output
|
250
249
|
end
|
251
250
|
|
252
251
|
def require_attributes(*names)
|
@@ -147,8 +147,8 @@ class Prawn::SVG::Elements::Gradient < Prawn::SVG::Elements::Base
|
|
147
147
|
offset = result.last.first
|
148
148
|
end
|
149
149
|
|
150
|
-
if
|
151
|
-
result << [offset,
|
150
|
+
if color = Prawn::SVG::Color.css_color_to_prawn_color(child.properties.stop_color)
|
151
|
+
result << [offset, color]
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
@@ -67,15 +67,14 @@ class Prawn::SVG::Elements::Image < Prawn::SVG::Elements::Base
|
|
67
67
|
protected
|
68
68
|
|
69
69
|
def image_dimensions(data)
|
70
|
-
handler =
|
71
|
-
|
72
|
-
elsif data[0, 8].unpack("C*") == [137, 80, 78, 71, 13, 10, 26, 10]
|
73
|
-
Prawn::Images::PNG
|
74
|
-
else
|
75
|
-
raise SkipElementError, "Unsupported image type supplied to image tag; Prawn only supports JPG and PNG"
|
70
|
+
unless (handler = find_image_handler(data))
|
71
|
+
raise SkipElementError, 'Unsupported image type supplied to image tag'
|
76
72
|
end
|
77
|
-
|
78
73
|
image = handler.new(data)
|
79
74
|
[image.width.to_f, image.height.to_f]
|
80
75
|
end
|
76
|
+
|
77
|
+
def find_image_handler(data)
|
78
|
+
Prawn.image_handler.find(data) rescue nil
|
79
|
+
end
|
81
80
|
end
|
@@ -9,7 +9,7 @@ class Prawn::SVG::Elements::Root < Prawn::SVG::Elements::Base
|
|
9
9
|
|
10
10
|
def apply
|
11
11
|
if [nil, 'inherit', 'none', 'currentColor'].include?(properties.fill)
|
12
|
-
add_call 'fill_color',
|
12
|
+
add_call 'fill_color', Prawn::SVG::Color.default_color(@document.color_mode).value
|
13
13
|
end
|
14
14
|
|
15
15
|
add_call 'transformation_matrix', @document.sizing.x_scale, 0, 0, @document.sizing.y_scale, 0, 0
|
@@ -41,14 +41,15 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
41
41
|
font = select_font
|
42
42
|
apply_font(font) if font
|
43
43
|
|
44
|
-
# text_anchor
|
45
|
-
# and so we handle
|
44
|
+
# text_anchor and dominant_baseline aren't Prawn options; we have to do some math to support them
|
45
|
+
# and so we handle them in Prawn::SVG::Interface#rewrite_call_arguments
|
46
46
|
opts = {
|
47
47
|
size: computed_properties.numerical_font_size,
|
48
48
|
style: font && font.subfamily,
|
49
49
|
text_anchor: computed_properties.text_anchor,
|
50
50
|
}
|
51
51
|
|
52
|
+
opts[:dominant_baseline] = computed_properties.dominant_baseline unless computed_properties.dominant_baseline == 'auto'
|
52
53
|
opts[:decoration] = computed_properties.text_decoration unless computed_properties.text_decoration == 'none'
|
53
54
|
|
54
55
|
if state.text.parent
|
@@ -171,7 +172,11 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
171
172
|
|
172
173
|
def svg_text_children
|
173
174
|
text_children.select do |child|
|
174
|
-
child.node_type == :text ||
|
175
|
+
child.node_type == :text || (
|
176
|
+
child.node_type == :element && (
|
177
|
+
child.namespace == SVG_NAMESPACE || child.namespace == ''
|
178
|
+
)
|
179
|
+
)
|
175
180
|
end
|
176
181
|
end
|
177
182
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Prawn::SVG
|
2
|
+
class Gradients
|
3
|
+
def initialize(document)
|
4
|
+
@document = document
|
5
|
+
@gradients_by_id = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def [](id)
|
9
|
+
id &&= id.strip
|
10
|
+
return unless id && id != ''
|
11
|
+
|
12
|
+
if element = @gradients_by_id[id]
|
13
|
+
element
|
14
|
+
elsif raw_element = find_raw_gradient_element_by_id(id)
|
15
|
+
create_gradient_element(raw_element)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(id, gradient)
|
20
|
+
@gradients_by_id[id] = gradient
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def find_raw_gradient_element_by_id(id)
|
26
|
+
raw_element = find_raw_element_by_id(id)
|
27
|
+
raw_element if gradient_element?(raw_element)
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_gradient_element(raw_element)
|
31
|
+
Elements::Gradient.new(@document, raw_element, [], new_state).tap(&:process)
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_raw_element_by_id(id)
|
35
|
+
REXML::XPath.match(@document.root, %(//*[@id="#{id.gsub('"', '\"')}"])).first
|
36
|
+
end
|
37
|
+
|
38
|
+
def gradient_element?(raw_element)
|
39
|
+
Elements::TAG_CLASS_MAPPING[raw_element.name.to_sym] == Elements::Gradient
|
40
|
+
end
|
41
|
+
|
42
|
+
def new_state
|
43
|
+
State.new.tap do |state|
|
44
|
+
state.viewport_sizing = @document.sizing
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/prawn/svg/interface.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
module Prawn
|
6
6
|
module SVG
|
7
7
|
class Interface
|
8
|
-
VALID_OPTIONS = [:at, :position, :vposition, :width, :height, :cache_images, :enable_web_requests, :enable_file_requests_with_root, :fallback_font_name]
|
8
|
+
VALID_OPTIONS = [:at, :position, :vposition, :width, :height, :cache_images, :enable_web_requests, :enable_file_requests_with_root, :fallback_font_name, :color_mode]
|
9
9
|
|
10
10
|
attr_reader :data, :prawn, :document, :options
|
11
11
|
|
@@ -149,6 +149,13 @@ module Prawn
|
|
149
149
|
at[0] = @cursor[0] if at[0] == :relative
|
150
150
|
at[1] = @cursor[1] if at[1] == :relative
|
151
151
|
|
152
|
+
case options.delete(:dominant_baseline)
|
153
|
+
when 'middle'
|
154
|
+
height = prawn.font.height
|
155
|
+
at[1] -= height / 2.0
|
156
|
+
@cursor = [at[0], at[1]]
|
157
|
+
end
|
158
|
+
|
152
159
|
if offset = options.delete(:offset)
|
153
160
|
at[0] += offset[0]
|
154
161
|
at[1] -= offset[1]
|
data/lib/prawn/svg/properties.rb
CHANGED
@@ -34,10 +34,12 @@ class Prawn::SVG::Properties
|
|
34
34
|
"stroke" => Config.new("none", true, %w(inherit none currentColor)),
|
35
35
|
"stroke-dasharray" => Config.new("none", true, %w(inherit none)),
|
36
36
|
"stroke-linecap" => Config.new("butt", true, %w(inherit butt round square), true),
|
37
|
+
"stroke-linejoin" => Config.new("miter", true, %w(inherit miter round bevel), true),
|
37
38
|
"stroke-opacity" => Config.new("1", true),
|
38
39
|
"stroke-width" => Config.new("1", true),
|
39
40
|
"text-anchor" => Config.new("start", true, %w(inherit start middle end), true),
|
40
41
|
'text-decoration' => Config.new('none', true, %w(inherit none underline), true),
|
42
|
+
"dominant-baseline" => Config.new("auto", true, %w(inherit auto middle), true),
|
41
43
|
}.freeze
|
42
44
|
|
43
45
|
PROPERTIES.each do |name, value|
|
data/lib/prawn/svg/version.rb
CHANGED
data/lib/prawn-svg.rb
CHANGED
data/prawn-svg.gemspec
CHANGED
@@ -1,27 +1,28 @@
|
|
1
|
-
|
2
|
-
require File.expand_path('../lib/prawn/svg/version', __FILE__)
|
1
|
+
require File.expand_path('lib/prawn/svg/version', __dir__)
|
3
2
|
|
4
3
|
Gem::Specification.new do |gem|
|
5
4
|
gem.name = 'prawn-svg'
|
6
5
|
gem.version = Prawn::SVG::VERSION
|
7
|
-
gem.summary =
|
8
|
-
gem.description =
|
9
|
-
|
10
|
-
|
11
|
-
gem.
|
6
|
+
gem.summary = 'SVG renderer for Prawn PDF library'
|
7
|
+
gem.description = <<-EOT.strip
|
8
|
+
This gem allows you to render SVG directly into a PDF using the 'prawn' gem. Since PDF is vector-based, you'll get nice scaled graphics if you use SVG instead of an image.
|
9
|
+
EOT
|
10
|
+
gem.author = 'Mog Nesbitt'
|
11
|
+
gem.email = 'mog@seriousorange.com'
|
12
|
+
gem.homepage = 'http://github.com/mogest/prawn-svg'
|
12
13
|
gem.license = 'MIT'
|
13
14
|
|
14
|
-
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
15
16
|
gem.files = `git ls-files`.split("\n")
|
16
17
|
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
-
gem.
|
18
|
-
gem.require_paths = ["lib"]
|
18
|
+
gem.require_paths = ['lib']
|
19
19
|
|
20
|
-
gem.required_ruby_version = '>= 2.
|
20
|
+
gem.required_ruby_version = '>= 2.5.0'
|
21
21
|
|
22
|
-
gem.add_runtime_dependency
|
23
|
-
gem.add_runtime_dependency
|
24
|
-
gem.add_runtime_dependency
|
25
|
-
gem.
|
26
|
-
gem.add_development_dependency
|
22
|
+
gem.add_runtime_dependency 'css_parser', '~> 1.6'
|
23
|
+
gem.add_runtime_dependency 'matrix', '~> 0.4.2'
|
24
|
+
gem.add_runtime_dependency 'prawn', '>= 0.11.1', '< 3'
|
25
|
+
gem.add_runtime_dependency 'rexml', '~> 3.2'
|
26
|
+
gem.add_development_dependency 'rake', '~> 13.0'
|
27
|
+
gem.add_development_dependency 'rspec', '~> 3.0'
|
27
28
|
end
|
@@ -1,38 +1,43 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
2
|
|
3
3
|
describe Prawn::SVG::Color do
|
4
|
-
describe "::
|
4
|
+
describe "::css_color_to_prawn_color" do
|
5
5
|
it "converts #xxx to a hex value" do
|
6
|
-
Prawn::SVG::Color.
|
6
|
+
Prawn::SVG::Color.css_color_to_prawn_color("#9ab").should == "99aabb"
|
7
7
|
end
|
8
8
|
|
9
9
|
it "converts #xxxxxx to a hex value" do
|
10
|
-
Prawn::SVG::Color.
|
10
|
+
Prawn::SVG::Color.css_color_to_prawn_color("#9ab123").should == "9ab123"
|
11
11
|
end
|
12
12
|
|
13
13
|
it "converts an html colour name to a hex value" do
|
14
|
-
Prawn::SVG::Color.
|
14
|
+
Prawn::SVG::Color.css_color_to_prawn_color("White").should == "ffffff"
|
15
15
|
end
|
16
16
|
|
17
17
|
it "converts an rgb string to a hex value" do
|
18
|
-
Prawn::SVG::Color.
|
19
|
-
Prawn::SVG::Color.
|
18
|
+
Prawn::SVG::Color.css_color_to_prawn_color("rgb(16, 32, 48)").should == "102030"
|
19
|
+
Prawn::SVG::Color.css_color_to_prawn_color("rgb(-5, 50%, 120%)").should == "007fff"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "converts a CMYK string to an array of numbers" do
|
23
|
+
expect(Prawn::SVG::Color.css_color_to_prawn_color("device-cmyk(0, 0.32, 0.48, 1)")).to eq [0, 32, 48, 100]
|
24
|
+
expect(Prawn::SVG::Color.css_color_to_prawn_color("device-cmyk(-5, 50%, 120%, -5%)")).to eq [0, 50, 100, 0]
|
20
25
|
end
|
21
26
|
|
22
27
|
it "scans the string and finds the first colour it can parse" do
|
23
|
-
Prawn::SVG::Color.
|
28
|
+
Prawn::SVG::Color.css_color_to_prawn_color("function(#someurl, 0) nonexistent rgb( 3 ,4,5 ) white").should == "030405"
|
24
29
|
end
|
25
30
|
|
26
31
|
it "ignores url()s" do
|
27
|
-
expect(Prawn::SVG::Color.
|
32
|
+
expect(Prawn::SVG::Color.css_color_to_prawn_color("url(#someplace) red")).to eq 'ff0000'
|
28
33
|
end
|
29
34
|
|
30
35
|
it "returns black if the color doesn't exist" do
|
31
|
-
expect(Prawn::SVG::Color.
|
36
|
+
expect(Prawn::SVG::Color.css_color_to_prawn_color("blurble")).to eq '000000'
|
32
37
|
end
|
33
38
|
|
34
39
|
it "returns nil if there's no fallback after a url()" do
|
35
|
-
expect(Prawn::SVG::Color.
|
40
|
+
expect(Prawn::SVG::Color.css_color_to_prawn_color("url(#someplace)")).to be nil
|
36
41
|
end
|
37
42
|
end
|
38
43
|
|
@@ -42,19 +47,27 @@ describe Prawn::SVG::Color do
|
|
42
47
|
let(:drob_gradient) { double }
|
43
48
|
|
44
49
|
it "returns a list of all colors parsed, ignoring impossible or non-existent colors" do
|
45
|
-
results = Prawn::SVG::Color.parse("url(#nope) url(#flan) blurble green #123", gradients)
|
50
|
+
results = Prawn::SVG::Color.parse("url(#nope) url(#flan) blurble green #123", gradients, :rgb)
|
46
51
|
expect(results).to eq [
|
47
52
|
flan_gradient,
|
48
|
-
Prawn::SVG::Color::
|
49
|
-
Prawn::SVG::Color::
|
53
|
+
Prawn::SVG::Color::RGB.new("008000"),
|
54
|
+
Prawn::SVG::Color::RGB.new("112233")
|
50
55
|
]
|
51
56
|
end
|
52
57
|
|
53
58
|
it "appends black to the list if there aren't any url() references" do
|
54
|
-
results = Prawn::SVG::Color.parse("blurble green", gradients)
|
59
|
+
results = Prawn::SVG::Color.parse("blurble green", gradients, :rgb)
|
60
|
+
expect(results).to eq [
|
61
|
+
Prawn::SVG::Color::RGB.new("008000"),
|
62
|
+
Prawn::SVG::Color::RGB.new("000000")
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
66
|
+
it "works in CMYK color mode" do
|
67
|
+
results = Prawn::SVG::Color.parse("blurble green", gradients, :cmyk)
|
55
68
|
expect(results).to eq [
|
56
|
-
Prawn::SVG::Color::
|
57
|
-
Prawn::SVG::Color::
|
69
|
+
Prawn::SVG::Color::CMYK.new([100, 0, 100, 50]),
|
70
|
+
Prawn::SVG::Color::CMYK.new([0, 0, 0, 100])
|
58
71
|
]
|
59
72
|
end
|
60
73
|
end
|
@@ -5,6 +5,23 @@ describe Prawn::SVG::Document do
|
|
5
5
|
let(:options) { {} }
|
6
6
|
|
7
7
|
describe "#initialize" do
|
8
|
+
context "with a well-formed document" do
|
9
|
+
let(:svg) { "<svg></svg>" }
|
10
|
+
let(:options) { {color_mode: :cmyk} }
|
11
|
+
|
12
|
+
it "parses the XML and yields itself to its block" do
|
13
|
+
yielded = nil
|
14
|
+
|
15
|
+
document = Prawn::SVG::Document.new(svg, bounds, options) do |doc|
|
16
|
+
yielded = doc
|
17
|
+
end
|
18
|
+
|
19
|
+
expect(yielded).to eq document
|
20
|
+
expect(document.color_mode).to eq :cmyk
|
21
|
+
expect(document.root.name).to eq 'svg'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
8
25
|
context "when unparsable XML is provided" do
|
9
26
|
let(:svg) { "this isn't SVG data" }
|
10
27
|
|
@@ -73,10 +73,10 @@ RSpec.describe Prawn::SVG::Elements::Marker do
|
|
73
73
|
["stroke_color", ["000000"], {}, []],
|
74
74
|
["line_width", [100.0], {}, []],
|
75
75
|
["cap_style", [:butt], {}, []],
|
76
|
+
["join_style", [:miter], {}, []],
|
76
77
|
["undash", [], {}, []],
|
77
78
|
["save", [], {}, []],
|
78
79
|
["fill", [], {}, [
|
79
|
-
["join_style", [:bevel], {}, []],
|
80
80
|
["move_to", [[0.0, 600.0]], {}, []],
|
81
81
|
["line_to", [[10.0, 595.0]], {}, []],
|
82
82
|
["line_to", [[0.0, 590.0]], {}, []],
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Prawn::SVG::Elements::Root do
|
4
|
+
let(:color_mode) { :rgb }
|
5
|
+
let(:sizing) do
|
6
|
+
instance_double(Prawn::SVG::Calculators::DocumentSizing, x_offset: 0, y_offset: 0, x_scale: 1, y_scale: 1)
|
7
|
+
end
|
8
|
+
let(:document) do
|
9
|
+
instance_double(Prawn::SVG::Document, color_mode: color_mode, sizing: sizing)
|
10
|
+
end
|
11
|
+
let(:source) { double(name: 'svg', attributes: {}) }
|
12
|
+
let(:state) { Prawn::SVG::State.new }
|
13
|
+
let(:element) { Prawn::SVG::Elements::Root.new(document, source, [], state) }
|
14
|
+
|
15
|
+
it 'uses RGB black as the default color' do
|
16
|
+
element.apply
|
17
|
+
expect(element.calls.first).to eq ['fill_color', ['000000'], {}, []]
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when in CMYK mode' do
|
21
|
+
let(:color_mode) { :cmyk }
|
22
|
+
|
23
|
+
it 'uses CMYK black as the default color' do
|
24
|
+
element.apply
|
25
|
+
expect(element.calls.first).to eq ['fill_color', [[0, 0, 0, 100]], {}, []]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -237,4 +237,31 @@ Even more
|
|
237
237
|
]
|
238
238
|
end
|
239
239
|
end
|
240
|
+
|
241
|
+
describe "when there's a comment inside the text element" do
|
242
|
+
let(:svg) { '<text>Hi <!-- comment --> there</text>' }
|
243
|
+
|
244
|
+
it "ignores the comment" do
|
245
|
+
element.process
|
246
|
+
|
247
|
+
expect(flatten_calls(element.calls)).to eq [
|
248
|
+
["text_group", [], {}],
|
249
|
+
["font", ["Helvetica"], {:style=>:normal}],
|
250
|
+
["draw_text",
|
251
|
+
["Hi "],
|
252
|
+
{:size=>16,
|
253
|
+
:style=>:normal,
|
254
|
+
:text_anchor=>"start",
|
255
|
+
:at=>[:relative, :relative],
|
256
|
+
:offset=>[0, 0]}],
|
257
|
+
["draw_text",
|
258
|
+
["there"],
|
259
|
+
{:size=>16,
|
260
|
+
:style=>:normal,
|
261
|
+
:text_anchor=>"start",
|
262
|
+
:at=>[:relative, :relative],
|
263
|
+
:offset=>[0, 0]}]
|
264
|
+
]
|
265
|
+
end
|
266
|
+
end
|
240
267
|
end
|
@@ -39,11 +39,19 @@ describe Prawn::SVG::Interface do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
context "when fill_and_stroke is issued" do
|
42
|
+
def expect_rectangle
|
43
|
+
if RUBY_VERSION.start_with?("2.7.")
|
44
|
+
expect(prawn).to receive(:rectangle)
|
45
|
+
else
|
46
|
+
expect(prawn).to receive(:rectangle).with([0, 100], 10, 10)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
42
50
|
context "and fill rule is not set" do
|
43
51
|
let(:interface) { Prawn::SVG::Interface.new('<svg width="250" height="100"><rect width="10" height="10" stroke="red"></rect></svg>', prawn, {}) }
|
44
52
|
|
45
53
|
it "adds content 'B'" do
|
46
|
-
|
54
|
+
expect_rectangle
|
47
55
|
expect(prawn).to receive(:add_content).with("W n")
|
48
56
|
expect(prawn).to receive(:add_content).with("B")
|
49
57
|
interface.draw
|
@@ -54,7 +62,7 @@ describe Prawn::SVG::Interface do
|
|
54
62
|
let(:interface) { Prawn::SVG::Interface.new('<svg width="250" height="100"><rect width="10" height="10" stroke="red" fill-rule="evenodd"></rect></svg>', prawn, {}) }
|
55
63
|
|
56
64
|
it "adds content 'B*'" do
|
57
|
-
|
65
|
+
expect_rectangle
|
58
66
|
expect(prawn).to receive(:add_content).with("W n")
|
59
67
|
expect(prawn).to receive(:add_content).with("B*")
|
60
68
|
interface.draw
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<svg width="154" height="28" viewBox="0 0 154 28" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<g fill="device-cmyk(0.5, 50%, 0.0, 0)" fill-rule="evenodd">
|
3
|
+
<path d="m19.88 23.715-4.083 3.337a4.243 4.243 0 0 1-1.626-.965c-.463-.44-.768-.943-.915-1.508l-3.185 1.812c-.125.068-.336.138-.635.212a3.29 3.29 0 0 1-.754.11c-2.451 0-4.476-.841-6.073-2.524C1.011 22.506.212 20.168.212 17.175c0-2.846.83-5.192 2.491-7.039 1.66-1.847 3.777-2.77 6.353-2.77 1.027 0 1.988.203 2.88.61.892.407 1.479.768 1.762 1.084l2.948-1.067 1.034.305V19.92c0 1.016.178 1.824.533 2.422.354.599.91 1.057 1.667 1.373zm-7.166-2.541v-7.945a4.404 4.404 0 0 0-1.33-1.087c-.548-.303-1.217-.454-2.008-.454-1.141 0-2.086.416-2.837 1.248-.751.831-1.126 2.208-1.126 4.131 0 1.527.35 2.752 1.05 3.674.7.922 1.683 1.383 2.948 1.383.723 0 1.4-.113 2.033-.339.632-.227 1.055-.431 1.27-.611zM39.463 26.51h-3.947l-1.118-1.66-2.728 1.491c-.226.125-.553.217-.982.28a7.964 7.964 0 0 1-1.118.093c-2.666 0-4.69-.897-6.073-2.693s-2.081-4.049-2.092-6.759c-.011-2.937.822-5.274 2.499-7.014 1.677-1.739 3.894-2.609 6.649-2.609.79 0 1.485.062 2.083.187a5.903 5.903 0 0 1 1.728.661V.491h5.099V26.51zm-5.099-5.607v-8.03c-.283-.35-.751-.652-1.406-.906a5.855 5.855 0 0 0-2.134-.381c-1.423 0-2.482.415-3.177 1.245-.694.83-1.042 2.149-1.042 3.956 0 1.841.37 3.216 1.109 4.125.74.91 1.742 1.364 3.007 1.364.813 0 1.527-.135 2.143-.406.616-.271 1.116-.595 1.5-.967zM67.009 26.51h-5.184V15.075c0-.666-.237-1.256-.711-1.77-.475-.514-1.203-.771-2.186-.771-.938 0-1.722.195-2.354.584-.633.39-1.135.754-1.508 1.093V26.51h-5.184V8.299l5.184-.373v1.779l2.155-1.237a7.47 7.47 0 0 1 1.976-.805 8.464 8.464 0 0 1 2.01-.246c1.798 0 3.215.557 4.25 1.669 1.035 1.112 1.553 2.578 1.553 4.396V26.51h-.001zM89.303 17.057c0 2.993-.857 5.376-2.572 7.149-1.716 1.773-4.068 2.66-7.059 2.66-3.216 0-5.625-.892-7.227-2.677-1.603-1.784-2.404-4.162-2.404-7.132 0-2.699.863-5.008 2.59-6.929 1.726-1.92 4.073-2.88 7.041-2.88 2.979 0 5.328.887 7.05 2.66 1.72 1.774 2.581 4.156 2.581 7.149zm-5.252 0c0-1.479-.387-2.795-1.16-3.947-.773-1.152-1.844-1.728-3.21-1.728-1.57 0-2.694.525-3.372 1.575-.677 1.05-1.016 2.417-1.016 4.1 0 1.57.406 2.908 1.22 4.015.813 1.107 1.903 1.66 3.27 1.66 1.524 0 2.617-.449 3.278-1.347.66-.897.99-2.34.99-4.328zM101.145 23.325l-3.762 3.541c-1.559-.643-2.727-1.581-3.506-2.814-.779-1.233-1.17-2.708-1.17-4.42v-7.944H90.98V8.3h1.727V2.286l5.185-.373v6.404h2.71v3.354h-2.71v6.486c0 1.295.294 2.37.881 3.226.587.856 1.377 1.503 2.372 1.942zM121.981 23.715l-4.083 3.337a4.243 4.243 0 0 1-1.626-.965c-.464-.44-.769-.943-.915-1.508l-3.185 1.812c-.124.068-.337.138-.636.212a3.29 3.29 0 0 1-.754.11c-2.451 0-4.475-.841-6.072-2.524-1.599-1.683-2.398-4.021-2.398-7.014 0-2.846.83-5.192 2.491-7.039 1.66-1.847 3.777-2.77 6.353-2.77 1.027 0 1.988.203 2.88.61.892.407 1.479.768 1.762 1.084l2.948-1.067 1.033.305V19.92c0 1.016.178 1.824.533 2.422.355.598.913 1.057 1.669 1.373zm-7.166-2.541v-7.945a4.412 4.412 0 0 0-1.329-1.087c-.549-.303-1.218-.454-2.008-.454-1.141 0-2.087.416-2.838 1.248-.752.831-1.127 2.208-1.127 4.131 0 1.527.35 2.752 1.051 3.674.7.922 1.683 1.383 2.947 1.383.723 0 1.4-.113 2.033-.339.634-.227 1.057-.431 1.271-.611zM153.068 26.51h-5.15V15.075c0-.666-.229-1.256-.687-1.77-.457-.514-1.177-.771-2.159-.771-.803 0-1.52.155-2.152.466a4.972 4.972 0 0 0-1.592 1.211V26.51h-5.15V15.075c0-.666-.229-1.256-.686-1.77-.458-.514-1.178-.771-2.16-.771-.926 0-1.705.189-2.338.567-.633.378-1.096.749-1.389 1.11V26.51h-5.15V8.299l5.15-.373v1.779l2.016-1.237c.666-.406 1.32-.683 1.965-.83a8.86 8.86 0 0 1 1.965-.22c1.197 0 2.219.246 3.066.737a4.986 4.986 0 0 1 1.948 2.024l2.796-1.711a6.749 6.749 0 0 1 1.846-.788 8.17 8.17 0 0 1 2.084-.263c1.795 0 3.207.557 4.234 1.669 1.028 1.112 1.543 2.578 1.543 4.396V26.51zm0 0"/>
|
4
|
+
</g>
|
5
|
+
</svg>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<svg width="240" height="240" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<defs>
|
3
|
+
<style type="text/css"><![CDATA[
|
4
|
+
#rect1 { fill: url(#Gradient1); }
|
5
|
+
.stop1 { stop-color: device-cmyk(0, 1, 1, 0); }
|
6
|
+
.stop2 { stop-color: device-cmyk(0, 0, 0, 1); stop-opacity: 0; }
|
7
|
+
.stop3 { stop-color: device-cmyk(1, 0, 1, 0); }
|
8
|
+
]]></style>
|
9
|
+
<linearGradient id="Gradient1">
|
10
|
+
<stop class="stop1" offset="0%"/>
|
11
|
+
<stop class="stop2" offset="50%"/>
|
12
|
+
<stop class="stop3" offset="100%"/>
|
13
|
+
</linearGradient>
|
14
|
+
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
|
15
|
+
<stop offset="25%" stop-color="device-cmyk(0, 1, 1, 0)"/>
|
16
|
+
<stop offset="50%" stop-color="device-cmyk(0, 0, 0, 1)" stop-opacity="0"/>
|
17
|
+
<stop offset="75%" stop-color="device-cmyk(1, 0, 1, 0)"/>
|
18
|
+
</linearGradient>
|
19
|
+
<linearGradient id="Gradient3" x1="0" x2="1" y1="0" y2="1">
|
20
|
+
<stop offset="0%" stop-color="device-cmyk(0, 1, 1, 0)"/>
|
21
|
+
<stop offset="30%" stop-color="device-cmyk(1, 0, 1, 0)"/>
|
22
|
+
<stop offset="60%" stop-color="device-cmyk(1, 0, 1, 0)"/>
|
23
|
+
<stop offset="100%" stop-color="device-cmyk(1, 0, 1, 0)"/>
|
24
|
+
</linearGradient>
|
25
|
+
<linearGradient id="userspace" gradientUnits="userSpaceOnUse" x1="160" y1="90" x2="220" y2="90">
|
26
|
+
<stop offset="0" stop-color="device-cmyk(0, 1, 1, 0)"/>
|
27
|
+
<stop offset="1" stop-color="device-cmyk(1, 0, 1, 0)"/>
|
28
|
+
</linearGradient>
|
29
|
+
</defs>
|
30
|
+
|
31
|
+
<rect id="rect1" x="10" y="10" rx="15" ry="15" width="50" height="100" stroke="black"/>
|
32
|
+
<rect x="10" y="120" rx="15" ry="15" width="50" height="100" fill="url(#Gradient2)" stroke="black"/>
|
33
|
+
<circle cx="110" cy="40" r="30" stroke="black" fill="url(#Gradient3)"/>
|
34
|
+
<ellipse cx="110" cy="100" rx="30" ry="20" stroke="black" fill="url(#Gradient1)"/>
|
35
|
+
<line x1="65" y1="120" x2="200" y2="200" stroke="url(#Gradient3)" stroke-width="5"/>
|
36
|
+
<path d="M80,140l0,50l30,20l0-40z" stroke="url(#Gradient3)" fill="url(#Gradient1)" stroke-width="3"/>
|
37
|
+
<polygon points="150,30 170,80 190,55 200,10" fill="url(#Gradient1)"/>
|
38
|
+
<rect x="150" y="90" width="80" height="30" fill="url(#userspace)" stroke="black"/>
|
39
|
+
<polyline points="150,130 170,170 190,155 200,110" stroke="url(#Gradient1)" stroke-width="3" fill="none"/>
|
40
|
+
</svg>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<svg width="177.649" height="244.929" version="1.1" viewBox="0 0 177.649 244.929" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
3
|
+
<g transform="translate(-21.1667 -31.75)">
|
4
|
+
<rect x="21.1667" y="31.75" width="177.649" height="244.929" fill="url(#linearGradient18)" stroke-width=".264583"/>
|
5
|
+
</g>
|
6
|
+
<defs>
|
7
|
+
<linearGradient id="linearGradient18" x1="109.032" x2="109.74" y1="34.6919" y2="273.287" gradientUnits="userSpaceOnUse">
|
8
|
+
<stop stop-color="orange" offset="0"/>
|
9
|
+
<stop stop-color="blue" offset="1"/>
|
10
|
+
</linearGradient>
|
11
|
+
</defs>
|
12
|
+
</svg>
|
metadata
CHANGED
@@ -1,49 +1,63 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prawn-svg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.33.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Mog Nesbitt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: css_parser
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 0.11.1
|
20
|
-
- - "<"
|
17
|
+
- - "~>"
|
21
18
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
19
|
+
version: '1.6'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
|
-
- - "
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 0.11.1
|
30
|
-
- - "<"
|
24
|
+
- - "~>"
|
31
25
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
26
|
+
version: '1.6'
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
28
|
+
name: matrix
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
36
30
|
requirements:
|
37
31
|
- - "~>"
|
38
32
|
- !ruby/object:Gem::Version
|
39
|
-
version:
|
33
|
+
version: 0.4.2
|
40
34
|
type: :runtime
|
41
35
|
prerelease: false
|
42
36
|
version_requirements: !ruby/object:Gem::Requirement
|
43
37
|
requirements:
|
44
38
|
- - "~>"
|
45
39
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
40
|
+
version: 0.4.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: prawn
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.11.1
|
48
|
+
- - "<"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '3'
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 0.11.1
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3'
|
47
61
|
- !ruby/object:Gem::Dependency
|
48
62
|
name: rexml
|
49
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -59,37 +73,37 @@ dependencies:
|
|
59
73
|
- !ruby/object:Gem::Version
|
60
74
|
version: '3.2'
|
61
75
|
- !ruby/object:Gem::Dependency
|
62
|
-
name:
|
76
|
+
name: rake
|
63
77
|
requirement: !ruby/object:Gem::Requirement
|
64
78
|
requirements:
|
65
79
|
- - "~>"
|
66
80
|
- !ruby/object:Gem::Version
|
67
|
-
version: '
|
81
|
+
version: '13.0'
|
68
82
|
type: :development
|
69
83
|
prerelease: false
|
70
84
|
version_requirements: !ruby/object:Gem::Requirement
|
71
85
|
requirements:
|
72
86
|
- - "~>"
|
73
87
|
- !ruby/object:Gem::Version
|
74
|
-
version: '
|
88
|
+
version: '13.0'
|
75
89
|
- !ruby/object:Gem::Dependency
|
76
|
-
name:
|
90
|
+
name: rspec
|
77
91
|
requirement: !ruby/object:Gem::Requirement
|
78
92
|
requirements:
|
79
93
|
- - "~>"
|
80
94
|
- !ruby/object:Gem::Version
|
81
|
-
version: '
|
95
|
+
version: '3.0'
|
82
96
|
type: :development
|
83
97
|
prerelease: false
|
84
98
|
version_requirements: !ruby/object:Gem::Requirement
|
85
99
|
requirements:
|
86
100
|
- - "~>"
|
87
101
|
- !ruby/object:Gem::Version
|
88
|
-
version: '
|
102
|
+
version: '3.0'
|
89
103
|
description: This gem allows you to render SVG directly into a PDF using the 'prawn'
|
90
104
|
gem. Since PDF is vector-based, you'll get nice scaled graphics if you use SVG
|
91
105
|
instead of an image.
|
92
|
-
email:
|
106
|
+
email: mog@seriousorange.com
|
93
107
|
executables: []
|
94
108
|
extensions: []
|
95
109
|
extra_rdoc_files: []
|
@@ -143,6 +157,7 @@ files:
|
|
143
157
|
- lib/prawn/svg/extensions/additional_gradient_transforms.rb
|
144
158
|
- lib/prawn/svg/font.rb
|
145
159
|
- lib/prawn/svg/font_registry.rb
|
160
|
+
- lib/prawn/svg/gradients.rb
|
146
161
|
- lib/prawn/svg/interface.rb
|
147
162
|
- lib/prawn/svg/loaders/data.rb
|
148
163
|
- lib/prawn/svg/loaders/file.rb
|
@@ -173,6 +188,7 @@ files:
|
|
173
188
|
- spec/prawn/svg/elements/path_spec.rb
|
174
189
|
- spec/prawn/svg/elements/polygon_spec.rb
|
175
190
|
- spec/prawn/svg/elements/polyline_spec.rb
|
191
|
+
- spec/prawn/svg/elements/root_spec.rb
|
176
192
|
- spec/prawn/svg/elements/text_spec.rb
|
177
193
|
- spec/prawn/svg/font_registry_spec.rb
|
178
194
|
- spec/prawn/svg/font_spec.rb
|
@@ -194,6 +210,7 @@ files:
|
|
194
210
|
- spec/sample_svg/circle01.svg
|
195
211
|
- spec/sample_svg/clip_path.svg
|
196
212
|
- spec/sample_svg/close_path.svg
|
213
|
+
- spec/sample_svg/cmyk.svg
|
197
214
|
- spec/sample_svg/cubic01.svg
|
198
215
|
- spec/sample_svg/cubic01a.svg
|
199
216
|
- spec/sample_svg/cubic02.svg
|
@@ -203,6 +220,7 @@ files:
|
|
203
220
|
- spec/sample_svg/gistfile1.svg
|
204
221
|
- spec/sample_svg/google_charts.svg
|
205
222
|
- spec/sample_svg/gradient_transform.svg
|
223
|
+
- spec/sample_svg/gradients-cmyk.svg
|
206
224
|
- spec/sample_svg/gradients.svg
|
207
225
|
- spec/sample_svg/hidden_paths.svg
|
208
226
|
- spec/sample_svg/highcharts.svg
|
@@ -221,6 +239,7 @@ files:
|
|
221
239
|
- spec/sample_svg/offset_viewport.svg
|
222
240
|
- spec/sample_svg/omnigraffle.svg
|
223
241
|
- spec/sample_svg/opacity01.svg
|
242
|
+
- spec/sample_svg/ordering.svg
|
224
243
|
- spec/sample_svg/path.svg
|
225
244
|
- spec/sample_svg/pie_piece.svg
|
226
245
|
- spec/sample_svg/polygon01.svg
|
@@ -270,14 +289,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
270
289
|
requirements:
|
271
290
|
- - ">="
|
272
291
|
- !ruby/object:Gem::Version
|
273
|
-
version: 2.
|
292
|
+
version: 2.5.0
|
274
293
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
275
294
|
requirements:
|
276
295
|
- - ">="
|
277
296
|
- !ruby/object:Gem::Version
|
278
297
|
version: '0'
|
279
298
|
requirements: []
|
280
|
-
rubygems_version: 3.
|
299
|
+
rubygems_version: 3.4.10
|
281
300
|
signing_key:
|
282
301
|
specification_version: 4
|
283
302
|
summary: SVG renderer for Prawn PDF library
|
@@ -300,6 +319,7 @@ test_files:
|
|
300
319
|
- spec/prawn/svg/elements/path_spec.rb
|
301
320
|
- spec/prawn/svg/elements/polygon_spec.rb
|
302
321
|
- spec/prawn/svg/elements/polyline_spec.rb
|
322
|
+
- spec/prawn/svg/elements/root_spec.rb
|
303
323
|
- spec/prawn/svg/elements/text_spec.rb
|
304
324
|
- spec/prawn/svg/font_registry_spec.rb
|
305
325
|
- spec/prawn/svg/font_spec.rb
|
@@ -321,6 +341,7 @@ test_files:
|
|
321
341
|
- spec/sample_svg/circle01.svg
|
322
342
|
- spec/sample_svg/clip_path.svg
|
323
343
|
- spec/sample_svg/close_path.svg
|
344
|
+
- spec/sample_svg/cmyk.svg
|
324
345
|
- spec/sample_svg/cubic01.svg
|
325
346
|
- spec/sample_svg/cubic01a.svg
|
326
347
|
- spec/sample_svg/cubic02.svg
|
@@ -330,6 +351,7 @@ test_files:
|
|
330
351
|
- spec/sample_svg/gistfile1.svg
|
331
352
|
- spec/sample_svg/google_charts.svg
|
332
353
|
- spec/sample_svg/gradient_transform.svg
|
354
|
+
- spec/sample_svg/gradients-cmyk.svg
|
333
355
|
- spec/sample_svg/gradients.svg
|
334
356
|
- spec/sample_svg/hidden_paths.svg
|
335
357
|
- spec/sample_svg/highcharts.svg
|
@@ -348,6 +370,7 @@ test_files:
|
|
348
370
|
- spec/sample_svg/offset_viewport.svg
|
349
371
|
- spec/sample_svg/omnigraffle.svg
|
350
372
|
- spec/sample_svg/opacity01.svg
|
373
|
+
- spec/sample_svg/ordering.svg
|
351
374
|
- spec/sample_svg/path.svg
|
352
375
|
- spec/sample_svg/pie_piece.svg
|
353
376
|
- spec/sample_svg/polygon01.svg
|