prawn-svg 0.9.1.10 → 0.9.1.11

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 (41) hide show
  1. data/.gitignore +5 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +14 -0
  4. data/Rakefile +26 -0
  5. data/lib/prawn/svg/element.rb +7 -2
  6. data/lib/prawn/svg/font.rb +7 -4
  7. data/lib/prawn/svg/parser.rb +12 -4
  8. data/lib/prawn/svg/version.rb +5 -0
  9. data/prawn-svg.gemspec +23 -0
  10. data/spec/lib/parser_spec.rb +58 -0
  11. data/spec/lib/path_spec.rb +38 -0
  12. data/spec/lib/svg_spec.rb +22 -0
  13. data/spec/prawn/svg/document_spec.rb +27 -0
  14. data/spec/prawn/svg/element_spec.rb +32 -0
  15. data/spec/prawn/svg/font_spec.rb +31 -0
  16. data/spec/prawn/svg/parser/text_spec.rb +4 -0
  17. data/spec/sample_output/directory +0 -0
  18. data/spec/sample_svg/arcs01.svg +21 -0
  19. data/spec/sample_svg/circle01.svg +12 -0
  20. data/spec/sample_svg/cubic01.svg +37 -0
  21. data/spec/sample_svg/cubic01a.svg +40 -0
  22. data/spec/sample_svg/cubic02.svg +86 -0
  23. data/spec/sample_svg/ellipse01.svg +17 -0
  24. data/spec/sample_svg/line01.svg +22 -0
  25. data/spec/sample_svg/maths.svg +34 -0
  26. data/spec/sample_svg/omnigraffle.svg +41 -0
  27. data/spec/sample_svg/opacity01.svg +42 -0
  28. data/spec/sample_svg/polygon01.svg +17 -0
  29. data/spec/sample_svg/polyline01.svg +18 -0
  30. data/spec/sample_svg/quad01.svg +28 -0
  31. data/spec/sample_svg/rect01.svg +12 -0
  32. data/spec/sample_svg/rect02.svg +16 -0
  33. data/spec/sample_svg/rotate_scale.svg +38 -0
  34. data/spec/sample_svg/scruffy_graph.svg +134 -0
  35. data/spec/sample_svg/triangle01.svg +12 -0
  36. data/spec/sample_svg/tspan01.svg +17 -0
  37. data/spec/sample_svg/tspan02.svg +22 -0
  38. data/spec/sample_svg/tspan03.svg +21 -0
  39. data/spec/sample_svg/use.svg +17 -0
  40. data/spec/spec_helper.rb +9 -0
  41. metadata +103 -15
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ spec/sample_output/*.pdf
3
+ prawn-svg-*.gem
4
+ Gemfile.lock
5
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Declare your gem's dependencies in prawn-svg.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+ gemspec
7
+
8
+ # Declare any dependencies that are still in development here instead of in
9
+ # your gemspec. These might include edge Rails or gems from your path or
10
+ # Git. Remember to move these dependencies to your gemspec before releasing
11
+ # your gem to rubygems.org.
12
+
13
+ # To use debugger
14
+ # gem 'ruby-debug19', :require => 'ruby-debug'
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/gem_tasks'
4
+
5
+ begin
6
+ require 'rdoc/task'
7
+ rescue LoadError
8
+ require 'rdoc/rdoc'
9
+ require 'rake/rdoctask'
10
+ RDoc::Task = Rake::RDocTask
11
+ end
12
+
13
+ RDoc::Task.new(:rdoc) do |rdoc|
14
+ rdoc.rdoc_dir = 'rdoc'
15
+ rdoc.title = 'prawn-svg documentation'
16
+ rdoc.options << '--line-numbers'
17
+ rdoc.rdoc_files.include('README')
18
+ rdoc.rdoc_files.include('lib/**/*.rb')
19
+ end
20
+
21
+ require 'rspec/core/rake_task'
22
+
23
+ RSpec::Core::RakeTask.new(:spec) do |spec|
24
+ end
25
+
26
+ task :default => :spec
@@ -56,8 +56,13 @@ class Prawn::Svg::Element
56
56
  x, y = x.split(/\s+/) if y.nil?
57
57
  add_call_and_enter name, @document.distance(x), -@document.distance(y)
58
58
 
59
- when 'rotate'
60
- add_call_and_enter name, -arguments.first.to_f, :origin => [0, @document.y('0')]
59
+ when 'rotate'
60
+ rotation_arguments = arguments.first.split(/\s+/)
61
+ if (rotation_arguments.length == 3)
62
+ add_call_and_enter name, -rotation_arguments.first.to_f, :origin => [@document.x(rotation_arguments[1].to_f), @document.y(rotation_arguments[2].to_f)]
63
+ else
64
+ add_call_and_enter name, -arguments.first.to_f, :origin => [0, @document.y('0')]
65
+ end
61
66
 
