prawn 1.0.0.rc1 → 1.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (203) hide show
  1. data/Gemfile +18 -0
  2. data/README.md +5 -3
  3. data/Rakefile +8 -14
  4. data/data/pdfs/nested_pages.pdf +13 -13
  5. data/lib/prawn.rb +3 -1
  6. data/lib/prawn/compatibility.rb +46 -10
  7. data/lib/prawn/core.rb +3 -1
  8. data/lib/prawn/core/document_state.rb +2 -1
  9. data/lib/prawn/core/object_store.rb +61 -5
  10. data/lib/prawn/core/page.rb +3 -6
  11. data/lib/prawn/core/pdf_object.rb +21 -4
  12. data/lib/prawn/core/reference.rb +6 -2
  13. data/lib/prawn/core/text.rb +4 -4
  14. data/lib/prawn/core/text/formatted/line_wrap.rb +23 -8
  15. data/lib/prawn/document.rb +21 -15
  16. data/lib/prawn/document/bounding_box.rb +3 -3
  17. data/lib/prawn/document/column_box.rb +22 -4
  18. data/lib/prawn/document/snapshot.rb +1 -1
  19. data/lib/prawn/encoding.rb +1 -1
  20. data/lib/prawn/errors.rb +4 -0
  21. data/lib/prawn/font.rb +1 -1
  22. data/lib/prawn/font/afm.rb +30 -72
  23. data/lib/prawn/font/ttf.rb +6 -33
  24. data/lib/prawn/graphics.rb +148 -23
  25. data/lib/prawn/graphics/color.rb +8 -1
  26. data/lib/prawn/graphics/patterns.rb +137 -0
  27. data/lib/prawn/images.rb +25 -19
  28. data/lib/prawn/images/jpg.rb +4 -4
  29. data/lib/prawn/images/png.rb +18 -12
  30. data/lib/prawn/security.rb +6 -4
  31. data/lib/prawn/soft_mask.rb +94 -0
  32. data/lib/prawn/table.rb +136 -31
  33. data/lib/prawn/table/cell.rb +260 -29
  34. data/lib/prawn/table/cell/span_dummy.rb +88 -0
  35. data/lib/prawn/table/cell/text.rb +36 -14
  36. data/lib/prawn/table/cells.rb +91 -41
  37. data/lib/prawn/text.rb +3 -2
  38. data/lib/prawn/text/formatted/box.rb +14 -5
  39. data/lib/prawn/text/formatted/fragment.rb +33 -22
  40. data/lib/prawn/text/formatted/parser.rb +5 -2
  41. data/lib/prawn/utilities.rb +44 -0
  42. data/manual/basic_concepts/adding_pages.rb +27 -0
  43. data/manual/basic_concepts/basic_concepts.rb +34 -0
  44. data/manual/basic_concepts/creation.rb +39 -0
  45. data/manual/basic_concepts/cursor.rb +33 -0
  46. data/manual/basic_concepts/measurement.rb +25 -0
  47. data/manual/basic_concepts/origin.rb +38 -0
  48. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  49. data/manual/bounding_box/bounding_box.rb +39 -0
  50. data/manual/bounding_box/bounds.rb +49 -0
  51. data/manual/bounding_box/canvas.rb +24 -0
  52. data/manual/bounding_box/creation.rb +23 -0
  53. data/manual/bounding_box/indentation.rb +46 -0
  54. data/manual/bounding_box/nesting.rb +45 -0
  55. data/manual/bounding_box/russian_boxes.rb +40 -0
  56. data/manual/bounding_box/stretchy.rb +31 -0
  57. data/manual/document_and_page_options/background.rb +27 -0
  58. data/manual/document_and_page_options/document_and_page_options.rb +31 -0
  59. data/manual/document_and_page_options/metadata.rb +23 -0
  60. data/manual/document_and_page_options/page_margins.rb +38 -0
  61. data/manual/document_and_page_options/page_size.rb +34 -0
  62. data/manual/example_file.rb +116 -0
  63. data/manual/example_helper.rb +430 -0
  64. data/manual/example_package.rb +53 -0
  65. data/manual/example_section.rb +46 -0
  66. data/manual/graphics/circle_and_ellipse.rb +22 -0
  67. data/manual/graphics/color.rb +24 -0
  68. data/manual/graphics/common_lines.rb +28 -0
  69. data/manual/graphics/fill_and_stroke.rb +42 -0
  70. data/manual/graphics/fill_rules.rb +37 -0
  71. data/manual/graphics/gradients.rb +37 -0
  72. data/manual/graphics/graphics.rb +58 -0
  73. data/manual/graphics/helper.rb +17 -0
  74. data/manual/graphics/line_width.rb +35 -0
  75. data/manual/graphics/lines_and_curves.rb +41 -0
  76. data/manual/graphics/polygon.rb +29 -0
  77. data/manual/graphics/rectangle.rb +21 -0
  78. data/manual/graphics/rotate.rb +28 -0
  79. data/manual/graphics/scale.rb +41 -0
  80. data/manual/graphics/soft_masks.rb +46 -0
  81. data/manual/graphics/stroke_cap.rb +31 -0
  82. data/manual/graphics/stroke_dash.rb +43 -0
  83. data/manual/graphics/stroke_join.rb +30 -0
  84. data/manual/graphics/translate.rb +29 -0
  85. data/manual/graphics/transparency.rb +35 -0
  86. data/manual/images/absolute_position.rb +23 -0
  87. data/manual/images/fit.rb +21 -0
  88. data/manual/images/horizontal.rb +25 -0
  89. data/manual/images/images.rb +40 -0
  90. data/manual/images/plain_image.rb +18 -0
  91. data/manual/images/scale.rb +22 -0
  92. data/manual/images/vertical.rb +28 -0
  93. data/manual/images/width_and_height.rb +25 -0
  94. data/manual/layout/boxes.rb +27 -0
  95. data/manual/layout/content.rb +25 -0
  96. data/manual/layout/layout.rb +28 -0
  97. data/manual/layout/simple_grid.rb +23 -0
  98. data/manual/manual/cover.rb +26 -0
  99. data/manual/manual/foreword.rb +13 -0
  100. data/manual/manual/how_to_read_this_manual.rb +41 -0
  101. data/manual/manual/manual.rb +36 -0
  102. data/manual/outline/add_subsection_to.rb +61 -0
  103. data/manual/outline/insert_section_after.rb +47 -0
  104. data/manual/outline/outline.rb +32 -0
  105. data/manual/outline/sections_and_pages.rb +67 -0
  106. data/manual/repeatable_content/page_numbering.rb +54 -0
  107. data/manual/repeatable_content/repeatable_content.rb +31 -0
  108. data/manual/repeatable_content/repeater.rb +55 -0
  109. data/manual/repeatable_content/stamp.rb +41 -0
  110. data/manual/security/encryption.rb +31 -0
  111. data/manual/security/permissions.rb +38 -0
  112. data/manual/security/security.rb +28 -0
  113. data/manual/syntax_highlight.rb +52 -0
  114. data/manual/table/basic_block.rb +53 -0
  115. data/manual/table/before_rendering_page.rb +26 -0
  116. data/manual/table/cell_border_lines.rb +24 -0
  117. data/manual/table/cell_borders_and_bg.rb +31 -0
  118. data/manual/table/cell_dimensions.rb +30 -0
  119. data/manual/table/cell_text.rb +38 -0
  120. data/manual/table/column_widths.rb +30 -0
  121. data/manual/table/content_and_subtables.rb +39 -0
  122. data/manual/table/creation.rb +27 -0
  123. data/manual/table/filtering.rb +36 -0
  124. data/manual/table/flow_and_header.rb +17 -0
  125. data/manual/table/image_cells.rb +33 -0
  126. data/manual/table/position.rb +29 -0
  127. data/manual/table/row_colors.rb +20 -0
  128. data/manual/table/span.rb +30 -0
  129. data/manual/table/style.rb +22 -0
  130. data/manual/table/table.rb +52 -0
  131. data/manual/table/width.rb +27 -0
  132. data/manual/templates/full_template.rb +23 -0
  133. data/manual/templates/page_template.rb +47 -0
  134. data/manual/templates/templates.rb +26 -0
  135. data/manual/text/alignment.rb +44 -0
  136. data/manual/text/color.rb +24 -0
  137. data/manual/text/column_box.rb +32 -0
  138. data/manual/text/fallback_fonts.rb +37 -0
  139. data/manual/text/font.rb +41 -0
  140. data/manual/text/font_size.rb +45 -0
  141. data/manual/text/font_style.rb +23 -0
  142. data/manual/text/formatted_callbacks.rb +60 -0
  143. data/manual/text/formatted_text.rb +50 -0
  144. data/manual/text/free_flowing_text.rb +51 -0
  145. data/manual/text/group.rb +29 -0
  146. data/manual/text/inline.rb +43 -0
  147. data/manual/text/kerning_and_character_spacing.rb +39 -0
  148. data/manual/text/leading.rb +25 -0
  149. data/manual/text/line_wrapping.rb +41 -0
  150. data/manual/text/paragraph_indentation.rb +26 -0
  151. data/manual/text/positioned_text.rb +38 -0
  152. data/manual/text/registering_families.rb +48 -0
  153. data/manual/text/rendering_and_color.rb +37 -0
  154. data/manual/text/right_to_left_text.rb +43 -0
  155. data/manual/text/rotation.rb +43 -0
  156. data/manual/text/single_usage.rb +37 -0
  157. data/manual/text/text.rb +75 -0
  158. data/manual/text/text_box_excess.rb +32 -0
  159. data/manual/text/text_box_extensions.rb +45 -0
  160. data/manual/text/text_box_overflow.rb +44 -0
  161. data/manual/text/utf8.rb +28 -0
  162. data/manual/text/win_ansi_charset.rb +59 -0
  163. data/prawn.gemspec +10 -7
  164. data/spec/bounding_box_spec.rb +107 -17
  165. data/spec/cell_spec.rb +66 -40
  166. data/spec/column_box_spec.rb +33 -0
  167. data/spec/document_spec.rb +45 -24
  168. data/spec/extensions/encoding_helpers.rb +6 -0
  169. data/spec/extensions/mocha.rb +1 -0
  170. data/spec/font_spec.rb +71 -53
  171. data/spec/formatted_text_arranger_spec.rb +19 -19
  172. data/spec/formatted_text_box_spec.rb +16 -16
  173. data/spec/formatted_text_fragment_spec.rb +6 -6
  174. data/spec/graphics_spec.rb +96 -31
  175. data/spec/grid_spec.rb +2 -2
  176. data/spec/images_spec.rb +18 -10
  177. data/spec/jpg_spec.rb +1 -1
  178. data/spec/line_wrap_spec.rb +14 -14
  179. data/spec/measurement_units_spec.rb +2 -2
  180. data/spec/name_tree_spec.rb +6 -6
  181. data/spec/object_store_spec.rb +17 -17
  182. data/spec/outline_spec.rb +35 -17
  183. data/spec/pdf_object_spec.rb +3 -1
  184. data/spec/png_spec.rb +22 -19
  185. data/spec/reference_spec.rb +24 -1
  186. data/spec/repeater_spec.rb +9 -9
  187. data/spec/security_spec.rb +3 -3
  188. data/spec/snapshot_spec.rb +3 -3
  189. data/spec/soft_mask_spec.rb +117 -0
  190. data/spec/span_spec.rb +4 -4
  191. data/spec/spec_helper.rb +12 -6
  192. data/spec/stamp_spec.rb +12 -12
  193. data/spec/stroke_styles_spec.rb +5 -5
  194. data/spec/table_spec.rb +458 -88
  195. data/spec/template_spec.rb +108 -54
  196. data/spec/text_at_spec.rb +17 -17
  197. data/spec/text_box_spec.rb +76 -45
  198. data/spec/text_rendering_mode_spec.rb +5 -5
  199. data/spec/text_spacing_spec.rb +4 -4
  200. data/spec/text_spec.rb +44 -40
  201. metadata +419 -250
  202. data/lib/prawn/graphics/gradient.rb +0 -84
  203. data/lib/prawn/security/arcfour.rb +0 -51
