prawn 1.1.0 → 2.4.0

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