62
67
  when 'scale'
63
68
  args = arguments.first.split(/\s+/)
@@ -1,5 +1,3 @@
1
- require 'iconv'
2
-
3
1
  class Prawn::Svg::Font
4
2
  BUILT_IN_FONTS = ["Courier", "Helvetica", "Times-Roman", "Symbol", "ZapfDingbats"]
5
3
 
@@ -59,7 +57,7 @@ class Prawn::Svg::Font
59
57
  def self.font_information(filename)
60
58
  File.open(filename, "r") do |f|
61
59
  x = f.read(12)
62
- table_count = x[4] * 256 + x[5]
60
+ table_count = x[4].ord * 256 + x[5].ord
63
61
  tables = f.read(table_count * 16)
64
62
 
65
63
  offset, length = table_count.times do |index|
@@ -86,7 +84,12 @@ class Prawn::Svg::Font
86
84
  field = data[offset..offset+length-1]
87
85
  names[name_id] = if platform_id == 0
88
86
  begin
89
- Iconv.iconv('UTF-8', 'UTF-16', field)
87
+ if field.respond_to?(:encode)
88
+ field.encode(Encoding::UTF16)
89
+ else
90
+ require "iconv"
91
+ Iconv.iconv('UTF-8', 'UTF-16', field)
92
+ end
90
93
  rescue
91
94
  field
92
95
  end
@@ -59,6 +59,9 @@ class Prawn::Svg::Parser
59
59
  "rect" => %w(width height),
60
60
  "path" => %w(d)
61
61
  }
62
+
63
+ USE_NEW_CIRCLE_CALL = Prawn::Document.new.respond_to?(:circle)
64
+ USE_NEW_ELLIPSE_CALL = Prawn::Document.new.respond_to?(:ellipse)
62
65
 
63
66
  def parse_element(element)
64
67
  attrs = element.attributes
@@ -106,11 +109,16 @@ class Prawn::Svg::Parser
106
109
  element.add_call "polygon", *points
107
110
 
108
111
  when 'circle'
109
- element.add_call "circle_at",
110
- [x(attrs['cx'] || "0"), y(attrs['cy'] || "0")], :radius => distance(attrs['r'])
111
-
112
+ if USE_NEW_CIRCLE_CALL
113
+ element.add_call "circle",
114
+ [x(attrs['cx'] || "0"), y(attrs['cy'] || "0")], distance(attrs['r'])
115
+ else
116
+ element.add_call "circle_at",
117
+ [x(attrs['cx'] || "0"), y(attrs['cy'] || "0")], :radius => distance(attrs['r'])
118
+ end
119
+
112
120
  when 'ellipse'
113
- element.add_call "ellipse_at",
121
+ element.add_call USE_NEW_ELLIPSE_CALL ? "ellipse" : "ellipse_at",
114
122
  [x(attrs['cx'] || "0"), y(attrs['cy'] || "0")], distance(attrs['rx']), distance(attrs['ry'])
115
123
 
116
124
  when 'rect'
