prawn-svg 0.21.0 → 0.22.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -0
  3. data/README.md +11 -4
  4. data/lib/prawn-svg.rb +9 -6
  5. data/lib/prawn/svg/attributes.rb +6 -0
  6. data/lib/prawn/svg/attributes/clip_path.rb +17 -0
  7. data/lib/prawn/svg/attributes/display.rb +5 -0
  8. data/lib/prawn/svg/attributes/font.rb +38 -0
  9. data/lib/prawn/svg/attributes/opacity.rb +15 -0
  10. data/lib/prawn/svg/attributes/stroke.rb +35 -0
  11. data/lib/prawn/svg/attributes/transform.rb +50 -0
  12. data/lib/prawn/svg/calculators/aspect_ratio.rb +1 -1
  13. data/lib/prawn/svg/calculators/document_sizing.rb +2 -2
  14. data/lib/prawn/svg/calculators/pixels.rb +1 -1
  15. data/lib/prawn/svg/color.rb +44 -14
  16. data/lib/prawn/svg/document.rb +6 -5
  17. data/lib/prawn/svg/elements.rb +33 -0
  18. data/lib/prawn/svg/elements/base.rb +228 -0
  19. data/lib/prawn/svg/elements/circle.rb +25 -0
  20. data/lib/prawn/svg/elements/container.rb +15 -0
  21. data/lib/prawn/svg/elements/ellipse.rb +23 -0
  22. data/lib/prawn/svg/elements/gradient.rb +117 -0
  23. data/lib/prawn/svg/elements/ignored.rb +5 -0
  24. data/lib/prawn/svg/elements/image.rb +85 -0
  25. data/lib/prawn/svg/elements/line.rb +16 -0
  26. data/lib/prawn/svg/elements/path.rb +405 -0
  27. data/lib/prawn/svg/elements/polygon.rb +17 -0
  28. data/lib/prawn/svg/elements/polyline.rb +22 -0
  29. data/lib/prawn/svg/elements/rect.rb +33 -0
  30. data/lib/prawn/svg/elements/root.rb +9 -0
  31. data/lib/prawn/svg/elements/style.rb +10 -0
  32. data/lib/prawn/svg/elements/text.rb +87 -0
  33. data/lib/prawn/svg/elements/use.rb +29 -0
  34. data/lib/prawn/svg/extension.rb +2 -2
  35. data/lib/prawn/svg/font.rb +3 -3
  36. data/lib/prawn/svg/interface.rb +12 -5
  37. data/lib/prawn/svg/url_loader.rb +1 -1
  38. data/lib/prawn/svg/version.rb +2 -2
  39. data/prawn-svg.gemspec +3 -3
  40. data/spec/integration_spec.rb +59 -2
  41. data/spec/prawn/svg/attributes/font_spec.rb +49 -0
  42. data/spec/prawn/svg/attributes/transform_spec.rb +56 -0
  43. data/spec/prawn/svg/calculators/aspect_ratio_spec.rb +2 -2
  44. data/spec/prawn/svg/calculators/document_sizing_spec.rb +3 -3
  45. data/spec/prawn/svg/color_spec.rb +36 -15
  46. data/spec/prawn/svg/document_spec.rb +4 -4
  47. data/spec/prawn/svg/elements/base_spec.rb +125 -0
  48. data/spec/prawn/svg/elements/gradient_spec.rb +61 -0
  49. data/spec/prawn/svg/elements/path_spec.rb +123 -0
  50. data/spec/prawn/svg/elements/style_spec.rb +23 -0
  51. data/spec/prawn/svg/{parser → elements}/text_spec.rb +7 -8
  52. data/spec/prawn/svg/font_spec.rb +12 -12
  53. data/spec/prawn/svg/interface_spec.rb +7 -7
  54. data/spec/prawn/svg/url_loader_spec.rb +2 -2
  55. data/spec/sample_svg/gradients.svg +40 -0
  56. data/spec/sample_svg/rect02.svg +8 -11
  57. data/spec/spec_helper.rb +1 -1
  58. metadata +46 -18
  59. data/lib/prawn/svg/element.rb +0 -304
  60. data/lib/prawn/svg/parser.rb +0 -268
  61. data/lib/prawn/svg/parser/image.rb +0 -81
  62. data/lib/prawn/svg/parser/path.rb +0 -392
  63. data/lib/prawn/svg/parser/text.rb +0 -80
  64. data/spec/prawn/svg/element_spec.rb +0 -127
  65. data/spec/prawn/svg/parser/path_spec.rb +0 -89
  66. 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