@@ -66,10 +66,10 @@ describe "Document encryption" do
66
66
  should == 0b1111_1111_1111_1111_1111_1111_1101_1111
67
67
  end
68
68
 
69
- it "should raise ArgumentError if invalid option is provided" do
69
+ it "should raise_error ArgumentError if invalid option is provided" do
70
70
  lambda {
71
71
  doc_with_permissions(:modify_document => false)
72
- }.should.raise(ArgumentError)
72
+ }.should raise_error(ArgumentError)
73
73
  end
74
74
 
75
75
  end
@@ -89,7 +89,7 @@ describe "Document encryption" do
89
89
  end
90
90
 
91
91
  it "should calculate the correct owner hash" do
92
- @pdf.owner_password_hash.unpack("H*").first.should.match(/^61CA855012/i)
92
+ @pdf.owner_password_hash.unpack("H*").first.should match(/^61CA855012/i)
93
93
  end
94
94
 
95
95
  it "should calculate the correct user hash" do
@@ -76,7 +76,7 @@ describe "Prawn::Document#transaction" do
76
76
  rescue Prawn::Errors::CannotGroup
77
77
  add_lines(pdf)
78
78
  end
79
- end.should.not.raise#(Prawn::Document::Snapshot::RollbackTransaction)
79
+ end.should_not raise_error#(Prawn::Document::Snapshot::RollbackTransaction)
80
80
  end
81
81
  end
82
82
 
@@ -141,7 +141,7 @@ describe "Prawn::Document#transaction" do
141
141
  Prawn::Document.new do |pdf|
142
142
  pdf.add_dest("dest", pdf.dest_fit_horizontally(pdf.cursor, pdf.page))
143
143
  pdf.text("Hello world")
144
- lambda { pdf.transaction{} }.should.not.raise
144
+ lambda { pdf.transaction{} }.should_not raise_error
145
145
  end
146
146
  end
147
147
 
@@ -166,7 +166,7 @@ describe "Prawn::Document#transaction" do
166
166
  rollback
167
167
  end
168
168
  end
169
- pdf.render.should.not =~ /\/Stamp1 Do/
169
+ pdf.render.should_not =~ /\/Stamp1 Do/
170
170
  end
171
171
 
172
172
  end
@@ -0,0 +1,117 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
2
+
3
+ module SoftMaskHelper
4
+ def make_soft_mask
5
+ @pdf.save_graphics_state do
6
+ @pdf.soft_mask do
7
+ if block_given?
8
+ yield
9
+ else
10
+ @pdf.fill_color '808080'
11
+ @pdf.fill_rectangle [100, 100], 200, 200
12
+ end
13
+ end
14
+
15
+ @pdf.fill_color '000000'
16
+ @pdf.fill_rectangle [0, 0], 200, 200
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "Document with soft masks" do
22
+
23
+ include SoftMaskHelper
24
+
25
+ it "should have PDF version at least 1.4" do
26
+ create_pdf
27
+ make_soft_mask
28
+ str = @pdf.render
29
+ str[0,8].should == "%PDF-1.4"
30
+ end
31
+
32
+ it "should create a new extended graphics state for each unique soft mask" do
33
+ create_pdf
34
+
35
+ make_soft_mask do
36
+ @pdf.fill_color '808080'
37
+ @pdf.fill_rectangle [100, 100], 200, 200
38
+ end
39
+
40
+ make_soft_mask do
41
+ @pdf.fill_color '808080'
42
+ @pdf.fill_rectangle [10, 10], 200, 200
43
+ end
44
+
45
+ extgstates = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates
46
+ extgstates.length.should == 2
47
+ end
48
+
49
+ it "a new extended graphics state should contain soft mask with drawing instructions" do
50
+ create_pdf
51
+
52
+ make_soft_mask do
53
+ @pdf.fill_color '808080'
54
+ @pdf.fill_rectangle [100, 100], 200, 200
55
+ end
56
+
57
+ extgstate = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates.first
58
+ extgstate[:soft_mask][:G].data.should == "q\n/DeviceRGB cs\n0.000 0.000 0.000 scn\n/DeviceRGB CS\n0.000 0.000 0.000 SCN\n1 w\n0 J\n0 j\n[ ] 0 d\n/DeviceRGB cs\n0.502 0.502 0.502 scn\n100.000 -100.000 200.000 200.000 re\nf\nQ\n"
59
+ end
60
+
61
+ it "should not create duplicate extended graphics states" do
62
+ create_pdf
63
+
64
+ make_soft_mask do
65
+ @pdf.fill_color '808080'
66
+ @pdf.fill_rectangle [100, 100], 200, 200
67
+ end
68
+
69
+ make_soft_mask do
70
+ @pdf.fill_color '808080'
71
+ @pdf.fill_rectangle [100, 100], 200, 200
72
+ end
73
+
74
+ extgstates = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates
75
+ extgstates.length.should == 1
76
+ end
77
+
78
+ it "should not have objects that are not used for extended graphic state" do
79
+ @pdf = Prawn::Document.new(:margin => 0, :optimize_objects => true)
80
+
81
+ make_soft_mask do
82
+ @pdf.fill_color '808080'
83
+ @pdf.fill_rectangle [100, 100], 200, 200
84
+ end
85
+
86
+ make_soft_mask do
87
+ @pdf.fill_color '808080'
88
+ @pdf.fill_rectangle [100, 100], 200, 200
89
+ end
90
+
91
+ reader = PDF::Reader.new(StringIO.open(@pdf.render))
92
+
93
+ groups = reader.objects.select { |obj|
94
+ o = obj[1]
95
+ o.is_a?(Hash) && o[:Type] == :Group
96
+ }
97
+ groups.length.should == 1
98
+
99
+ forms = reader.objects.select { |obj|
100
+ o = obj[1]
101
+ o.is_a?(PDF::Reader::Stream) && o.hash[:Type] == :XObject && o.hash[:Subtype] == :Form
102
+ }
103
+ forms.length.should == 1
104
+
105
+ masks = reader.objects.select { |obj|
106
+ o = obj[1]
107
+ o.is_a?(Hash) && o[:Type] == :Mask
108
+ }
109
+ masks.length.should == 1
110
+
111
+ ext_g_states = reader.objects.select { |obj|
112
+ o = obj[1]
113
+ o.is_a?(Hash) && o[:Type] == :ExtGState
114
+ }
115
+ ext_g_states.length.should == 1
116
+ end
117
+ end
@@ -4,22 +4,22 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
4
 
