prawn 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (282) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +2 -2
  3. data/Gemfile +18 -0
  4. data/LICENSE +1 -1
  5. data/README.md +17 -4
  6. data/Rakefile +18 -22
  7. data/data/images/indexed_color.dat +0 -0
  8. data/data/images/indexed_color.png +0 -0
  9. data/data/pdfs/nested_pages.pdf +13 -13
  10. data/lib/pdf/core.rb +35 -0
  11. data/lib/{prawn → pdf}/core/annotations.rb +6 -7
  12. data/lib/{prawn → pdf}/core/byte_string.rb +1 -1
  13. data/lib/{prawn → pdf}/core/destinations.rb +23 -23
  14. data/lib/{prawn → pdf}/core/document_state.rb +8 -8
  15. data/lib/pdf/core/filter_list.rb +51 -0
  16. data/lib/pdf/core/filters.rb +36 -0
  17. data/lib/pdf/core/graphics_state.rb +68 -0
  18. data/lib/{prawn → pdf}/core/literal_string.rb +1 -1
  19. data/lib/{prawn → pdf}/core/name_tree.rb +14 -2
  20. data/lib/{prawn → pdf}/core/object_store.rb +80 -24
  21. data/lib/pdf/core/outline.rb +315 -0
  22. data/lib/{prawn → pdf}/core/page.rb +23 -26
  23. data/lib/{prawn/document → pdf/core}/page_geometry.rb +11 -21
  24. data/lib/{prawn → pdf}/core/pdf_object.rb +48 -32
  25. data/lib/{prawn → pdf}/core/reference.rb +35 -44
  26. data/lib/pdf/core/stream.rb +98 -0
  27. data/lib/{prawn → pdf}/core/text.rb +24 -17
  28. data/lib/prawn.rb +95 -17
  29. data/lib/prawn/compatibility.rb +66 -26
  30. data/lib/prawn/document.rb +48 -30
  31. data/lib/prawn/document/bounding_box.rb +3 -3
  32. data/lib/prawn/document/column_box.rb +46 -8
  33. data/lib/prawn/document/graphics_state.rb +10 -73
  34. data/lib/prawn/document/internals.rb +24 -23
  35. data/lib/prawn/document/snapshot.rb +6 -7
  36. data/lib/prawn/document/span.rb +10 -10
  37. data/lib/prawn/encoding.rb +7 -7
  38. data/lib/prawn/errors.rb +18 -29
  39. data/lib/prawn/font.rb +64 -28
  40. data/lib/prawn/font/afm.rb +32 -74
  41. data/lib/prawn/font/dfont.rb +2 -2
  42. data/lib/prawn/font/ttf.rb +28 -57
  43. data/lib/prawn/font_metric_cache.rb +45 -0
  44. data/lib/prawn/graphics.rb +307 -41
  45. data/lib/prawn/graphics/cap_style.rb +3 -3
  46. data/lib/prawn/graphics/color.rb +12 -5
  47. data/lib/prawn/graphics/dash.rb +52 -31
  48. data/lib/prawn/graphics/join_style.rb +7 -7
  49. data/lib/prawn/graphics/patterns.rb +137 -0
  50. data/lib/prawn/graphics/transformation.rb +9 -9
  51. data/lib/prawn/graphics/transparency.rb +1 -1
  52. data/lib/prawn/image_handler.rb +30 -0
  53. data/lib/prawn/images.rb +86 -105
  54. data/lib/prawn/images/image.rb +48 -0
  55. data/lib/prawn/images/jpg.rb +14 -10
  56. data/lib/prawn/images/png.rb +50 -37
  57. data/lib/prawn/layout.rb +2 -2
  58. data/lib/prawn/layout/grid.rb +51 -51
  59. data/lib/prawn/measurement_extensions.rb +5 -5
  60. data/lib/prawn/measurements.rb +25 -21
  61. data/lib/prawn/outline.rb +4 -308
  62. data/lib/prawn/repeater.rb +8 -8
  63. data/lib/prawn/security.rb +50 -36
  64. data/lib/prawn/soft_mask.rb +94 -0
  65. data/lib/prawn/stamp.rb +3 -3
  66. data/lib/prawn/table.rb +292 -118
  67. data/lib/prawn/table/cell.rb +272 -45
  68. data/lib/prawn/table/cell/image.rb +70 -0
  69. data/lib/prawn/table/cell/in_table.rb +2 -2
  70. data/lib/prawn/table/cell/span_dummy.rb +92 -0
  71. data/lib/prawn/table/cell/subtable.rb +2 -2
  72. data/lib/prawn/table/cell/text.rb +42 -24
  73. data/lib/prawn/table/cells.rb +137 -48
  74. data/lib/prawn/text.rb +35 -23
  75. data/lib/prawn/text/box.rb +18 -5
  76. data/lib/prawn/text/formatted.rb +5 -4
  77. data/lib/prawn/text/formatted/arranger.rb +292 -0
  78. data/lib/prawn/text/formatted/box.rb +52 -13
  79. data/lib/prawn/text/formatted/fragment.rb +37 -22
  80. data/lib/prawn/text/formatted/line_wrap.rb +286 -0
  81. data/lib/prawn/text/formatted/parser.rb +14 -6
  82. data/lib/prawn/text/formatted/wrap.rb +151 -0
  83. data/lib/prawn/utilities.rb +44 -0
  84. data/manual/basic_concepts/adding_pages.rb +27 -0
  85. data/manual/basic_concepts/basic_concepts.rb +34 -0
  86. data/manual/basic_concepts/creation.rb +39 -0
  87. data/manual/basic_concepts/cursor.rb +33 -0
  88. data/manual/basic_concepts/measurement.rb +25 -0
  89. data/manual/basic_concepts/origin.rb +38 -0
  90. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  91. data/manual/bounding_box/bounding_box.rb +39 -0
  92. data/manual/bounding_box/bounds.rb +49 -0
  93. data/manual/bounding_box/canvas.rb +24 -0
  94. data/manual/bounding_box/creation.rb +23 -0
  95. data/manual/bounding_box/indentation.rb +46 -0
  96. data/manual/bounding_box/nesting.rb +45 -0
  97. data/manual/bounding_box/russian_boxes.rb +40 -0
  98. data/manual/bounding_box/stretchy.rb +31 -0
  99. data/manual/document_and_page_options/background.rb +27 -0
  100. data/manual/document_and_page_options/document_and_page_options.rb +31 -0
  101. data/manual/document_and_page_options/metadata.rb +23 -0
  102. data/manual/document_and_page_options/page_margins.rb +38 -0
  103. data/manual/document_and_page_options/page_size.rb +34 -0
  104. data/manual/example_file.rb +116 -0
  105. data/manual/example_helper.rb +411 -0
  106. data/manual/example_package.rb +53 -0
  107. data/manual/example_section.rb +46 -0
  108. data/manual/graphics/circle_and_ellipse.rb +22 -0
  109. data/manual/graphics/color.rb +24 -0
  110. data/manual/graphics/common_lines.rb +28 -0
  111. data/manual/graphics/fill_and_stroke.rb +42 -0
  112. data/manual/graphics/fill_rules.rb +37 -0
  113. data/manual/graphics/gradients.rb +37 -0
  114. data/manual/graphics/graphics.rb +58 -0
  115. data/manual/graphics/helper.rb +24 -0
  116. data/manual/graphics/line_width.rb +35 -0
  117. data/manual/graphics/lines_and_curves.rb +41 -0
  118. data/manual/graphics/polygon.rb +29 -0
  119. data/manual/graphics/rectangle.rb +21 -0
  120. data/manual/graphics/rotate.rb +28 -0
  121. data/manual/graphics/scale.rb +41 -0
  122. data/manual/graphics/soft_masks.rb +46 -0
  123. data/manual/graphics/stroke_cap.rb +31 -0
  124. data/manual/graphics/stroke_dash.rb +48 -0
  125. data/manual/graphics/stroke_join.rb +30 -0
  126. data/manual/graphics/translate.rb +29 -0
  127. data/manual/graphics/transparency.rb +35 -0
  128. data/manual/images/absolute_position.rb +23 -0
  129. data/manual/images/fit.rb +21 -0
  130. data/manual/images/horizontal.rb +25 -0
  131. data/manual/images/images.rb +40 -0
  132. data/manual/images/plain_image.rb +18 -0
  133. data/manual/images/scale.rb +22 -0
  134. data/manual/images/vertical.rb +28 -0
  135. data/manual/images/width_and_height.rb +25 -0
  136. data/manual/layout/boxes.rb +27 -0
  137. data/manual/layout/content.rb +25 -0
  138. data/manual/layout/layout.rb +28 -0
  139. data/manual/layout/simple_grid.rb +23 -0
  140. data/manual/manual/cover.rb +35 -0
  141. data/manual/manual/foreword.rb +85 -0
  142. data/manual/manual/how_to_read_this_manual.rb +41 -0
  143. data/manual/manual/manual.rb +35 -0
  144. data/manual/outline/add_subsection_to.rb +61 -0
  145. data/manual/outline/insert_section_after.rb +47 -0
  146. data/manual/outline/outline.rb +32 -0
  147. data/manual/outline/sections_and_pages.rb +67 -0
  148. data/manual/repeatable_content/page_numbering.rb +54 -0
  149. data/manual/repeatable_content/repeatable_content.rb +31 -0
  150. data/manual/repeatable_content/repeater.rb +55 -0
  151. data/manual/repeatable_content/stamp.rb +41 -0
  152. data/manual/security/encryption.rb +31 -0
  153. data/manual/security/permissions.rb +38 -0
  154. data/manual/security/security.rb +28 -0
  155. data/manual/syntax_highlight.rb +52 -0
  156. data/manual/table/basic_block.rb +53 -0
  157. data/manual/table/before_rendering_page.rb +26 -0
  158. data/manual/table/cell_border_lines.rb +24 -0
  159. data/manual/table/cell_borders_and_bg.rb +31 -0
  160. data/manual/table/cell_dimensions.rb +30 -0
  161. data/manual/table/cell_text.rb +38 -0
  162. data/manual/table/column_widths.rb +30 -0
  163. data/manual/table/content_and_subtables.rb +39 -0
  164. data/manual/table/creation.rb +27 -0
  165. data/manual/table/filtering.rb +36 -0
  166. data/manual/table/flow_and_header.rb +17 -0
  167. data/manual/table/image_cells.rb +33 -0
  168. data/manual/table/position.rb +29 -0
  169. data/manual/table/row_colors.rb +20 -0
  170. data/manual/table/span.rb +30 -0
  171. data/manual/table/style.rb +22 -0
  172. data/manual/table/table.rb +52 -0
  173. data/manual/table/width.rb +27 -0
  174. data/manual/templates/full_template.rb +25 -0
  175. data/manual/templates/page_template.rb +48 -0
  176. data/manual/templates/templates.rb +27 -0
  177. data/manual/text/alignment.rb +44 -0
  178. data/manual/text/color.rb +24 -0
  179. data/manual/text/column_box.rb +32 -0
  180. data/manual/text/fallback_fonts.rb +37 -0
  181. data/manual/text/font.rb +41 -0
  182. data/manual/text/font_size.rb +45 -0
  183. data/manual/text/font_style.rb +23 -0
  184. data/manual/text/formatted_callbacks.rb +60 -0
  185. data/manual/text/formatted_text.rb +54 -0
  186. data/manual/text/free_flowing_text.rb +51 -0
  187. data/manual/text/group.rb +29 -0
  188. data/manual/text/inline.rb +43 -0
  189. data/manual/text/kerning_and_character_spacing.rb +39 -0
  190. data/manual/text/leading.rb +25 -0
  191. data/manual/text/line_wrapping.rb +41 -0
  192. data/manual/text/paragraph_indentation.rb +26 -0
  193. data/manual/text/positioned_text.rb +38 -0
  194. data/manual/text/registering_families.rb +48 -0
  195. data/manual/text/rendering_and_color.rb +37 -0
  196. data/manual/text/right_to_left_text.rb +43 -0
  197. data/manual/text/rotation.rb +43 -0
  198. data/manual/text/single_usage.rb +37 -0
  199. data/manual/text/text.rb +75 -0
  200. data/manual/text/text_box_excess.rb +32 -0
  201. data/manual/text/text_box_extensions.rb +45 -0
  202. data/manual/text/text_box_overflow.rb +44 -0
  203. data/manual/text/utf8.rb +28 -0
  204. data/{examples/m17n → manual/text}/win_ansi_charset.rb +14 -10
  205. data/prawn.gemspec +18 -12
  206. data/spec/acceptance/png.rb +23 -0
  207. data/spec/annotations_spec.rb +16 -32
  208. data/spec/bounding_box_spec.rb +128 -15
  209. data/spec/cell_spec.rb +169 -38
  210. data/spec/column_box_spec.rb +33 -0
  211. data/spec/destinations_spec.rb +5 -5
  212. data/spec/document_spec.rb +150 -104
  213. data/spec/extensions/encoding_helpers.rb +10 -0
  214. data/spec/extensions/mocha.rb +1 -0
  215. data/spec/filters_spec.rb +34 -0
  216. data/spec/font_metric_cache_spec.rb +52 -0
  217. data/spec/font_spec.rb +183 -97
  218. data/spec/formatted_text_arranger_spec.rb +43 -43
  219. data/spec/formatted_text_box_spec.rb +30 -20
  220. data/spec/formatted_text_fragment_spec.rb +8 -8
  221. data/spec/graphics_spec.rb +158 -69
  222. data/spec/grid_spec.rb +15 -15
  223. data/spec/image_handler_spec.rb +42 -0
  224. data/spec/images_spec.rb +49 -24
  225. data/spec/inline_formatted_text_parser_spec.rb +73 -19
  226. data/spec/jpg_spec.rb +4 -4
  227. data/spec/line_wrap_spec.rb +26 -26
  228. data/spec/measurement_units_spec.rb +6 -6
  229. data/spec/name_tree_spec.rb +21 -21
  230. data/spec/object_store_spec.rb +39 -39
  231. data/spec/outline_spec.rb +93 -53
  232. data/spec/pdf_object_spec.rb +88 -86
  233. data/spec/png_spec.rb +31 -28
  234. data/spec/reference_spec.rb +32 -32
  235. data/spec/repeater_spec.rb +25 -11
  236. data/spec/security_spec.rb +44 -12
  237. data/spec/snapshot_spec.rb +8 -9
  238. data/spec/soft_mask_spec.rb +117 -0
  239. data/spec/span_spec.rb +10 -15
  240. data/spec/spec_helper.rb +25 -8
  241. data/spec/stamp_spec.rb +29 -30
  242. data/spec/stream_spec.rb +58 -0
  243. data/spec/stroke_styles_spec.rb +36 -18
  244. data/spec/table/span_dummy_spec.rb +17 -0
  245. data/spec/table_spec.rb +697 -105
  246. data/spec/template_spec.rb +108 -54
  247. data/spec/text_at_spec.rb +18 -17
  248. data/spec/text_box_spec.rb +111 -62
  249. data/spec/text_rendering_mode_spec.rb +5 -5
  250. data/spec/text_spacing_spec.rb +4 -4
  251. data/spec/text_spec.rb +57 -49
  252. data/spec/transparency_spec.rb +5 -5
  253. metadata +421 -213
  254. data/data/fonts/Action Man.dfont +0 -0
  255. data/data/fonts/Activa.ttf +0 -0
  256. data/data/fonts/Chalkboard.ttf +0 -0
  257. data/data/fonts/DejaVuSans.ttf +0 -0
  258. data/data/fonts/Dustismo_Roman.ttf +0 -0
  259. data/data/fonts/comicsans.ttf +0 -0
  260. data/data/fonts/gkai00mp.ttf +0 -0
  261. data/data/images/rails.dat +0 -0
  262. data/data/images/rails.png +0 -0
  263. data/examples/bounding_box/russian_boxes.rb +0 -37
  264. data/examples/example_helper.rb +0 -11
  265. data/examples/general/context_sensitive_headers.rb +0 -38
  266. data/examples/graphics/cmyk.rb +0 -13
  267. data/examples/graphics/gradient.rb +0 -23
  268. data/examples/graphics/png_types.rb +0 -23
  269. data/examples/graphics/remote_images.rb +0 -13
  270. data/examples/m17n/full_win_ansi_character_list.rb +0 -20
  271. data/examples/m17n/sjis.rb +0 -29
  272. data/examples/table/bill.rb +0 -54
  273. data/examples/table/header.rb +0 -15
  274. data/examples/text/font_calculations.rb +0 -92
  275. data/examples/text/hyphenation.rb +0 -45
  276. data/examples/text/indent_paragraphs.rb +0 -24
  277. data/lib/prawn/core.rb +0 -85
  278. data/lib/prawn/core/text/formatted/arranger.rb +0 -294
  279. data/lib/prawn/core/text/formatted/line_wrap.rb +0 -273
  280. data/lib/prawn/core/text/formatted/wrap.rb +0 -153
  281. data/lib/prawn/graphics/gradient.rb +0 -84
  282. data/lib/prawn/security/arcfour.rb +0 -51
