nurettin-prawn 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (303) hide show
  1. data/COPYING +2 -0
  2. data/GPLv2 +340 -0
  3. data/GPLv3 +674 -0
  4. data/Gemfile +18 -0
  5. data/LICENSE +56 -0
  6. data/README.md +98 -0
  7. data/Rakefile +45 -0
  8. data/data/encodings/win_ansi.txt +29 -0
  9. data/data/fonts/Action Man.dfont +0 -0
  10. data/data/fonts/Activa.ttf +0 -0
  11. data/data/fonts/Chalkboard.ttf +0 -0
  12. data/data/fonts/Courier-Bold.afm +342 -0
  13. data/data/fonts/Courier-BoldOblique.afm +342 -0
  14. data/data/fonts/Courier-Oblique.afm +342 -0
  15. data/data/fonts/Courier.afm +342 -0
  16. data/data/fonts/DejaVuSans.ttf +0 -0
  17. data/data/fonts/Dustismo_Roman.ttf +0 -0
  18. data/data/fonts/Helvetica-Bold.afm +2827 -0
  19. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  20. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  21. data/data/fonts/Helvetica.afm +3051 -0
  22. data/data/fonts/MustRead.html +19 -0
  23. data/data/fonts/Symbol.afm +213 -0
  24. data/data/fonts/Times-Bold.afm +2588 -0
  25. data/data/fonts/Times-BoldItalic.afm +2384 -0
  26. data/data/fonts/Times-Italic.afm +2667 -0
  27. data/data/fonts/Times-Roman.afm +2419 -0
  28. data/data/fonts/ZapfDingbats.afm +225 -0
  29. data/data/fonts/comicsans.ttf +0 -0
  30. data/data/fonts/gkai00mp.ttf +0 -0
  31. data/data/images/16bit.alpha +0 -0
  32. data/data/images/16bit.dat +0 -0
  33. data/data/images/16bit.png +0 -0
  34. data/data/images/arrow.png +0 -0
  35. data/data/images/arrow2.png +0 -0
  36. data/data/images/barcode_issue.png +0 -0
  37. data/data/images/dice.alpha +0 -0
  38. data/data/images/dice.dat +0 -0
  39. data/data/images/dice.png +0 -0
  40. data/data/images/dice_interlaced.png +0 -0
  41. data/data/images/fractal.jpg +0 -0
  42. data/data/images/letterhead.jpg +0 -0
  43. data/data/images/page_white_text.alpha +0 -0
  44. data/data/images/page_white_text.dat +0 -0
  45. data/data/images/page_white_text.png +0 -0
  46. data/data/images/pigs.jpg +0 -0
  47. data/data/images/prawn.png +0 -0
  48. data/data/images/rails.dat +0 -0
  49. data/data/images/rails.png +0 -0
  50. data/data/images/ruport.png +0 -0
  51. data/data/images/ruport_data.dat +0 -0
  52. data/data/images/ruport_transparent.png +0 -0
  53. data/data/images/ruport_type0.png +0 -0
  54. data/data/images/stef.jpg +0 -0
  55. data/data/images/tru256.bmp +0 -0
  56. data/data/images/web-links.dat +1 -0
  57. data/data/images/web-links.png +0 -0
  58. data/data/pdfs/complex_template.pdf +0 -0
  59. data/data/pdfs/contains_ttf_font.pdf +0 -0
  60. data/data/pdfs/encrypted.pdf +0 -0
  61. data/data/pdfs/form.pdf +820 -0
  62. data/data/pdfs/hexagon.pdf +61 -0
  63. data/data/pdfs/indirect_reference.pdf +86 -0
  64. data/data/pdfs/multipage_template.pdf +127 -0
  65. data/data/pdfs/nested_pages.pdf +118 -0
  66. data/data/pdfs/page_without_mediabox.pdf +193 -0
  67. data/data/pdfs/resources_as_indirect_object.pdf +83 -0
  68. data/data/pdfs/two_hexagons.pdf +90 -0
  69. data/data/pdfs/version_1_6.pdf +61 -0
  70. data/data/shift_jis_text.txt +1 -0
  71. data/lib/prawn.rb +29 -0
  72. data/lib/prawn/compatibility.rb +87 -0
  73. data/lib/prawn/core.rb +87 -0
  74. data/lib/prawn/core/annotations.rb +61 -0
  75. data/lib/prawn/core/byte_string.rb +9 -0
  76. data/lib/prawn/core/destinations.rb +90 -0
  77. data/lib/prawn/core/document_state.rb +79 -0
  78. data/lib/prawn/core/literal_string.rb +16 -0
  79. data/lib/prawn/core/name_tree.rb +177 -0
  80. data/lib/prawn/core/object_store.rb +320 -0
  81. data/lib/prawn/core/page.rb +212 -0
  82. data/lib/prawn/core/pdf_object.rb +125 -0
  83. data/lib/prawn/core/reference.rb +119 -0
  84. data/lib/prawn/core/text.rb +268 -0
  85. data/lib/prawn/core/text/formatted/arranger.rb +294 -0
  86. data/lib/prawn/core/text/formatted/line_wrap.rb +288 -0
  87. data/lib/prawn/core/text/formatted/wrap.rb +153 -0
  88. data/lib/prawn/document.rb +707 -0
  89. data/lib/prawn/document/bounding_box.rb +510 -0
  90. data/lib/prawn/document/column_box.rb +132 -0
  91. data/lib/prawn/document/graphics_state.rb +136 -0
  92. data/lib/prawn/document/internals.rb +173 -0
  93. data/lib/prawn/document/page_geometry.rb +136 -0
  94. data/lib/prawn/document/snapshot.rb +89 -0
  95. data/lib/prawn/document/span.rb +55 -0
  96. data/lib/prawn/encoding.rb +121 -0
  97. data/lib/prawn/errors.rb +99 -0
  98. data/lib/prawn/font.rb +386 -0
  99. data/lib/prawn/font/afm.rb +203 -0
  100. data/lib/prawn/font/dfont.rb +42 -0
  101. data/lib/prawn/font/ttf.rb +343 -0
  102. data/lib/prawn/graphics.rb +523 -0
  103. data/lib/prawn/graphics/cap_style.rb +46 -0
  104. data/lib/prawn/graphics/color.rb +231 -0
  105. data/lib/prawn/graphics/dash.rb +82 -0
  106. data/lib/prawn/graphics/join_style.rb +47 -0
  107. data/lib/prawn/graphics/patterns.rb +137 -0
  108. data/lib/prawn/graphics/transformation.rb +156 -0
  109. data/lib/prawn/graphics/transparency.rb +99 -0
  110. data/lib/prawn/images.rb +196 -0
  111. data/lib/prawn/images/image.rb +65 -0
  112. data/lib/prawn/images/jpg.rb +85 -0
  113. data/lib/prawn/images/png.rb +362 -0
  114. data/lib/prawn/layout.rb +20 -0
  115. data/lib/prawn/layout/grid.rb +259 -0
  116. data/lib/prawn/measurement_extensions.rb +46 -0
  117. data/lib/prawn/measurements.rb +71 -0
  118. data/lib/prawn/outline.rb +326 -0
  119. data/lib/prawn/repeater.rb +122 -0
  120. data/lib/prawn/security.rb +269 -0
  121. data/lib/prawn/soft_mask.rb +94 -0
  122. data/lib/prawn/stamp.rb +134 -0
  123. data/lib/prawn/table.rb +609 -0
  124. data/lib/prawn/table/cell.rb +776 -0
  125. data/lib/prawn/table/cell/image.rb +70 -0
  126. data/lib/prawn/table/cell/in_table.rb +27 -0
  127. data/lib/prawn/table/cell/span_dummy.rb +88 -0
  128. data/lib/prawn/table/cell/subtable.rb +65 -0
  129. data/lib/prawn/table/cell/text.rb +152 -0
  130. data/lib/prawn/table/cells.rb +260 -0
  131. data/lib/prawn/text.rb +420 -0
  132. data/lib/prawn/text/box.rb +141 -0
  133. data/lib/prawn/text/formatted.rb +4 -0
  134. data/lib/prawn/text/formatted/box.rb +563 -0
  135. data/lib/prawn/text/formatted/fragment.rb +253 -0
  136. data/lib/prawn/text/formatted/parser.rb +217 -0
  137. data/lib/prawn/utilities.rb +44 -0
  138. data/manual/basic_concepts/adding_pages.rb +27 -0
  139. data/manual/basic_concepts/basic_concepts.rb +34 -0
  140. data/manual/basic_concepts/creation.rb +39 -0
  141. data/manual/basic_concepts/cursor.rb +33 -0
  142. data/manual/basic_concepts/measurement.rb +25 -0
  143. data/manual/basic_concepts/origin.rb +38 -0
  144. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  145. data/manual/bounding_box/bounding_box.rb +39 -0
  146. data/manual/bounding_box/bounds.rb +49 -0
  147. data/manual/bounding_box/canvas.rb +24 -0
  148. data/manual/bounding_box/creation.rb +23 -0
  149. data/manual/bounding_box/indentation.rb +46 -0
  150. data/manual/bounding_box/nesting.rb +45 -0
  151. data/manual/bounding_box/russian_boxes.rb +40 -0
  152. data/manual/bounding_box/stretchy.rb +31 -0
  153. data/manual/document_and_page_options/background.rb +27 -0
  154. data/manual/document_and_page_options/document_and_page_options.rb +31 -0
  155. data/manual/document_and_page_options/metadata.rb +23 -0
  156. data/manual/document_and_page_options/page_margins.rb +38 -0
  157. data/manual/document_and_page_options/page_size.rb +34 -0
  158. data/manual/example_file.rb +116 -0
  159. data/manual/example_helper.rb +430 -0
  160. data/manual/example_package.rb +53 -0
  161. data/manual/example_section.rb +46 -0
  162. data/manual/graphics/circle_and_ellipse.rb +22 -0
  163. data/manual/graphics/color.rb +24 -0
  164. data/manual/graphics/common_lines.rb +28 -0
  165. data/manual/graphics/fill_and_stroke.rb +42 -0
  166. data/manual/graphics/fill_rules.rb +37 -0
  167. data/manual/graphics/gradients.rb +37 -0
  168. data/manual/graphics/graphics.rb +58 -0
  169. data/manual/graphics/helper.rb +17 -0
  170. data/manual/graphics/line_width.rb +35 -0
  171. data/manual/graphics/lines_and_curves.rb +41 -0
  172. data/manual/graphics/polygon.rb +29 -0
  173. data/manual/graphics/rectangle.rb +21 -0
  174. data/manual/graphics/rotate.rb +28 -0
  175. data/manual/graphics/scale.rb +41 -0
  176. data/manual/graphics/soft_masks.rb +46 -0
  177. data/manual/graphics/stroke_cap.rb +31 -0
  178. data/manual/graphics/stroke_dash.rb +43 -0
  179. data/manual/graphics/stroke_join.rb +30 -0
  180. data/manual/graphics/translate.rb +29 -0
  181. data/manual/graphics/transparency.rb +35 -0
  182. data/manual/images/absolute_position.rb +23 -0
  183. data/manual/images/fit.rb +21 -0
  184. data/manual/images/horizontal.rb +25 -0
  185. data/manual/images/images.rb +40 -0
  186. data/manual/images/plain_image.rb +18 -0
  187. data/manual/images/scale.rb +22 -0
  188. data/manual/images/vertical.rb +28 -0
  189. data/manual/images/width_and_height.rb +25 -0
  190. data/manual/layout/boxes.rb +27 -0
  191. data/manual/layout/content.rb +25 -0
  192. data/manual/layout/layout.rb +28 -0
  193. data/manual/layout/simple_grid.rb +23 -0
  194. data/manual/manual/cover.rb +26 -0
  195. data/manual/manual/foreword.rb +13 -0
  196. data/manual/manual/how_to_read_this_manual.rb +41 -0
  197. data/manual/manual/manual.rb +36 -0
  198. data/manual/outline/add_subsection_to.rb +61 -0
  199. data/manual/outline/insert_section_after.rb +47 -0
  200. data/manual/outline/outline.rb +32 -0
  201. data/manual/outline/sections_and_pages.rb +67 -0
  202. data/manual/repeatable_content/page_numbering.rb +54 -0
  203. data/manual/repeatable_content/repeatable_content.rb +31 -0
  204. data/manual/repeatable_content/repeater.rb +55 -0
  205. data/manual/repeatable_content/stamp.rb +41 -0
  206. data/manual/security/encryption.rb +31 -0
  207. data/manual/security/permissions.rb +38 -0
  208. data/manual/security/security.rb +28 -0
  209. data/manual/syntax_highlight.rb +52 -0
  210. data/manual/table/basic_block.rb +53 -0
  211. data/manual/table/before_rendering_page.rb +26 -0
  212. data/manual/table/cell_border_lines.rb +24 -0
  213. data/manual/table/cell_borders_and_bg.rb +31 -0
  214. data/manual/table/cell_dimensions.rb +30 -0
  215. data/manual/table/cell_text.rb +38 -0
  216. data/manual/table/column_widths.rb +30 -0
  217. data/manual/table/content_and_subtables.rb +39 -0
  218. data/manual/table/creation.rb +27 -0
  219. data/manual/table/filtering.rb +36 -0
  220. data/manual/table/flow_and_header.rb +17 -0
  221. data/manual/table/image_cells.rb +33 -0
  222. data/manual/table/position.rb +29 -0
  223. data/manual/table/row_colors.rb +20 -0
  224. data/manual/table/span.rb +30 -0
  225. data/manual/table/style.rb +22 -0
  226. data/manual/table/table.rb +52 -0
  227. data/manual/table/width.rb +27 -0
  228. data/manual/templates/full_template.rb +23 -0
  229. data/manual/templates/page_template.rb +47 -0
  230. data/manual/templates/templates.rb +26 -0
  231. data/manual/text/alignment.rb +44 -0
  232. data/manual/text/color.rb +24 -0
  233. data/manual/text/column_box.rb +32 -0
  234. data/manual/text/fallback_fonts.rb +37 -0
  235. data/manual/text/font.rb +41 -0
  236. data/manual/text/font_size.rb +45 -0
  237. data/manual/text/font_style.rb +23 -0
  238. data/manual/text/formatted_callbacks.rb +60 -0
  239. data/manual/text/formatted_text.rb +50 -0
  240. data/manual/text/free_flowing_text.rb +51 -0
  241. data/manual/text/group.rb +29 -0
  242. data/manual/text/inline.rb +43 -0
  243. data/manual/text/kerning_and_character_spacing.rb +39 -0
  244. data/manual/text/leading.rb +25 -0
  245. data/manual/text/line_wrapping.rb +41 -0
  246. data/manual/text/paragraph_indentation.rb +26 -0
  247. data/manual/text/positioned_text.rb +38 -0
  248. data/manual/text/registering_families.rb +48 -0
  249. data/manual/text/rendering_and_color.rb +37 -0
  250. data/manual/text/right_to_left_text.rb +43 -0
  251. data/manual/text/rotation.rb +43 -0
  252. data/manual/text/single_usage.rb +37 -0
  253. data/manual/text/text.rb +75 -0
  254. data/manual/text/text_box_excess.rb +32 -0
  255. data/manual/text/text_box_extensions.rb +45 -0
  256. data/manual/text/text_box_overflow.rb +44 -0
  257. data/manual/text/utf8.rb +28 -0
  258. data/manual/text/win_ansi_charset.rb +59 -0
  259. data/prawn.gemspec +46 -0
  260. data/spec/annotations_spec.rb +90 -0
  261. data/spec/bounding_box_spec.rb +493 -0
  262. data/spec/cell_spec.rb +584 -0
  263. data/spec/column_box_spec.rb +33 -0
  264. data/spec/data/curves.pdf +66 -0
  265. data/spec/destinations_spec.rb +15 -0
  266. data/spec/document_spec.rb +736 -0
  267. data/spec/extensions/encoding_helpers.rb +6 -0
  268. data/spec/extensions/mocha.rb +44 -0
  269. data/spec/font_spec.rb +433 -0
  270. data/spec/formatted_text_arranger_spec.rb +421 -0
  271. data/spec/formatted_text_box_spec.rb +640 -0
  272. data/spec/formatted_text_fragment_spec.rb +298 -0
  273. data/spec/graphics_spec.rb +651 -0
  274. data/spec/grid_spec.rb +85 -0
  275. data/spec/images_spec.rb +140 -0
  276. data/spec/inline_formatted_text_parser_spec.rb +515 -0
  277. data/spec/jpg_spec.rb +25 -0
  278. data/spec/line_wrap_spec.rb +333 -0
  279. data/spec/measurement_units_spec.rb +23 -0
  280. data/spec/name_tree_spec.rb +112 -0
  281. data/spec/object_store_spec.rb +170 -0
  282. data/spec/outline_spec.rb +426 -0
  283. data/spec/pdf_object_spec.rb +172 -0
  284. data/spec/png_spec.rb +240 -0
  285. data/spec/reference_spec.rb +105 -0
  286. data/spec/repeater_spec.rb +158 -0
  287. data/spec/security_spec.rb +126 -0
  288. data/spec/snapshot_spec.rb +186 -0
  289. data/spec/soft_mask_spec.rb +117 -0
  290. data/spec/span_spec.rb +49 -0
  291. data/spec/spec_helper.rb +36 -0
  292. data/spec/stamp_spec.rb +159 -0
  293. data/spec/stroke_styles_spec.rb +193 -0
  294. data/spec/table_spec.rb +1209 -0
  295. data/spec/template_spec.rb +351 -0
  296. data/spec/text_at_spec.rb +129 -0
  297. data/spec/text_box_spec.rb +1029 -0
  298. data/spec/text_rendering_mode_spec.rb +45 -0
  299. data/spec/text_spacing_spec.rb +93 -0
  300. data/spec/text_spec.rb +421 -0
  301. data/spec/text_with_inline_formatting_spec.rb +35 -0
  302. data/spec/transparency_spec.rb +89 -0
  303. metadata +544 -0
