prawn 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/README +16 -2
  2. data/Rakefile +3 -3
  3. data/data/images/arrow.png +0 -0
  4. data/data/images/arrow2.png +0 -0
  5. data/data/images/barcode_issue.png +0 -0
  6. data/data/images/ruport_type0.png +0 -0
  7. data/examples/cell.rb +14 -3
  8. data/examples/chinese_text_wrapping.rb +17 -0
  9. data/examples/family_based_styling.rb +21 -0
  10. data/examples/fancy_table.rb +4 -4
  11. data/examples/flowing_text_with_header_and_footer.rb +72 -0
  12. data/examples/font_size.rb +2 -2
  13. data/examples/lazy_bounding_boxes.rb +19 -0
  14. data/examples/table.rb +13 -11
  15. data/examples/text_flow.rb +1 -1
  16. data/lib/prawn.rb +44 -15
  17. data/lib/prawn/compatibility.rb +20 -7
  18. data/lib/prawn/document.rb +72 -122
  19. data/lib/prawn/document/bounding_box.rb +124 -24
  20. data/lib/prawn/document/internals.rb +107 -0
  21. data/lib/prawn/document/table.rb +99 -70
  22. data/lib/prawn/document/text.rb +92 -314
  23. data/lib/prawn/errors.rb +13 -2
  24. data/lib/prawn/font.rb +312 -1
  25. data/lib/prawn/font/cmap.rb +1 -1
  26. data/lib/prawn/font/metrics.rb +52 -49
  27. data/lib/prawn/font/wrapping.rb +14 -12
  28. data/lib/prawn/graphics.rb +23 -74
  29. data/lib/prawn/graphics/cell.rb +30 -25
  30. data/lib/prawn/graphics/color.rb +132 -0
  31. data/lib/prawn/images.rb +37 -16
  32. data/lib/prawn/images/png.rb +29 -24
  33. data/lib/prawn/pdf_object.rb +3 -1
  34. data/spec/bounding_box_spec.rb +12 -3
  35. data/spec/document_spec.rb +40 -72
  36. data/spec/font_spec.rb +97 -0
  37. data/spec/graphics_spec.rb +46 -99
  38. data/spec/images_spec.rb +4 -21
  39. data/spec/pdf_object_spec.rb +8 -8
  40. data/spec/png_spec.rb +47 -12
  41. data/spec/spec_helper.rb +5 -24
  42. data/spec/table_spec.rb +53 -59
  43. data/spec/text_spec.rb +28 -93
  44. data/vendor/pdf-inspector/README +18 -0
  45. data/vendor/pdf-inspector/lib/pdf/inspector.rb +25 -0
  46. data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +80 -0
  47. data/vendor/pdf-inspector/lib/pdf/inspector/page.rb +16 -0
  48. data/vendor/pdf-inspector/lib/pdf/inspector/text.rb +31 -0
  49. data/vendor/pdf-inspector/lib/pdf/inspector/xobject.rb +19 -0
  50. metadata +63 -38
  51. data/examples/on_page_start.rb +0 -17
  52. data/examples/table_bench.rb +0 -92
  53. data/spec/box_calculation_spec.rb +0 -17
data/spec/spec_helper.rb CHANGED
@@ -6,35 +6,16 @@ require "rubygems"
6
6
  require "test/spec"
7
7
  require "mocha"
8
8
  $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
9
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'vendor','pdf-inspector','lib')
9
10
  require "prawn"
10
- gem 'pdf-reader', ">=0.7.3"
11
- require "pdf/reader"
12
11
 
13
- module Prawn
14
- class Document
15
- public :ref
16
- end
17
- end
12
+ gem 'pdf-reader', ">=0.7.3"
13
+ require "pdf/reader"
14
+ require "pdf/inspector"
18
15
 
19
16
  def create_pdf
20
17
  @pdf = Prawn::Document.new(:left_margin => 0,
21
18
  :right_margin => 0,
22
19
  :top_margin => 0,
23
20
  :bottom_margin => 0)