5
5
  describe "drawing span" do
6
6
 
7
- def setup
7
+ before do
8
8
  Prawn.debug = false
9
9
  create_pdf
10
10
  end
11
11
 
12
- def teardown
12
+ after do
13
13
  Prawn.debug = true
14
14
  end
15
15
 
16
16
  it "should only accept :position as option in debug mode" do
17
17
  Prawn.debug = true
18
- lambda { @pdf.span(350, {:x => 3}) {} }.should.raise(Prawn::Errors::UnknownOption)
18
+ lambda { @pdf.span(350, {:x => 3}) {} }.should raise_error(Prawn::Errors::UnknownOption)
19
19
  end
20
20
 
21
21
  it "should have raise an error if :position is invalid" do
22
- lambda { @pdf.span(350, :position => :x) {} }.should.raise(ArgumentError)
22
+ lambda { @pdf.span(350, :position => :x) {} }.should raise_error(ArgumentError)
23
23
  end
24
24
 
25
25
  it "should restore the margin box when bounding box exits" do
@@ -2,7 +2,6 @@
2
2
 
3
3
  puts "Prawn specs: Running on Ruby Version: #{RUBY_VERSION}"
4
4
 
5
- require "rubygems"
6
5
  require "bundler"
7
6
  Bundler.setup
8
7
 
@@ -11,11 +10,21 @@ require "prawn"
11
10
 
12
11
  Prawn.debug = true
13
12
 
14
- require "test/spec"
15
- require "mocha"
13
+ #require "test/spec"
14
+ require "rspec"
15
+ require "mocha/api"
16
16
  require "pdf/reader"
17
17
  require "pdf/inspector"
18
18
 
19
+ # Requires supporting ruby files with custom matchers and macros, etc,
20
+ # in spec/extensions/ and its subdirectories.
21
+ Dir[File.dirname(__FILE__) + "/extensions/**/*.rb"].each {|f| require f }
22
+
23
+ RSpec.configure do |config|
24
+ config.mock_framework = :mocha
25
+ config.include EncodingHelpers
26
+ end
27
+
19
28
  def create_pdf(klass=Prawn::Document)
20
29
  @pdf = klass.new(:margin => 0)
21
30
  end
@@ -25,6 +34,3 @@ module Prawn::Graphics
25
34
  public :map_to_absolute
26
35
  end
27
36
 
28
- require File.expand_path(File.join(File.dirname(__FILE__),
29
- %w[extensions mocha]))
30
-
@@ -7,7 +7,7 @@ describe "create_stamp before any page is added" do
7
7
  @pdf.create_stamp("my_stamp") do
8
8
  @pdf.font.height
9
9
  end
10
- }.should.not.raise(Prawn::Errors::NotOnPage)
10
+ }.should_not raise_error(Prawn::Errors::NotOnPage)
11
11
  end
12
12
  it "should work with setting color" do
13
13
  @pdf = Prawn::Document.new(:skip_page_creation => true)
@@ -15,7 +15,7 @@ describe "create_stamp before any page is added" do
15
15
  @pdf.create_stamp("my_stamp") do
16
16
  @pdf.fill_color = 'ff0000'
17
17
  end
18
- }.should.not.raise(Prawn::Errors::NotOnPage)
18
+ }.should_not raise_error(Prawn::Errors::NotOnPage)
19
19
  end
20
20
  end
21
21
 
@@ -33,21 +33,21 @@ describe "#stamp_at" do
33
33
  end
34
34
 
35
35
  describe "Document with a stamp" do
36
- it "should raise NameTaken error when attempt to create stamp "+
36
+ it "should raise_error NameTaken error when attempt to create stamp "+
37
37
  "with same name as an existing stamp" do
38
38
  create_pdf
39
39
  @pdf.create_stamp("MyStamp")
40
40
  lambda {
41
41
  @pdf.create_stamp("MyStamp")
42
- }.should.raise(Prawn::Errors::NameTaken)
42
+ }.should raise_error(Prawn::Errors::NameTaken)
43
43
  end
44
44
 
45
- it "should raise InvalidName error when attempt to create "+
45
+ it "should raise_error InvalidName error when attempt to create "+
46
46
  "stamp with a blank name" do
47
47
  create_pdf
48
48
  lambda {
49
49
  @pdf.create_stamp("")
50
- }.should.raise(Prawn::Errors::InvalidName)
50
+ }.should raise_error(Prawn::Errors::InvalidName)
51
51
  end
52
52
 
53
53
  it "a new XObject should be defined for each stamp created" do
@@ -63,12 +63,12 @@ describe "Document with a stamp" do
63
63
  end
64
64
 
65
65
  it "calling stamp with a name that does not match an existing stamp "+
66
- "should raise UndefinedObjectName" do
66
+ "should raise_error UndefinedObjectName" do
67
67
  create_pdf
68
68
  @pdf.create_stamp("MyStamp")
69
69
  lambda {
70
70
  @pdf.stamp("OtherStamp")
71
- }.should.raise(Prawn::Errors::UndefinedObjectName)
71
+ }.should raise_error(Prawn::Errors::UndefinedObjectName)
72
72
  end
73
73
 
74
74
  it "stamp should be drawn into the document each time stamp is called" do
@@ -99,7 +99,7 @@ describe "Document with a stamp" do
99
99
  objects = output.split("endobj")
100
100
  objects.each do |object|
101
101
  if object =~ /\/Type \/Page$/
102
- object.should.not =~ /\/ExtGState/
102
+ object.should_not =~ /\/ExtGState/
103
103
  elsif object =~ /\/Type \/XObject$/
104
104
  object.should =~ /\/ExtGState/
105
105
  end
@@ -139,8 +139,8 @@ describe "Document with a stamp" do
139
139
  @pdf.stamp("MyStamp")
140
140
  stamps = PDF::Inspector::XObject.analyze(@pdf.render)
141
141
  stamp_stream = stamps.xobject_streams[:Stamp1].data
142
- stamp_stream.should.include "/DeviceCMYK cs\n1.000 1.000 0.200 0.000 scn"
143
- stamp_stream.should.include "/DeviceCMYK CS\n1.000 1.000 0.200 0.000 SCN"
142
+ stamp_stream.should include("/DeviceCMYK cs\n1.000 1.000 0.200 0.000 scn")
143
+ stamp_stream.should include("/DeviceCMYK CS\n1.000 1.000 0.200 0.000 SCN")
144
144
  end
145
145
 
146
146
  it "should save the color space even when same as current page color space" do
@@ -152,7 +152,7 @@ describe "Document with a stamp" do
152
152
  @pdf.stamp("MyStamp")
153
153
  stamps = PDF::Inspector::XObject.analyze(@pdf.render)
154
154
  stamp_stream = stamps.xobject_streams[:Stamp1].data
