prawn-svg 0.27.1 → 0.28.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.
- 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
|