@@ -7,24 +7,24 @@ describe "When stroking with default settings" do
7
7
  it "cap_style should be :butt" do
8
8
  @pdf.cap_style.should == :butt
9
9
  end
10
-
10
+
11
11
  it "join_style should be :miter" 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
 
20
20
  describe "Cap styles" do
21
21
  before(:each) { create_pdf }
22
-
22
+
23
23
  it "should be able to use assignment operator" do
24
24
  @pdf.cap_style = :round
25
25
  @pdf.cap_style.should == :round
26
26
  end
27
-
27
+
28
28
  describe "#cap_style(:butt)" do
29
29
  it "rendered PDF should include butt style cap" do
30
30
  @pdf.cap_style(:butt)
@@ -60,12 +60,12 @@ end
60
60
 
61
61
  describe "Join styles" do
62
62
  before(:each) { create_pdf }
63
-
63
+
64
64
  it "should be able to use assignment operator" do
65
65
  @pdf.join_style = :round
66
66
  @pdf.join_style.should == :round
67
67
  end
68
-
68
+
69
69
  describe "#join_style(:miter)" do
70
70
  it "rendered PDF should include miter style join" do
71
71
  @pdf.join_style(:miter)
@@ -101,16 +101,16 @@ end
101
101
 
102
102
  describe "Dashes" do