@@ -0,0 +1,1209 @@
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" do
7
+
8
+ describe "converting data to Cell objects" do
9
+ before(:each) do
10
+ @pdf = Prawn::Document.new
11
+ @table = @pdf.table([%w[R0C0 R0C1], %w[R1C0 R1C1]])
12
+ end
13
+
14
+ it "should return a Prawn::Table" do
15
+ @table.should be_a_kind_of Prawn::Table
16
+ end
17
+
18
+ it "should flatten the data into the @cells array in row-major order" do
19
+ @table.cells.map { |c| c.content }.should == %w[R0C0 R0C1 R1C0 R1C1]
20
+ end
21
+
22
+ it "should add row and column numbers to each cell" do
23
+ c = @table.cells.to_a.first
24
+ c.row.should == 0
25
+ c.column.should == 0
26
+ end
27
+
28
+ it "should allow empty fields" do
29
+ lambda {
30
+ data = [["foo","bar"],["baz",""]]
31
+ @pdf.table(data)
32
+ }.should_not raise_error
33
+ end
34
+
35
+ it "should allow a table with a header but no body" do
36
+ lambda { @pdf.table([["Header"]], :header => true) }.should_not raise_error
37
+ end
38
+
39
+ it "should accurately count columns from data" do
40
+ # First data row may contain colspan which would hide true column count
41
+ data = [["Name:", {:content => "Some very long name", :colspan => 5}]]
42
+ pdf = Prawn::Document.new
43
+ table = Prawn::Table.new data, pdf
44
+ table.column_widths.length.should == 6
45
+ end
46
+ end
47
+
48
+ describe "#initialize" do
49
+ before(:each) do
50
+ @pdf = Prawn::Document.new
51
+ end
52
+
53
+ it "should instance_eval a 0-arg block" do
54
+ initializer = mock()
55
+ initializer.expects(:kick).once
56
+
57
+ @pdf.table([["a"]]){
58
+ initializer.kick
59
+ }
60
+ end
61
+
62
+ it "should call a 1-arg block with the document as the argument" do
63
+ initializer = mock()
64
+ initializer.expects(:kick).once
65
+
66
+ @pdf.table([["a"]]){ |doc|
67
+ doc.should be_a_kind_of(Prawn::Table); initializer.kick }
68
+ end
69
+
70
+ it "should proxy cell methods to #cells" do
71
+ table = @pdf.table([["a"]], :cell_style => { :padding => 11 })
72
+ table.cells[0, 0].padding.should == [11, 11, 11, 11]
73
+ end
74
+
75
+ it "should set row and column length" do
76
+ table = @pdf.table([["a", "b", "c"], ["d", "e", "f"]])
77
+ table.row_length.should == 2
78
+ table.column_length.should == 3
79
+ end
80
+
81
+ it "should generate a text cell based on a String" do
82
+ t = @pdf.table([["foo"]])
83
+ t.cells[0,0].should be_a_kind_of(Prawn::Table::Cell::Text)
84
+ end
85
+
86
+ it "should pass through a text cell" do
87
+ c = Prawn::Table::Cell::Text.new(@pdf, [0,0], :content => "foo")
88
+ t = @pdf.table([[c]])
89
+ t.cells[0,0].should == c
90
+ end
91
+ end
92
+
93
+ describe "cell accessors" do
94
+ before(:each) do
95
+ @pdf = Prawn::Document.new
96
+ @table = @pdf.table([%w[R0C0 R0C1], %w[R1C0 R1C1]])
97
+ end
98
+
99
+ it "should select rows by number or range" do
100
+ Set.new(@table.row(0).map { |c| c.content }).should ==
101
+ Set.new(%w[R0C0 R0C1])
102
+ Set.new(@table.rows(0..1).map { |c| c.content }).should ==
103
+ Set.new(%w[R0C0 R0C1 R1C0 R1C1])
104
+ end
105
+
106
+ it "should select rows by array" do
107
+ Set.new(@table.rows([0, 1]).map { |c| c.content }).should ==
108
+ Set.new(%w[R0C0 R0C1 R1C0 R1C1])
109
+ end
110
+
111
+ it "should allow negative row selectors" do
112
+ Set.new(@table.row(-1).map { |c| c.content }).should ==
113
+ Set.new(%w[R1C0 R1C1])
114
+ Set.new(@table.rows(-2..-1).map { |c| c.content }).should ==
115
+ Set.new(%w[R0C0 R0C1 R1C0 R1C1])
116
+ Set.new(@table.rows(0..-1).map { |c| c.content }).should ==
117
+ Set.new(%w[R0C0 R0C1 R1C0 R1C1])
118
+ end
119
+
120
+ it "should select columns by number or range" do
121
+ Set.new(@table.column(0).map { |c| c.content }).should ==
122
+ Set.new(%w[R0C0 R1C0])
123
+ Set.new(@table.columns(0..1).map { |c| c.content }).should ==
124
+ Set.new(%w[R0C0 R0C1 R1C0 R1C1])
125
+ end
126
+
127
+ it "should select columns by array" do
128
+ Set.new(@table.columns([0, 1]).map { |c| c.content }).should ==
129
+ Set.new(%w[R0C0 R0C1 R1C0 R1C1])
130
+ end
131
+
132
+ it "should allow negative column selectors" do
133
+ Set.new(@table.column(-1).map { |c| c.content }).should ==
134
+ Set.new(%w[R0C1 R1C1])
135
+ Set.new(@table.columns(-2..-1).map { |c| c.content }).should ==
136
+ Set.new(%w[R0C0 R0C1 R1C0 R1C1])
137
+ Set.new(@table.columns(0..-1).map { |c| c.content }).should ==
138
+ Set.new(%w[R0C0 R0C1 R1C0 R1C1])
139
+ end
140
+
141
+ it "should allow rows and columns to be combined" do
142
+ @table.row(0).column(1).map { |c| c.content }.should == ["R0C1"]
143
+ end
144
+
145
+ it "should accept a filter block, returning a cell proxy" do
146
+ @table.cells.filter { |c| c.content =~ /R0/ }.column(1).map{ |c|
147
+ c.content }.should == ["R0C1"]
148
+ end
149
+
150
+ it "should accept the [] method, returning a Cell or nil" do
151
+ @table.cells[0, 0].content.should == "R0C0"
152
+ @table.cells[12, 12].should be_nil
153
+ end
154
+
155
+ it "should proxy unknown methods to the cells" do
156
+ @table.cells.height = 200
157
+ @table.row(1).height = 100
158
+
159
+ @table.cells[0, 0].height.should == 200
160
+ @table.cells[1, 0].height.should == 100
161
+ end
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
+
177
+ it "should accept the style method, proxying its calls to the cells" do
178
+ @table.cells.style(:height => 200, :width => 200)
179
+ @table.column(0).style(:width => 100)
180
+
181
+ @table.cells[0, 1].width.should == 200
182
+ @table.cells[1, 0].height.should == 200
183
+ @table.cells[1, 0].width.should == 100
184
+ end
185
+
186
+ it "style method should accept a block, passing each cell to be styled" do
187
+ @table.cells.style { |c| c.height = 200 }
188
+ @table.cells[0, 1].height.should == 200
189
+ end
190
+
191
+ it "should return the width of selected columns for #width" do
192
+ c0_width = @table.column(0).map{ |c| c.width }.max
193
+ c1_width = @table.column(1).map{ |c| c.width }.max
194
+
195
+ @table.column(0).width.should == c0_width
196
+ @table.column(1).width.should == c1_width
197
+
198
+ @table.columns(0..1).width.should == c0_width + c1_width
199
+ @table.cells.width.should == c0_width + c1_width
200
+ end
201
+
202
+ it "should return the height of selected rows for #height" do
203
+ r0_height = @table.row(0).map{ |c| c.height }.max
204
+ r1_height = @table.row(1).map{ |c| c.height }.max
205
+
206
+ @table.row(0).height.should == r0_height
207
+ @table.row(1).height.should == r1_height
208
+
209
+ @table.rows(0..1).height.should == r0_height + r1_height
210
+ @table.cells.height.should == r0_height + r1_height
211
+ end
212
+ end
213
+
214
+ describe "layout" do
215
+ before(:each) do
216
+ @pdf = Prawn::Document.new
217
+ @long_text = "The quick brown fox jumped over the lazy dogs. " * 5
218
+ end
219
+
220
+ describe "width" do
221
+ it "should raise_error an error if the given width is outside of range" do
222
+ lambda do
223
+ @pdf.table([["foo"]], :width => 1)
224
+ end.should raise_error(Prawn::Errors::CannotFit)
225
+
226
+ lambda do
227
+ @pdf.table([[@long_text]], :width => @pdf.bounds.width + 100)
228
+ end.should raise_error(Prawn::Errors::CannotFit)
229
+ end
230
+
231
+ it "should accept the natural width for small tables" do
232
+ pad = 10 # default padding
233
+ @table = @pdf.table([["a"]])
234
+ @table.width.should == @table.cells[0, 0].natural_content_width + pad
235
+ end
236
+
237
+ it "width should == sum(column_widths)" do
238
+ table = Prawn::Table.new([%w[ a b c ], %w[d e f]], @pdf) do
239
+ column(0).width = 50
240
+ column(1).width = 100
241
+ column(2).width = 150
242
+ end
243
+ table.width.should == 300
244
+ end
245
+
246
+ it "should accept Numeric for column_widths" do
247
+ table = Prawn::Table.new([%w[ a b c ], %w[d e f]], @pdf) do |t|
248
+ t.column_widths = 50
249
+ end
250
+ table.width.should == 150
251
+ end
252
+
253
+ it "should calculate unspecified column widths as "+
254
+ "(max(string_width) + 2*horizontal_padding)" do
255
+ hpad, fs = 3, 12
256
+ columns = 2
257
+ table = Prawn::Table.new( [%w[ foo b ], %w[d foobar]], @pdf,
258
+ :cell_style => { :padding => hpad, :size => fs } )
259
+
260
+ col0_width = @pdf.width_of("foo", :size => fs)
261
+ col1_width = @pdf.width_of("foobar", :size => fs)
262
+
263
+ table.width.should == col0_width + col1_width + 2*columns*hpad
264
+ end
265
+
266
+ it "should allow mixing autocalculated and preset"+
267
+ "column widths within a single table" do
268
+ hpad, fs = 10, 6
269
+ stretchy_columns = 2
270
+
271
+ col0_width = 50
272
+ col1_width = @pdf.width_of("foo", :size => fs)
273
+ col2_width = @pdf.width_of("foobar", :size => fs)
274
+ col3_width = 150
275
+
276
+ table = Prawn::Table.new( [%w[snake foo b apple],
277
+ %w[kitten d foobar banana]], @pdf,
278
+ :cell_style => { :padding => hpad, :size => fs }) do
279
+
280
+ column(0).width = col0_width
281
+ column(3).width = col3_width
282
+ end
283
+
284
+ table.width.should == col1_width + col2_width +
285
+ 2*stretchy_columns*hpad +
286
+ col0_width + col3_width
287
+ end
288
+
289
+ it "should preserve all manually requested column widths" do
290
+ col0_width = 50
291
+ col1_width = 20
292
+ col3_width = 60
293
+
294
+ table = Prawn::Table.new( [["snake", "foo", "b",
295
+ "some long, long text that will wrap"],
296
+ %w[kitten d foobar banana]], @pdf,
297
+ :width => 150) do
298
+
299
+ column(0).width = col0_width
300
+ column(1).width = col1_width
301
+ column(3).width = col3_width
302
+ end
303
+
304
+ table.draw
305
+
306
+ table.column(0).width.should == col0_width
307
+ table.column(1).width.should == col1_width
308
+ table.column(3).width.should == col3_width
309
+ end
310
+
311
+ it "should_not exceed the maximum width of the margin_box" do
312
+ expected_width = @pdf.margin_box.width
313
+ data = [
314
+ ['This is a column with a lot of text that should comfortably exceed '+
315
+ 'the width of a normal document margin_box width', 'Some more text',
316
+ 'and then some more', 'Just a bit more to be extra sure']
317
+ ]
318
+ table = Prawn::Table.new(data, @pdf)
319
+
320
+ table.width.should == expected_width
321
+ end
322
+
323
+ it "should_not exceed the maximum width of the margin_box even with" +
324
+ "manual widths specified" do
325
+ expected_width = @pdf.margin_box.width
326
+ data = [
327
+ ['This is a column with a lot of text that should comfortably exceed '+
328
+ 'the width of a normal document margin_box width', 'Some more text',
329
+ 'and then some more', 'Just a bit more to be extra sure']
330
+ ]
331
+ table = Prawn::Table.new(data, @pdf) { column(1).width = 100 }
332
+
333
+ table.width.should == expected_width
334
+ end
335
+
336
+ it "scales down only the non-preset column widths when the natural width" +
337
+ "exceeds the maximum width of the margin_box" do
338
+ expected_width = @pdf.margin_box.width
339
+ data = [
340
+ ['This is a column with a lot of text that should comfortably exceed '+
341
+ 'the width of a normal document margin_box width', 'Some more text',
342
+ 'and then some more', 'Just a bit more to be extra sure']
343
+ ]
344
+ table = Prawn::Table.new(data, @pdf) { column(1).width = 100; column(3).width = 50 }
345
+
346
+ table.width.should == expected_width
347
+ table.column_widths[1].should == 100
348
+ table.column_widths[3].should == 50
349
+ end
350
+
351
+ it "should allow width to be reset even after it has been calculated" do
352
+ @table = @pdf.table([[@long_text]])
353
+ @table.width
354
+ @table.width = 100
355
+ @table.width.should == 100
356
+ end
357
+
358
+ it "should shrink columns evenly when two equal columns compete" do
359
+ @table = @pdf.table([["foo", @long_text], [@long_text, "foo"]])
360
+ @table.cells[0, 0].width.should == @table.cells[0, 1].width
361
+ end
362
+
363
+ it "should grow columns evenly when equal deficient columns compete" do
364
+ @table = @pdf.table([["foo", "foobar"], ["foobar", "foo"]], :width => 500)
365
+ @table.cells[0, 0].width.should == @table.cells[0, 1].width
366
+ end
367
+
368
+ it "should respect manual widths" do
369
+ @table = @pdf.table([%w[foo bar baz], %w[baz bar foo]], :width => 500) do
370
+ column(1).width = 60
371
+ end
372
+ @table.column(1).width.should == 60
373
+ @table.column(0).width.should == @table.column(2).width
374
+ end
375
+
376
+ it "should allow table cells to be resized in block" do
377
+ lambda do
378
+ @pdf.table([%w[1 2 3 4 5]]) do |t|
379
+ t.width = 40
380
+ t.cells.size = 8
381
+ t.cells.padding = 0
382
+ end
383
+ end.should_not raise_error(Prawn::Errors::CannotFit)
384
+ end
385
+
386
+ it "should be the width of the :width parameter" do
387
+ expected_width = 300
388
+ table = Prawn::Table.new( [%w[snake foo b apple],
389
+ %w[kitten d foobar banana]], @pdf,
390
+ :width => expected_width)
391
+
392
+ table.width.should == expected_width
393
+ end
394
+
395
+ it "should_not exceed the :width option" do
396
+ expected_width = 400
397
+ data = [
398
+ ['This is a column with a lot of text that should comfortably exceed '+
399
+ 'the width of a normal document margin_box width', 'Some more text',
400
+ 'and then some more', 'Just a bit more to be extra sure']
401
+ ]
402
+ table = Prawn::Table.new(data, @pdf, :width => expected_width)
403
+
404
+ table.width.should == expected_width
405
+ end
406
+
407
+ it "should_not exceed the :width option even with manual widths specified" do
408
+ expected_width = 400
409
+ data = [
410
+ ['This is a column with a lot of text that should comfortably exceed '+
411
+ 'the width of a normal document margin_box width', 'Some more text',
412
+ 'and then some more', 'Just a bit more to be extra sure']
413
+ ]
414
+ table = Prawn::Table.new(data, @pdf, :width => expected_width) do
415
+ column(1).width = 100
416
+ end
417
+
418
+ table.width.should == expected_width
419
+ end
420
+
421
+ it "should calculate unspecified column widths even " +
422
+ "with colspan cells declared" do
423
+ pdf = Prawn::Document.new
424
+ hpad, fs = 3, 5
425
+ columns = 3
426
+
427
+ data = [ [ { :content => 'foo', :colspan => 2 }, "foobar" ],
428
+ [ "foo", "foo", "foo" ] ]
429
+ table = Prawn::Table.new( data, pdf,
430
+ :cell_style => {
431
+ :padding_left => hpad, :padding_right => hpad,
432
+ :size => fs
433
+ })
434
+
435
+ col0_width = pdf.width_of("foo", :size => fs) # cell 1, 0
436
+ col1_width = pdf.width_of("foo", :size => fs) # cell 1, 1
437
+ col2_width = pdf.width_of("foobar", :size => fs) # cell 0, 1 (at col 2)
438
+
439
+ table.width.should == col0_width + col1_width +
440
+ col2_width + 2*columns*hpad
441
+ end
442
+ end
443
+
444
+ describe "height" do
445
+ it "should set all cells in a row to the same height" do
446
+ @table = @pdf.table([["foo", @long_text]])
447
+ @table.cells[0, 0].height.should == @table.cells[0, 1].height
448
+ end
449
+
450
+ it "should move y-position to the bottom of the table after drawing" do
451
+ old_y = @pdf.y
452
+ table = @pdf.table([["foo"]])
453
+ @pdf.y.should == old_y - table.height
454
+ end
455
+
456
+ it "should_not wrap unnecessarily" do
457
+ # Test for FP errors and glitches
458
+ t = @pdf.table([["Bender Bending Rodriguez"]])
459
+ h = @pdf.height_of("one line")
460
+ (t.height - 10).should be < h*1.5
461
+ end
462
+
463
+ it "should have a height of n rows" do
464
+ data = [["foo"],["bar"],["baaaz"]]
465
+
466
+ vpad = 4
467
+ origin = @pdf.y
468
+ @pdf.table data, :cell_style => { :padding => vpad }
469
+
470
+ table_height = origin - @pdf.y
471
+ font_height = @pdf.font.height
472
+ line_gap = @pdf.font.line_gap
473
+
474
+ num_rows = data.length
475
+ table_height.should be_within(0.001).of(
476
+ num_rows * font_height + 2*vpad*num_rows )
477
+ end
478
+
479
+ end
480
+
481
+ describe "position" do
482
+ it "should center tables with :position => :center" do
483
+ @pdf.expects(:bounding_box).with do |(x, y), opts|
484
+ expected = (@pdf.bounds.width - 500) / 2.0
485
+ (x - expected).abs < 0.001
486
+ end
487
+
488
+ @pdf.table([["foo"]], :column_widths => 500, :position => :center)
489
+ end
490
+
491
+ it "should right-align tables with :position => :right" do
492
+ @pdf.expects(:bounding_box).with do |(x, y), opts|
493
+ expected = @pdf.bounds.width - 500
494
+ (x - expected).abs < 0.001
495
+ end
496
+
497
+ @pdf.table([["foo"]], :column_widths => 500, :position => :right)
498
+ end
499
+
500
+ it "should accept a Numeric" do
501
+ @pdf.expects(:bounding_box).with do |(x, y), opts|
502
+ expected = 123
503
+ (x - expected).abs < 0.001
504
+ end
505
+
506
+ @pdf.table([["foo"]], :column_widths => 500, :position => 123)
507
+ end
508
+
509
+ it "should raise_error an ArgumentError on unknown :position" do
510
+ lambda do
511
+ @pdf.table([["foo"]], :position => :bratwurst)
512
+ end.should raise_error(ArgumentError)
513
+ end
514
+ end
515
+
516
+ end
517
+
518
+ describe "Multi-page tables" do
519
+ it "should flow to the next page when hitting the bottom of the bounds" do
520
+ Prawn::Document.new { table([["foo"]] * 30) }.page_count.should == 1
521
+ Prawn::Document.new { table([["foo"]] * 31) }.page_count.should == 2
522
+ Prawn::Document.new { table([["foo"]] * 31); table([["foo"]] * 35) }.
523
+ page_count.should == 3
524
+ end
525
+
526
+ it "should respect the containing bounds" do
527
+ Prawn::Document.new do
528
+ bounding_box([0, cursor], :width => bounds.width, :height => 72) do
529
+ table([["foo"]] * 4)
530
+ end
531
+ end.page_count.should == 2
532
+ end
533
+
534
+ it "should_not start a new page before finishing out a row" do
535
+ Prawn::Document.new do
536
+ table([[ (1..80).map{ |i| "Line #{i}" }.join("\n"), "Column 2" ]])
537
+ end.page_count.should == 1
538
+ end
539
+
540
+ it "should only start new page on long cells if it would gain us height" do
541
+ Prawn::Document.new do
542
+ text "Hello"
543
+ table([[ (1..80).map{ |i| "Line #{i}" }.join("\n"), "Column 2" ]])
544
+ end.page_count.should == 2
545
+ end
546
+
547
+ it "should_not start a new page to gain height when at the top of " +
548
+ "a bounding box, even if stretchy" do
549
+ Prawn::Document.new do
550
+ bounding_box([bounds.left, bounds.top - 20], :width => 400) do
551
+ table([[ (1..80).map{ |i| "Line #{i}" }.join("\n"), "Column 2" ]])
552
+ end
553
+ end.page_count.should == 1
554
+ end
555
+
556
+ it "should still break to the next page if in a stretchy bounding box " +
557
+ "but not at the top" do
558
+ Prawn::Document.new do
559
+ bounding_box([bounds.left, bounds.top - 20], :width => 400) do
560
+ text "Hello"
561
+ table([[ (1..80).map{ |i| "Line #{i}" }.join("\n"), "Column 2" ]])
562
+ end
563
+ end.page_count.should == 2
564
+ end
565
+
566
+ it "should only draw first-page header if the first body row fits" do
567
+ pdf = Prawn::Document.new
568
+
569
+ pdf.y = 60 # not enough room for a table row
570
+ pdf.table [["Header"], ["Body"]], :header => true
571
+
572
+ output = PDF::Inspector::Page.analyze(pdf.render)
573
+ # Ensure we only drew the header once, on the second page
574
+ output.pages[0][:strings].should be_empty
575
+ output.pages[1][:strings].should == ["Header", "Body"]
576
+ end
577
+
578
+ it "should draw background before borders, but only within pages" do
579
+ seq = sequence("drawing_order")
580
+
581
+ @pdf = Prawn::Document.new
582
+
583
+ # give enough room for only the first row
584
+ @pdf.y = @pdf.bounds.absolute_bottom + 30
585
+ t = @pdf.make_table([["A", "B"],
586
+ ["C", "D"]],
587
+ :cell_style => {:background_color => 'ff0000'})
588
+
589
+ ca = t.cells[0, 0]
590
+ cb = t.cells[0, 1]
591
+ cc = t.cells[1, 0]
592
+ cd = t.cells[1, 1]
593
+
594
+ # All backgrounds should draw before any borders on page 1...
595
+ ca.expects(:draw_background).in_sequence(seq)
596
+ cb.expects(:draw_background).in_sequence(seq)
597
+ ca.expects(:draw_borders).in_sequence(seq)
598
+ cb.expects(:draw_borders).in_sequence(seq)
599
+ # ...and page 2
600
+ @pdf.expects(:start_new_page).in_sequence(seq)
601
+ cc.expects(:draw_background).in_sequence(seq)
602
+ cd.expects(:draw_background).in_sequence(seq)
603
+ cc.expects(:draw_borders).in_sequence(seq)
604
+ cd.expects(:draw_borders).in_sequence(seq)
605
+
606
+ t.draw
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
681
+ end
682
+
683
+ describe "#style" do
684
+ it "should send #style to its first argument, passing the style hash and" +
685
+ " block" do
686
+
687
+ stylable = stub()
688
+ stylable.expects(:style).with(:foo => :bar).once.yields
689
+
690
+ block = stub()
691
+ block.expects(:kick).once
692
+
693
+ Prawn::Document.new do
694
+ table([["x"]]) { style(stylable, :foo => :bar) { block.kick } }
695
+ end
696
+ end
697
+
698
+ it "should default to {} for the hash argument" do
699
+ stylable = stub()
700
+ stylable.expects(:style).with({}).once
701
+
702
+ Prawn::Document.new do
703
+ table([["x"]]) { style(stylable) }
704
+ end
705
+ end
706
+ end
707
+
708
+ describe "row_colors" do
709
+ it "should allow array syntax for :row_colors" do
710
+ data = [["foo"], ["bar"], ["baz"]]
711
+ pdf = Prawn::Document.new
712
+ t = pdf.table(data, :row_colors => ['cccccc', 'ffffff'])
713
+ t.cells.map{|x| x.background_color}.should == %w[cccccc ffffff cccccc]
714
+ end
715
+
716
+ it "should ignore headers" do
717
+ data = [["header"], ["foo"], ["bar"], ["baz"]]
718
+ pdf = Prawn::Document.new
719
+ t = pdf.table(data, :header => true,
720
+ :row_colors => ['cccccc', 'ffffff']) do
721
+ row(0).background_color = '333333'
722
+ end
723
+
724
+ t.cells.map{|x| x.background_color}.should ==
725
+ %w[333333 cccccc ffffff cccccc]
726
+ end
727
+
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
754
+ data = [["foo"], ["bar"], ["baz"]]
755
+ pdf = Prawn::Document.new
756
+ table = pdf.table(data, :row_colors => ['cccccc', 'ffffff']) { |t|
757
+ t.cells[0, 0].background_color = 'dddddd'
758
+ }
759
+ table.cells.map{|x| x.background_color}.should == %w[dddddd ffffff cccccc]
760
+ end
761
+ end
762
+
763
+ describe "inking" do
764
+ before(:each) do
765
+ @pdf = Prawn::Document.new
766
+ end
767
+
768
+ it "should set the x-position of each cell based on widths" do
769
+ @table = @pdf.table([["foo", "bar", "baz"]])
770
+
771
+ x = 0
772
+ (0..2).each do |col|
773
+ cell = @table.cells[0, col]
774
+ cell.x.should == x
775
+ x += cell.width
776
+ end
777
+ end
778
+
779
+ it "should set the y-position of each cell based on heights" do
780
+ y = 0
781
+ @table = @pdf.make_table([["foo"], ["bar"], ["baz"]])
782
+
783
+ (0..2).each do |row|
784
+ cell = @table.cells[row, 0]
785
+ cell.y.should be_within(0.01).of(y)
786
+ y -= cell.height
787
+ end
788
+ end
789
+
790
+ it "should output content cell by cell, row by row" do
791
+ data = [["foo","bar"],["baz","bang"]]
792
+ @pdf = Prawn::Document.new
793
+ @pdf.table(data)
794
+ output = PDF::Inspector::Text.analyze(@pdf.render)
795
+ output.strings.should == data.flatten
796
+ end
797
+
798
+ it "should_not cause an error if rendering the very first row causes a " +
799
+ "page break" do
800
+ Prawn::Document.new do |pdf|
801
+ arr = Array(1..5).collect{|i| ["cell #{i}"] }
802
+
803
+ pdf.move_down( pdf.y - (pdf.bounds.absolute_bottom + 3) )
804
+
805
+ lambda {
806
+ pdf.table(arr)
807
+ }.should_not raise_error
808
+ end
809
+ end
810
+
811
+ it "should draw all backgrounds before any borders" do
812
+ # lest backgrounds overlap borders:
813
+ # https://github.com/sandal/prawn/pull/226
814
+
815
+ seq = sequence("drawing_order")
816
+
817
+ t = @pdf.make_table([["A", "B"]],
818
+ :cell_style => {:background_color => 'ff0000'})
819
+ ca = t.cells[0, 0]
820
+ cb = t.cells[0, 1]
821
+
822
+ # XXX Not a perfectly general test, because it would still be acceptable
823
+ # if we drew B then A
824
+ ca.expects(:draw_background).in_sequence(seq)
825
+ cb.expects(:draw_background).in_sequence(seq)
826
+ ca.expects(:draw_borders).in_sequence(seq)
827
+ cb.expects(:draw_borders).in_sequence(seq)
828
+
829
+ t.draw
830
+ end
831
+
832
+ it "should allow multiple inkings of the same table" do
833
+ pdf = Prawn::Document.new
834
+ t = Prawn::Table.new([["foo"]], pdf)
835
+
836
+ pdf.expects(:bounding_box).with{|(x, y), options| y.to_i == 495}.yields
837
+ pdf.expects(:bounding_box).with{|(x, y), options| y.to_i == 395}.yields
838
+ pdf.expects(:draw_text!).with{ |text, options| text == 'foo' }.twice
839
+
840
+ pdf.move_cursor_to(500)
841
+ t.draw
842
+
843
+ pdf.move_cursor_to(400)
844
+ t.draw
845
+ end
846
+
847
+ describe "in stretchy bounding boxes" do
848
+ it "should draw all cells on a row at the same y-position" do
849
+ pdf = Prawn::Document.new
850
+
851
+ text_y = pdf.y.to_i - 5 # text starts 5pt below current y pos (padding)
852
+
853
+ pdf.bounding_box([0, pdf.cursor], :width => pdf.bounds.width) do
854
+ pdf.expects(:draw_text!).checking { |text, options|
855
+ pdf.bounds.absolute_top.should == text_y
856
+ }.times(3)
857
+
858
+ pdf.table([%w[a b c]])
859
+ end
860
+ end
861
+ end
862
+ end
863
+
864
+ describe "headers" do
865
+ it "should add headers to output when specified" do
866
+ data = [["a", "b"], ["foo","bar"],["baz","bang"]]
867
+ @pdf = Prawn::Document.new
868
+ @pdf.table(data, :header => true)
869
+ output = PDF::Inspector::Text.analyze(@pdf.render)
870
+ output.strings.should == data.flatten
871
+ end
872
+
873
+ it "should repeat headers across pages" do
874
+ data = [["foo","bar"]] * 30
875
+ headers = ["baz","foobar"]
876
+ @pdf = Prawn::Document.new
877
+ @pdf.table([headers] + data, :header => true)
878
+ output = PDF::Inspector::Text.analyze(@pdf.render)
879
+ output.strings.should == headers + data.flatten[0..-3] + headers +
880
+ data.flatten[-2..-1]
881
+ end
882
+
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
903
+ @pdf = Prawn::Document.new
904
+ @pdf.y = 0
905
+ @pdf.table([["Header"], ["Body"]], :header => true)
906
+ output = PDF::Inspector::Text.analyze(@pdf.render)
907
+ output.strings.should == ["Header", "Body"]
908
+ end
909
+ end
910
+
911
+ describe "nested tables" do
912
+ before(:each) do
913
+ @pdf = Prawn::Document.new
914
+ @subtable = Prawn::Table.new([["foo"]], @pdf)
915
+ @table = @pdf.table([[@subtable, "bar"]])
916
+ end
917
+
918
+ it "can be created from an Array" do
919
+ cell = Prawn::Table::Cell.make(@pdf, [["foo"]])
920
+ cell.should be_a_kind_of(Prawn::Table::Cell::Subtable)
921
+ cell.subtable.should be_a_kind_of(Prawn::Table)
922
+ end
923
+
924
+ it "defaults its padding to zero" do
925
+ @table.cells[0, 0].padding.should == [0, 0, 0, 0]
926
+ end
927
+
928
+ it "has a subtable accessor" do
929
+ @table.cells[0, 0].subtable.should == @subtable
930
+ end
931
+
932
+ it "determines its dimensions from the subtable" do
933
+ @table.cells[0, 0].width.should == @subtable.width
934
+ @table.cells[0, 0].height.should == @subtable.height
935
+ end
936
+
937
+ end
938
+
939
+ describe "An invalid table" do
940
+
941
+ before(:each) do
942
+ @pdf = Prawn::Document.new
943
+ @bad_data = ["Single Nested Array"]
944
+ end
945
+
946
+ it "should raise_error error when invalid table data is given" do
947
+ lambda {
948
+ @pdf.table(@bad_data)
949
+ }.should raise_error(Prawn::Errors::InvalidTableData)
950
+ end
951
+
952
+ it "should raise_error an EmptyTableError with empty table data" do
953
+ lambda {
954
+ data = []
955
+ @pdf = Prawn::Document.new
956
+ @pdf.table(data)
957
+ }.should raise_error( Prawn::Errors::EmptyTable )
958
+ end
959
+
960
+ it "should raise_error an EmptyTableError with nil table data" do
961
+ lambda {
962
+ data = nil
963
+ @pdf = Prawn::Document.new
964
+ @pdf.table(data)
965
+ }.should raise_error( Prawn::Errors::EmptyTable )
966
+ end
967
+
968
+ end
969
+
970
+ end
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
+