24
- end
25
-
26
- def observer(klass)
27
- @output = @pdf.render
28
- obs = klass.new
29
- PDF::Reader.string(@output,obs)
30
- obs
31
- end
32
-
33
- def parse_pdf_object(obj)
34
- PDF::Reader::Parser.new(
35
- PDF::Reader::Buffer.new(sio = StringIO.new(obj)), nil).parse_token
36
- end
37
-
38
- def rb_flag
39
- ruby_18 { "rb" } || ruby_19 { "rb:ASCII-8BIT" }
40
- end
21
+ end
data/spec/table_spec.rb CHANGED
@@ -12,17 +12,17 @@ describe "A table's width" do
12
12
  end
13
13
 
14
14
  it "should calculate unspecified column widths as "+
15
- "(max(string_width) + 2*horizontal_padding)" do
15
+ "(max(string_width).ceil + 2*horizontal_padding)" do
16
16
  pdf = Prawn::Document.new
17
17
  hpad, fs = 3, 12
18
18
  columns = 2
19
19
  table = Prawn::Document::Table.new( [%w[ foo b ], %w[d foobar]], pdf,
20
20
  :horizontal_padding => hpad, :font_size => fs)
21
21
 
22
- col0_width = pdf.font_metrics.string_width("foo",fs)
23
- col1_width = pdf.font_metrics.string_width("foobar",fs)
22
+ col0_width = pdf.font.metrics.string_width("foo",fs)
23
+ col1_width = pdf.font.metrics.string_width("foobar",fs)
24
24
 
25
- table.width.should == col0_width + col1_width + 2*columns*hpad
25
+ table.width.should == col0_width.ceil + col1_width.ceil + 2*columns*hpad
26
26
  end
27
27
 
28
28
  it "should allow mixing autocalculated and preset"+
@@ -33,8 +33,8 @@ describe "A table's width" do
33
33
  stretchy_columns = 2
34
34
 
35
35
  col0_width = 50
36
- col1_width = pdf.font_metrics.string_width("foo",fs)
37
- col2_width = pdf.font_metrics.string_width("foobar",fs)
36
+ col1_width = pdf.font.metrics.string_width("foo",fs)
37
+ col2_width = pdf.font.metrics.string_width("foobar",fs)
38
38
  col3_width = 150
39
39
 
40
40
  table = Prawn::Document::Table.new( [%w[snake foo b apple],
@@ -42,76 +42,44 @@ describe "A table's width" do
42
42
  :horizontal_padding => hpad, :font_size => fs,
43
43
  :widths => { 0 => col0_width, 3 => col3_width } )
44
44
 
45
- table.width.should == col1_width + col2_width + 2*stretchy_columns*hpad +
46
- col0_width + col3_width
45
+ table.width.should == col1_width.ceil + col2_width.ceil +
46
+ 2*stretchy_columns*hpad +
47
+ col0_width.ceil + col3_width.ceil
47
48
 
48
49
  end
50
+
51
+ end
49
52
 
50
- it "should paginate large tables" do
51
- # 30 rows fit on the table with default setting, 31 exceed.
52
- data = [["foo"]] * 31
53
- pdf = Prawn::Document.new
54
-
55
- pdf.table data
56
- pdf.page_count.should == 2
57
-
58
- pdf.table data
59
- pdf.page_count.should == 3
60
- end
61
-
62
- it "should have a height of n rows + vertical padding" do
53
+ describe "A table's height" do
54
+
55
+ before :each do
63
56
  data = [["foo"],["bar"],["baaaz"]]
64
57
  pdf = Prawn::Document.new
65
- num_rows = data.length
66
- vpad = 4
67
-
58
+ @num_rows = data.length
59
+
60
+ @vpad = 4
68
61
  origin = pdf.y
69
- pdf.table data, :vertical_padding => vpad
70
-
71
- table_height = origin - pdf.y
72
-
73
- font_height = pdf.font_metrics.font_height(12)
74
-
75
- table_height.should.be.close(num_rows*font_height + 2*vpad*num_rows + vpad, 0.001)
76
- end
62
+ pdf.table data, :vertical_padding => @vpad
77
63
 
78
- end
64
+ @table_height = origin - pdf.y
79
65
 
80
- class TableTextObserver
81
- attr_accessor :font_settings, :size, :strings
82
-
83
- def initialize
84
- @font_settings = []
85
- @fonts = {}
86
- @strings = []
87
- end
66
+ @font_height = pdf.font.height
67
+ end
88
68
 
89
- def resource_font(*params)
90
- @fonts[params[0]] = params[1].basefont
69
+ it "should have a height of n rows" do
70
+ @table_height.should.be.close(
71
+ @num_rows*@font_height + 2*@vpad*@num_rows, 0.001 )
91
72
  end
92
-
93
- def set_text_font_and_size(*params)
94
- @font_settings << { :name => @fonts[params[0]], :size => params[1] }
95
- end
96
73
 
97
- def show_text(*params)
98
- @strings << params[0]
99
- end
100
-
101
- def show_text_with_positioning(*params)
102
- # ignore kerning information
103
- @strings << params[0].reject { |e| Numeric === e }.join
104
- end
105
74
  end
106
75
 
107
-
108
76
  describe "A table's content" do
109
77
 
110
78
  it "should output content cell by cell, row by row" do
111
79
  data = [["foo","bar"],["baz","bang"]]
112
80
  @pdf = Prawn::Document.new
113
81
  @pdf.table(data)
114
- output = observer(TableTextObserver)
82
+ output = PDF::Inspector::Text.analyze(@pdf.render)
115
83
  output.strings.should == data.flatten
116
84
  end
117
85
 
@@ -120,7 +88,7 @@ describe "A table's content" do
120
88
  headers = %w[a b]
121
89
  @pdf = Prawn::Document.new
122
90
  @pdf.table(data, :headers => headers)
123
- output = observer(TableTextObserver)
91
+ output = PDF::Inspector::Text.analyze(@pdf.render)
124
92
  output.strings.should == headers + data.flatten
125
93
  end
126
94
 
@@ -129,7 +97,7 @@ describe "A table's content" do
129
97
  headers = ["baz","foobar"]
130
98
  @pdf = Prawn::Document.new
131
99
  @pdf.table(data, :headers => headers)
132
- output = observer(TableTextObserver)
100
+ output = PDF::Inspector::Text.analyze(@pdf.render)
133
101
  output.strings.should == headers + data.flatten[0..-3] + headers +
134
102
  data.flatten[-2..-1]
135
103
  end
@@ -140,6 +108,32 @@ describe "A table's content" do
140
108
  @pdf = Prawn::Document.new
141
109
  @pdf.table(data)
142
110
  }.should.not.raise