155
- stamp_stream.should.include "/DeviceCMYK CS\n1.000 1.000 0.200 0.000 SCN"
155
+ stamp_stream.should include("/DeviceCMYK CS\n1.000 1.000 0.200 0.000 SCN")
156
156
  end
157
157
 
158
158
 
@@ -12,8 +12,8 @@ describe "When stroking with default settings" do
12
12
  @pdf.join_style.should == :miter
13
13
  end
14
14
 
15
- it "dashed? should be false" do
16
- @pdf.should.not.be.dashed
15
+ it "dashed? should be_false" do
16
+ @pdf.should_not be_dashed
17
17
  end
18
18
  end
19
19
 
@@ -104,13 +104,13 @@ describe "Dashes" do
104
104
 
105
105
  it "should be able to use assignment operator" do
106
106
  @pdf.dash = 2
107
- @pdf.should.be.dashed
107
+ @pdf.should be_dashed
108
108
  end
109
109
 
110
110
  describe "setting a dash" do
111
- it "dashed? should be true" do
111
+ it "dashed? should be_true" do
112
112
  @pdf.dash(2)
113
- @pdf.should.be.dashed
113
+ @pdf.should be_dashed
114
114
  end
115
115
  it "rendered PDF should include a stroked dash" do
116
116
  @pdf.dash(2)
@@ -1,6 +1,6 @@
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
  require 'set'
5
5
 
6
6
  describe "Prawn::Table" do
@@ -12,7 +12,7 @@ describe "Prawn::Table" do
12
12
  end
13
13
 
14
14
  it "should return a Prawn::Table" do
15
- @table.should.be.an.instance_of Prawn::Table
15
+ @table.should be_a_kind_of Prawn::Table
16
16
  end
17
17
 
18
18
  it "should flatten the data into the @cells array in row-major order" do
@@ -29,17 +29,16 @@ describe "Prawn::Table" do
29
29
  lambda {
30
30
  data = [["foo","bar"],["baz",""]]
31
31
  @pdf.table(data)
32
- }.should.not.raise
33
- end
32
+ }.should_not raise_error
33
+ end
34
34
 
35
35
  it "should allow a table with a header but no body" do
36
- lambda { @pdf.table([["Header"]], :header => true) }.should.not.raise
36
+ lambda { @pdf.table([["Header"]], :header => true) }.should_not raise_error
37
37
  end
38
38
 
39
- # TODO: pending colspan
40
- xit "should accurately count columns from data" do
39
+ it "should accurately count columns from data" do
41
40
  # First data row may contain colspan which would hide true column count
42
- data = [["Name:",{:text => "Some very long name", :colspan => 5}]]
41
+ data = [["Name:", {:content => "Some very long name", :colspan => 5}]]
43
42
  pdf = Prawn::Document.new
44
43
  table = Prawn::Table.new data, pdf
45
44
  table.column_widths.length.should == 6
@@ -56,7 +55,8 @@ describe "Prawn::Table" do
56
55
  initializer.expects(:kick).once
57
56
 
58
57
  @pdf.table([["a"]]){
59
- self.should.be.an.instance_of(Prawn::Table); initializer.kick }
58
+ initializer.kick
59
+ }
60
60
  end
61
61
 
62
62
  it "should call a 1-arg block with the document as the argument" do
@@ -64,7 +64,7 @@ describe "Prawn::Table" do
64
64
  initializer.expects(:kick).once
65
65
 
66
66
  @pdf.table([["a"]]){ |doc|
67
- doc.should.be.an.instance_of(Prawn::Table); initializer.kick }
67
+ doc.should be_a_kind_of(Prawn::Table); initializer.kick }
68
68
  end
69
69
 
70
70
  it "should proxy cell methods to #cells" do
@@ -80,7 +80,7 @@ describe "Prawn::Table" do
80
80
 
81
81
  it "should generate a text cell based on a String" do
82
82
  t = @pdf.table([["foo"]])
83
- t.cells[0,0].should.be.a.kind_of(Prawn::Table::Cell::Text)
83
+ t.cells[0,0].should be_a_kind_of(Prawn::Table::Cell::Text)
84
84
  end
85
85
 
86
86
  it "should pass through a text cell" do
@@ -97,14 +97,14 @@ describe "Prawn::Table" do
97
97
  end
98
98
 
99
99
  it "should select rows by number or range" do
100
- Set.new(@table.row(0).map { |c| c.content }).should ==
100
+ Set.new(@table.row(0).map { |c| c.content }).should ==
101
101
  Set.new(%w[R0C0 R0C1])
102
- Set.new(@table.rows(0..1).map { |c| c.content }).should ==
102
+ Set.new(@table.rows(0..1).map { |c| c.content }).should ==
103
103
  Set.new(%w[R0C0 R0C1 R1C0 R1C1])
104
104
  end
105
105
 
106
106
  it "should select rows by array" do
107
- Set.new(@table.rows([0, 1]).map { |c| c.content }).should ==
107
+ Set.new(@table.rows([0, 1]).map { |c| c.content }).should ==
108
108
  Set.new(%w[R0C0 R0C1 R1C0 R1C1])
109
109
  end
110
110
 
@@ -118,14 +118,14 @@ describe "Prawn::Table" do
118
118
  end
119
119
 
120
120
  it "should select columns by number or range" do
121
- Set.new(@table.column(0).map { |c| c.content }).should ==
121
+ Set.new(@table.column(0).map { |c| c.content }).should ==
122
122
  Set.new(%w[R0C0 R1C0])
123
- Set.new(@table.columns(0..1).map { |c| c.content }).should ==
123
+ Set.new(@table.columns(0..1).map { |c| c.content }).should ==
124
124
  Set.new(%w[R0C0 R0C1 R1C0 R1C1])
125
125
  end
126
126
 
127
127
  it "should select columns by array" do
128
- Set.new(@table.columns([0, 1]).map { |c| c.content }).should ==
128
+ Set.new(@table.columns([0, 1]).map { |c| c.content }).should ==
129
129
  Set.new(%w[R0C0 R0C1 R1C0 R1C1])
130
130
  end
131
131
 
@@ -143,13 +143,13 @@ describe "Prawn::Table" do
143
143
  end
144
144
 
145
145
  it "should accept a filter block, returning a cell proxy" do