103
103
  before(:each) { create_pdf }
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)
@@ -143,6 +143,24 @@ describe "Dashes" do
143
143
  end
144
144
  end
145
145
 
146
+ describe "setting a dash by using an array" do
147
+ it "dash and spaces should be set from the array" do
148
+ @pdf.dash([1,2,3,4])
149
+ dashes = PDF::Inspector::Graphics::Dash.analyze(@pdf.render)
150
+ dashes.stroke_dash.should == [[1, 2, 3, 4], 0]
151
+ end
152
+ it "space options has to be ignored" do
153
+ @pdf.dash([1,2,3,4], :space => 3)
154
+ dashes = PDF::Inspector::Graphics::Dash.analyze(@pdf.render)
155
+ dashes.stroke_dash.should == [[1, 2, 3, 4], 0]
156
+ end
157
+ it "phase options should be correctly used" do
158
+ @pdf.dash([1,2,3,4], :phase => 3)
159
+ dashes = PDF::Inspector::Graphics::Dash.analyze(@pdf.render)
160
+ dashes.stroke_dash.should == [[1, 2, 3, 4], 3]
161
+ end
162
+ end
163
+
146
164
  describe "clearing stroke dash" do
147
165
  it "should restore solid line" do
148
166
  @pdf.dash(2)
@@ -151,7 +169,7 @@ describe "Dashes" do
151
169
  dashes.stroke_dash.should == [[], 0]
152
170
  end
153
171
  end
154
-
172
+
155
173
  it "should carry the current dash settings over to new pages" do
156
174
  @pdf.dash(2)
157
175
  @pdf.start_new_page
@@ -159,19 +177,19 @@ describe "Dashes" do
159
177
  dashes.stroke_dash_count.should == 2
160
178
  dashes.stroke_dash.should == [[2, 2], 0]
161
179
  end
162
-
180
+
163
181
  describe "#dashed?" do
164
-
182
+
165
183
  it "an initial document should not be dashed" do
166
184
  @pdf.dashed?.should == false
167
185
  end
168
-
186
+
169
187
  it "should return true if any of the currently active settings are dashed" do
170
188
  @pdf.dash(2)
171
189
  @pdf.save_graphics_state
172
190
  @pdf.dashed?.should == true
173
191
  end
174
-
192
+
175
193
  it "should return false if the document was most recently undashed" do
176
194
  @pdf.dash(2)
177
195
  @pdf.save_graphics_state
@@ -179,7 +197,7 @@ describe "Dashes" do
179
197
  @pdf.save_graphics_state
180
198
  @pdf.dashed?.should == false
181
199
  end
182
-
200
+
183
201
  it "should return true when restoring to a state that was dashed" do
184
202
  @pdf.dash(2)
185
203
  @pdf.save_graphics_state
@@ -187,7 +205,7 @@ describe "Dashes" do
187
205
  @pdf.restore_graphics_state
188
206
  @pdf.dashed?.should == true
189
207
  end
190
-
208
+
191
209
  end
192
210
 
193
211
  end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "..", "spec_helper")
4
+ require 'set'
5
+
6
+ describe "Prawn::Table::Cell::SpanDummy" do
7
+ before(:each) do
8
+ @pdf = Prawn::Document.new
9
+ @table = @pdf.table([[{:content => "Row", :colspan => 2}]])
10
+ @master_cell = @table.cells[0,0]
11
+ @span_dummy = @master_cell.dummy_cells.first
12
+ end
13
+
14
+ it "delegates background_color to the master cell" do
15
+ @span_dummy.background_color.should == @master_cell.background_color
16
+ end
17
+ end
@@ -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,23 +29,79 @@ 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
46
45
  end
47
46
  end
48
47
 
48
+ describe "You can explicitly set the column widths and use a colspan > 1" do
49
+ it "should work with a colspan > 1 with given column_widths (issue #407)" do
50
+ #normal entries in line 1
51
+ data = [
52
+ [ '','',''],
53
+ [ { :content => "", :colspan => 3 } ],
54
+ [ "", "", "" ],
55
+ ]
56
+ pdf = Prawn::Document.new
57
+ table = Prawn::Table.new data, pdf, :column_widths => [100 , 200, 240]
58
+
59
+ #colspan entry in line 1
60
+ data = [
61
+ [ { :content => "", :colspan => 3 } ],
62
+ [ "", "", "" ],
63
+ ]
64
+ pdf = Prawn::Document.new
65
+ table = Prawn::Table.new data, pdf, :column_widths => [100 , 200, 240]
66
+
67
+ #mixed entries in line 1
68
+ data = [
69
+ [ { :content => "", :colspan =>2 }, "" ],
70
+ [ "", "", "" ],
71
+ ]
72
+ pdf = Prawn::Document.new
73
+ table = Prawn::Table.new data, pdf, :column_widths => [100 , 200, 240]
74
+
75
+ data = [['', '', {:content => '', :colspan => 2}, '',''],
76
+ ['',{:content => '', :colspan => 5}]
77
+ ]
78
+ pdf = Prawn::Document.new
79
+ table = Prawn::Table.new data, pdf, :column_widths => [50 , 100, 50, 50, 50, 50]
80
+
81
+ end
82
+
83
+ it "illustrate issue #533" do
84
+ data = [['', '', '', '', '',''],
85
+ ['',{:content => '', :colspan => 5}]]
86
+ pdf = Prawn::Document.new
87
+ table = Prawn::Table.new data, pdf, :column_widths => [50, 200, 40, 40, 50, 50]
88
+ end
89
+
90
+ it "illustrates issue #502" do
91
+ pdf = Prawn::Document.new
92
+ first = {:content=>"Foooo fo foooooo",:width=>50,:align=>:center}
93
+ second = {:content=>"Foooo",:colspan=>2,:width=>70,:align=>:center}
94
+ third = {:content=>"fooooooooooo, fooooooooooooo, fooo, foooooo fooooo",:width=>55,:align=>:center}
95
+ fourth = {:content=>"Bar",:width=>15,:align=>:center}
96
+ table_content = [[
97
+ first,
98
+ [[second],[third,fourth]]
99
+ ]]
100
+ pdf.move_down(20)
101
+ pdf.table(table_content)
102
+ end
103
+ end
104
+
49
105
  describe "#initialize" do
50
106
  before(:each) do
51
107
  @pdf = Prawn::Document.new
@@ -56,7 +112,8 @@ describe "Prawn::Table" do
56
112
  initializer.expects(:kick).once
57
113
 
58
114
  @pdf.table([["a"]]){
59
- self.should.be.an.instance_of(Prawn::Table); initializer.kick }
115
+ initializer.kick
116
+ }
60
117
  end
61
118
 
62
119
  it "should call a 1-arg block with the document as the argument" do
@@ -64,7 +121,7 @@ describe "Prawn::Table" do
64
121
  initializer.expects(:kick).once
65
122
 
66
123
  @pdf.table([["a"]]){ |doc|
67
- doc.should.be.an.instance_of(Prawn::Table); initializer.kick }
124
+ doc.should be_a_kind_of(Prawn::Table); initializer.kick }
68
125
  end
69
126
 
70
127
  it "should proxy cell methods to #cells" do
@@ -80,7 +137,7 @@ describe "Prawn::Table" do
80
137
 
81
138
  it "should generate a text cell based on a String" do
82
139
  t = @pdf.table([["foo"]])
83
- t.cells[0,0].should.be.a.kind_of(Prawn::Table::Cell::Text)
140
+ t.cells[0,0].should be_a_kind_of(Prawn::Table::Cell::Text)
84
141
  end
85
142
 
86
143
  it "should pass through a text cell" do
@@ -97,9 +154,14 @@ describe "Prawn::Table" do
97
154
  end
98
155
 
99
156
  it "should select rows by number or range" do
100
- Set.new(@table.row(0).map { |c| c.content }).should ==
157
+ Set.new(@table.row(0).map { |c| c.content }).should ==
101
158
  Set.new(%w[R0C0 R0C1])