111
+ end
112
+
113
+ it "should paginate for large tables" do
114
+ # 30 rows fit on the table with default setting, 31 exceed.
115
+ data = [["foo"]] * 31
116
+ pdf = Prawn::Document.new
117
+
118
+ pdf.table data
119
+ pdf.page_count.should == 2
120
+
121
+ pdf.table data
122
+ pdf.page_count.should == 3
143
123
  end
144
124
 
145
125
  end
126
+
127
+ describe "An invalid table" do
128
+
129
+ before(:each) do
130
+ @pdf = Prawn::Document.new
131
+ @bad_data = ["Single Nested Array"]
132
+ end
133
+
134
+ it "should raise error when invalid table data is given" do
135
+ assert_raises(Prawn::Errors::InvalidTableData) do
136
+ @pdf.table(@bad_data)
137
+ end
138
+ end
139
+ end
data/spec/text_spec.rb CHANGED
@@ -1,69 +1,8 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
4
 
5
- class TextObserver
6
-
7
- attr_accessor :font_settings, :size, :string
8
-
9
- def initialize
10
- @font_settings = []
11
- @fonts = {}
12
- end
13
-
14
- def resource_font(*params)
15
- @fonts[params[0]] = params[1].basefont
16
- end
17
-
18
- def set_text_font_and_size(*params)
19
- @font_settings << { :name => @fonts[params[0]], :size => params[1] }
20
- end
21
-
22
- def show_text(*params)
23
- @string = params[0]
24
- end
25
-
26
- def show_text_with_positioning(*params)
27
- @string = params[0].join
28
- end
29
- end
30
-
31
- class FontObserver
32
- attr_accessor :page_fonts
33
-
34
- def initialize
35
- @page_fonts = []
36
- end
37
-
38
- def resource_font(*params)
39
- @page_fonts.last << params[1].basefont
40
- end
41
-
42
- def begin_page(*params)
43
- @page_fonts << []
44
- end
45
- end
46
-
47
- describe "Font Metrics" do
48
-
49
- it "should default to Helvetica if no font is specified" do
50
- @pdf = Prawn::Document.new
51
- @pdf.font_metrics.should == Prawn::Font::Metrics["Helvetica"]
52
- end
53
-
54
- it "should use the currently set font for font_metrics" do
55
- @pdf = Prawn::Document.new
56
- @pdf.font "Courier"
57
- @pdf.font_metrics.should == Prawn::Font::Metrics["Courier"]
58
-
59
- comicsans = "#{Prawn::BASEDIR}/data/fonts/comicsans.ttf"
60
- @pdf.font(comicsans)
61
- @pdf.font_metrics.should == Prawn::Font::Metrics[comicsans]
62
- end
63
-
64
- end
65
-
66
- describe "when drawing text" do
5
+ describe "when drawing text" do
67
6
 