146
- @table.cells.filter { |c| c.content =~ /R0/ }.column(1).map{ |c|
146
+ @table.cells.filter { |c| c.content =~ /R0/ }.column(1).map{ |c|
147
147
  c.content }.should == ["R0C1"]
148
148
  end
149
149
 
150
150
  it "should accept the [] method, returning a Cell or nil" do
151
151
  @table.cells[0, 0].content.should == "R0C0"
152
- @table.cells[12, 12].should.be.nil
152
+ @table.cells[12, 12].should be_nil
153
153
  end
154
154
 
155
155
  it "should proxy unknown methods to the cells" do
@@ -160,6 +160,20 @@ describe "Prawn::Table" do
160
160
  @table.cells[1, 0].height.should == 100
161
161
  end
162
162
 
163
+ it "should ignore non-setter methods" do
164
+ lambda {
165
+ @table.cells.content_width
166
+ }.should raise_error(NoMethodError)
167
+ end
168
+
169
+ it "skips cells that don't respond to the given method" do
170
+ table = @pdf.make_table([[{:content => "R0", :colspan => 2}],
171
+ %w[R1C0 R1C1]])
172
+ lambda {
173
+ table.row(0).font_style = :bold
174
+ }.should_not raise_error
175
+ end
176
+
163
177
  it "should accept the style method, proxying its calls to the cells" do
164
178
  @table.cells.style(:height => 200, :width => 200)
165
179
  @table.column(0).style(:width => 100)
@@ -204,14 +218,14 @@ describe "Prawn::Table" do
204
218
  end
205
219
 
206
220
  describe "width" do
207
- it "should raise an error if the given width is outside of range" do
221
+ it "should raise_error an error if the given width is outside of range" do
208
222
  lambda do
209
223
  @pdf.table([["foo"]], :width => 1)
210
- end.should.raise(Prawn::Errors::CannotFit)
224
+ end.should raise_error(Prawn::Errors::CannotFit)
211
225
 
212
226
  lambda do
213
227
  @pdf.table([[@long_text]], :width => @pdf.bounds.width + 100)
214
- end.should.raise(Prawn::Errors::CannotFit)
228
+ end.should raise_error(Prawn::Errors::CannotFit)
215
229
  end
216
230
 
217
231
  it "should accept the natural width for small tables" do
@@ -220,7 +234,7 @@ describe "Prawn::Table" do
220
234
  @table.width.should == @table.cells[0, 0].natural_content_width + pad
221
235
  end
222
236
 
223
- it "width should equal sum(column_widths)" do
237
+ it "width should == sum(column_widths)" do
224
238
  table = Prawn::Table.new([%w[ a b c ], %w[d e f]], @pdf) do
225
239
  column(0).width = 50
226
240
  column(1).width = 100
@@ -253,13 +267,13 @@ describe "Prawn::Table" do
253
267
  "column widths within a single table" do
254
268
  hpad, fs = 10, 6
255
269
  stretchy_columns = 2
256
-
270
+
257
271
  col0_width = 50
258
272
  col1_width = @pdf.width_of("foo", :size => fs)
259
273
  col2_width = @pdf.width_of("foobar", :size => fs)
260
274
  col3_width = 150
261
275
 
262
- table = Prawn::Table.new( [%w[snake foo b apple],
276
+ table = Prawn::Table.new( [%w[snake foo b apple],
263
277
  %w[kitten d foobar banana]], @pdf,
264
278
  :cell_style => { :padding => hpad, :size => fs }) do
265
279
 
@@ -267,8 +281,8 @@ describe "Prawn::Table" do
267
281
  column(3).width = col3_width
268
282
  end
269
283
 
270
- table.width.should == col1_width + col2_width +
271
- 2*stretchy_columns*hpad +
284
+ table.width.should == col1_width + col2_width +
285
+ 2*stretchy_columns*hpad +
272
286
  col0_width + col3_width
273
287
  end
274
288
 
@@ -277,7 +291,7 @@ describe "Prawn::Table" do
277
291
  col1_width = 20
278
292
  col3_width = 60
279
293
 
280
- table = Prawn::Table.new( [["snake", "foo", "b",
294
+ table = Prawn::Table.new( [["snake", "foo", "b",
281
295
  "some long, long text that will wrap"],
282
296
  %w[kitten d foobar banana]], @pdf,
283
297
  :width => 150) do
@@ -294,11 +308,11 @@ describe "Prawn::Table" do
294
308
  table.column(3).width.should == col3_width
295
309
  end
296
310
 
297
- it "should not exceed the maximum width of the margin_box" do
311
+ it "should_not exceed the maximum width of the margin_box" do
298
312
  expected_width = @pdf.margin_box.width
299
313
  data = [
300
314
  ['This is a column with a lot of text that should comfortably exceed '+
301
- 'the width of a normal document margin_box width', 'Some more text',
315
+ 'the width of a normal document margin_box width', 'Some more text',
302
316
  'and then some more', 'Just a bit more to be extra sure']
303
317
  ]
304
318
  table = Prawn::Table.new(data, @pdf)
@@ -306,25 +320,25 @@ describe "Prawn::Table" do
306
320
  table.width.should == expected_width
307
321
  end
308
322
 
309
- it "should not exceed the maximum width of the margin_box even with" +
323
+ it "should_not exceed the maximum width of the margin_box even with" +
310
324
  "manual widths specified" do
311
325
  expected_width = @pdf.margin_box.width
312
326
  data = [
313
327
  ['This is a column with a lot of text that should comfortably exceed '+
314
- 'the width of a normal document margin_box width', 'Some more text',
328
+ 'the width of a normal document margin_box width', 'Some more text',
315
329
  'and then some more', 'Just a bit more to be extra sure']
316
330
  ]
317
331
  table = Prawn::Table.new(data, @pdf) { column(1).width = 100 }
318
332
 
319
333
  table.width.should == expected_width
320
334
  end
321
-
335
+
322
336
  it "scales down only the non-preset column widths when the natural width" +
323
337
  "exceeds the maximum width of the margin_box" do
324
338
  expected_width = @pdf.margin_box.width
325
339
  data = [
326
340
  ['This is a column with a lot of text that should comfortably exceed '+
327
- 'the width of a normal document margin_box width', 'Some more text',
341
+ 'the width of a normal document margin_box width', 'Some more text',
328
342
  'and then some more', 'Just a bit more to be extra sure']
329
343
  ]
330
344
  table = Prawn::Table.new(data, @pdf) { column(1).width = 100; column(3).width = 50 }
@@ -366,23 +380,23 @@ describe "Prawn::Table" do
366
380
  t.cells.size = 8
367
381
  t.cells.padding = 0
368
382
  end
369
- end.should.not.raise(Prawn::Errors::CannotFit)
383
+ end.should_not raise_error(Prawn::Errors::CannotFit)
370
384
  end
371
385
 
372
386
  it "should be the width of the :width parameter" do
373
387
  expected_width = 300
374
- table = Prawn::Table.new( [%w[snake foo b apple],
388
+ table = Prawn::Table.new( [%w[snake foo b apple],
375
389
  %w[kitten d foobar banana]], @pdf,
376
390
  :width => expected_width)
377
391
 
378
392
  table.width.should == expected_width
379
393
  end
380
394
 
381
- it "should not exceed the :width option" do
395
+ it "should_not exceed the :width option" do
382
396
  expected_width = 400
383
397
  data = [
384
398
  ['This is a column with a lot of text that should comfortably exceed '+
385
- 'the width of a normal document margin_box width', 'Some more text',
399
+ 'the width of a normal document margin_box width', 'Some more text',
386
400
  'and then some more', 'Just a bit more to be extra sure']
387
401
  ]
388
402
  table = Prawn::Table.new(data, @pdf, :width => expected_width)
@@ -390,11 +404,11 @@ describe "Prawn::Table" do
390
404
  table.width.should == expected_width
391
405
  end
392
406
 
393
- it "should not exceed the :width option even with manual widths specified" do
407
+ it "should_not exceed the :width option even with manual widths specified" do
394
408
  expected_width = 400
395
409
  data = [
396
410
  ['This is a column with a lot of text that should comfortably exceed '+
397
- 'the width of a normal document margin_box width', 'Some more text',
411
+ 'the width of a normal document margin_box width', 'Some more text',
398
412
  'and then some more', 'Just a bit more to be extra sure']
399
413
  ]
400
414
  table = Prawn::Table.new(data, @pdf, :width => expected_width) do
@@ -404,25 +418,26 @@ describe "Prawn::Table" do
404
418
  table.width.should == expected_width
405
419
  end
406
420
 
407
- # TODO: pending colspan
408
- xit "should calculate unspecified column widths even " +
421
+ it "should calculate unspecified column widths even " +
409
422
  "with colspan cells declared" do
410
423
  pdf = Prawn::Document.new
411
424
  hpad, fs = 3, 5
412
425
  columns = 3
413
426
 
414
- data = [ [ { :text => 'foo', :colspan => 2 }, "foobar" ],
427
+ data = [ [ { :content => 'foo', :colspan => 2 }, "foobar" ],
415
428
  [ "foo", "foo", "foo" ] ]
416
429
  table = Prawn::Table.new( data, pdf,
417
- :horizontal_padding => hpad,
418
- :font_size => fs )
430
+ :cell_style => {
431
+ :padding_left => hpad, :padding_right => hpad,
432
+ :size => fs
433
+ })
419
434
 
420
435
  col0_width = pdf.width_of("foo", :size => fs) # cell 1, 0
421
436
  col1_width = pdf.width_of("foo", :size => fs) # cell 1, 1
422
437
  col2_width = pdf.width_of("foobar", :size => fs) # cell 0, 1 (at col 2)
423
438
 
424
- table.width.should == col0_width.ceil + col1_width.ceil +
425
- col2_width.ceil + 2*columns*hpad
439
+ table.width.should == col0_width + col1_width +
440
+ col2_width + 2*columns*hpad
426
441
  end
427
442
  end
428
443
 
@@ -438,16 +453,16 @@ describe "Prawn::Table" do
438
453
  @pdf.y.should == old_y - table.height
439
454
  end
440
455
 
441
- it "should not wrap unnecessarily" do
456
+ it "should_not wrap unnecessarily" do
442
457
  # Test for FP errors and glitches
443
458
  t = @pdf.table([["Bender Bending Rodriguez"]])
444
459
  h = @pdf.height_of("one line")
445
- (t.height - 10).should.be < h*1.5
460
+ (t.height - 10).should be < h*1.5
446
461
  end
447
462
 
448
- it "should have a height of n rows" do
463
+ it "should have a height of n rows" do
449
464
  data = [["foo"],["bar"],["baaaz"]]
450
-
465
+
451
466
  vpad = 4
452
467
  origin = @pdf.y
453
468
  @pdf.table data, :cell_style => { :padding => vpad }
@@ -457,8 +472,8 @@ describe "Prawn::Table" do
457
472
  line_gap = @pdf.font.line_gap
458
473
 
459
474
  num_rows = data.length
460
- table_height.should.be.close(
461
- num_rows * font_height + 2*vpad*num_rows, 0.001 )
475
+ table_height.should be_within(0.001).of(
476
+ num_rows * font_height + 2*vpad*num_rows )
462
477
  end
463
478
 
464
479
  end
@@ -491,10 +506,10 @@ describe "Prawn::Table" do
491
506
  @pdf.table([["foo"]], :column_widths => 500, :position => 123)
492
507
  end
493
508
 
494
- it "should raise an ArgumentError on unknown :position" do
509
+ it "should raise_error an ArgumentError on unknown :position" do
495
510
  lambda do
496
511
  @pdf.table([["foo"]], :position => :bratwurst)
497
- end.should.raise(ArgumentError)
512
+ end.should raise_error(ArgumentError)
498
513
  end
499
514
  end
500
515
 
@@ -516,7 +531,7 @@ describe "Prawn::Table" do
516
531
  end.page_count.should == 2
517
532
  end
518
533
 
519
- it "should not start a new page before finishing out a row" do
534
+ it "should_not start a new page before finishing out a row" do
520
535
  Prawn::Document.new do
521
536
  table([[ (1..80).map{ |i| "Line #{i}" }.join("\n"), "Column 2" ]])
522
537
  end.page_count.should == 1
@@ -529,7 +544,7 @@ describe "Prawn::Table" do
529
544
  end.page_count.should == 2
530
545
  end
531
546
 
532
- it "should not start a new page to gain height when at the top of " +
547
+ it "should_not start a new page to gain height when at the top of " +
533
548
  "a bounding box, even if stretchy" do
534
549
  Prawn::Document.new do
535
550
  bounding_box([bounds.left, bounds.top - 20], :width => 400) do
@@ -556,7 +571,7 @@ describe "Prawn::Table" do
556
571
 
557
572
  output = PDF::Inspector::Page.analyze(pdf.render)
558
573
  # Ensure we only drew the header once, on the second page
559
- output.pages[0][:strings].should.be.empty
574
+ output.pages[0][:strings].should be_empty
560
575
  output.pages[1][:strings].should == ["Header", "Body"]
561
576
  end
562
577
 
@@ -590,6 +605,79 @@ describe "Prawn::Table" do
590
605
 
591
606
  t.draw
592
607
  end
608
+
609
+ describe "before_rendering_page callback" do
610
+ before(:each) { @pdf = Prawn::Document.new }
611
+
612
+ it "is passed all cells to be rendered on that page" do
613
+ kicked = 0
614
+
615
+ @pdf.table([["foo"]] * 100) do |t|
616
+ t.before_rendering_page do |page|
617
+ page.row_count.should == ((kicked < 3) ? 30 : 10)
618
+ page.column_count.should == 1
619
+ page.row(0).first.content.should == "foo"
620
+ page.row(-1).first.content.should == "foo"
621
+ kicked += 1
622
+ end
623
+ end
624
+
625
+ kicked.should == 4
626
+ end
627
+
628
+ it "numbers cells relative to their position on page" do
629
+ @pdf.table([["foo"]] * 100) do |t|
630
+ t.before_rendering_page do |page|
631
+ page[0, 0].content.should == "foo"
632
+ end
633
+ end
634
+ end
635
+
636
+ it "changing cells in the callback affects their rendering" do
637
+ seq = sequence("render order")
638
+
639
+ t = @pdf.make_table([["foo"]] * 40) do |table|
640
+ table.before_rendering_page do |page|
641
+ page[0, 0].background_color = "ff0000"
642
+ end
643
+ end
644
+
645
+ t.cells[30, 0].stubs(:draw_background).checking do |xy|
646
+ t.cells[30, 0].background_color.should == 'ff0000'
647
+ end
648
+ t.cells[31, 0].stubs(:draw_background).checking do |xy|
649
+ t.cells[31, 0].background_color.should == nil
650
+ end
651
+
652
+ t.draw
653
+ end
654
+
655
+ it "passes headers on page 2+" do
656
+ @pdf.table([["header"]] + [["foo"]] * 100, :header => true) do |t|
657
+ t.before_rendering_page do |page|
658
+ page[0, 0].content.should == "header"
659
+ end
660
+ end
661
+ end
662
+
663
+ it "allows headers to be changed" do
664
+ seq = sequence("render order")
665
+ @pdf.expects(:draw_text!).with { |t, _| t == "hdr1"}.in_sequence(seq)
666
+ @pdf.expects(:draw_text!).with { |t, _| t == "foo"}.times(29).in_sequence(seq)
667
+ # Verify that the changed cell doesn't mutate subsequent pages
668
+ @pdf.expects(:draw_text!).with { |t, _| t == "header"}.in_sequence(seq)
669
+ @pdf.expects(:draw_text!).with { |t, _| t == "foo"}.times(11).in_sequence(seq)
670
+
671
+ set_first_page_headers = false
672
+ @pdf.table([["header"]] + [["foo"]] * 40, :header => true) do |t|
673
+ t.before_rendering_page do |page|
674
+ # only change first page header
675
+ page[0, 0].content = "hdr1" unless set_first_page_headers
676
+ set_first_page_headers = true
677
+ end
678
+ end
679
+ end
680
+ end
593
681
  end
594
682
 
595
683
  describe "#style" do
@@ -610,7 +698,7 @@ describe "Prawn::Table" do
610
698
  it "should default to {} for the hash argument" do
611
699
  stylable = stub()
612
700
  stylable.expects(:style).with({}).once
613
-
701
+
614
702
  Prawn::Document.new do
615
703
  table([["x"]]) { style(stylable) }
616
704
  end
@@ -628,16 +716,41 @@ describe "Prawn::Table" do
628
716
  it "should ignore headers" do
629
717
  data = [["header"], ["foo"], ["bar"], ["baz"]]
630
718
  pdf = Prawn::Document.new
631
- t = pdf.table(data, :header => true,
719
+ t = pdf.table(data, :header => true,
632
720
  :row_colors => ['cccccc', 'ffffff']) do
633
721
  row(0).background_color = '333333'
634
722
  end
635
723
 
636
- t.cells.map{|x| x.background_color}.should ==
724
+ t.cells.map{|x| x.background_color}.should ==
637
725
  %w[333333 cccccc ffffff cccccc]
638
726
  end
639
727
 
640
- it "should not override an explicit background_color" do
728
+ it "stripes rows consistently from page to page, skipping header rows" do
729
+ data = [["header"]] + [["foo"]] * 70
730
+ pdf = Prawn::Document.new
731
+ t = pdf.make_table(data, :header => true,
732
+ :row_colors => ['cccccc', 'ffffff']) do
733
+ cells.padding = 0
734
+ cells.size = 9
735
+ row(0).size = 11
736
+ end
737
+
738
+ # page 1: header + 67 cells (odd number -- verifies that the next
739
+ # page disrupts the even/odd coloring, since both the last data cell
740
+ # on this page and the first one on the next are colored cccccc)
741
+ Prawn::Table::Cell.expects(:draw_cells).with do |cells|
742
+ cells.map { |c, (x, y)| c.background_color } ==
743
+ [nil] + (%w[cccccc ffffff] * 33) + %w[cccccc]
744
+ end
745
+ # page 2: header and 3 data cells
746
+ Prawn::Table::Cell.expects(:draw_cells).with do |cells|
747
+ cells.map { |c, (x, y)| c.background_color } ==
748
+ [nil] + %w[cccccc ffffff cccccc]
749
+ end
750
+ t.draw
751
+ end
752
+
753
+ it "should_not override an explicit background_color" do
641
754
  data = [["foo"], ["bar"], ["baz"]]
642
755
  pdf = Prawn::Document.new
643
756
  table = pdf.table(data, :row_colors => ['cccccc', 'ffffff']) { |t|
@@ -654,7 +767,7 @@ describe "Prawn::Table" do
654
767
 
655
768
  it "should set the x-position of each cell based on widths" do
656
769
  @table = @pdf.table([["foo", "bar", "baz"]])
657
-
770
+
658
771
  x = 0
659
772
  (0..2).each do |col|
660
773
  cell = @table.cells[0, col]
@@ -669,7 +782,7 @@ describe "Prawn::Table" do
669
782
 
670
783
  (0..2).each do |row|
671
784
  cell = @table.cells[row, 0]
672
- cell.y.should.be.close(y, 0.01)
785
+ cell.y.should be_within(0.01).of(y)
673
786
  y -= cell.height
674
787
  end
675
788
  end
@@ -682,16 +795,16 @@ describe "Prawn::Table" do
682
795
  output.strings.should == data.flatten
683
796
  end
684
797
 
685
- it "should not cause an error if rendering the very first row causes a " +
798
+ it "should_not cause an error if rendering the very first row causes a " +
686
799
  "page break" do
687
- Prawn::Document.new do
800
+ Prawn::Document.new do |pdf|
688
801
  arr = Array(1..5).collect{|i| ["cell #{i}"] }
689
802
 
690
- move_down( y - (bounds.absolute_bottom + 3) )
803
+ pdf.move_down( pdf.y - (pdf.bounds.absolute_bottom + 3) )
691
804
 
692
805
  lambda {
693
- table(arr)
694
- }.should.not.raise
806
+ pdf.table(arr)
807
+ }.should_not raise_error
695
808
  end
696
809
  end
697
810
 
@@ -734,7 +847,7 @@ describe "Prawn::Table" do
734
847
  describe "in stretchy bounding boxes" do
735
848
  it "should draw all cells on a row at the same y-position" do
736
849
  pdf = Prawn::Document.new
737
-
850
+
738
851
  text_y = pdf.y.to_i - 5 # text starts 5pt below current y pos (padding)
739
852
 
740
853
  pdf.bounding_box([0, pdf.cursor], :width => pdf.bounds.width) do
@@ -753,7 +866,7 @@ describe "Prawn::Table" do
753
866
  data = [["a", "b"], ["foo","bar"],["baz","bang"]]
754
867
  @pdf = Prawn::Document.new
755
868
  @pdf.table(data, :header => true)
756
- output = PDF::Inspector::Text.analyze(@pdf.render)
869
+ output = PDF::Inspector::Text.analyze(@pdf.render)
757
870
  output.strings.should == data.flatten
758
871
  end
759
872
 
@@ -762,16 +875,35 @@ describe "Prawn::Table" do
762
875
  headers = ["baz","foobar"]
763
876
  @pdf = Prawn::Document.new
764
877
  @pdf.table([headers] + data, :header => true)
765
- output = PDF::Inspector::Text.analyze(@pdf.render)
878
+ output = PDF::Inspector::Text.analyze(@pdf.render)
766
879
  output.strings.should == headers + data.flatten[0..-3] + headers +
767
880
  data.flatten[-2..-1]
768
881
  end
769
882
 
770
- it "should not draw header twice when starting new page" do
883
+ it "draws headers at the correct position" do
884
+ data = [["header"]] + [["foo"]] * 40
885
+
886
+ Prawn::Table::Cell.expects(:draw_cells).times(2).checking do |cells|
887
+ cells.each do |cell, pt|
888
+ if cell.content == "header"
889
+ # Assert that header text is drawn at the same location on each page
890
+ if @header_location
891
+ pt.should == @header_location
892
+ else
893
+ @header_location = pt
894
+ end
895
+ end
896
+ end
897
+ end
898
+ @pdf = Prawn::Document.new
899
+ @pdf.table(data, :header => true)
900
+ end
901
+
902
+ it "should_not draw header twice when starting new page" do
771
903
  @pdf = Prawn::Document.new
772
904
  @pdf.y = 0
773
905
  @pdf.table([["Header"], ["Body"]], :header => true)
774
- output = PDF::Inspector::Text.analyze(@pdf.render)
906
+ output = PDF::Inspector::Text.analyze(@pdf.render)
775
907
  output.strings.should == ["Header", "Body"]
776
908
  end
777
909
  end
@@ -785,8 +917,8 @@ describe "Prawn::Table" do
785
917
 
786
918
  it "can be created from an Array" do
787
919
  cell = Prawn::Table::Cell.make(@pdf, [["foo"]])
788
- cell.should.be.an.instance_of(Prawn::Table::Cell::Subtable)
789
- cell.subtable.should.be.an.instance_of(Prawn::Table)
920
+ cell.should be_a_kind_of(Prawn::Table::Cell::Subtable)
921
+ cell.subtable.should be_a_kind_of(Prawn::Table)
790
922
  end
791
923
 
792
924
  it "defaults its padding to zero" do
@@ -796,7 +928,7 @@ describe "Prawn::Table" do
796
928
  it "has a subtable accessor" do
797
929
  @table.cells[0, 0].subtable.should == @subtable
798
930
  end
799
-
931
+
800
932
  it "determines its dimensions from the subtable" do
801
933
  @table.cells[0, 0].width.should == @subtable.width
802
934
  @table.cells[0, 0].height.should == @subtable.height
@@ -805,35 +937,273 @@ describe "Prawn::Table" do
805
937
  end
806
938
 
807
939
  describe "An invalid table" do
808
-
940
+
809
941
  before(:each) do
810
942
  @pdf = Prawn::Document.new
811
943
  @bad_data = ["Single Nested Array"]
812
944
  end
813
-
814
- it "should raise error when invalid table data is given" do
815
- assert_raises(Prawn::Errors::InvalidTableData) do
945
+
946
+ it "should raise_error error when invalid table data is given" do
947
+ lambda {
816
948
  @pdf.table(@bad_data)
817
- end
949
+ }.should raise_error(Prawn::Errors::InvalidTableData)
818
950
  end
819
951
 
820
- it "should raise an EmptyTableError with empty table data" do
952
+ it "should raise_error an EmptyTableError with empty table data" do
821
953
  lambda {
822
954
  data = []
823
955
  @pdf = Prawn::Document.new
824
956
  @pdf.table(data)
825
- }.should.raise( Prawn::Errors::EmptyTable )
826
- end
957
+ }.should raise_error( Prawn::Errors::EmptyTable )
958
+ end
827
959
 
828
- it "should raise an EmptyTableError with nil table data" do
960
+ it "should raise_error an EmptyTableError with nil table data" do
829
961
  lambda {
830
962
  data = nil
831
963
  @pdf = Prawn::Document.new
832
964
  @pdf.table(data)
833
- }.should.raise( Prawn::Errors::EmptyTable )
834
- end
965
+ }.should raise_error( Prawn::Errors::EmptyTable )
966
+ end
835
967
 
836
968
  end
837
969
 
838
970
  end
839
971
 
972
+ describe "colspan / rowspan" do
973
+ before(:each) { create_pdf }
974
+
975
+ it "doesn't raise an error" do
976
+ lambda {
977
+ @pdf.table([[{:content => "foo", :colspan => 2, :rowspan => 2}]])
978
+ }.should_not raise_error
979
+ end
980
+
981
+ it "colspan is properly counted" do
982
+ t = @pdf.make_table([[{:content => "foo", :colspan => 2}]])
983
+ t.column_length.should == 2
984
+ end
985
+
986
+ it "rowspan is properly counted" do
987
+ t = @pdf.make_table([[{:content => "foo", :rowspan => 2}]])
988
+ t.row_length.should == 2
989
+ end
990
+
991
+ it "raises if colspan or rowspan are called after layout" do
992
+ lambda {
993
+ @pdf.table([["foo"]]) { cells[0, 0].colspan = 2 }
994
+ }.should raise_error(Prawn::Errors::InvalidTableSpan)
995
+
996
+ lambda {
997
+ @pdf.table([["foo"]]) { cells[0, 0].rowspan = 2 }
998
+ }.should raise_error(Prawn::Errors::InvalidTableSpan)
999
+ end
1000
+
1001
+ it "raises when spans overlap" do
1002
+ lambda {
1003
+ @pdf.table([["foo", {:content => "bar", :rowspan => 2}],
1004
+ [{:content => "baz", :colspan => 2}]])
1005
+ }.should raise_error(Prawn::Errors::InvalidTableSpan)
1006
+ end
1007
+
1008
+ it "table and cell width account for colspan" do
1009
+ t = @pdf.table([["a", {:content => "b", :colspan => 2}]],
1010
+ :column_widths => [100, 100, 100])
1011
+ spanned = t.cells[0, 1]
1012
+ spanned.colspan.should == 2
1013
+ t.width.should == 300
1014
+ t.cells.min_width.should == 300
1015
+ t.cells.max_width.should == 300
1016
+ spanned.width.should == 200
1017
+ end
1018
+
1019
+ it "table and cell height account for rowspan" do
1020
+ t = @pdf.table([["a"], [{:content => "b", :rowspan => 2}]]) do
1021
+ row(0..2).height = 100
1022
+ end
1023
+ spanned = t.cells[1, 0]
1024
+ spanned.rowspan.should == 2
1025
+ t.height.should == 300
1026
+ spanned.height.should == 200
1027
+ end
1028
+
1029
+ it "provides the full content_width as drawing space" do
1030
+ w = @pdf.make_table([["foo"]]).cells[0, 0].content_width
1031
+
1032
+ t = @pdf.make_table([[{:content => "foo", :colspan => 2}]])
1033
+ t.cells[0, 0].spanned_content_width.should == w
1034
+ end
1035
+
1036
+ it "dummy cells are not drawn" do
1037
+ # make a fake master cell for the dummy cell to slave to
1038
+ t = @pdf.make_table([[{:content => "foo", :colspan => 2}]])
1039
+
1040
+ # drawing just a dummy cell should_not ink
1041
+ @pdf.expects(:stroke_line).never
1042
+ @pdf.expects(:draw_text!).never
1043
+ Prawn::Table::Cell.draw_cells([t.cells[0, 1]])
1044
+ end
1045
+
1046
+ it "dummy cells do not add any height or width" do
1047
+ t1 = @pdf.table([["foo"]])
1048
+
1049
+ t2 = @pdf.table([[{:content => "foo", :colspan => 2}]])
1050
+ t2.width.should == t1.width
1051
+
1052
+ t3 = @pdf.table([[{:content => "foo", :rowspan => 2}]])
1053
+ t3.height.should == t1.height
1054
+ end
1055
+
1056
+ it "dummy cells ignored by #style" do
1057
+ t = @pdf.table([[{:content => "blah", :colspan => 2}]],
1058
+ :cell_style => { :size => 9 })
1059
+ t.cells[0, 0].size.should == 9
1060
+ end
1061
+
1062
+ context "inheriting master cell styles from dummy cell" do
1063
+ # Relatively full coverage for all these attributes that should be
1064
+ # inherited.
1065
+ [["border_X_width", 20],
1066
+ ["border_X_color", "123456"],
1067
+ ["padding_X", 20]].each do |attribute, val|
1068
+ attribute_right = attribute.sub("X", "right")
1069
+ attribute_left = attribute.sub("X", "left")
1070
+ attribute_bottom = attribute.sub("X", "bottom")
1071
+ attribute_top = attribute.sub("X", "top")
1072
+
1073
+ specify "#{attribute_right} of right column is inherited" do
1074
+ t = @pdf.table([[{:content => "blah", :colspan => 2}]]) do |table|
1075
+ table.column(1).send("#{attribute_right}=", val)
1076
+ end
1077
+
1078
+ t.cells[0, 0].send(attribute_right).should == val
1079
+ end
1080
+
1081
+ specify "#{attribute_bottom} of bottom row is inherited" do
1082
+ t = @pdf.table([[{:content => "blah", :rowspan => 2}]]) do |table|
1083
+ table.row(1).send("#{attribute_bottom}=", val)
1084
+ end
1085
+
1086
+ t.cells[0, 0].send(attribute_bottom).should == val
1087
+ end
1088
+
1089
+ specify "#{attribute_left} of right column is not inherited" do
1090
+ t = @pdf.table([[{:content => "blah", :colspan => 2}]]) do |table|
1091
+ table.column(1).send("#{attribute_left}=", val)
1092
+ end
1093
+
1094
+ t.cells[0, 0].send(attribute_left).should_not == val
1095
+ end
1096
+
1097
+ specify "#{attribute_right} of interior column is not inherited" do
1098
+ t = @pdf.table([[{:content => "blah", :colspan => 3}]]) do |table|
1099
+ table.column(1).send("#{attribute_right}=", val)
1100
+ end
1101
+
1102
+ t.cells[0, 0].send(attribute_right).should_not == val
1103
+ end
1104
+
1105
+ specify "#{attribute_bottom} of interior row is not inherited" do
1106
+ t = @pdf.table([[{:content => "blah", :rowspan => 3}]]) do |table|
1107
+ table.row(1).send("#{attribute_bottom}=", val)
1108
+ end
1109
+
1110
+ t.cells[0, 0].send(attribute_bottom).should_not == val
1111
+ end
1112
+
1113
+ specify "#{attribute_top} of bottom row is not inherited" do
1114
+ t = @pdf.table([[{:content => "blah", :rowspan => 2}]]) do |table|
1115
+ table.row(1).send("#{attribute_top}=", val)
1116
+ end
1117
+
1118
+ t.cells[0, 0].send(attribute_top).should_not == val
1119
+ end
1120
+ end
1121
+ end
1122
+
1123
+ it "splits natural width between cols in the group" do
1124
+ t = @pdf.table([[{:content => "foo", :colspan => 2}]])
1125
+ widths = t.column_widths
1126
+ widths[0].should == widths[1]
1127
+ end
1128
+
1129
+ it "splits natural width between cols when width is increased" do
1130
+ t = @pdf.table([[{:content => "foo", :colspan => 2}]],
1131
+ :width => @pdf.bounds.width)
1132
+ widths = t.column_widths
1133
+ widths[0].should == widths[1]
1134
+ end
1135
+
1136
+ it "splits min-width between cols in the group" do
1137
+ # Since column_widths, when reducing column widths, reduces proportional to
1138
+ # the remaining width after each column's min width, we must ensure that the
1139
+ # min-width is split proportionally in order to ensure the width is still
1140
+ # split evenly when the width is reduced. (See "splits natural width between
1141
+ # cols when width is reduced".)
1142
+ t = @pdf.table([[{:content => "foo", :colspan => 2}]],
1143
+ :width => 20)
1144
+ t.column(0).min_width.should == t.column(1).min_width
1145
+ end
1146
+
1147
+ it "splits natural width between cols when width is reduced" do
1148
+ t = @pdf.table([[{:content => "foo", :colspan => 2}]],
1149
+ :width => 20)
1150
+ widths = t.column_widths
1151
+ widths[0].should == widths[1]
1152
+ end
1153
+
1154
+ it "honors a large, explicitly set table width" do
1155
+ t = @pdf.table([[{:content => "AAAAAAAAAA", :colspan => 3}],
1156
+ ["A", "B", "C"]],
1157
+ :width => 400)
1158
+
1159
+ t.column_widths.inject(0) { |sum, w| sum + w }.
1160
+ should be_within(0.01).of(400)
1161
+ end
1162
+
1163
+ it "honors a small, explicitly set table width" do
1164
+ t = @pdf.table([[{:content => "Lorem ipsum dolor sit amet " * 20,
1165
+ :colspan => 3}],
1166
+ ["A", "B", "C"]],
1167
+ :width => 200)
1168
+
1169
+ t.column_widths.inject(0) { |sum, w| sum + w }.
1170
+ should be_within(0.01).of(200)
1171
+ end
1172
+
1173
+ it "splits natural_content_height between rows in the group" do
1174
+ t = @pdf.table([[{:content => "foo", :rowspan => 2}]])
1175
+ heights = t.row_heights
1176
+ heights[0].should == heights[1]
1177
+ end
1178
+
1179
+ it "skips column numbers that have been col-spanned" do
1180
+ t = @pdf.table([["a", "b", {:content => "c", :colspan => 3}, "d"]])
1181
+ t.cells[0, 0].content.should == "a"
1182
+ t.cells[0, 1].content.should == "b"
1183
+ t.cells[0, 2].content.should == "c"
1184
+ t.cells[0, 3].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
1185
+ t.cells[0, 4].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
1186
+ t.cells[0, 5].content.should == "d"
1187
+ end
1188
+
1189
+ it "skips row/col positions that have been row-spanned" do
1190
+ t = @pdf.table([["a", {:content => "b", :colspan => 2, :rowspan => 2}, "c"],
1191
+ ["d", "e"],
1192
+ ["f", "g", "h", "i"]])
1193
+ t.cells[0, 0].content.should == "a"
1194
+ t.cells[0, 1].content.should == "b"
1195
+ t.cells[0, 2].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
1196
+ t.cells[0, 3].content.should == "c"
1197
+
1198
+ t.cells[1, 0].content.should == "d"
1199
+ t.cells[1, 1].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
1200
+ t.cells[1, 2].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
1201
+ t.cells[1, 3].content.should == "e"
1202
+
1203
+ t.cells[2, 0].content.should == "f"
1204
+ t.cells[2, 1].content.should == "g"
1205
+ t.cells[2, 2].content.should == "h"
1206
+ t.cells[2, 3].content.should == "i"
1207
+ end
1208
+ end
1209
+