prawn-svg 0.32.0 → 0.34.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +20 -3
  3. data/.gitignore +1 -0
  4. data/README.md +32 -24
  5. data/lib/prawn/svg/attributes/stroke.rb +5 -0
  6. data/lib/prawn/svg/color.rb +46 -22
  7. data/lib/prawn/svg/css/selector_parser.rb +11 -11
  8. data/lib/prawn/svg/css/stylesheets.rb +4 -4
  9. data/lib/prawn/svg/document.rb +20 -4
  10. data/lib/prawn/svg/elements/base.rb +10 -11
  11. data/lib/prawn/svg/elements/gradient.rb +2 -2
  12. data/lib/prawn/svg/elements/image.rb +54 -21
  13. data/lib/prawn/svg/elements/path.rb +0 -2
  14. data/lib/prawn/svg/elements/root.rb +1 -1
  15. data/lib/prawn/svg/elements/text_component.rb +12 -3
  16. data/lib/prawn/svg/elements/use.rb +20 -12
  17. data/lib/prawn/svg/elements.rb +1 -0
  18. data/lib/prawn/svg/gradients.rb +48 -0
  19. data/lib/prawn/svg/interface.rb +17 -187
  20. data/lib/prawn/svg/loaders/data.rb +4 -3
  21. data/lib/prawn/svg/properties.rb +2 -0
  22. data/lib/prawn/svg/renderer.rb +236 -0
  23. data/lib/prawn/svg/version.rb +1 -1
  24. data/lib/prawn-svg.rb +2 -0
  25. data/prawn-svg.gemspec +18 -16
  26. data/spec/prawn/svg/color_spec.rb +29 -16
  27. data/spec/prawn/svg/document_spec.rb +17 -0
  28. data/spec/prawn/svg/elements/marker_spec.rb +1 -1
  29. data/spec/prawn/svg/elements/root_spec.rb +28 -0
  30. data/spec/prawn/svg/elements/text_spec.rb +27 -0
  31. data/spec/prawn/svg/interface_spec.rb +10 -2
  32. data/spec/sample_images/image_svg_embed.svg +6 -0
  33. data/spec/sample_svg/cmyk.svg +5 -0
  34. data/spec/sample_svg/gradients-cmyk.svg +40 -0
  35. data/spec/sample_svg/image_svg.svg +9 -0
  36. data/spec/sample_svg/ordering.svg +12 -0
  37. data/spec/sample_svg/symbol.svg +12 -0
  38. data/spec/spec_helper.rb +29 -5
  39. metadata +67 -23
data/prawn-svg.gemspec CHANGED
@@ -1,27 +1,29 @@
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 'base64', '~> 0.1.0'
23
+ gem.add_runtime_dependency 'css_parser', '~> 1.6'
24
+ gem.add_runtime_dependency 'matrix', '~> 0.4.2'
25
+ gem.add_runtime_dependency 'prawn', '>= 0.11.1', '< 3'
26
+ gem.add_runtime_dependency 'rexml', '~> 3.2'
27
+ gem.add_development_dependency 'rake', '~> 13.0'
28
+ gem.add_development_dependency 'rspec', '~> 3.0'
27
29
  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,6 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
4
+ <rect x="0" y="0" width="100" height="100" fill="red" stroke="blue" stroke-width="5" />
5
+ </svg>
6
+
@@ -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,9 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
4
+ <rect x="20" y="20" width="960" height="460" stroke="black" fill="white" />
5
+ <image xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjExMDAiIGhlaWdodD0iNDAwIj4KICA8cmVjdCB4PSIxMDAiIHk9IjEwMCIgd2lkdGg9IjkwMCIgaGVpZ2h0PSIzMDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmx1ZSIgc3Ryb2tlLXdpZHRoPSIyIiAvPgoKICA8cGF0aCBkPSJNMjAwIDIwMCBMODAwIDMwMCBMODAwIDIwMCIgc3Ryb2tlPSJncmVlbiIgc3Ryb2tlLXdpZHRoPSIxMCIgZmlsbD0icmVkIiAvPgo8L3N2Zz4K" x="100" y="200" width="200" height="200" />
6
+ <image xlink:href="sample_images/image_svg_embed.svg" x="400" y="200" width="200" height="200" />
7
+ <image xlink:href="sample_images/image_svg_embed.svg" x="650" y="200" width="100" height="200" />
8
+ <image xlink:href="sample_images/image_svg_embed.svg" x="800" y="200" width="100" height="200" preserveAspectRatio="none" />
9
+ </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>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg" xmlns:xlink='http://www.w3.org/1999/xlink' version="1.1">
4
+ <symbol id="rects" viewBox="0 0 100 100">
5
+ <rect x="10" y="10" width="80" height="80" fill="none" stroke="red"/>
6
+ <rect x="20" y="20" width="60" height="60" fill="none" stroke="green"/>
7
+ <rect x="30" y="30" width="40" height="40" fill="none" stroke="blue"/>
8
+ </symbol>
9
+ <rect width="1000" height="1000" fill="none" stroke="black"/>
10
+ <use x="100" y="100" width="800" height="800" xlink:href="#rects"/>
11
+ <use x="400" y="400" width="200" height="200" xlink:href="#rects"/>
12
+ </svg>
data/spec/spec_helper.rb CHANGED
@@ -3,12 +3,12 @@ Bundler.require(:default, :development)
3
3
 
4
4
  # Requires supporting files with custom matchers and macros, etc,
5
5
  # in ./support/ and its subdirectories.
6
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
6
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
7
7
 
8
8
  module Support
9
- def flatten_calls(calls)
9
+ def flatten_calls(_calls)
10
10
  [].tap do |flattened_calls|
11
- add = -> (local_calls) do
11
+ add = lambda do |local_calls|
12
12
  local_calls.each do |call|
13
13
  flattened_calls << call[0..2]
14
14
  add.call call[3]
@@ -28,12 +28,36 @@ end
28
28
 
29
29
  RSpec.configure do |config|
30
30
  config.mock_with :rspec do |c|
31
- c.syntax = [:should, :expect]
31
+ c.syntax = %i[should expect]
32
32
  end
33
33
 
34
34
  config.expect_with :rspec do |c|
35
- c.syntax = [:should, :expect]
35
+ c.syntax = %i[should expect]
36
36
  end
37
37
 
38
38
  config.include Support
39
+
40
+ config.before(:suite) do
41
+ # calculate the MD5 of all files in spec/sample_output and store in a hash
42
+ $hashes = {}
43
+
44
+ Dir["#{File.dirname(__FILE__)}/sample_output/*.pdf"].each do |file|
45
+ hash = Digest::MD5.file(file).hexdigest
46
+ $hashes[file] = hash
47
+ end
48
+ end
49
+
50
+ config.after(:suite) do
51
+ # print out the PDFs that have changed
52
+ changed = $hashes.select do |file, hash|
53
+ new_hash = Digest::MD5.file(file).hexdigest
54
+ new_hash != hash
55
+ end
56
+
57
+ if changed.any?
58
+ puts "\nThese PDFs have changed since the last test run:"
59
+ cwd = "#{Dir.pwd}/"
60
+ changed.each { |file, _| puts " #{file.sub(cwd, '')}" }
61
+ end
62
+ end
39
63
  end