68
7
  before(:each) { create_pdf }
69
8
 
@@ -71,92 +10,88 @@ describe "when drawing text" do
71
10
  position = @pdf.y
72
11
  @pdf.text "Foo"
73
12
 
74
- @pdf.y.should.be.close(position - @pdf.font_metrics.font_height(12),
75
- 0.0001)
13
+ @pdf.y.should.be.close(position - @pdf.font.height, 0.0001)
76
14
 
77
15
  position = @pdf.y
78
16
  @pdf.text "Foo\nBar\nBaz"
79
- @pdf.y.should.be.close(position - 3*@pdf.font_metrics.font_height(12),
80
- 0.0001)
17
+ @pdf.y.should.be.close(position - 3*@pdf.font.height, 0.0001)
81
18
  end
82
19
 
83
20
  it "should default to 12 point helvetica" do
84
21
  @pdf.text "Blah", :at => [100,100]
85
- text = observer(TextObserver)
22
+ text = PDF::Inspector::Text.analyze(@pdf.render)
86
23
  text.font_settings[0][:name].should == :Helvetica
87
24
  text.font_settings[0][:size].should == 12
88
- text.string.should == "Blah"
25
+ text.strings.first.should == "Blah"
89
26
  end
90
27
 
91
28
  it "should allow setting font size" do
92
29
  @pdf.text "Blah", :at => [100,100], :size => 16
93
- text = observer(TextObserver)
30
+ text = PDF::Inspector::Text.analyze(@pdf.render)
94
31
  text.font_settings[0][:size].should == 16
95
32
  end
96
33
 
97
34
  it "should allow setting a default font size" do
98
- @pdf.font_size! 16
35
+ @pdf.font.size = 16
99
36
  @pdf.text "Blah"
100
- text = observer(TextObserver)
37
+ text = PDF::Inspector::Text.analyze(@pdf.render)
101
38
  text.font_settings[0][:size].should == 16
102
39
  end
103
40
 
104
41
  it "should allow overriding default font for a single instance" do
105
- @pdf.font_size! 16
42
+ @pdf.font.size = 16
106
43
 
107
44
  @pdf.text "Blah", :size => 11
108
45
  @pdf.text "Blaz"
109
- text = observer(TextObserver)
46
+ text = PDF::Inspector::Text.analyze(@pdf.render)
110
47
  text.font_settings[0][:size].should == 11
111
48
  text.font_settings[1][:size].should == 16
112
49
  end
113
50
 
114
-
115
51
  it "should allow setting a font size transaction with a block" do
116
- @pdf.font_size 16 do
52
+ @pdf.font.size 16 do
117
53
  @pdf.text 'Blah'
118
54
  end
119
55
 
120
56
  @pdf.text 'blah'
121
57
 
122
- text = observer(TextObserver)
58
+ text = PDF::Inspector::Text.analyze(@pdf.render)
123
59
  text.font_settings[0][:size].should == 16
124
60
  text.font_settings[1][:size].should == 12
125
61
  end
126
62
 
127
63
  it "should allow manual setting the font size " +
128
64
  "when in a font size block" do
129
- @pdf.font_size 16 do
65
+ @pdf.font.size(16) do
130
66
  @pdf.text 'Foo'
131
67
  @pdf.text 'Blah', :size => 11
132
68
  @pdf.text 'Blaz'
133
69
  end
134
- text = observer(TextObserver)
70
+ text = PDF::Inspector::Text.analyze(@pdf.render)
135
71
  text.font_settings[0][:size].should == 16
136
72
  text.font_settings[1][:size].should == 11
137
73
  text.font_settings[2][:size].should == 16
138
74
  end
139
75
 
140
76
  it "should allow registering of built-in font_settings on the fly" do