@@ -0,0 +1,5 @@
1
+ module Prawn
2
+ module Svg
3
+ VERSION = '0.9.1.11'
4
+ end
5
+ end
data/prawn-svg.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/prawn/svg/version', __FILE__)
3
+
4
+ spec = Gem::Specification.new do |gem|
5
+ gem.name = 'prawn-svg'
6
+ gem.version = Prawn::Svg::VERSION
7
+ gem.summary = "SVG renderer for Prawn PDF library"
8
+ gem.description = "SVG renderer for Prawn PDF library"
9
+ gem.has_rdoc = false
10
+ gem.author = "Roger Nesbitt"
11
+ gem.email = "roger@seriousorange.com"
12
+ gem.homepage = "http://github.com/mogest/prawn-svg"
13
+
14
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
15
+ gem.files = `git ls-files`.split("\n")
16
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ gem.name = "prawn-svg"
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "prawn", ">= 0.8.4"
21
+ gem.add_development_dependency "rspec"
22
+ gem.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,58 @@
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 = mock
36
+ e.stub!(:name).and_return(name)
37
+ e.stub!(:attributes).and_return(attributes)
38
+
39
+ Prawn::Svg::Element.new(@document, e, [], {})
40
+ end
41
+
42
+ it "ignores tags it doesn't know about" do
43
+ calls = []
44
+ @parser.send :parse_element, mock_element("unknown")
45
+ calls.should == []
46
+ @document.warnings.length.should == 1
47
+ @document.warnings.first.should include("Unknown tag")
48
+ end
49
+
50
+ it "ignores tags that don't have all required attributes set" do
51
+ calls = []
52
+ @parser.send :parse_element, mock_element("ellipse", "rx" => "1")
53
+ calls.should == []
54
+ @document.warnings.length.should == 1
55
+ @document.warnings.first.should include("Must have attributes ry on tag ellipse")
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Prawn::Svg::Parser::Path do
4
+ before :each do
5
+ @path = Prawn::Svg::Parser::Path.new
6
+ end
7
+
8
+ describe "command parsing" do
9
+ it "correctly parses a valid path" do
10
+ calls = []
11
+ @path.stub!(:run_path_command) {|*args| calls << args}
12
+ @path.parse("A12.34 -56.78 89B4 5 C 6,7 T QX 0")
13
+
14
+ calls.should == [
15
+ ["A", [12.34, -56.78, 89]],
16
+ ["B", [4, 5]],
17
+ ["C", [6, 7]],
18
+ ["T", []],
19
+ ["Q", []],
20
+ ["X", [0]]
21
+ ]
22
+ end
23
+
24
+ it "correctly parses an empty path" do
25
+ @path.should_not_receive(:run_path_command)
26
+ @path.parse("").should == []
27
+ @path.parse(" ").should == []
28
+ end
29
+
30
+ it "raises on invalid characters in the path" do
31
+ lambda {@path.parse("M 10 % 20")}.should raise_error(Prawn::Svg::Parser::Path::InvalidError)
32
+ end
33
+
34
+ it "raises on numerical data before a command letter" do
35
+ lambda {@path.parse("10 P")}.should raise_error(Prawn::Svg::Parser::Path::InvalidError)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Prawn::Svg::Interface do
4
+ describe "sample file rendering" do
5
+ root = "#{File.dirname(__FILE__)}/../.."
6
+ files = Dir["#{root}/spec/sample_svg/*.svg"]
7
+
8
+ it "has at least 10 SVG sample files to test" do
9
+ files.length.should >= 10
10
+ end
11
+
12
+ files.each do |file|
13
+ it "renders the #{File.basename file} sample file without warnings or crashing" do
14
+ Prawn::Document.generate("#{root}/spec/sample_output/#{File.basename file}.pdf") do
15
+ r = svg IO.read(file), :at => [0, y], :width => 612 - 72
16
+ warnings = r[:warnings].reject {|w| w =~ /Verdana/ && w =~ /is not a known font/ }
17
+ warnings.should == []
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Prawn::Svg::Document do
4
+ before(:each) do
5
+ @document = Prawn::Svg::Document.new("<svg></svg>", [100, 100], {})
6
+ end
7
+
8
+ describe :points do
9
+ 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
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Prawn::Svg::Element do
4
+ before :each do
5
+ e = mock
6
+ e.stub!(:attributes).and_return({})
7
+ @element = Prawn::Svg::Element.new(nil, e, [], {})
8
+ end
9
+
10
+ describe :color_to_hex do
11
+ it "converts #xxx to a hex value" do
12
+ @element.send(:color_to_hex, "#9ab").should == "99aabb"
13
+ end
14
+
15
+ it "converts #xxxxxx to a hex value" do
16
+ @element.send(:color_to_hex, "#9ab123").should == "9ab123"
17
+ end
18
+
19
+ it "converts an html colour name to a hex value" do
20
+ @element.send(:color_to_hex, "White").should == "ffffff"
21
+ end
22
+
23
+ it "converts an rgb string to a hex value" do
24
+ @element.send(:color_to_hex, "rgb(16, 32, 48)").should == "102030"
25
+ @element.send(:color_to_hex, "rgb(-5, 50%, 120%)").should == "007fff"
26
+ end
27
+
28
+ it "scans the string and finds the first colour it can parse" do
29
+ @element.send(:color_to_hex, "function(#someurl, 0) nonexistent rgb( 3 ,4,5 ) white").should == "030405"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Prawn::Svg::Font do
4
+ describe :map_font_family_to_pdf_font do
5
+ it "matches a built in font" do
6
+ Prawn::Svg::Font.map_font_family_to_pdf_font("blah, 'courier', nothing").should == 'Courier'
7
+ end
8
+
9
+ it "matches a default font" do
10
+ Prawn::Svg::Font.map_font_family_to_pdf_font("serif").should == 'Times-Roman'
11
+ Prawn::Svg::Font.map_font_family_to_pdf_font("blah, serif").should == 'Times-Roman'
12
+ Prawn::Svg::Font.map_font_family_to_pdf_font("blah, serif , test").should == 'Times-Roman'
13
+ end
14
+
15
+ if !Prawn::Svg::Font.installed_fonts.empty?
16
+ it "matches a font installed on the system" do
17
+ Prawn::Svg::Font.map_font_family_to_pdf_font("verdana, sans-serif").should == 'verdana'
18
+ Prawn::Svg::Font.map_font_family_to_pdf_font("VERDANA, sans-serif").should == 'verdana'
19
+ Prawn::Svg::Font.map_font_family_to_pdf_font("something, \"Times New Roman\", serif").should == "times new roman"
20
+ Prawn::Svg::Font.map_font_family_to_pdf_font("something, Times New Roman, serif").should == "times new roman"
21
+ end
22
+ else
23
+ it "not running font test because we couldn't find a font directory"
24
+ end
25
+
26
+ it "returns nil if it can't find any such font" do
27
+ Prawn::Svg::Font.map_font_family_to_pdf_font("blah, thing").should be_nil
28
+ Prawn::Svg::Font.map_font_family_to_pdf_font("").should be_nil
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ describe Prawn::Svg::Parser::Text do
4
+ end
File without changes
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
3
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <svg width="12cm" height="5.25cm" viewBox="0 0 1200 400"
5
+ xmlns="http://www.w3.org/2000/svg" version="1.1">
6
+ <title>Example arcs01 - arc commands in path data</title>
7
+ <desc>Picture of a pie chart with two pie wedges and
8
+ a picture of a line with arc blips</desc>
9
+ <rect x="1" y="1" width="1198" height="398"
10
+ fill="none" stroke="blue" stroke-width="1" />
11
+ <path d="M300,200 h-150 a150,150 0 1,0 150,-150 z"
12
+ fill="red" stroke="blue" stroke-width="5" />
13
+ <path d="M275,175 v-150 a150,150 0 0,0 -150,150 z"
14
+ fill="yellow" stroke="blue" stroke-width="5" />
15
+ <path d="M600,350 l 50,-25
16
+ a25,25 -30 0,1 50,-25 l 50,-25
17
+ a25,50 -30 0,1 50,-25 l 50,-25
18
+ a25,75 -30 0,1 50,-25 l 50,-25
19
+ a25,100 -30 0,1 50,-25 l 50,-25"
20
+ fill="none" stroke="red" stroke-width="5" />
21
+ </svg>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
3
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <svg width="12cm" height="4cm" viewBox="0 0 1200 400"
5
+ xmlns="http://www.w3.org/2000/svg" version="1.1">
6
+ <desc>Example circle01 - circle filled with red and stroked with blue</desc>
7
+ <!-- Show outline of canvas using 'rect' element -->
8
+ <rect x="1" y="1" width="1198" height="398"
9
+ fill="none" stroke="blue" stroke-width="2"/>
10
+ <circle cx="600" cy="200" r="100"
11
+ fill="red" stroke="blue" stroke-width="10" />
12
+ </svg>
@@ -0,0 +1,37 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
3
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <svg width="5cm" height="4cm" viewBox="0 0 500 400"
5
+ xmlns="http://www.w3.org/2000/svg" version="1.1">
6
+ <title>Example cubic01- cubic Bézier commands in path data</title>
7
+ <desc>Picture showing a simple example of path data
8
+ using both a "C" and an "S" command,
9
+ along with annotations showing the control points
10
+ and end points</desc>
11
+ <style type="text/css"><![CDATA[
12
+ .Border { fill:none; stroke:blue; stroke-width:1 }
13
+ .Connect { fill:none; stroke:#888888; stroke-width:2 }
14
+ .SamplePath { fill:none; stroke:red; stroke-width:5 }
15
+ .EndPoint { fill:none; stroke:#888888; stroke-width:2 }
16
+ .CtlPoint { fill:#888888; stroke:none }
17
+ .AutoCtlPoint { fill:none; stroke:blue; stroke-width:4 }
18
+ .Label { font-size:22; font-family:Verdana }
19
+ ]]></style>
20
+ <rect class="Border" x="1" y="1" width="498" height="398" />
21
+ <polyline class="Connect" points="100,200 100,100" />
22
+ <polyline class="Connect" points="250,100 250,200" />
23
+ <polyline class="Connect" points="250,200 250,300" />
24
+ <polyline class="Connect" points="400,300 400,200" />
25
+ <path style=" fill:none; stroke:red; stroke-width:5" d="M100,200 C100,100 250,100 250,200
26
+ S400,300 400,200" />
27
+ <circle class="EndPoint" cx="100" cy="200" r="10" />
28
+ <circle class="EndPoint" cx="250" cy="200" r="10" />
29
+ <circle class="EndPoint" cx="400" cy="200" r="10" />
30
+ <circle class="CtlPoint" cx="100" cy="100" r="10" />
31
+ <circle class="CtlPoint" cx="250" cy="100" r="10" />
32
+ <circle class="CtlPoint" cx="400" cy="300" r="10" />
33
+ <circle class="AutoCtlPoint" cx="250" cy="300" r="9" />
34
+ <text class="Label" x="25" y="70">M100,200 C100,100 250,100 250,200</text>
35
+ <text class="Label" x="325" y="350"
36
+ style="text-anchor:middle">S400,300 400,200</text>
37
+ </svg>