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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d054ded3422dc4cd1cb870dc09549c0a82c1e2813e314565c7a3aec70d95d188
4
- data.tar.gz: 123a00b97f78f284a8e11fad37fed42cbec06d678675ec15ff7dafbcfe6505d5
3
+ metadata.gz: c9f71b82858f8ee91e1ccaf2903c2e2a0d49108b1532a81d15c2275362a0d136
4
+ data.tar.gz: f3336c6a727df24e6a5877e4d26cf030eabbb1c9af9852f95c35c5bfe5128995
5
5
  SHA512:
6
- metadata.gz: 4ece284ced16e7b6f4d7562c17a97780375f4e255eb4199d489cfb1fa473dc1f65e6b9fa48894079f0756b42a749cc58f34c00507e19ccdfddab55723c82311d
7
- data.tar.gz: 8826f6f457372cc7df38869e6448974f5731fdee1040bca44b8446b71c57e0713340481a3400705c8fd11ec5d13959dcd99f24a0fe8820c89a37ca725545656c
6
+ metadata.gz: 94f1a140e837ab37b088cc659bc8ef63a3e89778539236441f43e7b4885b9028cce1cb83c9e9636b00ff2ac740770ec8d853c123aa42b0764fb1264405269e76
7
+ data.tar.gz: 4d02f2e14875214bb5460ace33925f9ab17302b39f46e0a37e0e5521e63b0fdd2f23f6018c8e048bf2bce7d148548aa86b5b9967dca827faddccfa23e8e09b13
@@ -1,14 +1,31 @@
1
1
  name: test
2
2
  on: [push, pull_request]
3
3
  jobs:
4
- rake:
4
+ rake_new:
5
5
  runs-on: ubuntu-latest
6
6
  strategy:
7
7
  fail-fast: false
8
8
  matrix:
9
- ruby: [2.3, 2.4, 2.5, 2.6, 2.7, '3.0']
9
+ ruby: [2.6, 2.7, '3.0', 3.1, 3.2]
10
10
  steps:
11
- - uses: actions/checkout@v2
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>&lt;svg&gt;</tt>, <tt>&lt;g&gt;</tt> and <tt>&lt;symbol&gt;</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>&lt;style&gt;</tt> (see CSS section below)
71
72
 
72
- - <tt>&lt;image&gt;</tt> with <tt>http:</tt>, <tt>https:</tt>, <tt>data:image/\*;base64</tt> and `file:` schemes
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>&lt;clipPath&gt;</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
@@ -1,17 +1,9 @@
1
1
  class Prawn::SVG::Color
2
- class Hex
3
- attr_reader :value
2
+ RGB = Struct.new(:value)
3
+ CMYK = Struct.new(:value)
4
4
 
5
- def initialize(value)
6
- @value = value
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
- RGB_VALUE_REGEXP = "\s*(-?[0-9.]+%?)\s*"
167
- RGB_REGEXP = /\Argb\(#{RGB_VALUE_REGEXP},#{RGB_VALUE_REGEXP},#{RGB_VALUE_REGEXP}\)\z/i
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
- Hex.new("#{m[1] * 2}#{m[2] * 2}#{m[3] * 2}")
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
- Hex.new(color[1..6])
173
+ RGB.new(color[1..6])
181
174
 
182
175
  elsif hex = HTML_COLORS[color.downcase]
183
- Hex.new(hex)
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
- Hex.new(hex)
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 << DEFAULT_COLOR unless url_specified
208
+ result << default_color(color_mode) unless url_specified
207
209
 
208
210
  result.compact
209
211
  end
210
212
 
211
- def self.color_to_hex(color)
212
- result = parse(color).detect {|result| result.is_a?(Hex)}
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 = []
@@ -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::Hex
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.gsub!(/(^[\s]*)|([\s]*$)/, '')
241
-
242
- output = {}
243
- declarations.split(/[\;$]+/m).each do |decs|
244
- if matches = decs.match(/\s*(.[^:]*)\s*\:\s*(.[^;]*)\s*(;|\Z)/i)
245
- property, value, _ = matches.captures
246
- output[property.downcase] = value
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 color_hex = Prawn::SVG::Color.color_to_hex(child.properties.stop_color)
151
- result << [offset, color_hex]
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 = if data[0, 3].unpack("C*") == [255, 216, 255]
71
- Prawn::Images::JPG
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
@@ -55,8 +55,6 @@ class Prawn::SVG::Elements::Path < Prawn::SVG::Elements::Base
55
55
  end
56
56
 
57
57
  def apply
58
- add_call 'join_style', :bevel
59
-
60
58
  apply_commands
61
59
  apply_markers
62
60
  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', '000000'
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 isn't a Prawn option; we have to do some math to support it
45
- # and so we handle this in Prawn::SVG::Interface#rewrite_call_arguments
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 || child.namespace == SVG_NAMESPACE || child.namespace == ''
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
@@ -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]
@@ -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|
@@ -1,5 +1,5 @@
1
1
  module Prawn
