prawn-svg 0.22.1 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -13
  3. data/lib/prawn-svg.rb +7 -0
  4. data/lib/prawn/svg/attributes.rb +1 -1
  5. data/lib/prawn/svg/attributes/color.rb +5 -0
  6. data/lib/prawn/svg/attributes/display.rb +1 -1
  7. data/lib/prawn/svg/attributes/font.rb +11 -11
  8. data/lib/prawn/svg/attributes/opacity.rb +3 -3
  9. data/lib/prawn/svg/css.rb +40 -0
  10. data/lib/prawn/svg/document.rb +23 -8
  11. data/lib/prawn/svg/elements/base.rb +20 -10
  12. data/lib/prawn/svg/elements/container.rb +1 -1
  13. data/lib/prawn/svg/elements/gradient.rb +7 -4
  14. data/lib/prawn/svg/elements/image.rb +2 -6
  15. data/lib/prawn/svg/elements/root.rb +4 -0
  16. data/lib/prawn/svg/elements/text.rb +14 -14
  17. data/lib/prawn/svg/font.rb +9 -91
  18. data/lib/prawn/svg/font_registry.rb +73 -0
  19. data/lib/prawn/svg/interface.rb +9 -22
  20. data/lib/prawn/svg/loaders/data.rb +18 -0
  21. data/lib/prawn/svg/loaders/file.rb +66 -0
  22. data/lib/prawn/svg/loaders/web.rb +28 -0
  23. data/lib/prawn/svg/state.rb +39 -0
  24. data/lib/prawn/svg/ttf.rb +61 -0
  25. data/lib/prawn/svg/url_loader.rb +35 -23
  26. data/lib/prawn/svg/version.rb +1 -1
  27. data/spec/integration_spec.rb +7 -6
  28. data/spec/prawn/svg/attributes/font_spec.rb +8 -5
  29. data/spec/prawn/svg/css_spec.rb +24 -0
  30. data/spec/prawn/svg/document_spec.rb +35 -10
  31. data/spec/prawn/svg/elements/base_spec.rb +32 -10
  32. data/spec/prawn/svg/elements/gradient_spec.rb +1 -1
  33. data/spec/prawn/svg/elements/text_spec.rb +4 -4
  34. data/spec/prawn/svg/font_registry_spec.rb +54 -0
  35. data/spec/prawn/svg/font_spec.rb +0 -27
  36. data/spec/prawn/svg/loaders/data_spec.rb +55 -0
  37. data/spec/prawn/svg/loaders/file_spec.rb +84 -0
  38. data/spec/prawn/svg/loaders/web_spec.rb +37 -0
  39. data/spec/prawn/svg/ttf_spec.rb +32 -0
  40. data/spec/prawn/svg/url_loader_spec.rb +90 -24
  41. data/spec/sample_svg/image03.svg +30 -0
  42. data/spec/sample_svg/tspan03-cc.svg +21 -0
  43. data/spec/sample_ttf/OpenSans-SemiboldItalic.ttf +0 -0
  44. data/spec/spec_helper.rb +17 -2
  45. metadata +28 -2
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Prawn::SVG::Elements::Gradient do
4
4
  let(:document) { Prawn::SVG::Document.new(svg, [800, 600], {width: 800, height: 600}) }
5
- let(:element) { Prawn::SVG::Elements::Gradient.new(document, document.root, [], {}) }
5
+ let(:element) { Prawn::SVG::Elements::Gradient.new(document, document.root, [], Prawn::SVG::State.new) }
6
6
 
7
7
  before do
8
8
  allow(element).to receive(:assert_compatible_prawn_version)
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/../../../spec_helper'
2
2
 
3
3
  describe Prawn::SVG::Elements::Text do
4
4
  let(:document) { Prawn::SVG::Document.new(svg, [800, 600], {}) }
5
- let(:element) { Prawn::SVG::Elements::Text.new(document, document.root, [], {}) }
5
+ let(:element) { Prawn::SVG::Elements::Text.new(document, document.root, [], Prawn::SVG::State.new) }
6
6
 
7
7
  describe "xml:space preserve" do
