prawn-svg 0.27.1 → 0.28.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -2
- data/lib/prawn-svg.rb +3 -1
- 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 +125 -0
- data/lib/prawn/svg/document.rb +3 -15
- data/lib/prawn/svg/elements.rb +2 -2
- data/lib/prawn/svg/elements/base.rb +20 -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/text.rb +1 -1
- data/lib/prawn/svg/elements/text_component.rb +46 -13
- data/lib/prawn/svg/font_registry.rb +4 -3
- data/lib/prawn/svg/interface.rb +3 -1
- data/lib/prawn/svg/properties.rb +1 -0
- data/lib/prawn/svg/state.rb +6 -3
- data/lib/prawn/svg/version.rb +1 -1
- data/prawn-svg.gemspec +2 -2
- 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 +113 -0
- data/spec/prawn/svg/document_spec.rb +0 -33
- data/spec/prawn/svg/elements/base_spec.rb +56 -0
- data/spec/prawn/svg/elements/text_spec.rb +59 -16
- data/spec/prawn/svg/font_registry_spec.rb +30 -0
- data/spec/prawn/svg/interface_spec.rb +33 -1
- data/spec/sample_output/{directory → .keep} +0 -0
- data/spec/sample_svg/text_stroke.svg +41 -0
- data/spec/sample_svg/warning-radioactive.svg +98 -0
- data/spec/spec_helper.rb +2 -2
- metadata +108 -9
@@ -1,9 +1,7 @@
|
|
1
1
|
class Prawn::SVG::Elements::Container < Prawn::SVG::Elements::Base
|
2
2
|
def parse
|
3
|
-
state.disable_drawing = true if name == 'clipPath'
|
4
|
-
|
5
3
|
set_display_none if name == 'symbol' && !state.inside_use
|
6
|
-
set_display_none if
|
4
|
+
set_display_none if name == 'defs'
|
7
5
|
end
|
8
6
|
|
9
7
|
def container?
|
@@ -7,7 +7,7 @@ class Prawn::SVG::Elements::Text < Prawn::SVG::Elements::DepthFirstBase
|
|
7
7
|
# of the element, delegating it to our root text component.
|
8
8
|
|
9
9
|
def parse_step
|
10
|
-
state.text = Prawn::SVG::Elements::TextComponent::
|
10
|
+
state.text = Prawn::SVG::Elements::TextComponent::TextState.new
|
11
11
|
|
12
12
|
@text_root = Prawn::SVG::Elements::TextComponent.new(document, source, nil, state.dup)
|
13
13
|
@text_root.parse_step
|
@@ -2,14 +2,20 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
2
2
|
attr_reader :commands
|
3
3
|
|
4
4
|
Printable = Struct.new(:element, :text, :leading_space?, :trailing_space?)
|
5
|
-
|
5
|
+
TextState = Struct.new(:parent, :x, :y, :dx, :dy, :rotation, :spacing, :mode)
|
6
6
|
|
7
7
|
def parse
|
8
|
-
state.
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
state.text.
|
8
|
+
if state.inside_clip_path
|
9
|
+
raise SkipElementError, "<text> elements are not supported in clip paths"
|
10
|
+
end
|
11
|
+
|
12
|
+
state.text.x = (attributes['x'] || "").split(COMMA_WSP_REGEXP).collect { |n| x(n) }
|
13
|
+
state.text.y = (attributes['y'] || "").split(COMMA_WSP_REGEXP).collect { |n| y(n) }
|
14
|
+
state.text.dx = (attributes['dx'] || "").split(COMMA_WSP_REGEXP).collect { |n| x_pixels(n) }
|
15
|
+
state.text.dy = (attributes['dy'] || "").split(COMMA_WSP_REGEXP).collect { |n| y_pixels(n) }
|
16
|
+
state.text.rotation = (attributes['rotate'] || "").split(COMMA_WSP_REGEXP).collect(&:to_f)
|
17
|
+
state.text.spacing = calculate_character_spacing
|
18
|
+
state.text.mode = calculate_text_rendering_mode
|
13
19
|
|
14
20
|
@commands = []
|
15
21
|
|
@@ -41,10 +47,13 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
41
47
|
text_anchor: computed_properties.text_anchor
|
42
48
|
}
|
43
49
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
50
|
+
if state.text.parent
|
51
|
+
add_call_and_enter 'character_spacing', state.text.spacing unless state.text.spacing == state.text.parent.spacing
|
52
|
+
add_call_and_enter 'text_rendering_mode', state.text.mode unless state.text.mode == state.text.parent.mode
|
53
|
+
else
|
54
|
+
add_call_and_enter 'character_spacing', state.text.spacing unless state.text.spacing == 0
|
55
|
+
add_call_and_enter 'text_rendering_mode', state.text.mode unless state.text.mode == :fill
|
56
|
+
end
|
48
57
|
|
49
58
|
@commands.each do |command|
|
50
59
|
case command
|
@@ -59,8 +68,8 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
59
68
|
end
|
60
69
|
end
|
61
70
|
|
62
|
-
# It's possible there was no text to render. In that case, add a 'noop' so
|
63
|
-
#
|
71
|
+
# It's possible there was no text to render. In that case, add a 'noop' so character_spacing/text_rendering_mode
|
72
|
+
# don't blow up when they find they don't have a block to execute.
|
64
73
|
add_call 'noop' if calls.empty?
|
65
74
|
end
|
66
75
|
|
@@ -81,7 +90,7 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
81
90
|
|
82
91
|
def append_child(child)
|
83
92
|
new_state = state.dup
|
84
|
-
new_state.text =
|
93
|
+
new_state.text = TextState.new(state.text)
|
85
94
|
|
86
95
|
element = self.class.new(document, child, calls, new_state)
|
87
96
|
@commands << element
|
@@ -189,4 +198,28 @@ class Prawn::SVG::Elements::TextComponent < Prawn::SVG::Elements::DepthFirstBase
|
|
189
198
|
def apply_font(font)
|
190
199
|
add_call 'font', font.name, style: font.subfamily
|
191
200
|
end
|
201
|
+
|
202
|
+
def calculate_text_rendering_mode
|
203
|
+
fill = computed_properties.fill != 'none'
|
204
|
+
stroke = computed_properties.stroke != 'none'
|
205
|
+
|
206
|
+
if fill && stroke
|
207
|
+
:fill_stroke
|
208
|
+
elsif fill
|
209
|
+
:fill
|
210
|
+
elsif stroke
|
211
|
+
:stroke
|
212
|
+
else
|
213
|
+
:invisible
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def calculate_character_spacing
|
218
|
+
spacing = computed_properties.letter_spacing
|
219
|
+
spacing == 'normal' ? 0 : pixels(spacing)
|
220
|
+
end
|
221
|
+
|
222
|
+
# overridden, we don't want to call fill/stroke as draw_text does this for us
|
223
|
+
def apply_drawing_call
|
224
|
+
end
|
192
225
|
end
|
@@ -24,7 +24,7 @@ class Prawn::SVG::FontRegistry
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def load(family, weight = nil, style = nil)
|
27
|
-
Prawn::SVG::CSS.
|
27
|
+
Prawn::SVG::CSS::FontFamilyParser.parse(family).detect do |name|
|
28
28
|
name = name.gsub(/\s{2,}/, ' ').downcase
|
29
29
|
|
30
30
|
font = Prawn::SVG::Font.new(name, weight, style, font_registry: self)
|
@@ -37,8 +37,9 @@ class Prawn::SVG::FontRegistry
|
|
37
37
|
def merge_external_fonts
|
38
38
|
if @font_case_mapping.nil?
|
39
39
|
self.class.load_external_fonts unless self.class.external_font_families
|
40
|
-
@font_families.merge!(self.class.external_font_families)
|
41
|
-
|
40
|
+
@font_families.merge!(self.class.external_font_families) do |key, v1, v2|
|
41
|
+
v1
|
42
|
+
end
|
42
43
|
@font_case_mapping = @font_families.keys.each.with_object({}) do |key, result|
|
43
44
|
result[key.downcase] = key
|
44
45
|
end
|
data/lib/prawn/svg/interface.rb
CHANGED
@@ -186,7 +186,9 @@ module Prawn
|
|
186
186
|
# prawn (as at 2.0.1 anyway) uses 'b' for its fill_and_stroke. 'b' is 'h' (closepath) + 'B', and we
|
187
187
|
# never want closepath to be automatically run as it stuffs up many drawing operations, such as dashes
|
188
188
|
# and line caps, and makes paths close that we didn't ask to be closed when fill is specified.
|
189
|
-
|
189
|
+
even_odd = arguments[0].is_a?(Hash) && arguments[0][:fill_rule] == :even_odd
|
190
|
+
content = even_odd ? 'B*' : 'B'
|
191
|
+
prawn.add_content content
|
190
192
|
|
191
193
|
when 'noop'
|
192
194
|
yield
|
data/lib/prawn/svg/properties.rb
CHANGED
@@ -18,6 +18,7 @@ class Prawn::SVG::Properties
|
|
18
18
|
"display" => Config.new("inline", false, %w(inherit inline none), true),
|
19
19
|
"fill" => Config.new("black", true, %w(inherit none currentColor)),
|
20
20
|
"fill-opacity" => Config.new("1", true),
|
21
|
+
"fill-rule" => Config.new("nonzero", true, %w(inherit nonzero evenodd)),
|
21
22
|
"font-family" => Config.new("sans-serif", true),
|
22
23
|
"font-size" => Config.new("medium", true, %w(inherit xx-small x-small small medium large x-large xx-large larger smaller)),
|
23
24
|
"font-style" => Config.new("normal", true, %w(inherit normal italic oblique), true),
|
data/lib/prawn/svg/state.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
class Prawn::SVG::State
|
2
|
-
attr_accessor :
|
3
|
-
:text, :preserve_space,
|
2
|
+
attr_accessor :text, :preserve_space,
|
4
3
|
:fill_opacity, :stroke_opacity, :stroke_width,
|
5
4
|
:computed_properties,
|
6
5
|
:viewport_sizing,
|
7
|
-
:inside_use
|
6
|
+
:inside_use, :inside_clip_path
|
8
7
|
|
9
8
|
def initialize
|
10
9
|
@stroke_width = 1
|
@@ -16,4 +15,8 @@ class Prawn::SVG::State
|
|
16
15
|
def initialize_dup(other)
|
17
16
|
@computed_properties = @computed_properties.dup
|
18
17
|
end
|
18
|
+
|
19
|
+
def disable_drawing
|
20
|
+
inside_clip_path
|
21
|
+
end
|
19
22
|
end
|
data/lib/prawn/svg/version.rb
CHANGED
data/prawn-svg.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
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"
|
@@ -21,7 +21,7 @@ spec = Gem::Specification.new do |gem|
|
|
21
21
|
gem.required_ruby_version = '>= 2.1.0'
|
22
22
|
|
23
23
|
gem.add_runtime_dependency "prawn", ">= 0.11.1", "< 3"
|
24
|
-
gem.add_runtime_dependency "css_parser", "~> 1.
|
24
|
+
gem.add_runtime_dependency "css_parser", "~> 1.6"
|
25
25
|
gem.add_development_dependency "rspec", "~> 3.0"
|
26
26
|
gem.add_development_dependency "rake", "~> 10.1"
|
27
27
|
end
|
@@ -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,113 @@
|
|
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
|
+
|
15
|
+
circle:first-child { fill: #550000; }
|
16
|
+
circle:nth-child(2) { fill: #660000; }
|
17
|
+
circle:last-child { fill: #770000; }
|
18
|
+
|
19
|
+
square[chocolate] { fill: #880000; }
|
20
|
+
square[abc=def] { fill: #990000; }
|
21
|
+
square[abc^=ghi] { fill: #aa0000; }
|
22
|
+
square[abc$=jkl] { fill: #bb0000; }
|
23
|
+
square[abc*=mno] { fill: #cc0000; }
|
24
|
+
square[abc~=pqr] { fill: #dd0000; }
|
25
|
+
square[abc|=stu] { fill: #ee0000; }
|
26
|
+
</style>
|
27
|
+
|
28
|
+
<rect width="1" height="1" />
|
29
|
+
<rect width="2" height="2" id="outer" />
|
30
|
+
|
31
|
+
<g class="hero large">
|
32
|
+
<rect width="3" height="3" />
|
33
|
+
<rect width="4" height="4" style="fill: #777777;" />
|
34
|
+
<rect width="5" height="5" />
|
35
|
+
|
36
|
+
<g id="inner">
|
37
|
+
<rect width="6" height="6" />
|
38
|
+
</g>
|
39
|
+
|
40
|
+
<circle width="7" />
|
41
|
+
<circle width="8" />
|
42
|
+
<circle width="9" />
|
43
|
+
</g>
|
44
|
+
|
45
|
+
<square width="10" chocolate="hi there" />
|
46
|
+
<square width="11" abc="def" />
|
47
|
+
<square width="12" abc="ghidef" />
|
48
|
+
<square width="13" abc="aghidefjkl" />
|
49
|
+
<square width="14" abc="agmnohidefjklx" />
|
50
|
+
<square width="15" abc="aeo cnj pqr" />
|
51
|
+
<square width="16" abc="eij-stu-asd" />
|
52
|
+
</svg>
|
53
|
+
SVG
|
54
|
+
|
55
|
+
it "associates styles with elements" do
|
56
|
+
result = Prawn::SVG::CSS::Stylesheets.new(CssParser::Parser.new, REXML::Document.new(svg)).load
|
57
|
+
width_and_styles = result.map { |k, v| [k.attributes["width"].to_i, v] }.sort_by(&:first)
|
58
|
+
|
59
|
+
expect(width_and_styles).to eq [
|
60
|
+
[1, [["fill", "#ff0000", false]]],
|
61
|
+
[2, [["fill", "#ff0000", false], ["fill", "#330000", false], ["fill", "#440000", false], ["fill", "#220000", false]]],
|
62
|
+
[3, [["fill", "#ff0000", false], ["fill", "#00ff00", false]]],
|
63
|
+
[4, [["fill", "#ff0000", false], ["fill", "#330000", false], ["fill", "#440000", false], ["fill", "#00ff00", false]]],
|
64
|
+
[5, [["fill", "#ff0000", false], ["fill", "#330000", false], ["fill", "#330000", false], ["fill", "#00ff00", false]]],
|
65
|
+
[6, [["fill", "#ff0000", false], ["fill", "#0000ff", false]]],
|
66
|
+
[7, [["fill", "#550000", false]]],
|
67
|
+
[8, [["fill", "#660000", false]]],
|
68
|
+
[9, [["fill", "#770000", false]]],
|
69
|
+
[10, [["fill", "#880000", false]]],
|
70
|
+
[11, [["fill", "#990000", false]]],
|
71
|
+
[12, [["fill", "#aa0000", false]]],
|
72
|
+
[13, [["fill", "#bb0000", false]]],
|
73
|
+
[14, [["fill", "#cc0000", false]]],
|
74
|
+
[15, [["fill", "#dd0000", false]]],
|
75
|
+
[16, [["fill", "#ee0000", false]]],
|
76
|
+
]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "style tag parsing" do
|
81
|
+
let(:svg) do
|
82
|
+
<<-SVG
|
83
|
+
<svg>
|
84
|
+
<some-tag>
|
85
|
+
<style>a
|
86
|
+
before>
|
87
|
+
x <![CDATA[ y
|
88
|
+
inside <>>
|
89
|
+
k ]]> j
|
90
|
+
after
|
91
|
+
z</style>
|
92
|
+
</some-tag>
|
93
|
+
|
94
|
+
<other-tag>
|
95
|
+
<more-tag>
|
96
|
+
<style>hello</style>
|
97
|
+
</more-tag>
|
98
|
+
</other-tag>
|
99
|
+
</svg>
|
100
|
+
SVG
|
101
|
+
end
|
102
|
+
|
103
|
+
it "scans the document for style tags and adds the style information to the css parser" do
|
104
|
+
css_parser = instance_double(CssParser::Parser)
|
105
|
+
|
106
|
+
expect(css_parser).to receive(:add_block!).with("a\n before>\n x y\n inside <>>\n k j\n after\nz")
|
107
|
+
expect(css_parser).to receive(:add_block!).with("hello")
|
108
|
+
allow(css_parser).to receive(:each_rule_set)
|
109
|
+
|
110
|
+
Prawn::SVG::CSS::Stylesheets.new(css_parser, REXML::Document.new(svg)).load
|
111
|
+
end
|
112
|
+
end
|
113
|
+
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
|
@@ -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
|