141
- @pdf.font "Courier"
77
+ @pdf.font "Times-Roman"
142
78
  @pdf.text "Blah", :at => [100,100]
143
- @pdf.font "Times-Roman"
79
+ @pdf.font "Courier"
144
80
  @pdf.text "Blaz", :at => [150,150]
145
- text = observer(TextObserver)
146
-
147
- text.font_settings[0][:name].should == :Courier
148
- text.font_settings[1][:name].should == :"Times-Roman"
81
+ text = PDF::Inspector::Text.analyze(@pdf.render)
82
+ text.font_settings[0][:name].should == :"Times-Roman"
83
+ text.font_settings[1][:name].should == :Courier
149
84
  end
150
85
 
151
86
  it "should utilise the same default font across multiple pages" do
152
87
  @pdf.text "Blah", :at => [100,100]
153
88
  @pdf.start_new_page
154
89
  @pdf.text "Blaz", :at => [150,150]
155
- text = observer(FontObserver)
90
+ text = PDF::Inspector::Text.analyze(@pdf.render)
156
91
 
157
- text.page_fonts.size.should == 2
158
- text.page_fonts[0][0].should == :Helvetica
159
- text.page_fonts[1][0].should == :Helvetica
92
+ text.font_settings.size.should == 2
93
+ text.font_settings[0][:name].should == :Helvetica
94
+ text.font_settings[1][:name].should == :Helvetica
160
95
  end
161
96
 
162
97
  it "should raise an exception when an unknown font is used" do
@@ -168,10 +103,10 @@ describe "when drawing text" do
168
103
  @pdf.text str
169
104
 
170
105
  # grab the text from the rendered PDF and ensure it matches
171
- text = observer(TextObserver)
172
- text.string.should == str
106
+ text = PDF::Inspector::Text.analyze(@pdf.render)
107
+ text.strings.first.should == str
173
108
  end
174
-
109
+
175
110
  if "spec".respond_to?(:encode!)
176
111
  # Handle non utf-8 string encodings in a sane way on M17N aware VMs
177
112
  it "should raise an exception when a utf-8 incompatible string is rendered" do
@@ -195,6 +130,6 @@ describe "when drawing text" do
195
130
  sjis_str = File.read("#{Prawn::BASEDIR}/data/shift_jis_text.txt")
196
131
  lambda { @pdf.text sjis_str }.should.raise(Prawn::Errors::IncompatibleStringEncoding)
197
132
  end
198
- end
133
+ end
199
134
 
200
135
  end
@@ -0,0 +1,18 @@
1
+ PDF::Inspector : A tool for analyzing PDF output
2
+
3
+ This library provides a number of PDF::Reader[0] based tools for use in testing
4
+ PDF output. Presently, the primary purpose of this tool is to support the
5
+ tests found in Prawn[1], a pure Ruby PDF generation library.
6
+
7
+ However, it may be useful to others, so we have made it available on Github[2]
8
+
9
+ Questions can be directed to the Prawn mailing list[3], but please remember
10
+ that this code is not necessarily suitable for production and has no officially
11
+ planned release date.
12
+
13
+ That having been said, patches are welcome!
14
+
15
+ [0] http://github.com/yob/pdf-reader
16
+ [1] http://github.com/sandal/prawn
17
+ [2] http://github.com/sandal/pdf-inspector
18
+ [3] http://groups.google.com/group/prawn-ruby
@@ -0,0 +1,25 @@
1
+ require "rubygems"
2
+ require "pdf/reader"
3
+ require "pdf/inspector/text"
4
+ require "pdf/inspector/xobject"
5
+ require "pdf/inspector/graphics"
6
+ require "pdf/inspector/page"
7
+
8
+ module PDF
9
+ class Inspector
10
+ def self.analyze(output,*args,&block)
11
+ obs = self.new(*args, &block)
12
+ PDF::Reader.string(output,obs)
13
+ obs
14
+ end
15
+
16
+ def self.analyze_file(filename,*args,&block)
17
+ analyze(File.open(filenmame, "rb") { |f| f.read },*args,&block)
18
+ end
19
+
20
+ def self.parse(obj)
21
+ PDF::Reader::Parser.new(
22
+ PDF::Reader::Buffer.new(StringIO.new(obj)), nil).parse_token
23
+ end
24
+ end
25
+ end