102
- Set.new(@table.rows(0..1).map { |c| c.content }).should ==
159
+ Set.new(@table.rows(0..1).map { |c| c.content }).should ==
160
+ Set.new(%w[R0C0 R0C1 R1C0 R1C1])
161
+ end
162
+
163
+ it "should select rows by array" do
164
+ Set.new(@table.rows([0, 1]).map { |c| c.content }).should ==
103
165
  Set.new(%w[R0C0 R0C1 R1C0 R1C1])
104
166
  end
105
167
 
@@ -113,9 +175,14 @@ describe "Prawn::Table" do
113
175
  end
114
176
 
115
177
  it "should select columns by number or range" do
116
- Set.new(@table.column(0).map { |c| c.content }).should ==
178
+ Set.new(@table.column(0).map { |c| c.content }).should ==
117
179
  Set.new(%w[R0C0 R1C0])
118
- Set.new(@table.columns(0..1).map { |c| c.content }).should ==
180
+ Set.new(@table.columns(0..1).map { |c| c.content }).should ==
181
+ Set.new(%w[R0C0 R0C1 R1C0 R1C1])
182
+ end
183
+
184
+ it "should select columns by array" do
185
+ Set.new(@table.columns([0, 1]).map { |c| c.content }).should ==
119
186
  Set.new(%w[R0C0 R0C1 R1C0 R1C1])
120
187
  end
121
188
 
@@ -133,13 +200,13 @@ describe "Prawn::Table" do
133
200
  end
134
201
 
135
202
  it "should accept a filter block, returning a cell proxy" do