2
2
  module SVG
3
- VERSION = '0.32.0'
3
+ VERSION = '0.33.0'
4
4
  end
5
5
  end
data/lib/prawn-svg.rb CHANGED
@@ -27,6 +27,7 @@ require 'prawn/svg/css/selector_parser'
27
27
  require 'prawn/svg/css/stylesheets'
28
28
  require 'prawn/svg/ttf'
29
29
  require 'prawn/svg/font'
30
+ require 'prawn/svg/gradients'
30
31
  require 'prawn/svg/document'
31
32
  require 'prawn/svg/state'
32
33
 
data/prawn-svg.gemspec CHANGED
@@ -1,27 +1,28 @@
1
- # -*- encoding: utf-8 -*-
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 = "SVG renderer for Prawn PDF library"
8
- gem.description = "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
- gem.author = "Roger Nesbitt"
10
- gem.email = "roger@seriousorange.com"
11
- gem.homepage = "http://github.com/mogest/prawn-svg"
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.name = "prawn-svg"
18
- gem.require_paths = ["lib"]
18
+ gem.require_paths = ['lib']
19
19
 
20
- gem.required_ruby_version = '>= 2.3.0'
20
+ gem.required_ruby_version = '>= 2.5.0'
21
21
 
22
- gem.add_runtime_dependency "prawn", ">= 0.11.1", "< 3"
23
- gem.add_runtime_dependency "css_parser", "~> 1.6"
24
- gem.add_runtime_dependency "rexml", "~> 3.2"
25
- gem.add_development_dependency "rspec", "~> 3.0"
26
- gem.add_development_dependency "rake", "~> 13.0"
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 "::color_to_hex" do
4
+ describe "::css_color_to_prawn_color" do
5
5
  it "converts #xxx to a hex value" do
6
- Prawn::SVG::Color.color_to_hex("#9ab").should == "99aabb"
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.color_to_hex("#9ab123").should == "9ab123"
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.color_to_hex("White").should == "ffffff"
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.color_to_hex("rgb(16, 32, 48)").should == "102030"
19
- Prawn::SVG::Color.color_to_hex("rgb(-5, 50%, 120%)").should == "007fff"
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.color_to_hex("function(#someurl, 0) nonexistent rgb( 3 ,4,5 ) white").should == "030405"
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.color_to_hex("url(#someplace) red")).to eq 'ff0000'
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.color_to_hex("blurble")).to eq '000000'
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.color_to_hex("url(#someplace)")).to be nil
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::Hex.new("008000"),
49
- Prawn::SVG::Color::Hex.new("112233")
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::Hex.new("008000"),
57
- Prawn::SVG::Color::Hex.new("000000")
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
- expect(prawn).to receive(:rectangle).with([0, 100], 10, 10)
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
- expect(prawn).to receive(:rectangle).with([0, 100], 10, 10)
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.32.0
4
+ version: 0.33.0
5
5
  platform: ruby
6
6
  authors:
7
- - Roger Nesbitt
7
+ - Mog Nesbitt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-07 00:00:00.000000000 Z
11
+ date: 2023-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: prawn
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: '3'
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: '3'
26
+ version: '1.6'
33
27
  - !ruby/object:Gem::Dependency
34
- name: css_parser
28
+ name: matrix
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
31
  - - "~>"
38
32
  - !ruby/object:Gem::Version
39
- version: '1.6'
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: '1.6'
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: rspec
76
+ name: rake
63
77
  requirement: !ruby/object:Gem::Requirement
64
78
  requirements:
65
79
  - - "~>"
66
80
  - !ruby/object:Gem::Version
67
- version: '3.0'
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: '3.0'
88
+ version: '13.0'
75
89
  - !ruby/object:Gem::Dependency
76
- name: rake
90
+ name: rspec
77
91
  requirement: !ruby/object:Gem::Requirement
78
92
  requirements:
79
93
  - - "~>"
80
94
  - !ruby/object:Gem::Version
81
- version: '13.0'
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: '13.0'
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: roger@seriousorange.com
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.3.0
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.2.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