8
8
  let(:svg) { %(<text#{attributes}>some\n\t text</text>) }
@@ -33,11 +33,11 @@ describe Prawn::SVG::Elements::Text do
33
33
  end
34
34
 
35
35
  describe "when text-anchor is specified" do
36
- let(:svg) { '<g text-anchor="middle" font-family="sans-serif" font-size="12"><text x="50" y="14">Text</text></g>' }
36
+ let(:svg) { '<g text-anchor="middle" font-size="12"><text x="50" y="14">Text</text></g>' }
37
37
 
38
38
  it "should inherit text-anchor from parent element" do
39
39
  element.process
40
- expect(element.state[:text_anchor]).to eq 'middle'
40
+ expect(element.state.text_anchor).to eq 'middle'
41
41
  end
42
42
  end
43
43
 
@@ -48,7 +48,7 @@ describe Prawn::SVG::Elements::Text do
48
48
  element.process
49
49
 
50
50
  expect(element.base_calls).to eq [
51
- ["end_path", [], [
51
+ ["fill", [], [
52
52
  ["text_group", [], [
53
53
  ["character_spacing", [5.0], [
54
54
  ["draw_text", ["spaced", {:style=>nil, :at=>[0.0, 150.0]}], []]
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Prawn::SVG::FontRegistry do
4
+ describe "#load" do
5
+ let(:pdf) { Prawn::Document.new }
6
+ let(:font_registry) { Prawn::SVG::FontRegistry.new(pdf.font_families) }
7
+
8
+ it "matches a built in font" do
9
+ font_registry.load("blah, 'courier', nothing").name.should == 'Courier'
10
+ end
11
+
12
+ it "matches a default font" do
13
+ font_registry.load("serif").name.should == 'Times-Roman'
14
+ font_registry.load("blah, serif").name.should == 'Times-Roman'
15
+ font_registry.load("blah, serif , test").name.should == 'Times-Roman'
16
+ end
17
+
18
+ if Prawn::SVG::FontRegistry.new({}).installed_fonts["Verdana"]
19
+ it "matches a font installed on the system" do
20
+ font_registry.load("verdana, sans-serif").name.should == 'Verdana'
21
+ font_registry.load("VERDANA, sans-serif").name.should == 'Verdana'
22
+ font_registry.load("something, \"Times New Roman\", serif").name.should == "Times New Roman"
23
+ font_registry.load("something, Times New Roman, serif").name.should == "Times New Roman"
24
+ end
25
+ else
26
+ it "not running font test because we couldn't find Verdana installed on the system"
27
+ end
28
+
29
+ it "returns nil if it can't find any such font" do
30
+ font_registry.load("blah, thing").should be_nil
31
+ font_registry.load("").should be_nil
32
+ end
33
+ end
34
+
35
+ describe "::load_external_fonts" do
36
+ let(:ttf) { instance_double(Prawn::SVG::TTF, family: "Awesome Font", subfamily: "Italic") }
37
+ let(:ttf2) { instance_double(Prawn::SVG::TTF, family: "Awesome Font", subfamily: "Regular") }
38
+
39
+ before { Prawn::SVG::FontRegistry.external_font_families.clear }
40
+
41
+ it "scans the font path and loads in some fonts" do
42
+ expect(Prawn::SVG::FontRegistry).to receive(:font_path).and_return(["x"])
43
+ expect(Dir).to receive(:[]).with("x/**/*").and_return(["file.ttf", "second.ttf"])
44
+ expect(Prawn::SVG::TTF).to receive(:new).with("file.ttf").and_return(ttf)
45
+ expect(Prawn::SVG::TTF).to receive(:new).with("second.ttf").and_return(ttf2)
46
+ expect(File).to receive(:file?).at_least(:once).and_return(true)
47
+
48
+ Prawn::SVG::FontRegistry.load_external_fonts
49
+
50
+ result = Prawn::SVG::FontRegistry.external_font_families
51
+ expect(result).to eq("Awesome Font" => {:italic => "file.ttf", :normal => "second.ttf"})
52
+ end
53
+ end
54
+ end
@@ -1,31 +1,4 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper'
2
2
 
3
3
  describe Prawn::SVG::Font do
4
- describe :load do
5
- it "matches a built in font" do
6
- Prawn::SVG::Font.load("blah, 'courier', nothing").name.should == 'Courier'
7
- end
8
-
9
- it "matches a default font" do
10
- Prawn::SVG::Font.load("serif").name.should == 'Times-Roman'
11
- Prawn::SVG::Font.load("blah, serif").name.should == 'Times-Roman'
12
- Prawn::SVG::Font.load("blah, serif , test").name.should == 'Times-Roman'
13
- end
14
-
15
- if Prawn::SVG::Font.installed_fonts["Verdana"]
16
- it "matches a font installed on the system" do
17
- Prawn::SVG::Font.load("verdana, sans-serif").name.should == 'Verdana'
18
- Prawn::SVG::Font.load("VERDANA, sans-serif").name.should == 'Verdana'
19
- Prawn::SVG::Font.load("something, \"Times New Roman\", serif").name.should == "Times New Roman"
20
- Prawn::SVG::Font.load("something, Times New Roman, serif").name.should == "Times New Roman"
21
- end
22
- else
23
- it "not running font test because we couldn't find Verdana installed on the system"
24
- end
25
-
26
- it "returns nil if it can't find any such font" do
27
- Prawn::SVG::Font.load("blah, thing").should be_nil
28
- Prawn::SVG::Font.load("").should be_nil
29
- end
30
- end
31
4
  end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Prawn::SVG::Loaders::Data do
4
+ let(:uri) { URI(url) }
5
+
6
+ subject { Prawn::SVG::Loaders::Data.new.from_url(url) }
7
+
8
+ context "with a valid image/png data URL" do
9
+ let(:url) { "data:image/png;base64,aGVsbG8=" }
10
+
11
+ it "loads the data" do
12
+ expect(subject).to eq "hello"
13
+ end
14
+ end
15
+
16
+ context "with a valid image/jpeg data URL" do
17
+ let(:url) { "data:image/jpeg;base64,aGVsbG8=" }
18
+
19
+ it "loads the data" do
20
+ expect(subject).to eq "hello"
21
+ end
22
+ end
23
+
24
+ context "with a data URL that has extra metadata" do
25
+ let(:url) { "data:image/png;base64;metadata;here,aGVsbG8=" }
26
+
27
+ it "loads the data" do
28
+ expect(subject).to eq "hello"
29
+ end
30
+ end
31
+
32
+ context "with a URL that's not a data scheme" do
33
+ let(:url) { "http://some.host" }
34
+
35
+ it "returns nil" do
36
+ expect(subject).to be nil
37
+ end
38
+ end
39
+
40
+ context "with a data URL that's not an image" do
41
+ let(:url) { "data:application/pdf;base64,aGVsbG8=" }
42
+
43
+ it "raises" do
44
+ expect { subject }.to raise_error Prawn::SVG::UrlLoader::Error, /image/
45
+ end
46
+ end
47
+
48
+ context "with a data URL that's not base64 encoded" do
49
+ let(:url) { "data:image/png;base32,agvsbg" }
50
+
51
+ it "raises" do
52
+ expect { subject }.to raise_error Prawn::SVG::UrlLoader::Error, /base64/
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Prawn::SVG::Loaders::File do
4
+ let(:root_path) { "." }
5
+
6
+ subject { Prawn::SVG::Loaders::File.new(root_path).from_url(url) }
7
+
8
+ context "when an invalid path is supplied" do
9
+ let(:root_path) { "/does/not/exist" }
10
+
11
+ it "raises with an ArgumentError" do
12
+ expect { subject }.to raise_error ArgumentError, /is not a directory/
13
+ end
14
+ end
15
+
16
+ context "when a relative path is supplied" do
17
+ let(:url) { "some/relative/./path" }
18
+
19
+ it "loads the file" do
20
+ expect(File).to receive(:expand_path).with(".").and_return("/our/root/path")
21
+ expect(File).to receive(:expand_path).with("/our/root/path/some/relative/./path").and_return("/our/root/path/some/relative/path")
22
+
23
+ expect(Dir).to receive(:exist?).with("/our/root/path").and_return(true)
24
+ expect(File).to receive(:exist?).with("/our/root/path/some/relative/path").and_return(true)
25
+ expect(IO).to receive(:read).with("/our/root/path/some/relative/path").and_return("data")
26
+
27
+ expect(subject).to eq 'data'
28
+ end
29
+ end
30
+
31
+ context "when an absolute path without file scheme is supplied" do
32
+ let(:url) { "/some/absolute/./path" }
33
+
34
+ it "loads the file" do
35
+ expect(File).to receive(:expand_path).with(".").and_return("/some")
36
+ expect(File).to receive(:expand_path).with(url).and_return("/some/absolute/path")
37
+
38
+ expect(Dir).to receive(:exist?).with("/some").and_return(true)
39
+ expect(File).to receive(:exist?).with("/some/absolute/path").and_return(true)
40
+ expect(IO).to receive(:read).with("/some/absolute/path").and_return("data")
41
+
42
+ expect(subject).to eq 'data'
43
+ end
44
+ end
45
+
46
+ context "when an absolute path with file scheme is supplied" do
47
+ let(:url) { "file:///some/absolute/./path" }
48
+
49
+ it "loads the file" do
50
+ expect(File).to receive(:expand_path).with(".").and_return("/some")
51
+ expect(File).to receive(:expand_path).with("/some/absolute/./path").and_return("/some/absolute/path")
52
+
53
+ expect(Dir).to receive(:exist?).with("/some").and_return(true)
54
+ expect(File).to receive(:exist?).with("/some/absolute/path").and_return(true)
55
+ expect(IO).to receive(:read).with("/some/absolute/path").and_return("data")
56
+
57
+ expect(subject).to eq 'data'
58
+ end
59
+ end
60
+
61
+ context "when a path outside of our root is specified" do
62
+ let(:url) { "/some/absolute/./path" }
63
+
64
+ it "raises" do
65
+ expect(File).to receive(:expand_path).with(".").and_return("/other")
66
+ expect(File).to receive(:expand_path).with(url).and_return("/some/absolute/path")
67
+
68
+ expect(Dir).to receive(:exist?).with("/other").and_return(true)
69
+
70
+ expect { subject }.to raise_error Prawn::SVG::UrlLoader::Error, /not inside the root path/
71
+ end
72
+ end
73
+
74
+ context "when a file: url with a host is specified" do
75
+ let(:url) { "file://somewhere/somefile" }
76
+
77
+ it "raises" do
78
+ expect(File).to receive(:expand_path).with(".").and_return("/other")
79
+ expect(Dir).to receive(:exist?).with("/other").and_return(true)
80
+
81
+ expect { subject }.to raise_error Prawn::SVG::UrlLoader::Error, /with a host/
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Prawn::SVG::Loaders::Web do
4
+ let(:url) { "http://hello.there/path" }
5
+ let(:uri) { URI(url) }
6
+
7
+ subject { Prawn::SVG::Loaders::Web.new.from_url(url) }
8
+
9
+ it "loads an HTTP URL" do
10
+ expect(Net::HTTP).to receive(:get).with(uri).and_return("hello!")
11
+ expect(subject).to eq "hello!"
12
+ end
13
+
14
+ context "with an https URL" do
15
+ let(:url) { "https://hello.there/path"}
16
+
17
+ it "loads the HTTPS URL" do
18
+ expect(Net::HTTP).to receive(:get).with(uri).and_return("hello!")
19
+ expect(subject).to eq "hello!"
20
+ end
21
+ end
22
+
23
+ context "when the HTTP call raises" do
24
+ it "re-raises the error as UrlLoader errors" do
25
+ expect(Net::HTTP).to receive(:get).with(uri).and_raise(SocketError, "argh")
26
+ expect { subject }.to raise_error Prawn::SVG::UrlLoader::Error, 'argh'
27
+ end
28
+ end
29
+
30
+ context "with a non-http, non-https URL" do
31
+ let(:url) { "mailto:someone@something" }
32
+
33
+ it "returns nil" do
34
+ expect(subject).to be nil
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Prawn::SVG::TTF do
4
+ subject { Prawn::SVG::TTF.new(filename) }
5
+
6
+ context "with a truetype font" do
7
+ let(:filename) { "#{File.dirname(__FILE__)}/../../sample_ttf/OpenSans-SemiboldItalic.ttf" }
8
+
9
+ it "gets the English family and subfamily from the font file" do
10
+ expect(subject.family).to eq 'Open Sans'
11
+ expect(subject.subfamily).to eq 'Semibold Italic'
12
+ end
13
+ end
14
+
15
+ context "with a file that isn't a TTF" do
16
+ let(:filename) { __FILE__ }
17
+
18
+ it "has a nil family and subfamily" do
19
+ expect(subject.family).to be nil
20
+ expect(subject.subfamily).to be nil
21
+ end
22
+ end
23
+
24
+ context "with a file that doesn't exist" do
25
+ let(:filename) { "does_not_exist" }
26
+
27
+ it "has a nil family and subfamily" do
28
+ expect(subject.family).to be nil
29
+ expect(subject.subfamily).to be nil
30
+ end
31
+ end
32
+ end
@@ -1,46 +1,112 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Prawn::SVG::UrlLoader do
4
- let(:loader) { Prawn::SVG::UrlLoader.new(:enable_cache => true, :enable_web => true) }
4
+ let(:enable_cache) { true }
5
+ let(:enable_web) { true }
6
+ let(:enable_file) { "." }
7
+ let(:loader) { Prawn::SVG::UrlLoader.new(enable_cache: enable_cache, enable_web: enable_web, enable_file_with_root: enable_file) }
5
8
 
6
9
  describe "#initialize" do
7
10
  it "sets options" do
8
11
  expect(loader.enable_cache).to be true
9
- expect(loader.enable_web).to be true
10
12
  end
11
13
  end
12
14
 
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
15
+ describe "#load" do
16
+ let(:url) { "http://hello/there" }
17
+ let(:data_loader) { instance_double(Prawn::SVG::Loaders::Data) }
18
+ let(:web_loader) { instance_double(Prawn::SVG::Loaders::Web) }
19
+ let(:file_loader) { instance_double(Prawn::SVG::Loaders::File) }
20
+
21
+ before do
22
+ allow(Prawn::SVG::Loaders::Data).to receive(:new).and_return(data_loader)
23
+ allow(Prawn::SVG::Loaders::Web).to receive(:new).and_return(web_loader)
24
+ allow(Prawn::SVG::Loaders::File).to receive(:new).with(enable_file).and_return(file_loader)
17
25
  end
18
26
 
19
- it "doesn't accept schemes it doesn't like" do
20
- expect(loader.valid?("mail://valid.example/url")).to be false
27
+ subject { loader.load(url) }
28
+
29
+ it "calls the Data loader and returns its output if successful" do
30
+ expect(data_loader).to receive(:from_url).with(url).and_return("data")
31
+ expect(web_loader).not_to receive(:from_url)
32
+
33
+ expect(subject).to eq 'data'
21
34
  end
22
- end
23
35
 
24
- describe "#load" do
25
- let(:url) { "http://hello/there" }
36
+ it "calls the Web loader if the Data loader returns nothing, and returns its output if successful" do
37
+ expect(data_loader).to receive(:from_url).with(url)
38
+ expect(web_loader).to receive(:from_url).with(url).and_return("data")
39
+
40
+ expect(subject).to eq 'data'
41
+ end
42
+
43
+ it "calls the File loader if the Data and Web loaders return nothing, and returns its output if successful" do
44
+ expect(data_loader).to receive(:from_url).with(url)
45
+ expect(web_loader).to receive(:from_url).with(url)
46
+ expect(file_loader).to receive(:from_url).with(url).and_return("data")
47
+
48
+ expect(subject).to eq 'data'
49
+ end
50
+
51
+ it "raises if none of the loaders return any data" do
52
+ expect(data_loader).to receive(:from_url).with(url)
53
+ expect(web_loader).to receive(:from_url).with(url)
54
+ expect(file_loader).to receive(:from_url).with(url)
26
55
 
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!"
56
+ expect { subject }.to raise_error(Prawn::SVG::UrlLoader::Error, /No handler available/)
33
57
  end
34
58
 
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"
59
+ context "when caching is enabled" do
60
+ it "caches the result" do
61
+ expect(data_loader).to receive(:from_url).with(url).and_return("data")
62
+ expect(subject).to eq 'data'
63
+ expect(loader.retrieve_from_cache(url)).to eq 'data'
64
+ end
39
65
  end
40
66
 
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"
67
+ context "when caching is disabled" do
68
+ let(:enable_cache) { false }
69
+
70
+ it "does not cache the result" do
71
+ expect(data_loader).to receive(:from_url).with(url).and_return("data")
72
+ expect(subject).to eq 'data'
73
+ expect(loader.retrieve_from_cache(url)).to be nil
74
+ end
75
+ end
76
+
77
+ context "when the cache is populated" do
78
+ before { loader.add_to_cache(url, 'data') }
79
+
80
+ it "returns the cached value without calling a loader" do
81
+ expect(data_loader).not_to receive(:from_url)
82
+ expect(web_loader).not_to receive(:from_url)
83
+
84
+ expect(subject).to eq 'data'
85
+ end
86
+ end
87
+
88
+ context "when web requests are disabled" do
89
+ let(:enable_web) { false }
90
+
91
+ it "doesn't use the web loader" do
92
+ expect(data_loader).to receive(:from_url)
93
+ expect(web_loader).not_to receive(:from_url)
94
+ expect(file_loader).to receive(:from_url)
95
+
96
+ expect { subject }.to raise_error(Prawn::SVG::UrlLoader::Error, /No handler available/)
97
+ end
98
+ end
99
+
100
+ context "when file requests are disabled" do
101
+ let(:enable_file) { nil }
102
+
103
+ it "doesn't use the file loader" do
104
+ expect(data_loader).to receive(:from_url)
105
+ expect(web_loader).to receive(:from_url)
106
+ expect(file_loader).not_to receive(:from_url)
107
+
108
+ expect { subject }.to raise_error(Prawn::SVG::UrlLoader::Error, /No handler available/)
109
+ end
44
110
  end
45
111
  end
46
112
  end