prawn 0.13.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.yardopts +10 -0
  4. data/GPLv2 +20 -21
  5. data/Gemfile +3 -16
  6. data/Rakefile +17 -39
  7. data/lib/prawn/document/bounding_box.rb +85 -42
  8. data/lib/prawn/document/column_box.rb +21 -11
  9. data/lib/prawn/document/internals.rb +40 -147
  10. data/lib/prawn/document/span.rb +25 -17
  11. data/lib/prawn/document.rb +286 -245
  12. data/lib/prawn/encoding.rb +68 -101
  13. data/lib/prawn/errors.rb +47 -43
  14. data/lib/prawn/font.rb +204 -155
  15. data/lib/prawn/font_metric_cache.rb +25 -21
  16. data/lib/prawn/fonts/afm.rb +292 -0
  17. data/lib/prawn/{font → fonts}/dfont.rb +7 -13
  18. data/lib/prawn/fonts/otf.rb +11 -0
  19. data/lib/prawn/fonts/ttc.rb +36 -0
  20. data/lib/prawn/{font → fonts}/ttf.rb +142 -80
  21. data/lib/prawn/graphics/blend_mode.rb +65 -0
  22. data/lib/prawn/graphics/cap_style.rb +6 -5
  23. data/lib/prawn/graphics/color.rb +47 -44
  24. data/lib/prawn/graphics/dash.rb +30 -13
  25. data/lib/prawn/graphics/join_style.rb +13 -6
  26. data/lib/prawn/graphics/patterns.rb +221 -90
  27. data/lib/prawn/graphics/transformation.rb +21 -12
  28. data/lib/prawn/graphics/transparency.rb +21 -17
  29. data/lib/prawn/graphics.rb +155 -128
  30. data/lib/prawn/{layout/grid.rb → grid.rb} +110 -47
  31. data/lib/prawn/image_handler.rb +16 -2
  32. data/lib/prawn/images/image.rb +4 -2
  33. data/lib/prawn/images/jpg.rb +39 -30
  34. data/lib/prawn/images/png.rb +132 -169
  35. data/lib/prawn/images.rb +70 -62
  36. data/lib/prawn/measurement_extensions.rb +15 -10
  37. data/lib/prawn/measurements.rb +22 -23
  38. data/lib/prawn/outline.rb +301 -13
  39. data/lib/prawn/repeater.rb +19 -17
  40. data/lib/prawn/security/arcfour.rb +54 -0
  41. data/lib/prawn/security.rb +108 -86
  42. data/lib/prawn/soft_mask.rb +40 -41
  43. data/lib/prawn/stamp.rb +29 -12
  44. data/lib/prawn/text/box.rb +27 -29
  45. data/lib/prawn/text/formatted/arranger.rb +110 -67
  46. data/lib/prawn/text/formatted/box.rb +233 -165
  47. data/lib/prawn/text/formatted/fragment.rb +27 -27
  48. data/lib/prawn/text/formatted/line_wrap.rb +137 -97
  49. data/lib/prawn/text/formatted/parser.rb +149 -127
  50. data/lib/prawn/text/formatted/wrap.rb +57 -37
  51. data/lib/prawn/text/formatted.rb +6 -4
  52. data/lib/prawn/text.rb +105 -73
  53. data/lib/prawn/transformation_stack.rb +44 -0
  54. data/lib/prawn/utilities.rb +11 -21
  55. data/lib/prawn/version.rb +5 -0
  56. data/lib/prawn/view.rb +101 -0
  57. data/lib/prawn.rb +42 -68
  58. data/{data/images/fractal.jpg → manual/absolute_position.pdf} +0 -0
  59. data/manual/basic_concepts/adding_pages.rb +9 -10
  60. data/manual/basic_concepts/basic_concepts.rb +33 -24
  61. data/manual/basic_concepts/creation.rb +10 -11
  62. data/manual/basic_concepts/cursor.rb +9 -10
  63. data/manual/basic_concepts/measurement.rb +10 -11
  64. data/manual/basic_concepts/origin.rb +8 -9
  65. data/manual/basic_concepts/other_cursor_helpers.rb +17 -18
  66. data/manual/basic_concepts/view.rb +48 -0
  67. data/manual/bounding_box/bounding_box.rb +31 -29
  68. data/manual/bounding_box/bounds.rb +17 -18
  69. data/manual/bounding_box/canvas.rb +8 -9
  70. data/manual/bounding_box/creation.rb +8 -9
  71. data/manual/bounding_box/indentation.rb +22 -23
  72. data/manual/bounding_box/nesting.rb +32 -25
  73. data/manual/bounding_box/russian_boxes.rb +19 -19
  74. data/manual/bounding_box/stretchy.rb +18 -20
  75. data/manual/contents.rb +35 -0
  76. data/manual/cover.rb +43 -0
  77. data/manual/document_and_page_options/background.rb +16 -14
  78. data/manual/document_and_page_options/document_and_page_options.rb +26 -23
  79. data/manual/document_and_page_options/metadata.rb +21 -19
  80. data/manual/document_and_page_options/page_margins.rb +20 -22
  81. data/manual/document_and_page_options/page_size.rb +15 -15
  82. data/manual/document_and_page_options/print_scaling.rb +23 -0
  83. data/manual/example_helper.rb +5 -408
  84. data/manual/graphics/blend_mode.rb +52 -0
  85. data/manual/graphics/circle_and_ellipse.rb +8 -9
  86. data/manual/graphics/color.rb +11 -13
  87. data/manual/graphics/common_lines.rb +13 -12
  88. data/manual/graphics/fill_and_stroke.rb +10 -11
  89. data/manual/graphics/fill_rules.rb +13 -12
  90. data/manual/graphics/gradients.rb +28 -22
  91. data/manual/graphics/graphics.rb +52 -46
  92. data/manual/graphics/helper.rb +20 -10
  93. data/manual/graphics/line_width.rb +13 -12
  94. data/manual/graphics/lines_and_curves.rb +13 -14
  95. data/manual/graphics/polygon.rb +10 -12
  96. data/manual/graphics/rectangle.rb +7 -8
  97. data/manual/graphics/rotate.rb +9 -12
  98. data/manual/graphics/scale.rb +19 -18
  99. data/manual/graphics/soft_masks.rb +5 -7
  100. data/manual/graphics/stroke_cap.rb +10 -11
  101. data/manual/graphics/stroke_dash.rb +16 -17
  102. data/manual/graphics/stroke_join.rb +10 -11
  103. data/manual/graphics/translate.rb +13 -13
  104. data/manual/graphics/transparency.rb +11 -13
  105. data/manual/{manual/how_to_read_this_manual.rb → how_to_read_this_manual.rb} +23 -25
  106. data/manual/images/absolute_position.rb +9 -10
  107. data/manual/images/fit.rb +9 -10
  108. data/manual/images/horizontal.rb +13 -14
  109. data/manual/images/images.rb +31 -30
  110. data/manual/images/plain_image.rb +6 -7
  111. data/manual/images/scale.rb +12 -13
  112. data/manual/images/vertical.rb +19 -17
  113. data/manual/images/width_and_height.rb +13 -14
  114. data/manual/layout/boxes.rb +14 -15
  115. data/manual/layout/content.rb +12 -13
  116. data/manual/layout/layout.rb +19 -20
  117. data/manual/layout/simple_grid.rb +8 -9
  118. data/manual/outline/add_subsection_to.rb +26 -27
  119. data/manual/outline/insert_section_after.rb +19 -20
  120. data/manual/outline/outline.rb +23 -22
  121. data/manual/outline/sections_and_pages.rb +24 -25
  122. data/manual/repeatable_content/alternate_page_numbering.rb +36 -0
  123. data/manual/repeatable_content/page_numbering.rb +20 -19
  124. data/manual/repeatable_content/repeatable_content.rb +26 -22
  125. data/manual/repeatable_content/repeater.rb +18 -19
  126. data/manual/repeatable_content/stamp.rb +18 -19
  127. data/manual/security/encryption.rb +8 -11
  128. data/manual/security/permissions.rb +20 -15
  129. data/manual/security/security.rb +20 -20
  130. data/manual/table.rb +16 -0
  131. data/manual/text/alignment.rb +17 -18
  132. data/manual/text/color.rb +13 -13
  133. data/manual/text/column_box.rb +10 -12
  134. data/manual/text/fallback_fonts.rb +29 -25
  135. data/manual/text/font.rb +17 -18
  136. data/manual/text/font_size.rb +21 -22
  137. data/manual/text/font_style.rb +12 -10
  138. data/manual/text/formatted_callbacks.rb +36 -26
  139. data/manual/text/formatted_text.rb +41 -34
  140. data/manual/text/free_flowing_text.rb +28 -29
  141. data/manual/text/inline.rb +23 -26
  142. data/manual/text/kerning_and_character_spacing.rb +20 -21
  143. data/manual/text/leading.rb +10 -11
  144. data/manual/text/line_wrapping.rb +40 -21
  145. data/manual/text/paragraph_indentation.rb +17 -12
  146. data/manual/text/positioned_text.rb +19 -20
  147. data/manual/text/registering_families.rb +33 -30
  148. data/manual/text/rendering_and_color.rb +11 -12
  149. data/manual/text/right_to_left_text.rb +31 -20
  150. data/manual/text/rotation.rb +36 -27
  151. data/manual/text/single_usage.rb +13 -14
  152. data/manual/text/text.rb +62 -62
  153. data/manual/text/text_box_excess.rb +22 -19
  154. data/manual/text/text_box_extensions.rb +21 -18
  155. data/manual/text/text_box_overflow.rb +28 -21
  156. data/manual/text/utf8.rb +16 -17
  157. data/manual/text/win_ansi_charset.rb +29 -26
  158. data/prawn.gemspec +45 -43
  159. data/spec/extensions/encoding_helpers.rb +4 -3
  160. data/spec/prawn/document/bounding_box_spec.rb +550 -0
  161. data/spec/prawn/document/column_box_spec.rb +75 -0
  162. data/spec/prawn/document/security_spec.rb +176 -0
  163. data/spec/prawn/document_annotations_spec.rb +76 -0
  164. data/spec/prawn/document_destinations_spec.rb +15 -0
  165. data/spec/prawn/document_grid_spec.rb +99 -0
  166. data/spec/prawn/document_reference_spec.rb +27 -0
  167. data/spec/prawn/document_span_spec.rb +44 -0
  168. data/spec/prawn/document_spec.rb +805 -0
  169. data/spec/prawn/font_metric_cache_spec.rb +54 -0
  170. data/spec/prawn/font_spec.rb +544 -0
  171. data/spec/prawn/graphics/blend_mode_spec.rb +63 -0
  172. data/spec/prawn/graphics/transparency_spec.rb +81 -0
  173. data/spec/prawn/graphics_spec.rb +872 -0
  174. data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
  175. data/spec/prawn/image_handler_spec.rb +53 -0
  176. data/spec/prawn/images/jpg_spec.rb +20 -0
  177. data/spec/prawn/images/png_spec.rb +283 -0
  178. data/spec/prawn/images_spec.rb +229 -0
  179. data/spec/prawn/measurements_extensions_spec.rb +24 -0
  180. data/spec/prawn/outline_spec.rb +512 -0
  181. data/spec/prawn/repeater_spec.rb +166 -0
  182. data/spec/prawn/soft_mask_spec.rb +74 -0
  183. data/spec/prawn/stamp_spec.rb +173 -0
  184. data/spec/prawn/text/box_spec.rb +1110 -0
  185. data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
  186. data/spec/prawn/text/formatted/box_spec.rb +849 -0
  187. data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
  188. data/spec/prawn/text/formatted/line_wrap_spec.rb +495 -0
  189. data/spec/prawn/text/formatted/parser_spec.rb +697 -0
  190. data/spec/prawn/text_draw_text_spec.rb +150 -0
  191. data/spec/prawn/text_rendering_mode_spec.rb +48 -0
  192. data/spec/prawn/text_spacing_spec.rb +95 -0
  193. data/spec/prawn/text_spec.rb +603 -0
  194. data/spec/prawn/text_with_inline_formatting_spec.rb +35 -0
  195. data/spec/prawn/transformation_stack_spec.rb +66 -0
  196. data/spec/prawn/view_spec.rb +63 -0
  197. data/spec/prawn_manual_spec.rb +35 -0
  198. data/spec/spec_helper.rb +22 -21
  199. data.tar.gz.sig +0 -0
  200. metadata +168 -307
  201. metadata.gz.sig +0 -0
  202. data/README.md +0 -109
  203. data/data/encodings/win_ansi.txt +0 -29
  204. data/data/images/16bit.alpha +0 -0
  205. data/data/images/16bit.dat +0 -0
  206. data/data/images/16bit.png +0 -0
  207. data/data/images/arrow.png +0 -0
  208. data/data/images/arrow2.png +0 -0
  209. data/data/images/barcode_issue.png +0 -0
  210. data/data/images/dice.alpha +0 -0
  211. data/data/images/dice.dat +0 -0
  212. data/data/images/dice.png +0 -0
  213. data/data/images/dice_interlaced.png +0 -0
  214. data/data/images/indexed_color.dat +0 -0
  215. data/data/images/indexed_color.png +0 -0
  216. data/data/images/letterhead.jpg +0 -0
  217. data/data/images/page_white_text.alpha +0 -0
  218. data/data/images/page_white_text.dat +0 -0
  219. data/data/images/page_white_text.png +0 -0
  220. data/data/images/pigs.jpg +0 -0
  221. data/data/images/prawn.png +0 -0
  222. data/data/images/ruport.png +0 -0
  223. data/data/images/ruport_data.dat +0 -0
  224. data/data/images/ruport_transparent.png +0 -0
  225. data/data/images/ruport_type0.png +0 -0
  226. data/data/images/stef.jpg +0 -0
  227. data/data/images/tru256.bmp +0 -0
  228. data/data/images/web-links.dat +0 -1
  229. data/data/images/web-links.png +0 -0
  230. data/data/pdfs/complex_template.pdf +0 -0
  231. data/data/pdfs/contains_ttf_font.pdf +0 -0
  232. data/data/pdfs/encrypted.pdf +0 -0
  233. data/data/pdfs/form.pdf +1 -819
  234. data/data/pdfs/hexagon.pdf +0 -61
  235. data/data/pdfs/indirect_reference.pdf +0 -86
  236. data/data/pdfs/multipage_template.pdf +0 -127
  237. data/data/pdfs/nested_pages.pdf +0 -118
  238. data/data/pdfs/page_without_mediabox.pdf +0 -193
  239. data/data/pdfs/resources_as_indirect_object.pdf +0 -83
  240. data/data/pdfs/two_hexagons.pdf +0 -90
  241. data/data/pdfs/version_1_6.pdf +0 -61
  242. data/data/shift_jis_text.txt +0 -1
  243. data/lib/pdf/core/annotations.rb +0 -60
  244. data/lib/pdf/core/byte_string.rb +0 -9
  245. data/lib/pdf/core/destinations.rb +0 -90
  246. data/lib/pdf/core/document_state.rb +0 -78
  247. data/lib/pdf/core/filter_list.rb +0 -51
  248. data/lib/pdf/core/filters.rb +0 -36
  249. data/lib/pdf/core/graphics_state.rb +0 -68
  250. data/lib/pdf/core/literal_string.rb +0 -16
  251. data/lib/pdf/core/name_tree.rb +0 -177
  252. data/lib/pdf/core/object_store.rb +0 -320
  253. data/lib/pdf/core/outline.rb +0 -315
  254. data/lib/pdf/core/page.rb +0 -212
  255. data/lib/pdf/core/page_geometry.rb +0 -126
  256. data/lib/pdf/core/pdf_object.rb +0 -124
  257. data/lib/pdf/core/reference.rb +0 -103
  258. data/lib/pdf/core/stream.rb +0 -98
  259. data/lib/pdf/core/text.rb +0 -275
  260. data/lib/pdf/core.rb +0 -35
  261. data/lib/prawn/compatibility.rb +0 -91
  262. data/lib/prawn/document/graphics_state.rb +0 -73
  263. data/lib/prawn/document/snapshot.rb +0 -89
  264. data/lib/prawn/font/afm.rb +0 -203
  265. data/lib/prawn/layout.rb +0 -20
  266. data/lib/prawn/table/cell/image.rb +0 -70
  267. data/lib/prawn/table/cell/in_table.rb +0 -27
  268. data/lib/prawn/table/cell/span_dummy.rb +0 -92
  269. data/lib/prawn/table/cell/subtable.rb +0 -65
  270. data/lib/prawn/table/cell/text.rb +0 -153
  271. data/lib/prawn/table/cell.rb +0 -770
  272. data/lib/prawn/table/cells.rb +0 -295
  273. data/lib/prawn/table.rb +0 -643
  274. data/manual/example_file.rb +0 -116
  275. data/manual/example_package.rb +0 -53
  276. data/manual/example_section.rb +0 -46
  277. data/manual/manual/cover.rb +0 -35
  278. data/manual/manual/foreword.rb +0 -85
  279. data/manual/manual/manual.rb +0 -35
  280. data/manual/syntax_highlight.rb +0 -52
  281. data/manual/table/basic_block.rb +0 -53
  282. data/manual/table/before_rendering_page.rb +0 -26
  283. data/manual/table/cell_border_lines.rb +0 -24
  284. data/manual/table/cell_borders_and_bg.rb +0 -31
  285. data/manual/table/cell_dimensions.rb +0 -30
  286. data/manual/table/cell_text.rb +0 -38
  287. data/manual/table/column_widths.rb +0 -30
  288. data/manual/table/content_and_subtables.rb +0 -39
  289. data/manual/table/creation.rb +0 -27
  290. data/manual/table/filtering.rb +0 -36
  291. data/manual/table/flow_and_header.rb +0 -17
  292. data/manual/table/image_cells.rb +0 -33
  293. data/manual/table/position.rb +0 -29
  294. data/manual/table/row_colors.rb +0 -20
  295. data/manual/table/span.rb +0 -30
  296. data/manual/table/style.rb +0 -22
  297. data/manual/table/table.rb +0 -52
  298. data/manual/table/width.rb +0 -27
  299. data/manual/templates/full_template.rb +0 -25
  300. data/manual/templates/page_template.rb +0 -48
  301. data/manual/templates/templates.rb +0 -27
  302. data/manual/text/group.rb +0 -29
  303. data/spec/acceptance/png.rb +0 -23
  304. data/spec/annotations_spec.rb +0 -74
  305. data/spec/bounding_box_spec.rb +0 -493
  306. data/spec/cell_spec.rb +0 -628
  307. data/spec/column_box_spec.rb +0 -33
  308. data/spec/destinations_spec.rb +0 -15
  309. data/spec/document_spec.rb +0 -761
  310. data/spec/extensions/mocha.rb +0 -44
  311. data/spec/filters_spec.rb +0 -34
  312. data/spec/font_metric_cache_spec.rb +0 -52
  313. data/spec/font_spec.rb +0 -464
  314. data/spec/formatted_text_arranger_spec.rb +0 -421
  315. data/spec/formatted_text_box_spec.rb +0 -650
  316. data/spec/formatted_text_fragment_spec.rb +0 -298
  317. data/spec/graphics_spec.rb +0 -651
  318. data/spec/grid_spec.rb +0 -85
  319. data/spec/image_handler_spec.rb +0 -42
  320. data/spec/images_spec.rb +0 -157
  321. data/spec/inline_formatted_text_parser_spec.rb +0 -564
  322. data/spec/jpg_spec.rb +0 -25
  323. data/spec/line_wrap_spec.rb +0 -333
  324. data/spec/measurement_units_spec.rb +0 -23
  325. data/spec/name_tree_spec.rb +0 -112
  326. data/spec/object_store_spec.rb +0 -170
  327. data/spec/outline_spec.rb +0 -448
  328. data/spec/pdf_object_spec.rb +0 -172
  329. data/spec/png_spec.rb +0 -240
  330. data/spec/reference_spec.rb +0 -82
  331. data/spec/repeater_spec.rb +0 -158
  332. data/spec/security_spec.rb +0 -158
  333. data/spec/snapshot_spec.rb +0 -186
  334. data/spec/soft_mask_spec.rb +0 -117
  335. data/spec/span_spec.rb +0 -44
  336. data/spec/stamp_spec.rb +0 -158
  337. data/spec/stream_spec.rb +0 -58
  338. data/spec/stroke_styles_spec.rb +0 -211
  339. data/spec/table/span_dummy_spec.rb +0 -17
  340. data/spec/table_spec.rb +0 -1355
  341. data/spec/template_spec.rb +0 -351
  342. data/spec/text_at_spec.rb +0 -130
  343. data/spec/text_box_spec.rb +0 -1030
  344. data/spec/text_rendering_mode_spec.rb +0 -45
  345. data/spec/text_spacing_spec.rb +0 -93
  346. data/spec/text_spec.rb +0 -425
  347. data/spec/text_with_inline_formatting_spec.rb +0 -35
  348. data/spec/transparency_spec.rb +0 -89
