prawn-svg 0.21.0 → 0.22.1
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/.travis.yml +6 -0
- data/README.md +11 -4
- data/lib/prawn-svg.rb +9 -6
- data/lib/prawn/svg/attributes.rb +6 -0
- data/lib/prawn/svg/attributes/clip_path.rb +17 -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/calculators/aspect_ratio.rb +1 -1
- data/lib/prawn/svg/calculators/document_sizing.rb +2 -2
- data/lib/prawn/svg/calculators/pixels.rb +1 -1
- data/lib/prawn/svg/color.rb +44 -14
- data/lib/prawn/svg/document.rb +6 -5
- data/lib/prawn/svg/elements.rb +33 -0
- data/lib/prawn/svg/elements/base.rb +228 -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 +117 -0
- data/lib/prawn/svg/elements/ignored.rb +5 -0
- data/lib/prawn/svg/elements/image.rb +85 -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 +9 -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/extension.rb +2 -2
- data/lib/prawn/svg/font.rb +3 -3
- data/lib/prawn/svg/interface.rb +12 -5
- data/lib/prawn/svg/url_loader.rb +1 -1
- data/lib/prawn/svg/version.rb +2 -2
- data/prawn-svg.gemspec +3 -3
- data/spec/integration_spec.rb +59 -2
- data/spec/prawn/svg/attributes/font_spec.rb +49 -0
- data/spec/prawn/svg/attributes/transform_spec.rb +56 -0
- data/spec/prawn/svg/calculators/aspect_ratio_spec.rb +2 -2
- data/spec/prawn/svg/calculators/document_sizing_spec.rb +3 -3
- data/spec/prawn/svg/color_spec.rb +36 -15
- data/spec/prawn/svg/document_spec.rb +4 -4
- data/spec/prawn/svg/elements/base_spec.rb +125 -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/{parser → elements}/text_spec.rb +7 -8
- data/spec/prawn/svg/font_spec.rb +12 -12
- data/spec/prawn/svg/interface_spec.rb +7 -7
- data/spec/prawn/svg/url_loader_spec.rb +2 -2
- data/spec/sample_svg/gradients.svg +40 -0
- data/spec/sample_svg/rect02.svg +8 -11
- data/spec/spec_helper.rb +1 -1
- metadata +46 -18
- data/lib/prawn/svg/element.rb +0 -304
- data/lib/prawn/svg/parser.rb +0 -268
- data/lib/prawn/svg/parser/image.rb +0 -81
- data/lib/prawn/svg/parser/path.rb +0 -392
- data/lib/prawn/svg/parser/text.rb +0 -80
- data/spec/prawn/svg/element_spec.rb +0 -127
- data/spec/prawn/svg/parser/path_spec.rb +0 -89
- data/spec/prawn/svg/parser_spec.rb +0 -55
@@ -1,80 +0,0 @@
|
|
1
|
-
class Prawn::Svg::Parser::Text
|
2
|
-
def parse(element)
|
3
|
-
element.add_call_and_enter "text_group"
|
4
|
-
internal_parse(element, [element.document.x(0)], [element.document.y(0)])
|
5
|
-
end
|
6
|
-
|
7
|
-
protected
|
8
|
-
def internal_parse(element, x_positions, y_positions, relative = false, preserve_space = false)
|
9
|
-
return if element.state[:display] == "none"
|
10
|
-
|
11
|
-
attrs = element.attributes
|
12
|
-
|
13
|
-
if attrs['x'] || attrs['y']
|
14
|
-
relative = false
|
15
|
-
x_positions = attrs['x'].split(/[\s,]+/).collect {|n| element.document.x(n)} if attrs['x']
|
16
|
-
y_positions = attrs['y'].split(/[\s,]+/).collect {|n| element.document.y(n)} if attrs['y']
|
17
|
-
end
|
18
|
-
|
19
|
-
if attrs['dx'] || attrs['dy']
|
20
|
-
element.add_call_and_enter "translate", element.document.distance(attrs['dx'] || 0), -element.document.distance(attrs['dy'] || 0)
|
21
|
-
end
|
22
|
-
|
23
|
-
case attrs['xml:space']
|
24
|
-
when 'preserve'
|
25
|
-
preserve_space = true
|
26
|
-
when 'default'
|
27
|
-
preserve_space = false
|
28
|
-
end
|
29
|
-
|
30
|
-
opts = {}
|
31
|
-
if size = element.state[:font_size]
|
32
|
-
opts[:size] = size
|
33
|
-
end
|
34
|
-
opts[:style] = element.state[:font_subfamily]
|
35
|
-
|
36
|
-
# This is not a prawn option but we can't work out how to render it here -
|
37
|
-
# it's handled by Svg#rewrite_call_arguments
|
38
|
-
if (anchor = attrs['text-anchor'] || element.state[:text_anchor]) &&
|
39
|
-
['start', 'middle', 'end'].include?(anchor)
|
40
|
-
opts[:text_anchor] = anchor
|
41
|
-
end
|
42
|
-
|
43
|
-
if spacing = attrs['letter-spacing']
|
44
|
-
element.add_call_and_enter 'character_spacing', element.document.points(spacing)
|
45
|
-
end
|
46
|
-
|
47
|
-
element.element.children.each do |child|
|
48
|
-
if child.node_type == :text
|
49
|
-
text = child.value.strip.gsub(preserve_space ? /[\n\t]/ : /\s+/, " ")
|
50
|
-
|
51
|
-
while text != ""
|
52
|
-
opts[:at] = [x_positions.first, y_positions.first]
|
53
|
-
|
54
|
-
if x_positions.length > 1 || y_positions.length > 1
|
55
|
-
element.add_call 'draw_text', text[0..0], opts.dup
|
56
|
-
text = text[1..-1]
|
57
|
-
|
58
|
-
x_positions.shift if x_positions.length > 1
|
59
|
-
y_positions.shift if y_positions.length > 1
|
60
|
-
else
|
61
|
-
element.add_call relative ? 'relative_draw_text' : 'draw_text', text, opts.dup
|
62
|
-
relative = true
|
63
|
-
break
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
elsif child.name == "tspan"
|
68
|
-
element.add_call 'save'
|
69
|
-
child.attributes['text-anchor'] ||= opts[:text_anchor] if opts[:text_anchor]
|
70
|
-
child_element = Prawn::Svg::Element.new(element.document, child, element.calls, element.state.dup)
|
71
|
-
internal_parse(child_element, x_positions, y_positions, relative, preserve_space)
|
72
|
-
child_element.append_calls_to_parent
|
73
|
-
element.add_call 'restore'
|
74
|
-
|
75
|
-
else
|
76
|
-
element.warnings << "Unknown tag '#{child.name}' inside text tag; ignoring"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
-
|
3
|
-
describe Prawn::Svg::Element do
|
4
|
-
let(:document) { double(css_parser: nil) }
|
5
|
-
let(:e) { double(:attributes => {}, :name => "path") }
|
6
|
-
let(:element) { Prawn::Svg::Element.new(document, e, [], {}) }
|
7
|
-
|
8
|
-
describe "#parse_font_attributes_and_call" do
|
9
|
-
before do
|
10
|
-
@document = Struct.new(:fallback_font_name, :css_parser, :warnings).new("Courier", nil, [])
|
11
|
-
@element = Prawn::Svg::Element.new(@document, e, [], {})
|
12
|
-
end
|
13
|
-
|
14
|
-
it "uses a font if it can find it" do
|
15
|
-
@element.should_receive(:add_call_and_enter).with('font', 'Helvetica', :style => :normal)
|
16
|
-
|
17
|
-
@element.attributes["font-family"] = "Helvetica"
|
18
|
-
@element.send :parse_font_attributes_and_call
|
19
|
-
end
|
20
|
-
|
21
|
-
it "uses the fallback font if the requested font is not defined" do
|
22
|
-
@element.should_receive(:add_call_and_enter).with('font', 'Courier', :style => :normal)
|
23
|
-
|
24
|
-
@element.attributes["font-family"] = "Font That Doesn't Exist"
|
25
|
-
@element.send :parse_font_attributes_and_call
|
26
|
-
end
|
27
|
-
|
28
|
-
it "doesn't call the font method if there's no fallback font" do
|
29
|
-
@document.fallback_font_name = nil
|
30
|
-
|
31
|
-
@element.should_not_receive(:add_call_and_enter)
|
32
|
-
|
33
|
-
@element.attributes["font-family"] = "Font That Doesn't Exist"
|
34
|
-
@element.send :parse_font_attributes_and_call
|
35
|
-
@document.warnings.length.should == 1
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
describe "#parse_fill_and_stroke_attributes_and_call" do
|
40
|
-
subject { element.send :parse_fill_and_stroke_attributes_and_call }
|
41
|
-
|
42
|
-
it "doesn't change anything if no fill attribute provided" do
|
43
|
-
subject
|
44
|
-
expect(element.state[:fill]).to be nil
|
45
|
-
end
|
46
|
-
|
47
|
-
it "doesn't change anything if 'inherit' fill attribute provided" do
|
48
|
-
element.attributes['fill'] = 'inherit'
|
49
|
-
subject
|
50
|
-
expect(element.state[:fill]).to be nil
|
51
|
-
end
|
52
|
-
|
53
|
-
it "turns off filling if 'none' fill attribute provided" do
|
54
|
-
element.attributes['fill'] = 'none'
|
55
|
-
subject
|
56
|
-
expect(element.state[:fill]).to be false
|
57
|
-
end
|
58
|
-
|
59
|
-
it "uses the fill attribute's color" do
|
60
|
-
expect(element).to receive(:add_call).with('fill_color', 'ff0000')
|
61
|
-
element.attributes['fill'] = 'red'
|
62
|
-
subject
|
63
|
-
expect(element.state[:fill]).to be true
|
64
|
-
end
|
65
|
-
|
66
|
-
it "uses black if the fill attribute's color is unparseable" do
|
67
|
-
expect(element).to receive(:add_call).with('fill_color', '000000')
|
68
|
-
element.attributes['fill'] = 'blarble'
|
69
|
-
subject
|
70
|
-
expect(element.state[:fill]).to be true
|
71
|
-
end
|
72
|
-
|
73
|
-
it "uses the color attribute if 'currentColor' fill attribute provided" do
|
74
|
-
expect(element).to receive(:add_call).with('fill_color', 'ff0000')
|
75
|
-
element.attributes['fill'] = 'currentColor'
|
76
|
-
element.attributes['color'] = 'red'
|
77
|
-
subject
|
78
|
-
expect(element.state[:fill]).to be true
|
79
|
-
end
|
80
|
-
|
81
|
-
it "turns off filling if UnresolvableURLWithNoFallbackError is raised" do
|
82
|
-
element.attributes['fill'] = 'url()'
|
83
|
-
subject
|
84
|
-
expect(element.state[:fill]).to be false
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
describe "#parse_transform_attribute_and_call" do
|
89
|
-
subject { element.send :parse_transform_attribute_and_call }
|
90
|
-
|
91
|
-
describe "translate" do
|
92
|
-
it "handles a missing y argument" do
|
93
|
-
expect(element).to receive(:add_call_and_enter).with('translate', -5.5, 0)
|
94
|
-
expect(document).to receive(:distance).with(-5.5, :x).and_return(-5.5)
|
95
|
-
expect(document).to receive(:distance).with(0.0, :y).and_return(0.0)
|
96
|
-
|
97
|
-
element.attributes['transform'] = 'translate(-5.5)'
|
98
|
-
subject
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
describe "rotate" do
|
103
|
-
it "handles a single angle argument" do
|
104
|
-
expect(element).to receive(:add_call_and_enter).with('rotate', -5.5, :origin => [0, 0])
|
105
|
-
expect(document).to receive(:y).with('0').and_return(0)
|
106
|
-
|
107
|
-
element.attributes['transform'] = 'rotate(5.5)'
|
108
|
-
subject
|
109
|
-
end
|
110
|
-
|
111
|
-
it "handles three arguments" do
|
112
|
-
expect(element).to receive(:add_call_and_enter).with('rotate', -5.5, :origin => [1.0, 2.0])
|
113
|
-
expect(document).to receive(:x).with(1.0).and_return(1.0)
|
114
|
-
expect(document).to receive(:y).with(2.0).and_return(2.0)
|
115
|
-
|
116
|
-
element.attributes['transform'] = 'rotate(5.5 1 2)'
|
117
|
-
subject
|
118
|
-
end
|
119
|
-
|
120
|
-
it "does nothing and warns if two arguments" do
|
121
|
-
expect(document).to receive(:warnings).and_return([])
|
122
|
-
element.attributes['transform'] = 'rotate(5.5 1)'
|
123
|
-
subject
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
@@ -1,89 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Prawn::Svg::Parser::Path do
|
4
|
-
let(:path) { Prawn::Svg::Parser::Path.new }
|
5
|
-
|
6
|
-
describe "command parsing" do
|
7
|
-
it "correctly parses a valid path" do
|
8
|
-
calls = []
|
9
|
-
path.stub(:run_path_command) {|*args| calls << args}
|
10
|
-
path.parse("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")
|
11
|
-
|
12
|
-
calls.should == [
|
13
|
-
["A", [12.34, -56.78, 89]],
|
14
|
-
["B", [4, 5, 12, -34, -0.5, 0.7, 3, 2.3e3, 4e4, 4e4]],
|
15
|
-
["c", [31, -2e-5]],
|
16
|
-
["C", [6, 7]],
|
17
|
-
["T", []],
|
18
|
-
["Q", []],
|
19
|
-
["X", [0]],
|
20
|
-
["Z", []]
|
21
|
-
]
|
22
|
-
end
|
23
|
-
|
24
|
-
it "treats subsequent points to m/M command as relative/absolute depending on command" do
|
25
|
-
[
|
26
|
-
["M", [1,2,3,4]],
|
27
|
-
["L", [3,4]],
|
28
|
-
["m", [5,6,7,8]],
|
29
|
-
["l", [7,8]]
|
30
|
-
].each do |args|
|
31
|
-
path.should_receive(:run_path_command).with(*args).and_call_original
|
32
|
-
end
|
33
|
-
|
34
|
-
path.parse("M 1,2 3,4 m 5,6 7,8")
|
35
|
-
end
|
36
|
-
|
37
|
-
it "correctly parses an empty path" do
|
38
|
-
path.should_not_receive(:run_path_command)
|
39
|
-
path.parse("").should == []
|
40
|
-
path.parse(" ").should == []
|
41
|
-
end
|
42
|
-
|
43
|
-
it "raises on invalid characters in the path" do
|
44
|
-
lambda {path.parse("M 10 % 20")}.should raise_error(Prawn::Svg::Parser::Path::InvalidError)
|
45
|
-
end
|
46
|
-
|
47
|
-
it "raises on numerical data before a command letter" do
|
48
|
-
lambda {path.parse("10 P")}.should raise_error(Prawn::Svg::Parser::Path::InvalidError)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context "when given an A path" do
|
53
|
-
it "uses bezier curves to approximate an arc path" do
|
54
|
-
result = path.parse("M 100 200 A 10 10 0 0 1 200 200")
|
55
|
-
|
56
|
-
expect(result).to eq [
|
57
|
-
["move_to", [100.0, 200.0]],
|
58
|
-
["curve_to", [150.0, 150.0, 100.0, 172.57081148225683, 122.57081148225683, 150.0]],
|
59
|
-
["curve_to", [200.0, 200.0, 177.42918851774317, 150.0, 200.0, 172.57081148225683]]
|
60
|
-
]
|
61
|
-
end
|
62
|
-
|
63
|
-
it "ignores a path that has an identical start and end point" do
|
64
|
-
result = path.parse("M 100 200 A 30 30 0 0 1 100 200")
|
65
|
-
|
66
|
-
expect(result).to eq [
|
67
|
-
["move_to", [100.0, 200.0]]
|
68
|
-
]
|
69
|
-
end
|
70
|
-
|
71
|
-
it "substitutes a line_to when rx is 0" do
|
72
|
-
result = path.parse("M 100 200 A 0 10 0 0 1 200 200")
|
73
|
-
|
74
|
-
expect(result).to eq [
|
75
|
-
["move_to", [100.0, 200.0]],
|
76
|
-
["line_to", [200.0, 200.0]]
|
77
|
-
]
|
78
|
-
end
|
79
|
-
|
80
|
-
it "substitutes a line_to when ry is 0" do
|
81
|
-
result = path.parse("M 100 200 A 10 0 0 0 1 200 200")
|
82
|
-
|
83
|
-
expect(result).to eq [
|
84
|
-
["move_to", [100.0, 200.0]],
|
85
|
-
["line_to", [200.0, 200.0]]
|
86
|
-
]
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Prawn::Svg::Parser do
|
4
|
-
describe "document width and height" do
|
5
|
-
it "handles the width and height being set as a %" do
|
6
|
-
svg = <<-SVG
|
7
|
-
<svg width="50%" height="50%" version="1.1">
|
8
|
-
<line x1="10%" y1="10%" x2="90%" y2="90%" />
|
9
|
-
</svg>
|
10
|
-
SVG
|
11
|
-
|
12
|
-
document = Prawn::Svg::Document.new(svg, [2000, 2000], {})
|
13
|
-
Prawn::Svg::Parser.new(document).parse[-2][-1].should == [["line", [100.0, 900.0, 900.0, 100.0], []]]
|
14
|
-
end
|
15
|
-
|
16
|
-
it "handles the width and height being set in inches" do
|
17
|
-
svg = <<-SVG
|
18
|
-
<svg width="10in" height="10in" version="1.1">
|
19
|
-
<line x1="1in" y1="1in" x2="9in" y2="9in" />
|
20
|
-
</svg>
|
21
|
-
SVG
|
22
|
-
|
23
|
-
document = Prawn::Svg::Document.new(svg, [2000, 2000], {})
|
24
|
-
Prawn::Svg::Parser.new(document).parse[-2][-1].should == [["line", [72.0, 720.0 - 72.0, 720.0 - 72.0, 72.0], []]]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
describe :parse_element do
|
29
|
-
before(:each) do
|
30
|
-
@document = Prawn::Svg::Document.new("<svg></svg>", [100, 100], {})
|
31
|
-
@parser = Prawn::Svg::Parser.new(@document)
|
32
|
-
end
|
33
|
-
|
34
|
-
def mock_element(name, attributes = {})
|
35
|
-
e = double(:name => name, :attributes => attributes)
|
36
|
-
Prawn::Svg::Element.new(@document, e, [], {})
|
37
|
-
end
|
38
|
-
|
39
|
-
it "ignores tags it doesn't know about" do
|
40
|
-
calls = []
|
41
|
-
@parser.send :parse_element, mock_element("unknown")
|
42
|
-
calls.should == []
|
43
|
-
@document.warnings.length.should == 1
|
44
|
-
@document.warnings.first.should include("Unknown tag")
|
45
|
-
end
|
46
|
-
|
47
|
-
it "ignores tags that don't have all required attributes set" do
|
48
|
-
calls = []
|
49
|
-
@parser.send :parse_element, mock_element("ellipse", "rx" => "1")
|
50
|
-
calls.should == []
|
51
|
-
@document.warnings.length.should == 1
|
52
|
-
@document.warnings.first.should include("Must have attributes ry on tag ellipse")
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|