prawn-svg 0.15.0.0 → 0.19.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -12
  3. data/lib/prawn/svg/calculators/aspect_ratio.rb +58 -0
  4. data/lib/prawn/svg/calculators/document_sizing.rb +75 -0
  5. data/lib/prawn/svg/calculators/pixels.rb +21 -0
  6. data/lib/prawn/svg/document.rb +16 -43
  7. data/lib/prawn/svg/element.rb +71 -18
  8. data/lib/prawn/svg/extension.rb +3 -3
  9. data/lib/prawn/svg/interface.rb +80 -19
  10. data/lib/prawn/svg/parser/image.rb +9 -62
  11. data/lib/prawn/svg/parser/path.rb +19 -1
  12. data/lib/prawn/svg/parser/text.rb +2 -0
  13. data/lib/prawn/svg/parser.rb +53 -18
  14. data/lib/prawn/svg/url_loader.rb +34 -0
  15. data/lib/prawn/svg/version.rb +1 -1
  16. data/lib/prawn-svg.rb +4 -0
  17. data/prawn-svg.gemspec +7 -4
  18. data/spec/integration_spec.rb +83 -0
  19. data/spec/prawn/svg/calculators/aspect_ratio_spec.rb +95 -0
  20. data/spec/prawn/svg/calculators/document_sizing_spec.rb +73 -0
  21. data/spec/prawn/svg/document_spec.rb +21 -17
  22. data/spec/prawn/svg/element_spec.rb +1 -1
  23. data/spec/prawn/svg/interface_spec.rb +59 -0
  24. data/spec/prawn/svg/parser/path_spec.rb +89 -0
  25. data/spec/prawn/svg/url_loader_spec.rb +46 -0
  26. data/spec/sample_images/mushroom-long.jpg +0 -0
  27. data/spec/sample_images/mushroom-wide.jpg +0 -0
  28. data/spec/sample_svg/cap_styles.svg +13 -0
  29. data/spec/sample_svg/display_none.svg +13 -0
  30. data/spec/sample_svg/gistfile1.svg +36 -0
  31. data/spec/sample_svg/hidden_paths.svg +6 -0
  32. data/spec/sample_svg/image01.svg +31 -31
  33. data/spec/sample_svg/negminy.svg +25 -0
  34. data/spec/sample_svg/path.svg +5 -0
  35. data/spec/sample_svg/pie_piece.svg +7 -0
  36. data/spec/sample_svg/viewbox.svg +4 -0
  37. data/spec/sample_svg/viewport.svg +23 -0
  38. data/spec/spec_helper.rb +7 -0
  39. metadata +81 -25
  40. data/spec/lib/path_spec.rb +0 -54
  41. data/spec/lib/svg_spec.rb +0 -47
  42. /data/spec/{lib → prawn/svg}/parser_spec.rb +0 -0