data/lib/prawn/table.rb DELETED
@@ -1,643 +0,0 @@
1
- # encoding: utf-8
2
- #
3
- # table.rb: Table drawing functionality.
4
- #
5
- # Copyright December 2009, Brad Ediger. All rights reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
-
9
- require 'prawn/table/cells'
10
- require 'prawn/table/cell'
11
- require 'prawn/table/cell/in_table'
12
- require 'prawn/table/cell/text'
13
- require 'prawn/table/cell/subtable'
14
- require 'prawn/table/cell/image'
15
- require 'prawn/table/cell/span_dummy'
16
-
17
- module Prawn
18
-
19
- class Document
20
-
21
- # Set up and draw a table on this document. A block can be given, which will
22
- # be run after cell setup but before layout and drawing.
23
- #
24
- # See the documentation on Prawn::Table for details on the arguments.
25
- #
26
- def table(data, options={}, &block)
27
- t = Table.new(data, self, options, &block)
28
- t.draw
29
- t
30
- end
31
-
32
- # Set up, but do not draw, a table. Useful for creating subtables to be
33
- # inserted into another Table. Call +draw+ on the resulting Table to ink it.
34
- #
35
- # See the documentation on Prawn::Table for details on the arguments.
36
- #
37
- def make_table(data, options={}, &block)
38
- Table.new(data, self, options, &block)
39
- end
40
-
41
- end
42
-
43
- # Next-generation table drawing for Prawn.
44
- #
45
- # = Data
46
- #
47
- # Data, for a Prawn table, is a two-dimensional array of objects that can be
48
- # converted to cells ("cellable" objects). Cellable objects can be:
49
- #
50
- # String::
51
- # Produces a text cell. This is the most common usage.
52
- # Prawn::Table::Cell::
53
- # If you have already built a Cell or have a custom subclass of Cell you
54
- # want to use in a table, you can pass through Cell objects.
55
- # Prawn::Table::
56
- # Creates a subtable (a table within a cell). You can use
57
- # Prawn::Document#make_table to create a table for use as a subtable
58
- # without immediately drawing it. See examples/table/bill.rb for a
59
- # somewhat complex use of subtables.
60
- # Array::
61
- # Creates a simple subtable. Create a Table object using make_table (see
62
- # above) if you need more control over the subtable's styling.
63
- #
64
- # = Options
65
- #
66
- # Prawn/Layout provides many options to control style and layout of your
67
- # table. These options are implemented with a uniform interface: the +:foo+
68
- # option always sets the +foo=+ accessor. See the accessor and method
69
- # documentation for full details on the options you can pass. Some
70
- # highlights:
71
- #
72
- # +cell_style+::
73
- # A hash of style options to style all cells. See the documentation on
74
- # Prawn::Table::Cell for all cell style options.
75
- # +header+::
76
- # If set to +true+, the first row will be repeated on every page. If set
77
- # to an Integer, the first +x+ rows will be repeated on every page. Row
78
- # numbering (for styling and other row-specific options) always indexes
79
- # based on your data array. Whether or not you have a header, row(n) always
80
- # refers to the nth element (starting from 0) of the +data+ array.
81
- # +column_widths+::
82
- # Sets widths for individual columns. Manually setting widths can give
83
- # better results than letting Prawn guess at them, as Prawn's algorithm
84
- # for defaulting widths is currently pretty boneheaded. If you experience
85
- # problems like weird column widths or CannotFit errors, try manually
86
- # setting widths on more columns.
87
- # +position+::
88
- # Either :left (the default), :center, :right, or a number. Specifies the
89
- # horizontal position of the table within its bounding box. If a number is
90
- # provided, it specifies the distance in points from the left edge.
91
- #
92
- # = Initializer Block
93
- #
94
- # If a block is passed to methods that initialize a table
95
- # (Prawn::Table.new, Prawn::Document#table, Prawn::Document#make_table), it
96
- # will be called after cell setup but before layout. This is a very flexible
97
- # way to specify styling and layout constraints. This code sets up a table
98
- # where the second through the fourth rows (1-3, indexed from 0) are each one
99
- # inch (72 pt) wide:
100
- #
101
- # pdf.table(data) do |table|
102
- # table.rows(1..3).width = 72
103
- # end
104
- #
105
- # As with Prawn::Document#initialize, if the block has no arguments, it will
106
- # be evaluated in the context of the object itself. The above code could be
107
- # rewritten as:
108
- #
109
- # pdf.table(data) do
110
- # rows(1..3).width = 72
111
- # end
112
- #
113
- class Table
114
-
115
- # Set up a table on the given document. Arguments:
116
- #
117
- # +data+::
118
- # A two-dimensional array of cell-like objects. See the "Data" section
119
- # above for the types of objects that can be put in a table.
120
- # +document+::
121
- # The Prawn::Document instance on which to draw the table.
122
- # +options+::
123
- # A hash of attributes and values for the table. See the "Options" block
124
- # above for details on available options.
125
- #
126
- def initialize(data, document, options={}, &block)
127
- @pdf = document
128
- @cells = make_cells(data)
129
- @header = false
130
- @epsilon = 1e-9
131
- options.each { |k, v| send("#{k}=", v) }
132
-
133
- if block
134
- block.arity < 1 ? instance_eval(&block) : block[self]
135
- end
136
-
137
- set_column_widths
138
- set_row_heights
139
- position_cells
140
- end
141
-
142
- # Number of rows in the table.
143
- #
144
- attr_reader :row_length
145
-
146
- # Number of columns in the table.
147
- #
148
- attr_reader :column_length
149
-
150
- # Manually set the width of the table.
151
- #
152
- attr_writer :width
153
-
154
- # Position (:left, :right, :center, or a number indicating distance in
155
- # points from the left edge) of the table within its parent bounds.
156
- #
157
- attr_writer :position
158
-
159
- # Returns a Prawn::Table::Cells object representing all of the cells in
160
- # this table.
161
- #
162
- attr_reader :cells
163
-
164
- # Specify a callback to be called before each page of cells is rendered.
165
- # The block is passed a Cells object containing all cells to be rendered on
166
- # that page. You can change styling of the cells in this block, but keep in
167
- # mind that the cells have already been positioned and sized.
168
- #
169
- def before_rendering_page(&block)
170
- @before_rendering_page = block
171
- end
172
-
173
- # Returns the width of the table in PDF points.
174
- #
175
- def width
176
- @width ||= [natural_width, @pdf.bounds.width].min
177
- end
178
-
179
- # Sets column widths for the table. The argument can be one of the following
180
- # types:
181
- #
182
- # +Array+::
183
- # <tt>[w0, w1, w2, ...]</tt> (specify a width for each column)
184
- # +Hash+::
185
- # <tt>{0 => w0, 1 => w1, ...}</tt> (keys are column names, values are
186
- # widths)
187
- # +Numeric+::
188
- # +72+ (sets width for all columns)
189
- #
190
- def column_widths=(widths)
191
- case widths
192
- when Array
193
- widths.each_with_index { |w, i| column(i).width = w }
194
- when Hash
195
- widths.each { |i, w| column(i).width = w }
196
- when Numeric
197
- cells.width = widths
198
- else
199
- raise ArgumentError, "cannot interpret column widths"
200
- end
201
- end
202
-
203
- # Returns the height of the table in PDF points.
204
- #
205
- def height
206
- cells.height
207
- end
208
-
209
- # If +true+, designates the first row as a header row to be repeated on
210
- # every page. If an integer, designates the number of rows to be treated
211
- # as a header Does not change row numbering -- row numbers always index
212
- # into the data array provided, with no modification.
213
- #
214
- attr_writer :header
215
-
216
- # Accepts an Array of alternating row colors to stripe the table.
217
- #
218
- attr_writer :row_colors
219
-
220
- # Sets styles for all cells.
221
- #
222
- # pdf.table(data, :cell_style => { :borders => [:left, :right] })
223
- #
224
- def cell_style=(style_hash)
225
- cells.style(style_hash)
226
- end
227
-
228
- # Allows generic stylable content. This is an alternate syntax that some
229
- # prefer to the attribute-based syntax. This code using style:
230
- #
231
- # pdf.table(data) do
232
- # style(row(0), :background_color => 'ff00ff')
233
- # style(column(0)) { |c| c.border_width += 1 }
234
- # end
235
- #
236
- # is equivalent to:
237
- #
238
- # pdf.table(data) do
239
- # row(0).style :background_color => 'ff00ff'
240
- # column(0).style { |c| c.border_width += 1 }
241
- # end
242
- #
243
- def style(stylable, style_hash={}, &block)
244
- stylable.style(style_hash, &block)
245
- end
246
-
247
- # Draws the table onto the document at the document's current y-position.
248
- #
249
- def draw
250
- with_position do
251
- # The cell y-positions are based on an infinitely long canvas. The offset
252
- # keeps track of how much we have to add to the original, theoretical
253
- # y-position to get to the actual position on the current page.
254
- offset = @pdf.y
255
-
256
- # Reference bounds are the non-stretchy bounds used to decide when to
257
- # flow to a new column / page.
258
- ref_bounds = @pdf.reference_bounds
259
-
260
- last_y = @pdf.y
261
-
262
- # Determine whether we're at the top of the current bounds (margin box or
263
- # bounding box). If we're at the top, we couldn't gain any more room by
264
- # breaking to the next page -- this means, in particular, that if the
265
- # first row is taller than the margin box, we will only move to the next
266
- # page if we're below the top. Some floating-point tolerance is added to
267
- # the calculation.
268
- #
269
- # Note that we use the actual bounds, not the reference bounds. This is
270
- # because even if we are in a stretchy bounding box, flowing to the next
271
- # page will not buy us any space if we are at the top.
272
- if @pdf.y > @pdf.bounds.height + @pdf.bounds.absolute_bottom - 0.001
273
- # we're at the top of our bounds
274
- started_new_page_at_row = 0
275
- else
276
- started_new_page_at_row = -1
277
-
278
- # If there isn't enough room left on the page to fit the first data row
279
- # (excluding the header), start the table on the next page.
280
- needed_height = row(0).height
281
- if @header
282
- if @header.is_a? Integer
283
- needed_height += row(1..@header).height
284
- else
285
- needed_height += row(1).height
286
- end
287
- end
288
- if needed_height > @pdf.y - ref_bounds.absolute_bottom
289
- @pdf.bounds.move_past_bottom
290
- offset = @pdf.y
291
- started_new_page_at_row = 0
292
- end
293
- end
294
-
295
- # Duplicate each cell of the header row into @header_row so it can be
296
- # modified in before_rendering_page callbacks.
297
- if @header
298
- @header_row = Cells.new
299
- if @header.is_a? Integer
300
- @header.times do |r|
301
- row(r).each { |cell| @header_row[cell.row, cell.column] = cell.dup }
302
- end
303
- else
304
- row(0).each { |cell| @header_row[cell.row, cell.column] = cell.dup }
305
- end
306
- end
307
-
308
- # Track cells to be drawn on this page. They will all be drawn when this
309
- # page is finished.
310
- cells_this_page = []
311
-
312
- @cells.each do |cell|
313
- if cell.height > (cell.y + offset) - ref_bounds.absolute_bottom &&
314
- cell.row > started_new_page_at_row
315
- # Ink all cells on the current page
316
- if defined?(@before_rendering_page) && @before_rendering_page
317
- c = Cells.new(cells_this_page.map { |ci, _| ci })
318
- @before_rendering_page.call(c)
319
- end
320
- Cell.draw_cells(cells_this_page)
321
- cells_this_page = []
322
-
323
- # start a new page or column
324
- @pdf.bounds.move_past_bottom
325
- x_offset = @pdf.bounds.left_side - @pdf.bounds.absolute_left
326
- if cell.row > 0 && @header
327
- if @header.is_a? Integer
328
- header_height = 0
329
- y_coord = @pdf.cursor
330
- @header.times do |h|
331
- additional_header_height = add_header(cells_this_page, x_offset, y_coord-header_height, cell.row-1, h)
332
- header_height += additional_header_height
333
- end
334
- else
335
- header_height = add_header(cells_this_page, x_offset, @pdf.cursor, cell.row-1)
336
- end
337
- else
338
- header_height = 0
339
- end
340
- offset = @pdf.y - cell.y - header_height
341
- started_new_page_at_row = cell.row
342
- end
343
-
344
- # Don't modify cell.x / cell.y here, as we want to reuse the original
345
- # values when re-inking the table. #draw should be able to be called
346
- # multiple times.
347
- x, y = cell.x, cell.y
348
- y += offset
349
-
350
- # Translate coordinates to the bounds we are in, since drawing is
351
- # relative to the cursor, not ref_bounds.
352
- x += @pdf.bounds.left_side - @pdf.bounds.absolute_left
353
- y -= @pdf.bounds.absolute_bottom
354
-
355
- # Set background color, if any.
356
- if defined?(@row_colors) && @row_colors && (!@header || cell.row > 0)
357
- # Ensure coloring restarts on every page (to make sure the header
358
- # and first row of a page are not colored the same way).
359
- if @header.is_a? Integer
360
- rows = @header
361
- elsif @header
362
- rows = 1
363
- else
364
- rows = 0
365
- end
366
- index = cell.row - [started_new_page_at_row, rows].max
367
-
368
- cell.background_color ||= @row_colors[index % @row_colors.length]
369
- end
370
-
371
- cells_this_page << [cell, [x, y]]
372
- last_y = y
373
- end
374
- # Draw the last page of cells
375
- if defined?(@before_rendering_page) && @before_rendering_page
376
- c = Cells.new(cells_this_page.map { |ci, _| ci })
377
- @before_rendering_page.call(c)
378
- end
379
- Cell.draw_cells(cells_this_page)
380
-
381
- @pdf.move_cursor_to(last_y - @cells.last.height)
382
- end
383
- end
384
-
385
- # Calculate and return the constrained column widths, taking into account
386
- # each cell's min_width, max_width, and any user-specified constraints on
387
- # the table or column size.
388
- #
389
- # Because the natural widths can be silly, this does not always work so well
390
- # at guessing a good size for columns that have vastly different content. If
391
- # you see weird problems like CannotFit errors or shockingly bad column
392
- # sizes, you should specify more column widths manually.
393
- #
394
- def column_widths
395
- @column_widths ||= begin
396
- if width - cells.min_width < -epsilon
397
- raise Errors::CannotFit,
398
- "Table's width was set too small to contain its contents " +
399
- "(min width #{cells.min_width}, requested #{width})"
400
- end
401
-
402
- if width - cells.max_width > epsilon
403
- raise Errors::CannotFit,
404
- "Table's width was set larger than its contents' maximum width " +
405
- "(max width #{cells.max_width}, requested #{width})"
406
- end
407
-
408
- if width - natural_width < -epsilon
409
- # Shrink the table to fit the requested width.
410
- f = (width - cells.min_width).to_f / (natural_width - cells.min_width)
411
-
412
- (0...column_length).map do |c|
413
- min, nat = column(c).min_width, natural_column_widths[c]
414
- (f * (nat - min)) + min
415
- end
416
- elsif width - natural_width > epsilon
417
- # Expand the table to fit the requested width.
418
- f = (width - cells.width).to_f / (cells.max_width - cells.width)
419
-
420
- (0...column_length).map do |c|
421
- nat, max = natural_column_widths[c], column(c).max_width
422
- (f * (max - nat)) + nat
423
- end
424
- else
425
- natural_column_widths
426
- end
427
- end
428
- end
429
-
430
- # Returns an array with the height of each row.
431
- #
432
- def row_heights
433
- @natural_row_heights ||=
434
- begin
435
- heights_by_row = Hash.new(0)
436
- cells.each do |cell|
437
- next if cell.is_a?(Cell::SpanDummy)
438
-
439
- # Split the height of row-spanned cells evenly by rows
440
- height_per_row = cell.height.to_f / cell.rowspan
441
- cell.rowspan.times do |i|
442
- heights_by_row[cell.row + i] =
443
- [heights_by_row[cell.row + i], height_per_row].max
444
- end
445
- end
446
- heights_by_row.sort_by { |row, _| row }.map { |_, h| h }
447
- end
448
- end
449
-
450
- protected
451
-
452
- # Converts the array of cellable objects given into instances of
453
- # Prawn::Table::Cell, and sets up their in-table properties so that they
454
- # know their own position in the table.
455
- #
456
- def make_cells(data)
457
- assert_proper_table_data(data)
458
-
459
- cells = Cells.new
460
-
461
- row_number = 0
462
- data.each do |row_cells|
463
- column_number = 0
464
- row_cells.each do |cell_data|
465
- # If we landed on a spanned cell (from a rowspan above), continue
466
- # until we find an empty spot.
467
- column_number += 1 until cells[row_number, column_number].nil?
468
-
469
- # Build the cell and store it in the Cells collection.
470
- cell = Cell.make(@pdf, cell_data)
471
- cells[row_number, column_number] = cell
472
-
473
- # Add dummy cells for the rest of the cells in the span group. This
474
- # allows Prawn to keep track of the horizontal and vertical space
475
- # occupied in each column and row spanned by this cell, while still
476
- # leaving the master (top left) cell in the group responsible for
477
- # drawing. Dummy cells do not put ink on the page.
478
- cell.rowspan.times do |i|
479
- cell.colspan.times do |j|
480
- next if i == 0 && j == 0
481
-
482
- # It is an error to specify spans that overlap; catch this here
483
- if cells[row_number + i, column_number + j]
484
- raise Prawn::Errors::InvalidTableSpan,
485
- "Spans overlap at row #{row_number + i}, " +
486
- "column #{column_number + j}."
487
- end
488
-
489
- dummy = Cell::SpanDummy.new(@pdf, cell)
490
- cells[row_number + i, column_number + j] = dummy
491
- cell.dummy_cells << dummy
492
- end
493
- end
494
-
495
- column_number += cell.colspan
496
- end
497
-
498
- row_number += 1
499
- end
500
-
501
- # Calculate the number of rows and columns in the table, taking into
502
- # account that some cells may span past the end of the physical cells we
503
- # have.
504
- @row_length = cells.map do |cell|
505
- cell.row + cell.rowspan
506
- end.max
507
-
508
- @column_length = cells.map do |cell|
509
- cell.column + cell.colspan
510
- end.max
511
-
512
- cells
513
- end
514
-
515
- # Add the header row(s) to the given array of cells at the given y-position.
516
- # Number the row with the given +row+ index, so that the header appears (in
517
- # any Cells built for this page) immediately prior to the first data row on
518
- # this page.
519
- #
520
- # Return the height of the header.
521
- #
522
- def add_header(page_of_cells, x_offset, y, row, row_of_header=nil)
523
- rows_to_operate_on = @header_row
524
- rows_to_operate_on = @header_row.rows(row_of_header) if row_of_header
525
- rows_to_operate_on.each do |cell|
526
- cell.row = row
527
- cell.dummy_cells.each {|c| c.row = row }
528
- page_of_cells << [cell, [cell.x + x_offset, y]]
529
- end
530
- rows_to_operate_on.height
531
- end
532
-
533
- # Raises an error if the data provided cannot be converted into a valid
534
- # table.
535
- #
536
- def assert_proper_table_data(data)
537
- if data.nil? || data.empty?
538
- raise Prawn::Errors::EmptyTable,
539
- "data must be a non-empty, non-nil, two dimensional array " +
540
- "of cell-convertible objects"
541
- end
542
-
543
- unless data.all? { |e| Array === e }
544
- raise Prawn::Errors::InvalidTableData,
545
- "data must be a two dimensional array of cellable objects"
546
- end
547
- end
548
-
549
- # Returns an array of each column's natural (unconstrained) width.
550
- #
551
- def natural_column_widths
552
- @natural_column_widths ||=
553
- begin
554
- widths_by_column = Hash.new(0)
555
- cells.each do |cell|
556
- next if cell.is_a?(Cell::SpanDummy)
557
-
558
- # Split the width of colspanned cells evenly by columns
559
- width_per_column = cell.width.to_f / cell.colspan
560
- cell.colspan.times do |i|
561
- widths_by_column[cell.column + i] =
562
- [widths_by_column[cell.column + i], width_per_column].max
563
- end
564
- end
565
- widths_by_column.sort_by { |col, _| col }.map { |_, w| w }
566
- end
567
- end
568
-
569
- # Returns the "natural" (unconstrained) width of the table. This may be
570
- # extremely silly; for example, the unconstrained width of a paragraph of
571
- # text is the width it would assume if it were not wrapped at all. Could be
572
- # a mile long.
573
- #
574
- def natural_width
575
- @natural_width ||= natural_column_widths.inject(0, &:+)
576
- end
577
-
578
- # Assigns the calculated column widths to each cell. This ensures that each
579
- # cell in a column is the same width. After this method is called,
580
- # subsequent calls to column_widths and width should return the finalized
581
- # values that will be used to ink the table.
582
- #
583
- def set_column_widths
584
- column_widths.each_with_index do |w, col_num|
585
- column(col_num).width = w
586
- end
587
- end
588
-
589
- # Assigns the row heights to each cell. This ensures that every cell in a
590
- # row is the same height.
591
- #
592
- def set_row_heights
593
- row_heights.each_with_index { |h, row_num| row(row_num).height = h }
594
- end
595
-
596
- # Set each cell's position based on the widths and heights of cells
597
- # preceding it.
598
- #
599
- def position_cells
600
- # Calculate x- and y-positions as running sums of widths / heights.
601
- x_positions = column_widths.inject([0]) { |ary, x|
602
- ary << (ary.last + x); ary }[0..-2]
603
- x_positions.each_with_index { |x, i| column(i).x = x }
604
-
605
- # y-positions assume an infinitely long canvas starting at zero -- this
606
- # is corrected for in Table#draw, and page breaks are properly inserted.
607
- y_positions = row_heights.inject([0]) { |ary, y|
608
- ary << (ary.last - y); ary}[0..-2]
609
- y_positions.each_with_index { |y, i| row(i).y = y }
610
- end
611
-
612
- # Sets up a bounding box to position the table according to the specified
613
- # :position option, and yields.
614
- #
615
- def with_position
616
- x = case defined?(@position) && @position || :left
617
- when :left then return yield
618
- when :center then (@pdf.bounds.width - width) / 2.0
619
- when :right then @pdf.bounds.width - width
620
- when Numeric then @position
621
- else raise ArgumentError, "unknown position #{@position.inspect}"
622
- end
623
- dy = @pdf.bounds.absolute_top - @pdf.y
624
- final_y = nil
625
-
626
- @pdf.bounding_box([x, @pdf.bounds.top], :width => width) do
627
- @pdf.move_down dy
628
- yield
629
- final_y = @pdf.y
630
- end
631
-
632
- @pdf.y = final_y
633
- end
634
-
635
- private
636
-
637
- def epsilon
638
- @epsilon
639
- end
640
- end
641
-
642
-
643
- end