136
- @table.cells.filter { |c| c.content =~ /R0/ }.column(1).map{ |c|
203
+ @table.cells.filter { |c| c.content =~ /R0/ }.column(1).map{ |c|
137
204
  c.content }.should == ["R0C1"]
138
205
  end
139
206
 
140
207
  it "should accept the [] method, returning a Cell or nil" do
141
208
  @table.cells[0, 0].content.should == "R0C0"
142
- @table.cells[12, 12].should.be.nil
209
+ @table.cells[12, 12].should be_nil
143
210
  end
144
211
 
145
212
  it "should proxy unknown methods to the cells" do
@@ -150,6 +217,20 @@ describe "Prawn::Table" do
150
217
  @table.cells[1, 0].height.should == 100
151
218
  end
152
219
 
220
+ it "should ignore non-setter methods" do
221
+ lambda {
222
+ @table.cells.content_width
223
+ }.should raise_error(NoMethodError)
224
+ end
225
+
226
+ it "skips cells that don't respond to the given method" do
227
+ table = @pdf.make_table([[{:content => "R0", :colspan => 2}],
228
+ %w[R1C0 R1C1]])
229
+ lambda {
230
+ table.row(0).font_style = :bold
231
+ }.should_not raise_error
232
+ end
233
+
153
234
  it "should accept the style method, proxying its calls to the cells" do
154
235
  @table.cells.style(:height => 200, :width => 200)
155
236
  @table.column(0).style(:width => 100)
@@ -194,14 +275,14 @@ describe "Prawn::Table" do
194
275
  end
195
276
 
196
277
  describe "width" do
197
- it "should raise an error if the given width is outside of range" do
278
+ it "should raise_error an error if the given width is outside of range" do
198
279
  lambda do
199
280
  @pdf.table([["foo"]], :width => 1)
200
- end.should.raise(Prawn::Errors::CannotFit)
281
+ end.should raise_error(Prawn::Errors::CannotFit)
201
282
 
202
283
  lambda do
203
284
  @pdf.table([[@long_text]], :width => @pdf.bounds.width + 100)
204
- end.should.raise(Prawn::Errors::CannotFit)
285
+ end.should raise_error(Prawn::Errors::CannotFit)
205
286
  end
206
287
 
207
288
  it "should accept the natural width for small tables" do
@@ -210,7 +291,7 @@ describe "Prawn::Table" do
210
291
  @table.width.should == @table.cells[0, 0].natural_content_width + pad
211
292
  end
212
293
 
213
- it "width should equal sum(column_widths)" do
294
+ it "width should == sum(column_widths)" do
214
295
  table = Prawn::Table.new([%w[ a b c ], %w[d e f]], @pdf) do
215
296
  column(0).width = 50
216
297
  column(1).width = 100
@@ -243,13 +324,13 @@ describe "Prawn::Table" do
243
324
  "column widths within a single table" do
244
325
  hpad, fs = 10, 6
245
326
  stretchy_columns = 2
246
-
327
+
247
328
  col0_width = 50
248
329
  col1_width = @pdf.width_of("foo", :size => fs)
249
330
  col2_width = @pdf.width_of("foobar", :size => fs)
250
331
  col3_width = 150
251
332
 
252
- table = Prawn::Table.new( [%w[snake foo b apple],
333
+ table = Prawn::Table.new( [%w[snake foo b apple],
253
334
  %w[kitten d foobar banana]], @pdf,
254
335
  :cell_style => { :padding => hpad, :size => fs }) do
255
336
 
@@ -257,16 +338,38 @@ describe "Prawn::Table" do
257
338
  column(3).width = col3_width
258
339
  end
259
340
 
260
- table.width.should == col1_width + col2_width +
261
- 2*stretchy_columns*hpad +
341
+ table.width.should == col1_width + col2_width +
342
+ 2*stretchy_columns*hpad +
262
343
  col0_width + col3_width
263
344
  end
264
345
 
265
- it "should not exceed the maximum width of the margin_box" do
346
+ it "should preserve all manually requested column widths" do
347
+ col0_width = 50
348
+ col1_width = 20
349
+ col3_width = 60
350
+
351
+ table = Prawn::Table.new( [["snake", "foo", "b",
352
+ "some long, long text that will wrap"],
353
+ %w[kitten d foobar banana]], @pdf,
354
+ :width => 150) do
355
+
356
+ column(0).width = col0_width
357
+ column(1).width = col1_width
358
+ column(3).width = col3_width
359
+ end
360
+
361
+ table.draw
362
+
363
+ table.column(0).width.should == col0_width
364
+ table.column(1).width.should == col1_width
365
+ table.column(3).width.should == col3_width
366
+ end
367
+
368
+ it "should_not exceed the maximum width of the margin_box" do
266
369
  expected_width = @pdf.margin_box.width
267
370
  data = [
268
371
  ['This is a column with a lot of text that should comfortably exceed '+
269
- 'the width of a normal document margin_box width', 'Some more text',
372
+ 'the width of a normal document margin_box width', 'Some more text',
270
373
  'and then some more', 'Just a bit more to be extra sure']
271
374
  ]
272
375
  table = Prawn::Table.new(data, @pdf)
@@ -274,25 +377,25 @@ describe "Prawn::Table" do
274
377
  table.width.should == expected_width
275
378
  end
276
379
 
277
- it "should not exceed the maximum width of the margin_box even with" +
380
+ it "should_not exceed the maximum width of the margin_box even with" +
278
381
  "manual widths specified" do
279
382
  expected_width = @pdf.margin_box.width
280
383
  data = [
281
384
  ['This is a column with a lot of text that should comfortably exceed '+
282
- 'the width of a normal document margin_box width', 'Some more text',
385
+ 'the width of a normal document margin_box width', 'Some more text',
283
386
  'and then some more', 'Just a bit more to be extra sure']
284
387
  ]
285
388
  table = Prawn::Table.new(data, @pdf) { column(1).width = 100 }
286
389
 
287
390
  table.width.should == expected_width
288
391
  end
289
-
392
+
290
393
  it "scales down only the non-preset column widths when the natural width" +
291
394
  "exceeds the maximum width of the margin_box" do
292
395
  expected_width = @pdf.margin_box.width
293
396
  data = [
294
397
  ['This is a column with a lot of text that should comfortably exceed '+
295
- 'the width of a normal document margin_box width', 'Some more text',
398
+ 'the width of a normal document margin_box width', 'Some more text',
296
399
  'and then some more', 'Just a bit more to be extra sure']
297
400
  ]
298
401
  table = Prawn::Table.new(data, @pdf) { column(1).width = 100; column(3).width = 50 }
@@ -328,29 +431,29 @@ describe "Prawn::Table" do
328
431
  end
329
432
 
330
433
  it "should allow table cells to be resized in block" do
331
- lambda do
332
- @pdf.table([%w[1 2 3 4 5]]) do |t|
333
- t.width = 40
334
- t.cells.size = 8
335
- t.cells.padding = 0
336
- end
337
- end.should.not.raise(Prawn::Errors::CannotFit)
434
+ # if anything goes wrong, a CannotFit error will be raised
435
+
436
+ @pdf.table([%w[1 2 3 4 5]]) do |t|
437
+ t.width = 40
438
+ t.cells.size = 8
439
+ t.cells.padding = 0
440
+ end
338
441
  end
339
442
 
340
443
  it "should be the width of the :width parameter" do
341
444
  expected_width = 300
342
- table = Prawn::Table.new( [%w[snake foo b apple],
445
+ table = Prawn::Table.new( [%w[snake foo b apple],
343
446
  %w[kitten d foobar banana]], @pdf,
344
447
  :width => expected_width)
345
448
 
346
449
  table.width.should == expected_width
347
450
  end
348
451
 
349
- it "should not exceed the :width option" do
452
+ it "should_not exceed the :width option" do
350
453
  expected_width = 400
351
454
  data = [
352
455
  ['This is a column with a lot of text that should comfortably exceed '+
353
- 'the width of a normal document margin_box width', 'Some more text',
456
+ 'the width of a normal document margin_box width', 'Some more text',
354
457
  'and then some more', 'Just a bit more to be extra sure']
355
458
  ]
356
459
  table = Prawn::Table.new(data, @pdf, :width => expected_width)
@@ -358,11 +461,11 @@ describe "Prawn::Table" do
358
461
  table.width.should == expected_width
359
462
  end
360
463
 
361
- it "should not exceed the :width option even with manual widths specified" do
464
+ it "should_not exceed the :width option even with manual widths specified" do
362
465
  expected_width = 400
363
466
  data = [
364
467
  ['This is a column with a lot of text that should comfortably exceed '+
365
- 'the width of a normal document margin_box width', 'Some more text',
468
+ 'the width of a normal document margin_box width', 'Some more text',
366
469
  'and then some more', 'Just a bit more to be extra sure']
367
470
  ]
368
471
  table = Prawn::Table.new(data, @pdf, :width => expected_width) do
@@ -372,25 +475,26 @@ describe "Prawn::Table" do
372
475
  table.width.should == expected_width
373
476
  end
374
477
 
375
- # TODO: pending colspan
376
- xit "should calculate unspecified column widths even " +
478
+ it "should calculate unspecified column widths even " +
377
479
  "with colspan cells declared" do
378
480
  pdf = Prawn::Document.new
379
481
  hpad, fs = 3, 5
380
482
  columns = 3
381
483
 
382
- data = [ [ { :text => 'foo', :colspan => 2 }, "foobar" ],
484
+ data = [ [ { :content => 'foo', :colspan => 2 }, "foobar" ],
383
485
  [ "foo", "foo", "foo" ] ]
384
486
  table = Prawn::Table.new( data, pdf,
385
- :horizontal_padding => hpad,
386
- :font_size => fs )
487
+ :cell_style => {
488
+ :padding_left => hpad, :padding_right => hpad,
489
+ :size => fs
490
+ })
387
491
 
388
492
  col0_width = pdf.width_of("foo", :size => fs) # cell 1, 0
389
493
  col1_width = pdf.width_of("foo", :size => fs) # cell 1, 1
390
494
  col2_width = pdf.width_of("foobar", :size => fs) # cell 0, 1 (at col 2)
391
495
 
392
- table.width.should == col0_width.ceil + col1_width.ceil +
393
- col2_width.ceil + 2*columns*hpad
496
+ table.width.should == col0_width + col1_width +
497
+ col2_width + 2*columns*hpad
394
498
  end
395
499
  end
396
500
 
@@ -406,16 +510,16 @@ describe "Prawn::Table" do
406
510
  @pdf.y.should == old_y - table.height
407
511
  end
408
512
 
409
- it "should not wrap unnecessarily" do
513
+ it "should_not wrap unnecessarily" do
410
514
  # Test for FP errors and glitches
411
515
  t = @pdf.table([["Bender Bending Rodriguez"]])
412
516
  h = @pdf.height_of("one line")
413
- (t.height - 10).should.be < h*1.5
517
+ (t.height - 10).should be < h*1.5
414
518
  end
415
519
 
416
- it "should have a height of n rows" do
520
+ it "should have a height of n rows" do
417
521
  data = [["foo"],["bar"],["baaaz"]]
418
-
522
+
419
523
  vpad = 4
420
524
  origin = @pdf.y
421
525
  @pdf.table data, :cell_style => { :padding => vpad }
@@ -425,10 +529,45 @@ describe "Prawn::Table" do
425
529
  line_gap = @pdf.font.line_gap
426
530
 
427
531
  num_rows = data.length
428
- table_height.should.be.close(
429
- num_rows * font_height + 2*vpad*num_rows, 0.001 )
532
+ table_height.should be_within(0.001).of(
533
+ num_rows * font_height + 2*vpad*num_rows )
534
+ end
535
+
536
+ end
537
+
538
+ describe "position" do
539
+ it "should center tables with :position => :center" do
540
+ @pdf.expects(:bounding_box).with do |(x, y), opts|
541
+ expected = (@pdf.bounds.width - 500) / 2.0
542
+ (x - expected).abs < 0.001
543
+ end
544
+
545
+ @pdf.table([["foo"]], :column_widths => 500, :position => :center)
546
+ end
547
+
548
+ it "should right-align tables with :position => :right" do
549
+ @pdf.expects(:bounding_box).with do |(x, y), opts|
550
+ expected = @pdf.bounds.width - 500
551
+ (x - expected).abs < 0.001
552
+ end
553
+
554
+ @pdf.table([["foo"]], :column_widths => 500, :position => :right)
555
+ end
556
+
557
+ it "should accept a Numeric" do
558
+ @pdf.expects(:bounding_box).with do |(x, y), opts|
559
+ expected = 123
560
+ (x - expected).abs < 0.001
561
+ end
562
+
563
+ @pdf.table([["foo"]], :column_widths => 500, :position => 123)
430
564
  end
431
565
 
566
+ it "should raise_error an ArgumentError on unknown :position" do
567
+ lambda do
568
+ @pdf.table([["foo"]], :position => :bratwurst)
569
+ end.should raise_error(ArgumentError)
570
+ end
432
571
  end
433
572
 
434
573
  end
@@ -449,7 +588,7 @@ describe "Prawn::Table" do
449
588
  end.page_count.should == 2
450
589
  end
451
590
 
452
- it "should not start a new page before finishing out a row" do
591
+ it "should_not start a new page before finishing out a row" do
453
592
  Prawn::Document.new do
454
593
  table([[ (1..80).map{ |i| "Line #{i}" }.join("\n"), "Column 2" ]])
455
594
  end.page_count.should == 1
@@ -462,7 +601,7 @@ describe "Prawn::Table" do
462
601
  end.page_count.should == 2
463
602
  end
464
603
 
465
- it "should not start a new page to gain height when at the top of " +
604
+ it "should_not start a new page to gain height when at the top of " +
466
605
  "a bounding box, even if stretchy" do
467
606
  Prawn::Document.new do
468
607
  bounding_box([bounds.left, bounds.top - 20], :width => 400) do
@@ -489,7 +628,7 @@ describe "Prawn::Table" do
489
628
 
490
629
  output = PDF::Inspector::Page.analyze(pdf.render)
491
630
  # Ensure we only drew the header once, on the second page
492
- output.pages[0][:strings].should.be.empty
631
+ output.pages[0][:strings].should be_empty
493
632
  output.pages[1][:strings].should == ["Header", "Body"]
494
633
  end
495
634
 
@@ -523,6 +662,90 @@ describe "Prawn::Table" do
523
662
 
524
663
  t.draw
525
664
  end
665
+
666
+ describe "before_rendering_page callback" do
667
+ before(:each) { @pdf = Prawn::Document.new }
668
+
669
+ it "is passed all cells to be rendered on that page" do
670
+ kicked = 0
671
+
672
+ @pdf.table([["foo"]] * 100) do |t|
673
+ t.before_rendering_page do |page|
674
+ page.row_count.should == ((kicked < 3) ? 30 : 10)
675
+ page.column_count.should == 1
676
+ page.row(0).first.content.should == "foo"
677
+ page.row(-1).first.content.should == "foo"
678
+ kicked += 1
679
+ end
680
+ end
681
+
682
+ kicked.should == 4
683
+ end
684
+
685
+ it "numbers cells relative to their position on page" do
686
+ @pdf.table([["foo"]] * 100) do |t|
687
+ t.before_rendering_page do |page|
688
+ page[0, 0].content.should == "foo"
689
+ end
690
+ end
691
+ end
692
+
693
+ it "changing cells in the callback affects their rendering" do
694
+ seq = sequence("render order")
695
+
696
+ t = @pdf.make_table([["foo"]] * 40) do |table|
697
+ table.before_rendering_page do |page|
698
+ page[0, 0].background_color = "ff0000"
699
+ end
700
+ end
701
+
702
+ t.cells[30, 0].stubs(:draw_background).checking do |xy|
703
+ t.cells[30, 0].background_color.should == 'ff0000'
704
+ end
705
+ t.cells[31, 0].stubs(:draw_background).checking do |xy|
706
+ t.cells[31, 0].background_color.should == nil
707
+ end
708
+
709
+ t.draw
710
+ end
711
+
712
+ it "passes headers on page 2+" do
713
+ @pdf.table([["header"]] + [["foo"]] * 100, :header => true) do |t|
714
+ t.before_rendering_page do |page|
715
+ page[0, 0].content.should == "header"
716
+ end
717
+ end
718
+ end
719
+
720
+ it "updates dummy cell header rows" do
721
+ header = [[{:content => "header", :colspan => 2}]]
722
+ data = [["foo", "bar"]] * 31
723
+ @pdf.table(header + data, :header => true) do |t|
724
+ t.before_rendering_page do |page|
725
+ cell = page[0, 0]
726
+ cell.dummy_cells.each {|dc| dc.row.should == cell.row }
727
+ end
728
+ end
729
+ end
730
+
731
+ it "allows headers to be changed" do
732
+ seq = sequence("render order")
733
+ @pdf.expects(:draw_text!).with { |t, _| t == "hdr1"}.in_sequence(seq)
734
+ @pdf.expects(:draw_text!).with { |t, _| t == "foo"}.times(29).in_sequence(seq)
735
+ # Verify that the changed cell doesn't mutate subsequent pages
736
+ @pdf.expects(:draw_text!).with { |t, _| t == "header"}.in_sequence(seq)
737
+ @pdf.expects(:draw_text!).with { |t, _| t == "foo"}.times(11).in_sequence(seq)
738
+
739
+ set_first_page_headers = false
740
+ @pdf.table([["header"]] + [["foo"]] * 40, :header => true) do |t|
741
+ t.before_rendering_page do |page|
742
+ # only change first page header
743
+ page[0, 0].content = "hdr1" unless set_first_page_headers
744
+ set_first_page_headers = true
745
+ end
746
+ end
747
+ end
748
+ end
526
749
  end
527
750
 
528
751
  describe "#style" do
@@ -543,11 +766,17 @@ describe "Prawn::Table" do
543
766
  it "should default to {} for the hash argument" do
544
767
  stylable = stub()
545
768
  stylable.expects(:style).with({}).once
546
-
769
+
547
770
  Prawn::Document.new do
548
771
  table([["x"]]) { style(stylable) }
549
772
  end
550
773
  end
774
+
775
+ it "ignores unknown values on a cell-by-cell basis" do
776
+ Prawn::Document.new do
777
+ table([["x", [["y"]]]], :cell_style => {:overflow => :shrink_to_fit})
778
+ end
779
+ end
551
780
  end
552
781
 
553
782
  describe "row_colors" do
@@ -561,14 +790,48 @@ describe "Prawn::Table" do
561
790
  it "should ignore headers" do
562
791
  data = [["header"], ["foo"], ["bar"], ["baz"]]
563
792
  pdf = Prawn::Document.new
564
- t = pdf.table(data, :header => true,
793
+ t = pdf.table(data, :header => true,
565
794
  :row_colors => ['cccccc', 'ffffff']) do
566
795
  row(0).background_color = '333333'
567
796
  end
568
797
 
569
- t.cells.map{|x| x.background_color}.should ==
798
+ t.cells.map{|x| x.background_color}.should ==
570
799
  %w[333333 cccccc ffffff cccccc]
571
800
  end
801
+
802
+ it "stripes rows consistently from page to page, skipping header rows" do
803
+ data = [["header"]] + [["foo"]] * 70
804
+ pdf = Prawn::Document.new
805
+ t = pdf.make_table(data, :header => true,
806
+ :row_colors => ['cccccc', 'ffffff']) do
807
+ cells.padding = 0
808
+ cells.size = 9
809
+ row(0).size = 11
810
+ end
811
+
812
+ # page 1: header + 67 cells (odd number -- verifies that the next
813
+ # page disrupts the even/odd coloring, since both the last data cell
814
+ # on this page and the first one on the next are colored cccccc)
815
+ Prawn::Table::Cell.expects(:draw_cells).with do |cells|
816
+ cells.map { |c, (x, y)| c.background_color } ==
817
+ [nil] + (%w[cccccc ffffff] * 33) + %w[cccccc]
818
+ end
819
+ # page 2: header and 3 data cells
820
+ Prawn::Table::Cell.expects(:draw_cells).with do |cells|
821
+ cells.map { |c, (x, y)| c.background_color } ==
822
+ [nil] + %w[cccccc ffffff cccccc]
823
+ end
824
+ t.draw
825
+ end
826
+
827
+ it "should_not override an explicit background_color" do
828
+ data = [["foo"], ["bar"], ["baz"]]
829
+ pdf = Prawn::Document.new
830
+ table = pdf.table(data, :row_colors => ['cccccc', 'ffffff']) { |t|
831
+ t.cells[0, 0].background_color = 'dddddd'
832
+ }
833
+ table.cells.map{|x| x.background_color}.should == %w[dddddd ffffff cccccc]
834
+ end
572
835
  end
573
836
 
574
837
  describe "inking" do
@@ -578,7 +841,7 @@ describe "Prawn::Table" do
578
841
 
579
842
  it "should set the x-position of each cell based on widths" do
580
843
  @table = @pdf.table([["foo", "bar", "baz"]])
581
-
844
+
582
845
  x = 0
583
846
  (0..2).each do |col|
584
847
  cell = @table.cells[0, col]
@@ -593,7 +856,7 @@ describe "Prawn::Table" do
593
856
 
594
857
  (0..2).each do |row|
595
858
  cell = @table.cells[row, 0]
596
- cell.y.should.be.close(y, 0.01)
859
+ cell.y.should be_within(0.01).of(y)
597
860
  y -= cell.height
598
861
  end
599
862
  end
@@ -606,16 +869,16 @@ describe "Prawn::Table" do
606
869
  output.strings.should == data.flatten
607
870
  end
608
871
 
609
- it "should not cause an error if rendering the very first row causes a " +
872
+ it "should_not cause an error if rendering the very first row causes a " +
610
873
  "page break" do
611
- Prawn::Document.new do
874
+ Prawn::Document.new do |pdf|
612
875
  arr = Array(1..5).collect{|i| ["cell #{i}"] }
613
876
 
614
- move_down( y - (bounds.absolute_bottom + 3) )
877
+ pdf.move_down( pdf.y - (pdf.bounds.absolute_bottom + 3) )
615
878
 
616
879
  lambda {
617
- table(arr)
618
- }.should.not.raise
880
+ pdf.table(arr)
881
+ }.should_not raise_error
619
882
  end
620
883
  end
621
884
 
@@ -658,7 +921,7 @@ describe "Prawn::Table" do
658
921
  describe "in stretchy bounding boxes" do
659
922
  it "should draw all cells on a row at the same y-position" do
660
923
  pdf = Prawn::Document.new
661
-
924
+
662
925
  text_y = pdf.y.to_i - 5 # text starts 5pt below current y pos (padding)
663
926
 
664
927
  pdf.bounding_box([0, pdf.cursor], :width => pdf.bounds.width) do
@@ -673,30 +936,123 @@ describe "Prawn::Table" do
673
936
  end
674
937
 
675
938
  describe "headers" do
676
- it "should add headers to output when specified" do
677
- data = [["a", "b"], ["foo","bar"],["baz","bang"]]
678
- @pdf = Prawn::Document.new
679
- @pdf.table(data, :header => true)
680
- output = PDF::Inspector::Text.analyze(@pdf.render)
681
- output.strings.should == data.flatten
682
- end
939
+ context "single row header" do
940
+ it "should add headers to output when specified" do
941
+ data = [["a", "b"], ["foo","bar"],["baz","bang"]]
942
+ @pdf = Prawn::Document.new
943
+ @pdf.table(data, :header => true)
944
+ output = PDF::Inspector::Text.analyze(@pdf.render)
945
+ output.strings.should == data.flatten
946
+ end
683
947
 
684
- it "should repeat headers across pages" do
685
- data = [["foo","bar"]] * 30
686
- headers = ["baz","foobar"]
687
- @pdf = Prawn::Document.new
688
- @pdf.table([headers] + data, :header => true)
689
- output = PDF::Inspector::Text.analyze(@pdf.render)
690
- output.strings.should == headers + data.flatten[0..-3] + headers +
691
- data.flatten[-2..-1]
948
+ it "should repeat headers across pages" do
949
+ data = [["foo","bar"]] * 30
950
+ headers = ["baz","foobar"]
951
+ @pdf = Prawn::Document.new
952
+ @pdf.table([headers] + data, :header => true)
953
+ output = PDF::Inspector::Text.analyze(@pdf.render)
954
+ output.strings.should == headers + data.flatten[0..-3] + headers +
955
+ data.flatten[-2..-1]
956
+ end
957
+
958
+ it "draws headers at the correct position" do
959
+ data = [["header"]] + [["foo"]] * 40
960
+
961
+ Prawn::Table::Cell.expects(:draw_cells).times(2).checking do |cells|
962
+ cells.each do |cell, pt|
963
+ if cell.content == "header"
964
+ # Assert that header text is drawn at the same location on each page
965
+ if @header_location
966
+ pt.should == @header_location
967
+ else
968
+ @header_location = pt
969
+ end
970
+ end
971
+ end
972
+ end
973
+ @pdf = Prawn::Document.new
974
+ @pdf.table(data, :header => true)
975
+ end
976
+
977
+ it "draws headers at the correct position with column box" do
978
+ data = [["header"]] + [["foo"]] * 40
979
+
980
+ Prawn::Table::Cell.expects(:draw_cells).times(2).checking do |cells|
981
+ cells.each do |cell, pt|
982
+ if cell.content == "header"
983
+ pt[0].should == @pdf.bounds.left
984
+ end
985
+ end
986
+ end
987
+ @pdf = Prawn::Document.new
988
+ @pdf.column_box [0, @pdf.cursor], :width => @pdf.bounds.width, :columns => 2 do
989
+ @pdf.table(data, :header => true)
990
+ end
991
+ end
992
+
993
+ it "should_not draw header twice when starting new page" do
994
+ @pdf = Prawn::Document.new
995
+ @pdf.y = 0
996
+ @pdf.table([["Header"], ["Body"]], :header => true)
997
+ output = PDF::Inspector::Text.analyze(@pdf.render)
998
+ output.strings.should == ["Header", "Body"]
999
+ end
692
1000
  end
693
1001
 
694
- it "should not draw header twice when starting new page" do
695
- @pdf = Prawn::Document.new
696
- @pdf.y = 0
697
- @pdf.table([["Header"], ["Body"]], :header => true)
698
- output = PDF::Inspector::Text.analyze(@pdf.render)
699
- output.strings.should == ["Header", "Body"]
1002
+ context "multiple row header" do
1003
+ it "should add headers to output when specified" do
1004
+ data = [["a", "b"], ["c", "d"], ["foo","bar"],["baz","bang"]]
1005
+ @pdf = Prawn::Document.new
1006
+ @pdf.table(data, :header => 2)
1007
+ output = PDF::Inspector::Text.analyze(@pdf.render)
1008
+ output.strings.should == data.flatten
1009
+ end
1010
+
1011
+ it "should repeat headers across pages" do
1012
+ data = [["foo","bar"]] * 30
1013
+ headers = ["baz","foobar"] + ["bas", "foobaz"]
1014
+ @pdf = Prawn::Document.new
1015
+ @pdf.table([headers] + data, :header => 2)
1016
+ output = PDF::Inspector::Text.analyze(@pdf.render)
1017
+ output.strings.should == headers + data.flatten[0..-3] + headers +
1018
+ data.flatten[-4..-1]
1019
+ end
1020
+
1021
+ it "draws headers at the correct position" do
1022
+ data = [["header"]] + [["header2"]] + [["foo"]] * 40
1023
+
1024
+ Prawn::Table::Cell.expects(:draw_cells).times(2).checking do |cells|
1025
+ cells.each do |cell, pt|
1026
+ if cell.content == "header"
1027
+ # Assert that header text is drawn at the same location on each page
1028
+ if @header_location
1029
+ pt.should == @header_location
1030
+ else
1031
+ @header_location = pt
1032
+ end
1033
+ end
1034
+
1035
+ if cell.content == "header2"
1036
+ # Assert that header text is drawn at the same location on each page
1037
+ if @header2_location
1038
+ pt.should == @header2_location
1039
+ else
1040
+ @header2_location = pt
1041
+ end
1042
+ end
1043
+ end
1044
+ end
1045
+ @pdf = Prawn::Document.new
1046
+ @pdf.table(data, :header => 2)
1047
+ end
1048
+
1049
+ it "should_not draw header twice when starting new page" do
1050
+ @pdf = Prawn::Document.new
1051
+ @pdf.y = 0
1052
+ @pdf.table([["Header"], ["Header2"], ["Body"]], :header => 2)
1053
+ output = PDF::Inspector::Text.analyze(@pdf.render)
1054
+ output.strings.should == ["Header", "Header2", "Body"]
1055
+ end
700
1056
  end
701
1057
  end
702
1058
 
@@ -709,8 +1065,8 @@ describe "Prawn::Table" do
709
1065
 
710
1066
  it "can be created from an Array" do
711
1067
  cell = Prawn::Table::Cell.make(@pdf, [["foo"]])
712
- cell.should.be.an.instance_of(Prawn::Table::Cell::Subtable)
713
- cell.subtable.should.be.an.instance_of(Prawn::Table)
1068
+ cell.should be_a_kind_of(Prawn::Table::Cell::Subtable)
1069
+ cell.subtable.should be_a_kind_of(Prawn::Table)
714
1070
  end
715
1071
 
716
1072
  it "defaults its padding to zero" do
@@ -720,7 +1076,7 @@ describe "Prawn::Table" do
720
1076
  it "has a subtable accessor" do
721
1077
  @table.cells[0, 0].subtable.should == @subtable
722
1078
  end
723
-
1079
+
724
1080
  it "determines its dimensions from the subtable" do
725
1081
  @table.cells[0, 0].width.should == @subtable.width
726
1082
  @table.cells[0, 0].height.should == @subtable.height
@@ -729,35 +1085,271 @@ describe "Prawn::Table" do
729
1085
  end
730
1086
 
731
1087
  describe "An invalid table" do
732
-
1088
+
733
1089
  before(:each) do
734
1090
  @pdf = Prawn::Document.new
735
1091
  @bad_data = ["Single Nested Array"]
736
1092
  end
737
-
738
- it "should raise error when invalid table data is given" do
739
- assert_raises(Prawn::Errors::InvalidTableData) do
1093
+
1094
+ it "should raise_error error when invalid table data is given" do
1095
+ lambda {
740
1096
  @pdf.table(@bad_data)
741
- end
1097
+ }.should raise_error(Prawn::Errors::InvalidTableData)
742
1098
  end
743
1099
 
744
- it "should raise an EmptyTableError with empty table data" do
1100
+ it "should raise_error an EmptyTableError with empty table data" do
745
1101
  lambda {
746
1102
  data = []
747
1103
  @pdf = Prawn::Document.new
748
1104
  @pdf.table(data)
749
- }.should.raise( Prawn::Errors::EmptyTable )
750
- end
1105
+ }.should raise_error( Prawn::Errors::EmptyTable )
1106
+ end
751
1107
 
752
- it "should raise an EmptyTableError with nil table data" do
1108
+ it "should raise_error an EmptyTableError with nil table data" do
753
1109
  lambda {
754
1110
  data = nil
755
1111
  @pdf = Prawn::Document.new
756
1112
  @pdf.table(data)
757
- }.should.raise( Prawn::Errors::EmptyTable )
758
- end
1113
+ }.should raise_error( Prawn::Errors::EmptyTable )
1114
+ end
759
1115
 
760
1116
  end
761
1117
 
762
1118
  end
763
1119
 
1120
+ describe "colspan / rowspan" do
1121
+ before(:each) { create_pdf }
1122
+
1123
+ it "doesn't raise an error" do
1124
+ lambda {
1125
+ @pdf.table([[{:content => "foo", :colspan => 2, :rowspan => 2}]])
1126
+ }.should_not raise_error
1127
+ end
1128
+
1129
+ it "colspan is properly counted" do
1130
+ t = @pdf.make_table([[{:content => "foo", :colspan => 2}]])
1131
+ t.column_length.should == 2
1132
+ end
1133
+
1134
+ it "rowspan is properly counted" do
1135
+ t = @pdf.make_table([[{:content => "foo", :rowspan => 2}]])
1136
+ t.row_length.should == 2
1137
+ end
1138
+
1139
+ it "raises if colspan or rowspan are called after layout" do
1140
+ lambda {
1141
+ @pdf.table([["foo"]]) { cells[0, 0].colspan = 2 }
1142
+ }.should raise_error(Prawn::Errors::InvalidTableSpan)
1143
+
1144
+ lambda {
1145
+ @pdf.table([["foo"]]) { cells[0, 0].rowspan = 2 }
1146
+ }.should raise_error(Prawn::Errors::InvalidTableSpan)
1147
+ end
1148
+
1149
+ it "raises when spans overlap" do
1150
+ lambda {
1151
+ @pdf.table([["foo", {:content => "bar", :rowspan => 2}],
1152
+ [{:content => "baz", :colspan => 2}]])
1153
+ }.should raise_error(Prawn::Errors::InvalidTableSpan)
1154
+ end
1155
+
1156
+ it "table and cell width account for colspan" do
1157
+ t = @pdf.table([["a", {:content => "b", :colspan => 2}]],
1158
+ :column_widths => [100, 100, 100])
1159
+ spanned = t.cells[0, 1]
1160
+ spanned.colspan.should == 2
1161
+ t.width.should == 300
1162
+ t.cells.min_width.should == 300
1163
+ t.cells.max_width.should == 300
1164
+ spanned.width.should == 200
1165
+ end
1166
+
1167
+ it "table and cell height account for rowspan" do
1168
+ t = @pdf.table([["a"], [{:content => "b", :rowspan => 2}]]) do
1169
+ row(0..2).height = 100
1170
+ end
1171
+ spanned = t.cells[1, 0]
1172
+ spanned.rowspan.should == 2
1173
+ t.height.should == 300
1174
+ spanned.height.should == 200
1175
+ end
1176
+
1177
+ it "provides the full content_width as drawing space" do
1178
+ w = @pdf.make_table([["foo"]]).cells[0, 0].content_width
1179
+
1180
+ t = @pdf.make_table([[{:content => "foo", :colspan => 2}]])
1181
+ t.cells[0, 0].spanned_content_width.should == w
1182
+ end
1183
+
1184
+ it "dummy cells are not drawn" do
1185
+ # make a fake master cell for the dummy cell to slave to
1186
+ t = @pdf.make_table([[{:content => "foo", :colspan => 2}]])
1187
+
1188
+ # drawing just a dummy cell should_not ink
1189
+ @pdf.expects(:stroke_line).never
1190
+ @pdf.expects(:draw_text!).never
1191
+ Prawn::Table::Cell.draw_cells([t.cells[0, 1]])
1192
+ end
1193
+
1194
+ it "dummy cells do not add any height or width" do
1195
+ t1 = @pdf.table([["foo"]])
1196
+
1197
+ t2 = @pdf.table([[{:content => "foo", :colspan => 2}]])
1198
+ t2.width.should == t1.width
1199
+
1200
+ t3 = @pdf.table([[{:content => "foo", :rowspan => 2}]])
1201
+ t3.height.should == t1.height
1202
+ end
1203
+
1204
+ it "dummy cells ignored by #style" do
1205
+ t = @pdf.table([[{:content => "blah", :colspan => 2}]],
1206
+ :cell_style => { :size => 9 })
1207
+ t.cells[0, 0].size.should == 9
1208
+ end
1209
+
1210
+ context "inheriting master cell styles from dummy cell" do
1211
+ # Relatively full coverage for all these attributes that should be
1212
+ # inherited.
1213
+ [["border_X_width", 20],
1214
+ ["border_X_color", "123456"],
1215
+ ["padding_X", 20]].each do |attribute, val|
1216
+ attribute_right = attribute.sub("X", "right")
1217
+ attribute_left = attribute.sub("X", "left")
1218
+ attribute_bottom = attribute.sub("X", "bottom")
1219
+ attribute_top = attribute.sub("X", "top")
1220
+
1221
+ specify "#{attribute_right} of right column is inherited" do
1222
+ t = @pdf.table([[{:content => "blah", :colspan => 2}]]) do |table|
1223
+ table.column(1).send("#{attribute_right}=", val)
1224
+ end
1225
+
1226
+ t.cells[0, 0].send(attribute_right).should == val
1227
+ end
1228
+
1229
+ specify "#{attribute_bottom} of bottom row is inherited" do
1230
+ t = @pdf.table([[{:content => "blah", :rowspan => 2}]]) do |table|
1231
+ table.row(1).send("#{attribute_bottom}=", val)
1232
+ end
1233
+
1234
+ t.cells[0, 0].send(attribute_bottom).should == val
1235
+ end
1236
+
1237
+ specify "#{attribute_left} of right column is not inherited" do
1238
+ t = @pdf.table([[{:content => "blah", :colspan => 2}]]) do |table|
1239
+ table.column(1).send("#{attribute_left}=", val)
1240
+ end
1241
+
1242
+ t.cells[0, 0].send(attribute_left).should_not == val
1243
+ end
1244
+
1245
+ specify "#{attribute_right} of interior column is not inherited" do
1246
+ t = @pdf.table([[{:content => "blah", :colspan => 3}]]) do |table|
1247
+ table.column(1).send("#{attribute_right}=", val)
1248
+ end
1249
+
1250
+ t.cells[0, 0].send(attribute_right).should_not == val
1251
+ end
1252
+
1253
+ specify "#{attribute_bottom} of interior row is not inherited" do
1254
+ t = @pdf.table([[{:content => "blah", :rowspan => 3}]]) do |table|
1255
+ table.row(1).send("#{attribute_bottom}=", val)
1256
+ end
1257
+
1258
+ t.cells[0, 0].send(attribute_bottom).should_not == val
1259
+ end
1260
+
1261
+ specify "#{attribute_top} of bottom row is not inherited" do
1262
+ t = @pdf.table([[{:content => "blah", :rowspan => 2}]]) do |table|
1263
+ table.row(1).send("#{attribute_top}=", val)
1264
+ end
1265
+
1266
+ t.cells[0, 0].send(attribute_top).should_not == val
1267
+ end
1268
+ end
1269
+ end
1270
+
1271
+ it "splits natural width between cols in the group" do
1272
+ t = @pdf.table([[{:content => "foo", :colspan => 2}]])
1273
+ widths = t.column_widths
1274
+ widths[0].should == widths[1]
1275
+ end
1276
+
1277
+ it "splits natural width between cols when width is increased" do
1278
+ t = @pdf.table([[{:content => "foo", :colspan => 2}]],
1279
+ :width => @pdf.bounds.width)
1280
+ widths = t.column_widths
1281
+ widths[0].should == widths[1]
1282
+ end
1283
+
1284
+ it "splits min-width between cols in the group" do
1285
+ # Since column_widths, when reducing column widths, reduces proportional to
1286
+ # the remaining width after each column's min width, we must ensure that the
1287
+ # min-width is split proportionally in order to ensure the width is still
1288
+ # split evenly when the width is reduced. (See "splits natural width between
1289
+ # cols when width is reduced".)
1290
+ t = @pdf.table([[{:content => "foo", :colspan => 2}]],
1291
+ :width => 20)
1292
+ t.column(0).min_width.should == t.column(1).min_width
1293
+ end
1294
+
1295
+ it "splits natural width between cols when width is reduced" do
1296
+ t = @pdf.table([[{:content => "foo", :colspan => 2}]],
1297
+ :width => 20)
1298
+ widths = t.column_widths
1299
+ widths[0].should == widths[1]
1300
+ end
1301
+
1302
+ it "honors a large, explicitly set table width" do
1303
+ t = @pdf.table([[{:content => "AAAAAAAAAA", :colspan => 3}],
1304
+ ["A", "B", "C"]],
1305
+ :width => 400)
1306
+
1307
+ t.column_widths.inject(0) { |sum, w| sum + w }.
1308
+ should be_within(0.01).of(400)
1309
+ end
1310
+
1311
+ it "honors a small, explicitly set table width" do
1312
+ t = @pdf.table([[{:content => "Lorem ipsum dolor sit amet " * 20,
1313
+ :colspan => 3}],
1314
+ ["A", "B", "C"]],
1315
+ :width => 200)
1316
+ t.column_widths.inject(0) { |sum, w| sum + w }.
1317
+ should be_within(0.01).of(200)
1318
+ end
1319
+
1320
+ it "splits natural_content_height between rows in the group" do
1321
+ t = @pdf.table([[{:content => "foo", :rowspan => 2}]])
1322
+ heights = t.row_heights
1323
+ heights[0].should == heights[1]
1324
+ end
1325
+
1326
+ it "skips column numbers that have been col-spanned" do
1327
+ t = @pdf.table([["a", "b", {:content => "c", :colspan => 3}, "d"]])
1328
+ t.cells[0, 0].content.should == "a"
1329
+ t.cells[0, 1].content.should == "b"
1330
+ t.cells[0, 2].content.should == "c"
1331
+ t.cells[0, 3].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
1332
+ t.cells[0, 4].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
1333
+ t.cells[0, 5].content.should == "d"
1334
+ end
1335
+
1336
+ it "skips row/col positions that have been row-spanned" do
1337
+ t = @pdf.table([["a", {:content => "b", :colspan => 2, :rowspan => 2}, "c"],
1338
+ ["d", "e"],
1339
+ ["f", "g", "h", "i"]])
1340
+ t.cells[0, 0].content.should == "a"
1341
+ t.cells[0, 1].content.should == "b"
1342
+ t.cells[0, 2].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
1343
+ t.cells[0, 3].content.should == "c"
1344
+
1345
+ t.cells[1, 0].content.should == "d"
1346
+ t.cells[1, 1].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
1347
+ t.cells[1, 2].should be_a_kind_of(Prawn::Table::Cell::SpanDummy)
1348
+ t.cells[1, 3].content.should == "e"
1349
+
1350
+ t.cells[2, 0].content.should == "f"
1351
+ t.cells[2, 1].content.should == "g"
1352
+ t.cells[2, 2].content.should == "h"
1353
+ t.cells[2, 3].content.should == "i"
1354
+ end
1355
+ end