@@ -0,0 +1,95 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ describe Prawn::Svg::Calculators::AspectRatio do
4
+ def test(*args)
5
+ aspect = Prawn::Svg::Calculators::AspectRatio.new(*args)
6
+ [[aspect.width, aspect.height], [aspect.x, aspect.y]]
7
+ end
8
+
9
+ it "handles none" do
10
+ expect(test "none", [50,80], [100,100]).to eq [[50, 80], [0, 0]]
11
+ expect(test "none", [100,100], [50,80]).to eq [[100, 100], [0, 0]]
12
+ end
13
+
14
+ context "using meet" do
15
+ context "with smaller containers than objects" do
16
+ let(:coords) { [[50,80], [100,100]] }
17
+
18
+ it "correctly calculates the result" do
19
+ expect(test "xMidYMid meet", *coords).to eq [[50, 50], [0, 15]]
20
+ expect(test "xMinYMin meet", *coords).to eq [[50, 50], [0, 0]]
21
+ expect(test "xMaxYMax meet", *coords).to eq [[50, 50], [0, 30]]
22
+ end
23
+ end
24
+
25
+ context "with bigger containers than objects" do
26
+ let(:coords) { [[100,80], [50,50]] }
27
+
28
+ it "correctly calculates the result" do
29
+ expect(test "xMidYMid meet", *coords).to eq [[80, 80], [10, 0]]
30
+ expect(test "xMinYMin meet", *coords).to eq [[80, 80], [0, 0]]
31
+ expect(test "xMaxYMax meet", *coords).to eq [[80, 80], [20, 0]]
32
+ end
33
+ end
34
+
35
+ context "with bigger square containers" do
36
+ let(:coords) { [[100,100], [50,80]] }
37
+
38
+ it "correctly calculates the result" do
39
+ expect(test "xMidYMid meet", *coords).to eq [[62.5, 100], [18.75, 0]]
40
+ expect(test "xMinYMin meet", *coords).to eq [[62.5, 100], [0, 0]]
41
+ expect(test "xMaxYMax meet", *coords).to eq [[62.5, 100], [37.5, 0]]
42
+ end
43
+ end
44
+
45
+ context "with oddly shaped containers" do
46
+ let(:coords) { [[100,20], [50,50]] }
47
+
48
+ it "correctly calculates the result" do
49
+ expect(test "xMidYMid meet", *coords).to eq [[20, 20], [40, 0]]
50
+ expect(test "xMinYMin meet", *coords).to eq [[20, 20], [0, 0]]
51
+ expect(test "xMaxYMax meet", *coords).to eq [[20, 20], [80, 0]]
52
+ end
53
+ end
54
+ end
55
+
56
+ context "using slice" do
57
+ context "with smaller containers than objects" do
58
+ let(:coords) { [[50,80], [100,100]] }
59
+
60
+ it "correctly calculates the result" do
61
+ expect(test "xMidYMid slice", *coords).to eq [[80, 80], [-15, 0]]
62
+ expect(test "xMinYMin slice", *coords).to eq [[80, 80], [0, 0]]
63
+ expect(test "xMaxYMax slice", *coords).to eq [[80, 80], [-30, 0]]
64
+ end
65
+ end
66
+
67
+ context "with bigger containers than objects" do
68
+ let(:coords) { [[100,80], [50,50]] }
69
+
70
+ it "correctly calculates the result" do
71
+ expect(test "xMidYMid slice", *coords).to eq [[100, 100], [0, -10]]
72
+ expect(test "xMinYMin slice", *coords).to eq [[100, 100], [0, 0]]
73
+ expect(test "xMaxYMax slice", *coords).to eq [[100, 100], [0, -20]]
74
+ end
75
+ end
76
+
77
+ context "with oddly shaped containers" do
78
+ let(:coords) { [[100,20], [50,50]] }
79
+
80
+ it "correctly calculates the result" do
81
+ expect(test "xMidYMid slice", *coords).to eq [[100, 100], [0, -40]]
82
+ expect(test "xMinYMin slice", *coords).to eq [[100, 100], [0, 0]]
83
+ expect(test "xMaxYMax slice", *coords).to eq [[100, 100], [0, -80]]
84
+ end
85
+ end
86
+ end
87
+
88
+ it "defaults to 'xMidYMid meet' if nothing is supplied" do
89
+ expect(test "", [50,80], [100,100]).to eq test "xMidYMid meet", [50,80], [100,100]
90
+ end
91
+
92
+ it "defaults to 'xMidYMid meet' if something invalid is supplied" do
93
+ expect(test "completely invalid", [50,80], [100,100]).to eq test "xMidYMid meet", [50,80], [100,100]
94
+ end
95
+ end
@@ -0,0 +1,73 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ describe Prawn::Svg::Calculators::DocumentSizing do
4
+ let(:attributes) do
5
+ {"width" => "150", "height" => "200", "viewBox" => "0 -30 300 800", "preserveAspectRatio" => "xMaxYMid meet"}
6
+ end
7
+
8
+ let(:bounds) { [1200, 800] }
9
+
10
+ let(:sizing) { Prawn::Svg::Calculators::DocumentSizing.new(bounds, attributes) }
11
+
12
+ describe "#initialize" do
13
+ it "takes bounds and a set of attributes and calls set_from_attributes" do
14
+ expect(sizing.instance_variable_get :@bounds).to eq bounds
15
+ expect(sizing.instance_variable_get :@document_width).to eq "150"
16
+ end
17
+ end
18
+
19
+ describe "#set_from_attributes" do
20
+ let(:sizing) { Prawn::Svg::Calculators::DocumentSizing.new(bounds) }
21
+
22
+ it "sets ivars from the passed-in attributes hash" do
23
+ sizing.set_from_attributes(attributes)
24
+ expect(sizing.instance_variable_get :@document_width).to eq "150"
25
+ expect(sizing.instance_variable_get :@document_height).to eq "200"
26
+ expect(sizing.instance_variable_get :@view_box).to eq "0 -30 300 800"
27
+ expect(sizing.instance_variable_get :@preserve_aspect_ratio).to eq "xMaxYMid meet"
28
+ end
29
+ end
30
+
31
+ describe "#calculate" do
32
+ it "calculates the document sizing measurements for a given set of inputs" do
33
+ sizing.calculate
34
+ expect(sizing.x_offset).to eq -75
35
+ expect(sizing.y_offset).to eq -30
36
+ expect(sizing.x_scale).to eq 0.25
37
+ expect(sizing.y_scale).to eq 0.25
38
+ expect(sizing.viewport_width).to eq 300
39
+ expect(sizing.viewport_height).to eq 800
40
+ expect(sizing.output_width).to eq 150
41
+ expect(sizing.output_height).to eq 200
42
+ end
43
+
44
+ it "scales again based on requested width" do
45
+ sizing.requested_width = 75
46
+ sizing.calculate
47
+ expect(sizing.x_scale).to eq 0.125
48
+ expect(sizing.y_scale).to eq 0.125
49
+ expect(sizing.viewport_width).to eq 300
50
+ expect(sizing.viewport_height).to eq 800
51
+ expect(sizing.output_width).to eq 75
52
+ expect(sizing.output_height).to eq 100
53
+ end
54
+
55
+ it "scales again based on requested height" do
56
+ sizing.requested_height = 100
57
+ sizing.calculate
58
+ expect(sizing.x_scale).to eq 0.125
59
+ expect(sizing.y_scale).to eq 0.125
60
+ expect(sizing.viewport_width).to eq 300
61
+ expect(sizing.viewport_height).to eq 800
62
+ expect(sizing.output_width).to eq 75
63
+ expect(sizing.output_height).to eq 100
64
+ end
65
+
66
+ it "correctly handles % values being passed in" do
67
+ sizing.document_width = sizing.document_height = "50%"
68
+ sizing.calculate
69
+ expect(sizing.output_width).to eq 600
70
+ expect(sizing.output_height).to eq 400
71
+ end
72
+ end
73
+ end
@@ -1,27 +1,31 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper'
2
2
 
