prawn-svg 0.15.0.0 → 0.23.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -0
- data/README.md +51 -20
- data/lib/prawn/svg/attributes/clip_path.rb +17 -0
- data/lib/prawn/svg/attributes/color.rb +5 -0
- data/lib/prawn/svg/attributes/display.rb +5 -0
- data/lib/prawn/svg/attributes/font.rb +38 -0
- data/lib/prawn/svg/attributes/opacity.rb +15 -0
- data/lib/prawn/svg/attributes/stroke.rb +35 -0
- data/lib/prawn/svg/attributes/transform.rb +50 -0
- data/lib/prawn/svg/attributes.rb +6 -0
- data/lib/prawn/svg/calculators/aspect_ratio.rb +58 -0
- data/lib/prawn/svg/calculators/document_sizing.rb +99 -0
- data/lib/prawn/svg/calculators/pixels.rb +21 -0
- data/lib/prawn/svg/color.rb +197 -12
- data/lib/prawn/svg/css.rb +40 -0
- data/lib/prawn/svg/document.rb +37 -48
- data/lib/prawn/svg/elements/base.rb +238 -0
- data/lib/prawn/svg/elements/circle.rb +25 -0
- data/lib/prawn/svg/elements/container.rb +15 -0
- data/lib/prawn/svg/elements/ellipse.rb +23 -0
- data/lib/prawn/svg/elements/gradient.rb +120 -0
- data/lib/prawn/svg/elements/ignored.rb +5 -0
- data/lib/prawn/svg/elements/image.rb +81 -0
- data/lib/prawn/svg/elements/line.rb +16 -0
- data/lib/prawn/svg/elements/path.rb +405 -0
- data/lib/prawn/svg/elements/polygon.rb +17 -0
- data/lib/prawn/svg/elements/polyline.rb +22 -0
- data/lib/prawn/svg/elements/rect.rb +33 -0
- data/lib/prawn/svg/elements/root.rb +13 -0
- data/lib/prawn/svg/elements/style.rb +10 -0
- data/lib/prawn/svg/elements/text.rb +87 -0
- data/lib/prawn/svg/elements/use.rb +29 -0
- data/lib/prawn/svg/elements.rb +33 -0
- data/lib/prawn/svg/extension.rb +4 -4
- data/lib/prawn/svg/font.rb +10 -92
- data/lib/prawn/svg/font_registry.rb +73 -0
- data/lib/prawn/svg/interface.rb +91 -31
- data/lib/prawn/svg/loaders/data.rb +18 -0
- data/lib/prawn/svg/loaders/file.rb +66 -0
- data/lib/prawn/svg/loaders/web.rb +28 -0
- data/lib/prawn/svg/state.rb +39 -0
- data/lib/prawn/svg/ttf.rb +61 -0
- data/lib/prawn/svg/url_loader.rb +46 -0
- data/lib/prawn/svg/version.rb +2 -2
- data/lib/prawn-svg.rb +20 -6
- data/prawn-svg.gemspec +8 -5
- data/spec/integration_spec.rb +141 -0
- data/spec/prawn/svg/attributes/font_spec.rb +52 -0
- data/spec/prawn/svg/attributes/transform_spec.rb +56 -0
- data/spec/prawn/svg/calculators/aspect_ratio_spec.rb +95 -0
- data/spec/prawn/svg/calculators/document_sizing_spec.rb +128 -0
- data/spec/prawn/svg/color_spec.rb +43 -8
- data/spec/prawn/svg/css_spec.rb +24 -0
- data/spec/prawn/svg/document_spec.rb +48 -19
- data/spec/prawn/svg/elements/base_spec.rb +147 -0
- data/spec/prawn/svg/elements/gradient_spec.rb +61 -0
- data/spec/prawn/svg/elements/path_spec.rb +123 -0
- data/spec/prawn/svg/elements/style_spec.rb +23 -0
- data/spec/prawn/svg/elements/text_spec.rb +61 -0
- data/spec/prawn/svg/font_registry_spec.rb +54 -0
- data/spec/prawn/svg/font_spec.rb +1 -28
- data/spec/prawn/svg/interface_spec.rb +94 -0
- data/spec/prawn/svg/loaders/data_spec.rb +55 -0
- data/spec/prawn/svg/loaders/file_spec.rb +84 -0
- data/spec/prawn/svg/loaders/web_spec.rb +37 -0
- data/spec/prawn/svg/ttf_spec.rb +32 -0
- data/spec/prawn/svg/url_loader_spec.rb +112 -0
- data/spec/sample_images/mushroom-long.jpg +0 -0
- data/spec/sample_images/mushroom-wide.jpg +0 -0
- data/spec/sample_svg/cap_styles.svg +13 -0
- data/spec/sample_svg/display_none.svg +13 -0
- data/spec/sample_svg/gistfile1.svg +36 -0
- data/spec/sample_svg/gradients.svg +40 -0
- data/spec/sample_svg/hidden_paths.svg +6 -0
- data/spec/sample_svg/image01.svg +31 -31
- data/spec/sample_svg/image03.svg +30 -0
- data/spec/sample_svg/negminy.svg +25 -0
- data/spec/sample_svg/no_width_or_height.svg +4 -0
- data/spec/sample_svg/path.svg +5 -0
- data/spec/sample_svg/pie_piece.svg +7 -0
- data/spec/sample_svg/preserve-space.svg +19 -0
- data/spec/sample_svg/rect02.svg +8 -11
- data/spec/sample_svg/tspan03-cc.svg +21 -0
- data/spec/sample_svg/viewbox.svg +4 -0
- data/spec/sample_svg/viewport.svg +23 -0
- data/spec/sample_ttf/OpenSans-SemiboldItalic.ttf +0 -0
- data/spec/spec_helper.rb +24 -2
- metadata +150 -36
- data/lib/prawn/svg/element.rb +0 -237
- data/lib/prawn/svg/parser/image.rb +0 -134
- data/lib/prawn/svg/parser/path.rb +0 -374
- data/lib/prawn/svg/parser/text.rb +0 -66
- data/lib/prawn/svg/parser.rb +0 -233
- data/spec/lib/parser_spec.rb +0 -55
- data/spec/lib/path_spec.rb +0 -54
- data/spec/lib/svg_spec.rb +0 -47
- data/spec/prawn/svg/element_spec.rb +0 -36
- data/spec/prawn/svg/parser/text_spec.rb +0 -4
@@ -0,0 +1,128 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
|
3
|
+
describe Prawn::SVG::Calculators::DocumentSizing do
|
4
|
+
let(:attributes) do
|
5
|
+
{"width" => "150", "height" => "200", "viewBox" => "0 -30 300 800", "preserveAspectRatio" => "xMaxYMid meet"}
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:bounds) { [1200, 800] }
|
9
|
+
|
10
|
+
let(:sizing) { Prawn::SVG::Calculators::DocumentSizing.new(bounds, attributes) }
|
11
|
+
|
12
|
+
describe "#initialize" do
|
13
|
+
it "takes bounds and a set of attributes and calls set_from_attributes" do
|
14
|
+
expect(sizing.instance_variable_get :@bounds).to eq bounds
|
15
|
+
expect(sizing.instance_variable_get :@document_width).to eq "150"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#set_from_attributes" do
|
20
|
+
let(:sizing) { Prawn::SVG::Calculators::DocumentSizing.new(bounds) }
|
21
|
+
|
22
|
+
it "sets ivars from the passed-in attributes hash" do
|
23
|
+
sizing.set_from_attributes(attributes)
|
24
|
+
expect(sizing.instance_variable_get :@document_width).to eq "150"
|
25
|
+
expect(sizing.instance_variable_get :@document_height).to eq "200"
|
26
|
+
expect(sizing.instance_variable_get :@view_box).to eq "0 -30 300 800"
|
27
|
+
expect(sizing.instance_variable_get :@preserve_aspect_ratio).to eq "xMaxYMid meet"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#calculate" do
|
32
|
+
it "calculates the document sizing measurements for a given set of inputs" do
|
33
|
+
sizing.calculate
|
34
|
+
expect(sizing.x_offset).to eq -75 / 0.25
|
35
|
+
expect(sizing.y_offset).to eq -30
|
36
|
+
expect(sizing.x_scale).to eq 0.25
|
37
|
+
expect(sizing.y_scale).to eq 0.25
|
38
|
+
expect(sizing.viewport_width).to eq 300
|
39
|
+
expect(sizing.viewport_height).to eq 800
|
40
|
+
expect(sizing.output_width).to eq 150
|
41
|
+
expect(sizing.output_height).to eq 200
|
42
|
+
end
|
43
|
+
|
44
|
+
it "scales again based on requested width" do
|
45
|
+
sizing.requested_width = 75
|
46
|
+
sizing.calculate
|
47
|
+
expect(sizing.x_scale).to eq 0.125
|
48
|
+
expect(sizing.y_scale).to eq 0.125
|
49
|
+
expect(sizing.viewport_width).to eq 300
|
50
|
+
expect(sizing.viewport_height).to eq 800
|
51
|
+
expect(sizing.output_width).to eq 75
|
52
|
+
expect(sizing.output_height).to eq 100
|
53
|
+
end
|
54
|
+
|
55
|
+
it "scales again based on requested height" do
|
56
|
+
sizing.requested_height = 100
|
57
|
+
sizing.calculate
|
58
|
+
expect(sizing.x_scale).to eq 0.125
|
59
|
+
expect(sizing.y_scale).to eq 0.125
|
60
|
+
expect(sizing.viewport_width).to eq 300
|
61
|
+
expect(sizing.viewport_height).to eq 800
|
62
|
+
expect(sizing.output_width).to eq 75
|
63
|
+
expect(sizing.output_height).to eq 100
|
64
|
+
end
|
65
|
+
|
66
|
+
it "correctly handles % values being passed in" do
|
67
|
+
sizing.document_width = sizing.document_height = "50%"
|
68
|
+
sizing.calculate
|
69
|
+
expect(sizing.output_width).to eq 600
|
70
|
+
expect(sizing.output_height).to eq 400
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when SVG does not specify width and height" do
|
74
|
+
context "when a viewBox is specified" do
|
75
|
+
let(:attributes) { {"viewBox" => "0 0 100 200"} }
|
76
|
+
|
77
|
+
it "defaults to 100% width and the width times aspect ratio in height" do
|
78
|
+
sizing.calculate
|
79
|
+
expect(sizing.viewport_width).to eq 100
|
80
|
+
expect(sizing.viewport_height).to eq 200
|
81
|
+
expect(sizing.output_width).to eq 1200
|
82
|
+
expect(sizing.output_height).to eq 2400
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "when a requested width and height are supplied" do
|
87
|
+
let(:attributes) { {} }
|
88
|
+
|
89
|
+
it "uses the requested width and height" do
|
90
|
+
sizing.requested_width = 550
|
91
|
+
sizing.requested_height = 400
|
92
|
+
sizing.calculate
|
93
|
+
|
94
|
+
expect(sizing.viewport_width).to eq 550
|
95
|
+
expect(sizing.viewport_height).to eq 400
|
96
|
+
expect(sizing.output_width).to eq 550
|
97
|
+
expect(sizing.output_height).to eq 400
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "when a viewBox and a requested width/height are supplied" do
|
102
|
+
let(:attributes) { {"viewBox" => "0 0 100 200"} }
|
103
|
+
|
104
|
+
it "uses the requested width and height" do
|
105
|
+
sizing.requested_width = 550
|
106
|
+
sizing.requested_height = 400
|
107
|
+
sizing.calculate
|
108
|
+
|
109
|
+
expect(sizing.viewport_width).to eq 100
|
110
|
+
expect(sizing.viewport_height).to eq 200
|
111
|
+
expect(sizing.output_width).to eq 550
|
112
|
+
expect(sizing.output_height).to eq 400
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "when neither viewBox nor requested width/height specified" do
|
117
|
+
let(:attributes) { {} }
|
118
|
+
|
119
|
+
it "defaults to 300x150, because that's what HTML does" do
|
120
|
+
sizing.calculate
|
121
|
+
|
122
|
+
expect(sizing.output_width).to eq 300
|
123
|
+
expect(sizing.output_height).to eq 150
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -1,26 +1,61 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
2
|
|
3
|
-
describe Prawn::
|
4
|
-
describe
|
3
|
+
describe Prawn::SVG::Color do
|
4
|
+
describe "::color_to_hex" do
|
5
5
|
it "converts #xxx to a hex value" do
|
6
|
-
Prawn::
|
6
|
+
Prawn::SVG::Color.color_to_hex("#9ab").should == "99aabb"
|
7
7
|
end
|
8
8
|
|
9
9
|
it "converts #xxxxxx to a hex value" do
|
10
|
-
Prawn::
|
10
|
+
Prawn::SVG::Color.color_to_hex("#9ab123").should == "9ab123"
|
11
11
|
end
|
12
12
|
|
13
13
|
it "converts an html colour name to a hex value" do
|
14
|
-
Prawn::
|
14
|
+
Prawn::SVG::Color.color_to_hex("White").should == "ffffff"
|
15
15
|
end
|
16
16
|
|
17
17
|
it "converts an rgb string to a hex value" do
|
18
|
-
Prawn::
|
19
|
-
Prawn::
|
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"
|
20
20
|
end
|
21
21
|
|
22
22
|
it "scans the string and finds the first colour it can parse" do
|
23
|
-
Prawn::
|
23
|
+
Prawn::SVG::Color.color_to_hex("function(#someurl, 0) nonexistent rgb( 3 ,4,5 ) white").should == "030405"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "ignores url()s" do
|
27
|
+
expect(Prawn::SVG::Color.color_to_hex("url(#someplace) red")).to eq 'ff0000'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns black if the color doesn't exist" do
|
31
|
+
expect(Prawn::SVG::Color.color_to_hex("blurble")).to eq '000000'
|
32
|
+
end
|
33
|
+
|
34
|
+
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
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "::parse" do
|
40
|
+
let(:gradients) { {"flan" => flan_gradient, "drob" => drob_gradient} }
|
41
|
+
let(:flan_gradient) { double }
|
42
|
+
let(:drob_gradient) { double }
|
43
|
+
|
44
|
+
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)
|
46
|
+
expect(results).to eq [
|
47
|
+
flan_gradient,
|
48
|
+
Prawn::SVG::Color::Hex.new("008000"),
|
49
|
+
Prawn::SVG::Color::Hex.new("112233")
|
50
|
+
]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "appends black to the list if there aren't any url() references" do
|
54
|
+
results = Prawn::SVG::Color.parse("blurble green", gradients)
|
55
|
+
expect(results).to eq [
|
56
|
+
Prawn::SVG::Color::Hex.new("008000"),
|
57
|
+
Prawn::SVG::Color::Hex.new("000000")
|
58
|
+
]
|
24
59
|
end
|
25
60
|
end
|
26
61
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Prawn::SVG::CSS do
|
4
|
+
describe "#parse_font_family_string" do
|
5
|
+
it "correctly handles quotes and escaping" do
|
6
|
+
tests = {
|
7
|
+
"" => [],
|
8
|
+
"font" => ["font"],
|
9
|
+
"font name, other font" => ["font name", "other font"],
|
10
|
+
"'font name', other font" => ["font name", "other font"],
|
11
|
+
"'font, name', other font" => ["font, name", "other font"],
|
12
|
+
'"font name", other font' => ["font name", "other font"],
|
13
|
+
'"font, name", other font' => ["font, name", "other font"],
|
14
|
+
'weird \\" name' => ['weird " name'],
|
15
|
+
'weird\\, name' => ["weird, name"],
|
16
|
+
' stupid , spacing ' => ["stupid", "spacing"],
|
17
|
+
}
|
18
|
+
|
19
|
+
tests.each do |string, expected|
|
20
|
+
expect(Prawn::SVG::CSS.parse_font_family_string(string)).to eq expected
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,27 +1,56 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
2
|
|
3
|
-
describe Prawn::
|
4
|
-
|
5
|
-
|
3
|
+
describe Prawn::SVG::Document do
|
4
|
+
let(:bounds) { [100, 100] }
|
5
|
+
let(:options) { {} }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
context "when unparsable XML is provided" do
|
9
|
+
let(:svg) { "this isn't SVG data" }
|
10
|
+
|
11
|
+
it "raises an exception" do
|
12
|
+
expect {
|
13
|
+
Prawn::SVG::Document.new(svg, bounds, options)
|
14
|
+
}.to raise_error Prawn::SVG::Document::InvalidSVGData, "The data supplied is not a valid SVG document."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when the user passes in a filename instead of SVG data" do
|
19
|
+
let(:svg) { "some_file.svg" }
|
20
|
+
|
21
|
+
it "raises an exception letting them know what they've done" do
|
22
|
+
expect {
|
23
|
+
Prawn::SVG::Document.new(svg, bounds, options)
|
24
|
+
}.to raise_error Prawn::SVG::Document::InvalidSVGData, "The data supplied is not a valid SVG document. It looks like you've supplied a filename instead; use IO.read(filename) to get the data before you pass it to prawn-svg."
|
25
|
+
end
|
26
|
+
end
|
6
27
|
end
|
7
28
|
|
8
|
-
describe
|
29
|
+
describe "#points" do
|
30
|
+
before do
|
31
|
+
sizing = instance_double(Prawn::SVG::Calculators::DocumentSizing, viewport_width: 600, viewport_height: 400, viewport_diagonal: 500, :requested_width= => nil, :requested_height= => nil)
|
32
|
+
expect(sizing).to receive(:calculate)
|
33
|
+
expect(Prawn::SVG::Calculators::DocumentSizing).to receive(:new).and_return(sizing)
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:document) { Prawn::SVG::Document.new("<svg></svg>", [100, 100], {}) }
|
37
|
+
|
9
38
|
it "converts a variety of measurement units to points" do
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
39
|
+
document.send(:points, 32).should == 32.0
|
40
|
+
document.send(:points, 32.0).should == 32.0
|
41
|
+
document.send(:points, "32").should == 32.0
|
42
|
+
document.send(:points, "32unknown").should == 32.0
|
43
|
+
document.send(:points, "32pt").should == 32.0
|
44
|
+
document.send(:points, "32in").should == 32.0 * 72
|
45
|
+
document.send(:points, "32ft").should == 32.0 * 72 * 12
|
46
|
+
document.send(:points, "32pc").should == 32.0 * 15
|
47
|
+
document.send(:points, "32mm").should be_within(0.0001).of(32 * 72 * 0.0393700787)
|
48
|
+
document.send(:points, "32cm").should be_within(0.0001).of(32 * 72 * 0.393700787)
|
49
|
+
document.send(:points, "32m").should be_within(0.0001).of(32 * 72 * 39.3700787)
|
50
|
+
|
51
|
+
document.send(:points, "50%").should == 250
|
52
|
+
document.send(:points, "50%", :x).should == 300
|
53
|
+
document.send(:points, "50%", :y).should == 200
|
25
54
|
end
|
26
55
|
end
|
27
56
|
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Prawn::SVG::Elements::Base do
|
4
|
+
let(:svg) { "<svg></svg>" }
|
5
|
+
let(:document) { Prawn::SVG::Document.new(svg, [800, 600], {}, font_registry: Prawn::SVG::FontRegistry.new("Helvetica" => {:normal => nil})) }
|
6
|
+
let(:parent_calls) { [] }
|
7
|
+
let(:element) { Prawn::SVG::Elements::Base.new(document, document.root, parent_calls, Prawn::SVG::State.new) }
|
8
|
+
|
9
|
+
describe "#initialize" do
|
10
|
+
let(:svg) { '<something id="hello"/>' }
|
11
|
+
|
12
|
+
it "adds itself to the elements_by_id hash if an id attribute is supplied" do
|
13
|
+
element
|
14
|
+
expect(document.elements_by_id["hello"]).to eq element
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#process" do
|
19
|
+
it "calls #parse and #apply so subclasses can parse the element" do
|
20
|
+
expect(element).to receive(:parse).ordered
|
21
|
+
expect(element).to receive(:apply).ordered
|
22
|
+
element.process
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "applying calls from the standard attributes" do
|
26
|
+
let(:svg) do
|
27
|
+
<<-SVG
|
28
|
+
<something transform="rotate(90)" fill-opacity="0.5" fill="red" stroke="blue" stroke-width="5" font-family="Helvetica"/>
|
29
|
+
SVG
|
30
|
+
end
|
31
|
+
|
32
|
+
it "appends the relevant calls" do
|
33
|
+
element.process
|
34
|
+
expect(element.base_calls).to eq [
|
35
|
+
["rotate", [-90.0, {origin: [0, 150.0]}], [
|
36
|
+
["transparent", [0.5, 1], [
|
37
|
+
["fill_color", ["ff0000"], []],
|
38
|
+
["stroke_color", ["0000ff"], []],
|
39
|
+
["line_width", [5.0], []],
|
40
|
+
["font", ["Helvetica", {:style=>:normal}], [
|
41
|
+
["fill_and_stroke", [], []]
|
42
|
+
]]
|
43
|
+
]]
|
44
|
+
]]
|
45
|
+
]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "appends calls to the parent element" do
|
50
|
+
expect(element).to receive(:apply) do
|
51
|
+
element.send :add_call, "test", "argument"
|
52
|
+
end
|
53
|
+
|
54
|
+
element.process
|
55
|
+
expect(element.parent_calls).to eq [["fill", [], [["test", ["argument"], []]]]]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "quietly absorbs a SkipElementQuietly exception" do
|
59
|
+
expect(element).to receive(:parse).and_raise(Prawn::SVG::Elements::Base::SkipElementQuietly)
|
60
|
+
expect(element).to_not receive(:apply)
|
61
|
+
element.process
|
62
|
+
expect(document.warnings).to be_empty
|
63
|
+
end
|
64
|
+
|
65
|
+
it "absorbs a SkipElementError exception, logging a warning" do
|
66
|
+
expect(element).to receive(:parse).and_raise(Prawn::SVG::Elements::Base::SkipElementError, "hello")
|
67
|
+
expect(element).to_not receive(:apply)
|
68
|
+
element.process
|
69
|
+
expect(document.warnings).to eq ["hello"]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#parse_fill_and_stroke_attributes_and_call" do
|
74
|
+
before do
|
75
|
+
element.send(:combine_attributes_and_style_declarations)
|
76
|
+
end
|
77
|
+
|
78
|
+
subject { element.send :parse_fill_and_stroke_attributes_and_call }
|
79
|
+
|
80
|
+
it "doesn't change anything if no fill attribute provided" do
|
81
|
+
subject
|
82
|
+
expect(element.state.fill).to be true
|
83
|
+
|
84
|
+
element.state.fill = false
|
85
|
+
subject
|
86
|
+
expect(element.state.fill).to be false
|
87
|
+
end
|
88
|
+
|
89
|
+
it "doesn't change anything if 'inherit' fill attribute provided" do
|
90
|
+
element.attributes['fill'] = 'inherit'
|
91
|
+
subject
|
92
|
+
expect(element.state.fill).to be true
|
93
|
+
|
94
|
+
element.state.fill = false
|
95
|
+
subject
|
96
|
+
expect(element.state.fill).to be false
|
97
|
+
end
|
98
|
+
|
99
|
+
it "turns off filling if 'none' fill attribute provided" do
|
100
|
+
element.attributes['fill'] = 'none'
|
101
|
+
subject
|
102
|
+
expect(element.state.fill).to be false
|
103
|
+
end
|
104
|
+
|
105
|
+
it "uses the fill attribute's color" do
|
106
|
+
expect(element).to receive(:add_call).with('fill_color', 'ff0000')
|
107
|
+
element.attributes['fill'] = 'red'
|
108
|
+
subject
|
109
|
+
expect(element.state.fill).to be true
|
110
|
+
end
|
111
|
+
|
112
|
+
it "uses black if the fill attribute's color is unparseable" do
|
113
|
+
expect(element).to receive(:add_call).with('fill_color', '000000')
|
114
|
+
element.attributes['fill'] = 'blarble'
|
115
|
+
subject
|
116
|
+
expect(element.state.fill).to be true
|
117
|
+
end
|
118
|
+
|
119
|
+
it "uses the color attribute if 'currentColor' fill attribute provided" do
|
120
|
+
expect(element).to receive(:add_call).with('fill_color', 'ff0000')
|
121
|
+
element.attributes['fill'] = 'currentColor'
|
122
|
+
element.attributes['color'] = 'red'
|
123
|
+
element.send :parse_color_attribute
|
124
|
+
subject
|
125
|
+
expect(element.state.fill).to be true
|
126
|
+
end
|
127
|
+
|
128
|
+
context "with a color attribute defined on a parent element" do
|
129
|
+
let(:svg) { '<svg style="color: green;"><g style="color: red;"><rect width="10" height="10" style="fill: currentColor;"></rect></g></svg>' }
|
130
|
+
let(:element) { Prawn::SVG::Elements::Root.new(document, document.root, parent_calls) }
|
131
|
+
let(:flattened_calls) { flatten_calls(element.base_calls) }
|
132
|
+
|
133
|
+
it "uses the parent's color element if 'currentColor' fill attribute provided" do
|
134
|
+
element.process
|
135
|
+
|
136
|
+
expect(flattened_calls).to include ['fill_color', ['ff0000']]
|
137
|
+
expect(flattened_calls).not_to include ['fill_color', ['00ff00']]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
it "turns off filling if UnresolvableURLWithNoFallbackError is raised" do
|
142
|
+
element.attributes['fill'] = 'url()'
|
143
|
+
subject
|
144
|
+
expect(element.state.fill).to be false
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Prawn::SVG::Elements::Gradient do
|
4
|
+
let(:document) { Prawn::SVG::Document.new(svg, [800, 600], {width: 800, height: 600}) }
|
5
|
+
let(:element) { Prawn::SVG::Elements::Gradient.new(document, document.root, [], Prawn::SVG::State.new) }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(element).to receive(:assert_compatible_prawn_version)
|
9
|
+
element.process
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "object bounding box" do
|
13
|
+
let(:svg) do
|
14
|
+
<<-SVG
|
15
|
+
<linearGradient id="flag" x1="0" x2="0.2" y1="0" y2="1">
|
16
|
+
<stop offset="25%" stop-color="red"/>
|
17
|
+
<stop offset="50%" stop-color="white"/>
|
18
|
+
<stop offset="75%" stop-color="blue"/>
|
19
|
+
</linearGradient>
|
20
|
+
SVG
|
21
|
+
end
|
22
|
+
|
23
|
+
it "is stored in the document gradients table" do
|
24
|
+
expect(document.gradients["flag"]).to eq element
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns correct gradient arguments for an element" do
|
28
|
+
arguments = element.gradient_arguments(double(bounding_box: [100, 100, 200, 0]))
|
29
|
+
expect(arguments).to eq(
|
30
|
+
from: [100.0, 100.0],
|
31
|
+
to: [120.0, 0.0],
|
32
|
+
stops: [[0, "ff0000"], [0.25, "ff0000"], [0.5, "ffffff"], [0.75, "0000ff"], [1, "0000ff"]]
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns nil if the element doesn't have a bounding box" do
|
37
|
+
arguments = element.gradient_arguments(double(bounding_box: nil))
|
38
|
+
expect(arguments).to be nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "user space on use" do
|
43
|
+
let(:svg) do
|
44
|
+
<<-SVG
|
45
|
+
<linearGradient id="flag" gradientUnits="userSpaceOnUse" x1="100" y1="500" x2="200" y2="600">
|
46
|
+
<stop offset="0" stop-color="red"/>
|
47
|
+
<stop offset="1" stop-color="blue"/>
|
48
|
+
</linearGradient>
|
49
|
+
SVG
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns correct gradient arguments for an element" do
|
53
|
+
arguments = element.gradient_arguments(double)
|
54
|
+
expect(arguments).to eq(
|
55
|
+
from: [100.0, 100.0],
|
56
|
+
to: [200.0, 0.0],
|
57
|
+
stops: [[0, "ff0000"], [1, "0000ff"]]
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Prawn::SVG::Elements::Path do
|
4
|
+
let(:source) { double(name: "path", attributes: {}) }
|
5
|
+
let(:path) { Prawn::SVG::Elements::Path.new(nil, source, [], {}) }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(path).to receive(:attributes).and_return("d" => d)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "command parsing" do
|
12
|
+
context "with a valid path" do
|
13
|
+
let(:d) { "A12.34 -56.78 89B4 5 12-34 -.5.7+3 2.3e3 4e4 4e+4 c31,-2e-5C 6,7 T QX 0 Z" }
|
14
|
+
|
15
|
+
it "correctly parses" do
|
16
|
+
calls = []
|
17
|
+
path.stub(:run_path_command) {|*args| calls << args}
|
18
|
+
path.parse
|
19
|
+
|
20
|
+
calls.should == [
|
21
|
+
["A", [12.34, -56.78, 89]],
|
22
|
+
["B", [4, 5, 12, -34, -0.5, 0.7, 3, 2.3e3, 4e4, 4e4]],
|
23
|
+
["c", [31, -2e-5]],
|
24
|
+
["C", [6, 7]],
|
25
|
+
["T", []],
|
26
|
+
["Q", []],
|
27
|
+
["X", [0]],
|
28
|
+
["Z", []]
|
29
|
+
]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "with m and M commands" do
|
34
|
+
let(:d) { "M 1,2 3,4 m 5,6 7,8" }
|
35
|
+
|
36
|
+
it "treats subsequent points to m/M command as relative/absolute depending on command" do
|
37
|
+
[
|
38
|
+
["M", [1,2,3,4]],
|
39
|
+
["L", [3,4]],
|
40
|
+
["m", [5,6,7,8]],
|
41
|
+
["l", [7,8]]
|
42
|
+
].each do |args|
|
43
|
+
path.should_receive(:run_path_command).with(*args).and_call_original
|
44
|
+
end
|
45
|
+
|
46
|
+
path.parse
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with an empty path" do
|
51
|
+
let(:d) { "" }
|
52
|
+
|
53
|
+
it "correctly parses" do
|
54
|
+
path.should_not_receive(:run_path_command)
|
55
|
+
path.parse
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "with a path with invalid characters" do
|
60
|
+
let(:d) { "M 10 % 20" }
|
61
|
+
|
62
|
+
it "raises" do
|
63
|
+
expect { path.parse }.to raise_error(Prawn::SVG::Elements::Base::SkipElementError)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "with a path with numerical data before a command letter" do
|
68
|
+
let(:d) { "M 10 % 20" }
|
69
|
+
|
70
|
+
it "raises" do
|
71
|
+
expect { path.parse }.to raise_error(Prawn::SVG::Elements::Base::SkipElementError)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when given an A path" do
|
77
|
+
subject { path.parse; path.commands }
|
78
|
+
|
79
|
+
context "that is pretty normal" do
|
80
|
+
let(:d) { "M 100 200 A 10 10 0 0 1 200 200" }
|
81
|
+
|
82
|
+
it "uses bezier curves to approximate an arc path" do
|
83
|
+
expect(subject).to eq [
|
84
|
+
["move_to", [100.0, 200.0]],
|
85
|
+
["curve_to", [150.0, 150.0, 100.0, 172.57081148225683, 122.57081148225683, 150.0]],
|
86
|
+
["curve_to", [200.0, 200.0, 177.42918851774317, 150.0, 200.0, 172.57081148225683]]
|
87
|
+
]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "with an identical start and end point" do
|
92
|
+
let(:d) { "M 100 200 A 30 30 0 0 1 100 200" }
|
93
|
+
|
94
|
+
it "ignores the path" do
|
95
|
+
expect(subject).to eq [
|
96
|
+
["move_to", [100.0, 200.0]]
|
97
|
+
]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "with an rx of 0" do
|
102
|
+
let(:d) { "M 100 200 A 0 10 0 0 1 200 200" }
|
103
|
+
|
104
|
+
it "substitutes a line_to" do
|
105
|
+
expect(subject).to eq [
|
106
|
+
["move_to", [100.0, 200.0]],
|
107
|
+
["line_to", [200.0, 200.0]]
|
108
|
+
]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "with an ry of 0" do
|
113
|
+
let(:d) { "M 100 200 A 10 0 0 0 1 200 200" }
|
114
|
+
|
115
|
+
it "substitutes a line_to" do
|
116
|
+
expect(subject).to eq [
|
117
|
+
["move_to", [100.0, 200.0]],
|
118
|
+
["line_to", [200.0, 200.0]]
|
119
|
+
]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Prawn::SVG::Elements::Style do
|
4
|
+
let(:svg) do
|
5
|
+
<<-SVG
|
6
|
+
<style>a
|
7
|
+
before>
|
8
|
+
x <![CDATA[ y
|
9
|
+
inside <>>
|
10
|
+
k ]]> j
|
11
|
+
after
|
12
|
+
z</style>
|
13
|
+
SVG
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:document) { Prawn::SVG::Document.new(svg, [800, 600], {}) }
|
17
|
+
let(:element) { Prawn::SVG::Elements::Style.new(document, document.root, [], {}) }
|
18
|
+
|
19
|
+
it "correctly collects the style information in a <style> tag" do
|
20
|
+
expect(document.css_parser).to receive(:add_block!).with("a\n before>\n x y\n inside <>>\n k j\n after\nz")
|
21
|
+
element.process
|
22
|
+
end
|
23
|
+
end
|