prawn-svg 0.15.0.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
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>