prawn-svg 0.22.1 → 0.23.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 (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