3
3
  describe Prawn::Svg::Document do
4
- before(:each) do
5
- @document = Prawn::Svg::Document.new("<svg></svg>", [100, 100], {})
4
+ before do
5
+ sizing = instance_double(Prawn::Svg::Calculators::DocumentSizing, viewport_width: 600, viewport_height: 400, viewport_diagonal: 500, :requested_width= => nil, :requested_height= => nil)
6
+ expect(sizing).to receive(:calculate)
7
+ expect(Prawn::Svg::Calculators::DocumentSizing).to receive(:new).and_return(sizing)
6
8
  end
7
9
 
10
+ let(:document) { Prawn::Svg::Document.new("<svg></svg>", [100, 100], {}) }
11
+
8
12
  describe :points do
9
13
  it "converts a variety of measurement units to points" do
10
- @document.send(:points, 32).should == 32.0
11
- @document.send(:points, 32.0).should == 32.0
12
- @document.send(:points, "32").should == 32.0
13
- @document.send(:points, "32unknown").should == 32.0
14
- @document.send(:points, "32pt").should == 32.0
15
- @document.send(:points, "32in").should == 32.0 * 72
16
- @document.send(:points, "32ft").should == 32.0 * 72 * 12
17
- @document.send(:points, "32mm").should be_within(0.0001).of(32 * 72 * 0.0393700787)
18
- @document.send(:points, "32cm").should be_within(0.0001).of(32 * 72 * 0.393700787)
19
- @document.send(:points, "32m").should be_within(0.0001).of(32 * 72 * 39.3700787)
20
-
21
- @document.send :instance_variable_set, "@actual_width", 600
22
- @document.send :instance_variable_set, "@actual_height", 400
23
- @document.send(:points, "50%").should == 300
24
- @document.send(:points, "50%", :y).should == 200
14
+ document.send(:points, 32).should == 32.0
15
+ document.send(:points, 32.0).should == 32.0
16
+ document.send(:points, "32").should == 32.0
17
+ document.send(:points, "32unknown").should == 32.0
18
+ document.send(:points, "32pt").should == 32.0
19
+ document.send(:points, "32in").should == 32.0 * 72
20
+ document.send(:points, "32ft").should == 32.0 * 72 * 12
21
+ document.send(:points, "32pc").should == 32.0 * 15
22
+ document.send(:points, "32mm").should be_within(0.0001).of(32 * 72 * 0.0393700787)
23
+ document.send(:points, "32cm").should be_within(0.0001).of(32 * 72 * 0.393700787)
24
+ document.send(:points, "32m").should be_within(0.0001).of(32 * 72 * 39.3700787)
25
+
26
+ document.send(:points, "50%").should == 250
27
+ document.send(:points, "50%", :x).should == 300
28
+ document.send(:points, "50%", :y).should == 200
25
29
  end
