prawn-svg 0.32.0 → 0.33.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/.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
|