prawn 0.1.2 → 0.2.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 (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