26
30
  end
27
31
  end
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/../../spec_helper'
2
2
 
3
3
  describe Prawn::Svg::Element do
4
4
  before :each do
5
- e = double(:attributes => {})
5
+ e = double(:attributes => {}, :name => "path")
6
6
 
7
7
  @document = Struct.new(:fallback_font_name, :css_parser, :warnings).new("Courier", nil, [])
8
8
  @element = Prawn::Svg::Element.new(@document, e, [], {})
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Prawn::Svg::Interface do
4
+ let(:bounds) { double(width: 800, height: 600) }
5
+ let(:prawn) { instance_double(Prawn::Document, font_families: {}, bounds: bounds, cursor: 600) }
6
+ let(:svg) { '<svg width="250" height="100"></svg>' }
7
+
8
+ describe "#initialize" do
9
+ describe "invalid option detection" do
10
+ it "rejects invalid options when debug is on" do
11
+ allow(Prawn).to receive(:debug).and_return(true)
12
+
13
+ expect {
14
+ Prawn::Svg::Interface.new(svg, prawn, :invalid => "option")
15
+ }.to raise_error(Prawn::Errors::UnknownOption)
16
+ end
17
+
18
+ it "does nothing if an invalid option is given and debug is off" do
19
+ Prawn::Svg::Interface.new(svg, prawn, :invalid => "option")
20
+ end
21
+ end
22
+ end
23
+
24
+ describe "#position" do
25
+ context "when options[:at] supplied" do
26
+ it "returns options[:at]" do
27
+ interface = Prawn::Svg::Interface.new(svg, prawn, at: [1, 2], position: :left)
28
+
29
+ expect(interface.position).to eq [1, 2]
30
+ end
31
+ end
32
+
33
+ context "when only a position is supplied" do
34
+ let(:interface) { Prawn::Svg::Interface.new(svg, prawn, position: position) }
35
+
36
+ subject { interface.position }
37
+
38
+ context "(:left)" do
39
+ let(:position) { :left }
40
+ it { is_expected.to eq [0, 600] }
41
+ end
42
+
43
+ context "(:center)" do
44
+ let(:position) { :center }
45
+ it { is_expected.to eq [275, 600] }
46
+ end
47
+
48
+ context "(:right)" do
49
+ let(:position) { :right }
50
+ it { is_expected.to eq [550, 600] }
51
+ end
52
+
53
+ context "a number" do
54
+ let(:position) { 25.5 }
55
+ it { is_expected.to eq [25.5, 600] }
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,89 @@
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
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Prawn::Svg::UrlLoader do
4
+ let(:loader) { Prawn::Svg::UrlLoader.new(:enable_cache => true, :enable_web => true) }
5
+
6
+ describe "#initialize" do
7
+ it "sets options" do
8
+ expect(loader.enable_cache).to be true
9
+ expect(loader.enable_web).to be true
10
+ end
11
+ end
12
+
13
+ describe "#valid?" do
14
+ it "knows what a valid URL looks like" do
15
+ expect(loader.valid?("http://valid.example/url")).to be true
16
+ expect(loader.valid?("not/a/valid/url")).to be false
17
+ end
18
+
19
+ it "doesn't accept schemes it doesn't like" do
20
+ expect(loader.valid?("mail://valid.example/url")).to be false
21
+ end
22
+ end
23
+
24
+ describe "#load" do
25
+ let(:url) { "http://hello/there" }
26
+
27
+ it "loads an HTTP URL and saves to the cache" do
28
+ o = double(:read => "hello!")
29
+ loader.should_receive(:open).with(url).and_return(o)
30
+
31
+ expect(loader.load(url)).to eq "hello!"
32
+ expect(loader.url_cache[url]).to eq "hello!"
33
+ end
34
+
35
+ it "loads an HTTP URL from the cache without calling open" do
36
+ loader.url_cache[url] = "hello"
37
+ loader.should_not_receive(:open)
38
+ expect(loader.load(url)).to eq "hello"
39
+ end
40
+
41
+ it "loads a data URL" do
42
+ loader.should_not_receive(:open)
43
+ expect(loader.load("data:image/png;base64,aGVsbG8=")).to eq "hello"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1100" height="210">
2
+ <rect x="100" y="100" width="900" height="100" fill="none" stroke="blue" stroke-width="2" />
3
+
4
+ <g stroke="green" stroke-width="10">
5
+ <line x1="120" y1="110" x2="120" y2="190" />
6
+ <line x1="140" y1="110" x2="140" y2="190" stroke-linecap="butt" />
7
+ <line x1="160" y1="110" x2="160" y2="190" stroke-linecap="round" />
8
+ <line x1="180" y1="110" x2="180" y2="190" stroke-linecap="square" />
9
+ <line x1="200" y1="110" x2="200" y2="190" />
10
+ <line x1="220" y1="110" x2="220" y2="190" stroke-linecap="invalid" />
11
+ </g>
12
+ </svg>
13
+
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600">
3
+ <g id="layer1" style="display:none">
4
+ <rect x="350" y="200" width="20" height="20" fill="green" />
5
+ <text x="361.80859" y="200" id="text2986" xml:space="preserve" style="font-size:24px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Helvetica">
6
+ <tspan x="361.80859" y="200" id="tspan2988">Hidden</tspan>
7
+ </text>
8
+ </g>
9
+ <g id="layer2">
10
+ <text x="365.24805" y="300" id="text2990" xml:space="preserve" style="font-size:24px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Helvetica">
11
+ <tspan x="365.24805" y="300" id="tspan2992">Visible</tspan></text>
12
+ </g>
13
+ </svg>
@@ -0,0 +1,36 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="205pt" style="width:362px;height:205px;" version="1.1" viewBox="0 0 362 205" width="362pt">
3
+ <g>
4
+ <line style="stroke: blue; stroke-width: 1.0; stroke-dasharray: 20,40;" x1="146" x2="146" y1="35.4883" y2="204.041"/>
5
+ <line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="204" x2="204" y1="35.4883" y2="204.041"/>
6
+ <rect fill="#FEFECE" height="30.4883" style="stroke: #A80036; stroke-width: 1.5;" width="47" x="121" y="0"/>
7
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="33" x="128" y="20.5352">Alice</text>
8
+ <rect fill="#FEFECE" height="30.4883" style="stroke: #A80036; stroke-width: 1.5;" width="40" x="182" y="0"/>
9
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="26" x="189" y="20.5352">Bob</text>
10
+ <polygon fill="#A80036" points="192,67.4883,202,71.4883,192,75.4883,196,71.4883" style="stroke: #A80036; stroke-width: 1.0;"/>
11
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="146.5" x2="198" y1="71.4883" y2="71.4883"/>
12
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="31" x="153.5" y="67.0566">hello</text>
13
+ <polygon fill="#FBFB77" points="5,50.4883,5,75.4883,138,75.4883,138,60.4883,128,50.4883,5,50.4883" style="stroke: #A80036; stroke-width: 1.0;"/>
14
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="128" x2="128" y1="50.4883" y2="60.4883"/>
15
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="138" x2="128" y1="60.4883" y2="60.4883"/>
16
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="112" x="11" y="68.0566">this is a first note</text>
17
+ <polygon fill="#A80036" points="157.5,106.7988,147.5,110.7988,157.5,114.7988,153.5,110.7988" style="stroke: #A80036; stroke-width: 1.0;"/>
18
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="151.5" x2="203" y1="110.7988" y2="110.7988"/>
19
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="16" x="163.5" y="106.3672">ok</text>
20
+ <polygon fill="#FBFB77" points="209,89.7988,209,114.7988,353,114.7988,353,99.7988,343,89.7988,209,89.7988" style="stroke: #A80036; stroke-width: 1.0;"/>
21
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="343" x2="343" y1="89.7988" y2="99.7988"/>
22
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="353" x2="343" y1="99.7988" y2="99.7988"/>
23
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="123" x="215" y="107.3672">this is another note</text>
24
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="204" x2="246" y1="159.2305" y2="159.2305"/>
25
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="246" x2="246" y1="159.2305" y2="172.2305"/>
26
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="205" x2="246" y1="172.2305" y2="172.2305"/>
27
+ <polygon fill="#A80036" points="215,168.2305,205,172.2305,215,176.2305,211,172.2305" style="stroke: #A80036; stroke-width: 1.0;"/>
28
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="84" x="211" y="154.4883">I am thinking</text>
29
+ <polygon fill="#FBFB77" points="52,129.1094,52,184.1094,195,184.1094,195,139.1094,185,129.1094,52,129.1094" style="stroke: #A80036; stroke-width: 1.0;"/>
30
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="185" x2="185" y1="129.1094" y2="139.1094"/>
31
+ <line style="stroke: #A80036; stroke-width: 1.0;" x1="195" x2="185" y1="139.1094" y2="139.1094"/>
32
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="39" x="58" y="146.6777">a note</text>
33
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="122" x="58" y="161.9883">can also be defined</text>
34
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="98" x="58" y="177.2988">on several lines</text>
35
+ </g>
36
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg style="fill: none;">
2
+ <g class="x axis" transform="translate(0,227.9359)">
3
+ <path class="domain" d="M0,0V0H470V0" style="fill: none; stroke: none;"></path>
4
+ <path d="M 44.82625 0 L 44.82625 -3 L 94 -3 L 94 0" class="cantilever" style="stroke: black; fill: none;"></path>
5
+ </g>
6
+ </svg>