prawn-svg 0.27.1 → 0.31.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +6 -4
- data/LICENSE +1 -1
- data/README.md +23 -9
- data/lib/prawn-svg.rb +7 -1
- data/lib/prawn/svg/attributes/opacity.rb +4 -4
- data/lib/prawn/svg/attributes/transform.rb +2 -44
- data/lib/prawn/svg/calculators/document_sizing.rb +2 -2
- data/lib/prawn/svg/{css.rb → css/font_family_parser.rb} +3 -4
- data/lib/prawn/svg/css/selector_parser.rb +174 -0
- data/lib/prawn/svg/css/stylesheets.rb +146 -0
- data/lib/prawn/svg/document.rb +3 -15
- data/lib/prawn/svg/elements.rb +4 -2
- data/lib/prawn/svg/elements/base.rb +26 -23
- data/lib/prawn/svg/elements/clip_path.rb +12 -0
- data/lib/prawn/svg/elements/container.rb +1 -3
- data/lib/prawn/svg/elements/gradient.rb +83 -25
- data/lib/prawn/svg/elements/image.rb +2 -2
- data/lib/prawn/svg/elements/path.rb +42 -29
- data/lib/prawn/svg/elements/root.rb +4 -1
- data/lib/prawn/svg/elements/text.rb +1 -1
- data/lib/prawn/svg/elements/text_component.rb +63 -14
- data/lib/prawn/svg/elements/use.rb +23 -7
- data/lib/prawn/svg/extensions/additional_gradient_transforms.rb +23 -0
- data/lib/prawn/svg/font_registry.rb +4 -3
- data/lib/prawn/svg/interface.rb +26 -2
- data/lib/prawn/svg/loaders/data.rb +1 -1
- data/lib/prawn/svg/loaders/file.rb +4 -2
- data/lib/prawn/svg/properties.rb +2 -0
- data/lib/prawn/svg/state.rb +6 -3
- data/lib/prawn/svg/transform_parser.rb +72 -0
- data/lib/prawn/svg/version.rb +1 -1
- data/prawn-svg.gemspec +3 -4
- data/spec/prawn/svg/attributes/opacity_spec.rb +85 -0
- data/spec/prawn/svg/attributes/transform_spec.rb +30 -35
- data/spec/prawn/svg/calculators/document_sizing_spec.rb +4 -4
- data/spec/prawn/svg/{css_spec.rb → css/font_family_parser_spec.rb} +3 -3
- data/spec/prawn/svg/css/selector_parser_spec.rb +33 -0
- data/spec/prawn/svg/css/stylesheets_spec.rb +142 -0
- data/spec/prawn/svg/document_spec.rb +0 -33
- data/spec/prawn/svg/elements/base_spec.rb +58 -2
- data/spec/prawn/svg/elements/gradient_spec.rb +79 -4
- data/spec/prawn/svg/elements/path_spec.rb +29 -17
- data/spec/prawn/svg/elements/text_spec.rb +74 -16
- data/spec/prawn/svg/font_registry_spec.rb +30 -0
- data/spec/prawn/svg/interface_spec.rb +33 -1
- data/spec/prawn/svg/loaders/data_spec.rb +8 -0
- data/spec/prawn/svg/transform_parser_spec.rb +94 -0
- data/spec/sample_output/{directory → .keep} +0 -0
- data/spec/sample_svg/double_opacity.svg +6 -0
- data/spec/sample_svg/gradient_transform.svg +19 -0
- data/spec/sample_svg/links.svg +18 -0
- data/spec/sample_svg/radgrad01-bounding.svg +26 -0
- data/spec/sample_svg/radgrad01.svg +26 -0
- data/spec/sample_svg/svg_fill.svg +5 -0
- data/spec/sample_svg/text-decoration.svg +4 -0
- data/spec/sample_svg/text_stroke.svg +41 -0
- data/spec/sample_svg/transform.svg +20 -0
- data/spec/sample_svg/use_disordered.svg +17 -0
- data/spec/sample_svg/warning-radioactive.svg +98 -0
- data/spec/spec_helper.rb +2 -2
- metadata +137 -15
data/prawn-svg.gemspec
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
require File.expand_path('../lib/prawn/svg/version', __FILE__)
|
3
3
|
|
4
|
-
|
4
|
+
Gem::Specification.new do |gem|
|
5
5
|
gem.name = 'prawn-svg'
|
6
6
|
gem.version = Prawn::SVG::VERSION
|
7
7
|
gem.summary = "SVG renderer for Prawn PDF library"
|
8
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.has_rdoc = false
|
10
9
|
gem.author = "Roger Nesbitt"
|
11
10
|
gem.email = "roger@seriousorange.com"
|
12
11
|
gem.homepage = "http://github.com/mogest/prawn-svg"
|
@@ -21,7 +20,7 @@ spec = Gem::Specification.new do |gem|
|
|
21
20
|
gem.required_ruby_version = '>= 2.1.0'
|
22
21
|
|
23
22
|
gem.add_runtime_dependency "prawn", ">= 0.11.1", "< 3"
|
24
|
-
gem.add_runtime_dependency "css_parser", "~> 1.
|
23
|
+
gem.add_runtime_dependency "css_parser", "~> 1.6"
|
25
24
|
gem.add_development_dependency "rspec", "~> 3.0"
|
26
|
-
gem.add_development_dependency "rake", "~>
|
25
|
+
gem.add_development_dependency "rake", "~> 13.0"
|
27
26
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Prawn::SVG::Attributes::Opacity do
|
4
|
+
class OpacityTestElement
|
5
|
+
include Prawn::SVG::Attributes::Opacity
|
6
|
+
|
7
|
+
attr_accessor :properties, :state
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@properties = ::Prawn::SVG::Properties.new
|
11
|
+
@state = ::Prawn::SVG::State.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def clamp(value, min_value, max_value)
|
15
|
+
[[value, min_value].max, max_value].min
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:element) { OpacityTestElement.new }
|
20
|
+
|
21
|
+
describe "#parse_opacity_attributes_and_call" do
|
22
|
+
subject { element.parse_opacity_attributes_and_call }
|
23
|
+
|
24
|
+
context "with no opacity specified" do
|
25
|
+
it "does nothing" do
|
26
|
+
expect(element).not_to receive(:add_call_and_enter)
|
27
|
+
subject
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "with opacity" do
|
32
|
+
it "sets fill and stroke opacity" do
|
33
|
+
element.properties.opacity = '0.4'
|
34
|
+
|
35
|
+
expect(element).to receive(:add_call_and_enter).with('transparent', 0.4, 0.4)
|
36
|
+
subject
|
37
|
+
|
38
|
+
expect(element.state.fill_opacity).to eq 0.4
|
39
|
+
expect(element.state.stroke_opacity).to eq 0.4
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "with just fill opacity" do
|
44
|
+
it "sets fill opacity and sets stroke opacity to 1" do
|
45
|
+
element.properties.fill_opacity = '0.4'
|
46
|
+
|
47
|
+
expect(element).to receive(:add_call_and_enter).with('transparent', 0.4, 1)
|
48
|
+
subject
|
49
|
+
|
50
|
+
expect(element.state.fill_opacity).to eq 0.4
|
51
|
+
expect(element.state.stroke_opacity).to eq 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "with an existing fill/stroke opacity" do
|
56
|
+
it "multiplies the new opacity by the old" do
|
57
|
+
element.state.fill_opacity = 0.5
|
58
|
+
element.state.stroke_opacity = 0.8
|
59
|
+
|
60
|
+
element.properties.fill_opacity = '0.4'
|
61
|
+
element.properties.stroke_opacity = '0.5'
|
62
|
+
|
63
|
+
expect(element).to receive(:add_call_and_enter).with('transparent', 0.2, 0.4)
|
64
|
+
subject
|
65
|
+
|
66
|
+
expect(element.state.fill_opacity).to eq 0.2
|
67
|
+
expect(element.state.stroke_opacity).to eq 0.4
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "with stroke, fill, and opacity all specified" do
|
72
|
+
it "choses the lower of them" do
|
73
|
+
element.properties.fill_opacity = '0.4'
|
74
|
+
element.properties.stroke_opacity = '0.6'
|
75
|
+
element.properties.opacity = '0.5'
|
76
|
+
|
77
|
+
expect(element).to receive(:add_call_and_enter).with('transparent', 0.4, 0.5)
|
78
|
+
subject
|
79
|
+
|
80
|
+
expect(element.state.fill_opacity).to eq 0.4
|
81
|
+
expect(element.state.stroke_opacity).to eq 0.5
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -14,43 +14,38 @@ describe Prawn::SVG::Attributes::Transform do
|
|
14
14
|
|
15
15
|
let(:element) { TransformTestElement.new }
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
17
|
+
subject { element.send :parse_transform_attribute_and_call }
|
18
|
+
|
19
|
+
context "when a non-identity matrix is requested" do
|
20
|
+
let(:transform) { 'translate(-5.5)' }
|
21
|
+
|
22
|
+
it "passes the transform and executes the returned matrix" do
|
23
|
+
expect(element).to receive(:parse_transform_attribute).with(transform).and_return([1, 2, 3, 4, 5, 6])
|
24
|
+
expect(element).to receive(:add_call_and_enter).with('transformation_matrix', 1, 2, 3, 4, 5, 6)
|
25
|
+
|
26
|
+
element.attributes['transform'] = transform
|
27
|
+
subject
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when an identity matrix is requested" do
|
32
|
+
let(:transform) { 'translate(0)' }
|
33
|
+
|
34
|
+
it "does not execute any commands" do
|
35
|
+
expect(element).to receive(:parse_transform_attribute).with(transform).and_return([1, 0, 0, 1, 0, 0])
|
36
|
+
expect(element).not_to receive(:add_call_and_enter)
|
37
|
+
|
38
|
+
element.attributes['transform'] = transform
|
39
|
+
subject
|
29
40
|
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when transform is blank" do
|
44
|
+
it "does nothing" do
|
45
|
+
expect(element).not_to receive(:parse_transform_attribute)
|
46
|
+
expect(element).not_to receive(:add_call_and_enter)
|
30
47
|
|
31
|
-
|
32
|
-
it "handles a single angle argument" do
|
33
|
-
expect(element).to receive(:add_call_and_enter).with('rotate', -5.5, :origin => [0, 0])
|
34
|
-
expect(element).to receive(:y).with('0').and_return(0)
|
35
|
-
|
36
|
-
element.attributes['transform'] = 'rotate(5.5)'
|
37
|
-
subject
|
38
|
-
end
|
39
|
-
|
40
|
-
it "handles three arguments" do
|
41
|
-
expect(element).to receive(:add_call_and_enter).with('rotate', -5.5, :origin => [1.0, 2.0])
|
42
|
-
expect(element).to receive(:x).with(1.0).and_return(1.0)
|
43
|
-
expect(element).to receive(:y).with(2.0).and_return(2.0)
|
44
|
-
|
45
|
-
element.attributes['transform'] = 'rotate(5.5 1 2)'
|
46
|
-
subject
|
47
|
-
end
|
48
|
-
|
49
|
-
it "does nothing and warns if two arguments" do
|
50
|
-
expect(element).to receive(:warnings).and_return([])
|
51
|
-
element.attributes['transform'] = 'rotate(5.5 1)'
|
52
|
-
subject
|
53
|
-
end
|
48
|
+
subject
|
54
49
|
end
|
55
50
|
end
|
56
51
|
end
|
@@ -31,8 +31,8 @@ describe Prawn::SVG::Calculators::DocumentSizing do
|
|
31
31
|
describe "#calculate" do
|
32
32
|
it "calculates the document sizing measurements for a given set of inputs" do
|
33
33
|
sizing.calculate
|
34
|
-
expect(sizing.x_offset).to eq
|
35
|
-
expect(sizing.y_offset).to eq
|
34
|
+
expect(sizing.x_offset).to eq(-75 / 0.25)
|
35
|
+
expect(sizing.y_offset).to eq(-30)
|
36
36
|
expect(sizing.x_scale).to eq 0.25
|
37
37
|
expect(sizing.y_scale).to eq 0.25
|
38
38
|
expect(sizing.viewport_width).to eq 300
|
@@ -74,12 +74,12 @@ describe Prawn::SVG::Calculators::DocumentSizing do
|
|
74
74
|
context "when a viewBox is specified" do
|
75
75
|
let(:attributes) { {"viewBox" => "0 0 100 200"} }
|
76
76
|
|
77
|
-
it "defaults to 100% width and height" do
|
77
|
+
it "defaults to 100% width and uses the viewbox ratio for height" do
|
78
78
|
sizing.calculate
|
79
79
|
expect(sizing.viewport_width).to eq 100
|
80
80
|
expect(sizing.viewport_height).to eq 200
|
81
81
|
expect(sizing.output_width).to eq 1200
|
82
|
-
expect(sizing.output_height).to eq
|
82
|
+
expect(sizing.output_height).to eq 2400
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
RSpec.describe Prawn::SVG::CSS do
|
4
|
-
describe "#
|
3
|
+
RSpec.describe Prawn::SVG::CSS::FontFamilyParser do
|
4
|
+
describe "#parse" do
|
5
5
|
it "correctly handles quotes and escaping" do
|
6
6
|
tests = {
|
7
7
|
"" => [],
|
@@ -17,7 +17,7 @@ RSpec.describe Prawn::SVG::CSS do
|
|
17
17
|
}
|
18
18
|
|
19
19
|
tests.each do |string, expected|
|
20
|
-
expect(Prawn::SVG::CSS.
|
20
|
+
expect(Prawn::SVG::CSS::FontFamilyParser.parse(string)).to eq expected
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Prawn::SVG::CSS::SelectorParser do
|
4
|
+
describe "::parse" do
|
5
|
+
it "parses a simple selector" do
|
6
|
+
expect(described_class.parse("div")).to eq [{name: "div"}]
|
7
|
+
expect(described_class.parse(".c1")).to eq [{class: ["c1"]}]
|
8
|
+
end
|
9
|
+
|
10
|
+
it "parses a complex selector" do
|
11
|
+
result = described_class.parse("div#count .c1.c2 > span.large + div~.other:first-child *:nth-child(3)")
|
12
|
+
expect(result).to eq [
|
13
|
+
{name: "div", id: ["count"]},
|
14
|
+
{combinator: :descendant, class: ["c1", "c2"]},
|
15
|
+
{combinator: :child, name: "span", class: ["large"]},
|
16
|
+
{combinator: :adjacent, name: "div"},
|
17
|
+
{combinator: :siblings, class: ["other"], pseudo_class: ["first-child"]},
|
18
|
+
{combinator: :descendant, name: "*", pseudo_class: ["nth-child(3)"]},
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "parses attributes" do
|
23
|
+
expect(described_class.parse("[abc]")).to eq [{attribute: [["abc", nil, nil]]}]
|
24
|
+
expect(described_class.parse("[abc=123]")).to eq [{attribute: [["abc", '=', '123']]}]
|
25
|
+
expect(described_class.parse("[abc^=123]")).to eq [{attribute: [["abc", '^=', '123']]}]
|
26
|
+
expect(described_class.parse("[ abc ^= 123 ]")).to eq [{attribute: [["abc", '^=', '123']]}]
|
27
|
+
expect(described_class.parse("[abc^='123']")).to eq [{attribute: [["abc", '^=', '123']]}]
|
28
|
+
expect(described_class.parse("[abc^= '123' ]")).to eq [{attribute: [["abc", '^=', '123']]}]
|
29
|
+
expect(described_class.parse("[abc^= '123\\'456' ]")).to eq [{attribute: [["abc", '^=', '123\'456']]}]
|
30
|
+
expect(described_class.parse('[abc^= "123\\"456" ]')).to eq [{attribute: [["abc", '^=', '123"456']]}]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Prawn::SVG::CSS::Stylesheets do
|
4
|
+
describe "typical usage" do
|
5
|
+
let(:svg) { <<-SVG }
|
6
|
+
<svg>
|
7
|
+
<style>
|
8
|
+
#inner rect { fill: #0000ff; }
|
9
|
+
#outer { fill: #220000; }
|
10
|
+
.hero > rect { fill: #00ff00; }
|
11
|
+
rect { fill: #ff0000; }
|
12
|
+
rect ~ rect { fill: #330000; }
|
13
|
+
rect + rect { fill: #440000; }
|
14
|
+
rect:first-child:last-child { fill: #441234; }
|
15
|
+
|
16
|
+
circle:first-child { fill: #550000; }
|
17
|
+
circle:nth-child(2) { fill: #660000; }
|
18
|
+
circle:last-child { fill: #770000; }
|
19
|
+
|
20
|
+
square[chocolate] { fill: #880000; }
|
21
|
+
square[abc=def] { fill: #990000; }
|
22
|
+
square[abc^=ghi] { fill: #aa0000; }
|
23
|
+
square[abc$=jkl] { fill: #bb0000; }
|
24
|
+
square[abc*=mno] { fill: #cc0000; }
|
25
|
+
square[abc~=pqr] { fill: #dd0000; }
|
26
|
+
square[abc|=stu] { fill: #ee0000; }
|
27
|
+
</style>
|
28
|
+
|
29
|
+
<rect width="1" height="1" />
|
30
|
+
<rect width="2" height="2" id="outer" />
|
31
|
+
|
32
|
+
<g class="hero large">
|
33
|
+
<rect width="3" height="3" />
|
34
|
+
<rect width="4" height="4" style="fill: #777777;" />
|
35
|
+
<rect width="5" height="5" />
|
36
|
+
|
37
|
+
<g id="inner">
|
38
|
+
<rect width="6" height="6" />
|
39
|
+
</g>
|
40
|
+
|
41
|
+
<circle width="100" />
|
42
|
+
|
43
|
+
<g id="circles">
|
44
|
+
<circle width="7" />
|
45
|
+
<circle width="8" />
|
46
|
+
<circle width="9" />
|
47
|
+
</g>
|
48
|
+
</g>
|
49
|
+
|
50
|
+
<square width="10" chocolate="hi there" />
|
51
|
+
<square width="11" abc="def" />
|
52
|
+
<square width="12" abc="ghidef" />
|
53
|
+
<square width="13" abc="aghidefjkl" />
|
54
|
+
<square width="14" abc="agmnohidefjklx" />
|
55
|
+
<square width="15" abc="aeo cnj pqr" />
|
56
|
+
<square width="16" abc="eij-stu-asd" />
|
57
|
+
</svg>
|
58
|
+
SVG
|
59
|
+
|
60
|
+
it "associates styles with elements" do
|
61
|
+
result = Prawn::SVG::CSS::Stylesheets.new(CssParser::Parser.new, REXML::Document.new(svg)).load
|
62
|
+
width_and_styles = result.map { |k, v| [k.attributes["width"].to_i, v] }.sort_by(&:first)
|
63
|
+
|
64
|
+
expected = [
|
65
|
+
[1, [["fill", "#ff0000", false]]],
|
66
|
+
[2, [["fill", "#ff0000", false], ["fill", "#330000", false], ["fill", "#440000", false], ["fill", "#220000", false]]],
|
67
|
+
[3, [["fill", "#ff0000", false], ["fill", "#00ff00", false]]],
|
68
|
+
[4, [["fill", "#ff0000", false], ["fill", "#330000", false], ["fill", "#440000", false], ["fill", "#00ff00", false]]],
|
69
|
+
]
|
70
|
+
|
71
|
+
#
|
72
|
+
# Under ruby < 2.6, a bug in REXML causes the /following-sibling selector to
|
73
|
+
# only pick the first matching sibling. This means the + CSS combinator behaves
|
74
|
+
# incorrectly in the following example:
|
75
|
+
#
|
76
|
+
# <a>
|
77
|
+
# <b a="1" />
|
78
|
+
# <b a="2" />
|
79
|
+
# <b a="3" />
|
80
|
+
# </a>
|
81
|
+
#
|
82
|
+
# The css selector `a b + b` will only pick the second <b>, whereas it should
|
83
|
+
# pick both the second and third <b> elements.
|
84
|
+
#
|
85
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.6.0')
|
86
|
+
expected << [5, [["fill", "#ff0000", false], ["fill", "#330000", false], ["fill", "#330000", false], ["fill", "#00ff00", false]]]
|
87
|
+
else
|
88
|
+
expected << [5, [["fill", "#ff0000", false], ["fill", "#330000", false], ["fill", "#330000", false], ["fill", "#440000", false], ["fill", "#00ff00", false]]]
|
89
|
+
end
|
90
|
+
|
91
|
+
expected.concat [
|
92
|
+
[6, [["fill", "#ff0000", false], ["fill", "#441234", false], ["fill", "#0000ff", false]]],
|
93
|
+
[7, [["fill", "#550000", false]]],
|
94
|
+
[8, [["fill", "#660000", false]]],
|
95
|
+
[9, [["fill", "#770000", false]]],
|
96
|
+
[10, [["fill", "#880000", false]]],
|
97
|
+
[11, [["fill", "#990000", false]]],
|
98
|
+
[12, [["fill", "#aa0000", false]]],
|
99
|
+
[13, [["fill", "#bb0000", false]]],
|
100
|
+
[14, [["fill", "#cc0000", false]]],
|
101
|
+
[15, [["fill", "#dd0000", false]]],
|
102
|
+
[16, [["fill", "#ee0000", false]]],
|
103
|
+
]
|
104
|
+
|
105
|
+
expect(width_and_styles).to eq(expected)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "style tag parsing" do
|
110
|
+
let(:svg) do
|
111
|
+
<<-SVG
|
112
|
+
<svg>
|
113
|
+
<some-tag>
|
114
|
+
<style>a
|
115
|
+
before>
|
116
|
+
x <![CDATA[ y
|
117
|
+
inside <>>
|
118
|
+
k ]]> j
|
119
|
+
after
|
120
|
+
z</style>
|
121
|
+
</some-tag>
|
122
|
+
|
123
|
+
<other-tag>
|
124
|
+
<more-tag>
|
125
|
+
<style>hello</style>
|
126
|
+
</more-tag>
|
127
|
+
</other-tag>
|
128
|
+
</svg>
|
129
|
+
SVG
|
130
|
+
end
|
131
|
+
|
132
|
+
it "scans the document for style tags and adds the style information to the css parser" do
|
133
|
+
css_parser = instance_double(CssParser::Parser)
|
134
|
+
|
135
|
+
expect(css_parser).to receive(:add_block!).with("a\n before>\n x y\n inside <>>\n k j\n after\nz")
|
136
|
+
expect(css_parser).to receive(:add_block!).with("hello")
|
137
|
+
allow(css_parser).to receive(:each_rule_set)
|
138
|
+
|
139
|
+
Prawn::SVG::CSS::Stylesheets.new(css_parser, REXML::Document.new(svg)).load
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -25,37 +25,4 @@ describe Prawn::SVG::Document do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
29
|
-
describe "#parse_style_elements" do
|
30
|
-
let(:svg) do
|
31
|
-
<<-SVG
|
32
|
-
<svg>
|
33
|
-
<some-tag>
|
34
|
-
<style>a
|
35
|
-
before>
|
36
|
-
x <![CDATA[ y
|
37
|
-
inside <>>
|
38
|
-
k ]]> j
|
39
|
-
after
|
40
|
-
z</style>
|
41
|
-
</some-tag>
|
42
|
-
|
43
|
-
<other-tag>
|
44
|
-
<more-tag>
|
45
|
-
<style>hello</style>
|
46
|
-
</more-tag>
|
47
|
-
</other-tag>
|
48
|
-
</svg>
|
49
|
-
SVG
|
50
|
-
end
|
51
|
-
|
52
|
-
it "scans the document for style tags and adds the style information to the css parser" do
|
53
|
-
css_parser = instance_double(CssParser::Parser)
|
54
|
-
|
55
|
-
expect(css_parser).to receive(:add_block!).with("a\n before>\n x y\n inside <>>\n k j\n after\nz")
|
56
|
-
expect(css_parser).to receive(:add_block!).with("hello")
|
57
|
-
|
58
|
-
Prawn::SVG::Document.new(svg, bounds, options, css_parser: css_parser)
|
59
|
-
end
|
60
|
-
end
|
61
28
|
end
|
@@ -25,14 +25,14 @@ describe Prawn::SVG::Elements::Base do
|
|
25
25
|
describe "applying calls from the standard attributes" do
|
26
26
|
let(:svg) do
|
27
27
|
<<-SVG
|
28
|
-
<something transform="
|
28
|
+
<something transform="scale(2)" fill-opacity="0.5" fill="red" stroke="blue" stroke-width="5"/>
|
29
29
|
SVG
|
30
30
|
end
|
31
31
|
|
32
32
|
it "appends the relevant calls" do
|
33
33
|
element.process
|
34
34
|
expect(element.base_calls).to eq [
|
35
|
-
["
|
35
|
+
["transformation_matrix", [2, 0, 0, 2, 0, 0], [
|
36
36
|
["transparent", [0.5, 1], [
|
37
37
|
["fill_color", ["ff0000"], []],
|
38
38
|
["stroke_color", ["0000ff"], []],
|
@@ -44,6 +44,36 @@ describe Prawn::SVG::Elements::Base do
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
describe "fills and strokes" do
|
48
|
+
before { element.process }
|
49
|
+
subject { element.base_calls.last }
|
50
|
+
|
51
|
+
context "with neither fill nor stroke" do
|
52
|
+
let(:svg) { '<rect style="fill: none;"></rect>' }
|
53
|
+
it { is_expected.to eq ['end_path', [], []] }
|
54
|
+
end
|
55
|
+
|
56
|
+
context "with a fill only" do
|
57
|
+
let(:svg) { '<rect style="fill: black;"></rect>' }
|
58
|
+
it { is_expected.to eq ['fill', [], []] }
|
59
|
+
end
|
60
|
+
|
61
|
+
context "with a stroke only" do
|
62
|
+
let(:svg) { '<rect style="fill: none; stroke: black;"></rect>' }
|
63
|
+
it { is_expected.to eq ['stroke', [], []] }
|
64
|
+
end
|
65
|
+
|
66
|
+
context "with fill and stroke" do
|
67
|
+
let(:svg) { '<rect style="fill: black; stroke: black;"></rect>' }
|
68
|
+
it { is_expected.to eq ['fill_and_stroke', [], []] }
|
69
|
+
end
|
70
|
+
|
71
|
+
context "with fill with evenodd fill rule" do
|
72
|
+
let(:svg) { '<rect style="fill: black; fill-rule: evenodd;"></rect>' }
|
73
|
+
it { is_expected.to eq ['fill', [{fill_rule: :even_odd}], []] }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
47
77
|
it "appends calls to the parent element" do
|
48
78
|
expect(element).to receive(:apply) do
|
49
79
|
element.send :add_call, "test", "argument"
|
@@ -131,4 +161,30 @@ describe Prawn::SVG::Elements::Base do
|
|
131
161
|
expect(element.computed_properties.fill).to eq 'none'
|
132
162
|
end
|
133
163
|
end
|
164
|
+
|
165
|
+
describe "stylesheets" do
|
166
|
+
let(:svg) { <<-SVG }
|
167
|
+
<svg>
|
168
|
+
<style>
|
169
|
+
.special rect { fill: green; }
|
170
|
+
rect { fill: red; }
|
171
|
+
</style>
|
172
|
+
<rect width="100" height="100"></rect>
|
173
|
+
<g class="special">
|
174
|
+
<rect width="100" height="100"></rect>
|
175
|
+
<rect width="100" height="100" style="fill: yellow;"></rect>
|
176
|
+
</g>
|
177
|
+
</svg>
|
178
|
+
SVG
|
179
|
+
|
180
|
+
it "applies stylesheet styling but style attributes take precedence" do
|
181
|
+
document = Prawn::SVG::Document.new(svg, [100, 100], {})
|
182
|
+
calls = []
|
183
|
+
element = Prawn::SVG::Elements::Root.new(document, document.root, calls)
|
184
|
+
element.process
|
185
|
+
|
186
|
+
fill_colors = calls.select { |cmd, _, _| cmd == 'fill_color' }.map { |_, args, _| args.first }
|
187
|
+
expect(fill_colors).to eq ['000000', 'ff0000', '008000', 'ffff00']
|
188
|
+
end
|
189
|
+
end
|
134
190
|
end
|