alphasights-prawn 0.10.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 (244) hide show
  1. data/COPYING +340 -0
  2. data/HACKING +50 -0
  3. data/LICENSE +56 -0
  4. data/README +141 -0
  5. data/Rakefile +52 -0
  6. data/data/encodings/win_ansi.txt +29 -0
  7. data/data/fonts/Action Man.dfont +0 -0
  8. data/data/fonts/Activa.ttf +0 -0
  9. data/data/fonts/Chalkboard.ttf +0 -0
  10. data/data/fonts/Courier-Bold.afm +342 -0
  11. data/data/fonts/Courier-BoldOblique.afm +342 -0
  12. data/data/fonts/Courier-Oblique.afm +342 -0
  13. data/data/fonts/Courier.afm +342 -0
  14. data/data/fonts/DejaVuSans.ttf +0 -0
  15. data/data/fonts/Dustismo_Roman.ttf +0 -0
  16. data/data/fonts/Helvetica-Bold.afm +2827 -0
  17. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  18. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  19. data/data/fonts/Helvetica.afm +3051 -0
  20. data/data/fonts/MustRead.html +19 -0
  21. data/data/fonts/Symbol.afm +213 -0
  22. data/data/fonts/Times-Bold.afm +2588 -0
  23. data/data/fonts/Times-BoldItalic.afm +2384 -0
  24. data/data/fonts/Times-Italic.afm +2667 -0
  25. data/data/fonts/Times-Roman.afm +2419 -0
  26. data/data/fonts/ZapfDingbats.afm +225 -0
  27. data/data/fonts/comicsans.ttf +0 -0
  28. data/data/fonts/gkai00mp.ttf +0 -0
  29. data/data/images/16bit.alpha +0 -0
  30. data/data/images/16bit.dat +0 -0
  31. data/data/images/16bit.png +0 -0
  32. data/data/images/arrow.png +0 -0
  33. data/data/images/arrow2.png +0 -0
  34. data/data/images/barcode_issue.png +0 -0
  35. data/data/images/dice.alpha +0 -0
  36. data/data/images/dice.dat +0 -0
  37. data/data/images/dice.png +0 -0
  38. data/data/images/dice_interlaced.png +0 -0
  39. data/data/images/fractal.jpg +0 -0
  40. data/data/images/letterhead.jpg +0 -0
  41. data/data/images/page_white_text.alpha +0 -0
  42. data/data/images/page_white_text.dat +0 -0
  43. data/data/images/page_white_text.png +0 -0
  44. data/data/images/pigs.jpg +0 -0
  45. data/data/images/rails.dat +0 -0
  46. data/data/images/rails.png +0 -0
  47. data/data/images/ruport.png +0 -0
  48. data/data/images/ruport_data.dat +0 -0
  49. data/data/images/ruport_transparent.png +0 -0
  50. data/data/images/ruport_type0.png +0 -0
  51. data/data/images/stef.jpg +0 -0
  52. data/data/images/tru256.bmp +0 -0
  53. data/data/images/web-links.dat +1 -0
  54. data/data/images/web-links.png +0 -0
  55. data/data/pdfs/complex_template.pdf +0 -0
  56. data/data/pdfs/contains_ttf_font.pdf +0 -0
  57. data/data/pdfs/encrypted.pdf +0 -0
  58. data/data/pdfs/hexagon.pdf +61 -0
  59. data/data/pdfs/indirect_reference.pdf +86 -0
  60. data/data/pdfs/nested_pages.pdf +118 -0
  61. data/data/pdfs/resources_as_indirect_object.pdf +83 -0
  62. data/data/pdfs/two_hexagons.pdf +90 -0
  63. data/data/pdfs/version_1_6.pdf +61 -0
  64. data/data/shift_jis_text.txt +1 -0
  65. data/examples/bounding_box/bounding_boxes.rb +43 -0
  66. data/examples/bounding_box/indentation.rb +34 -0
  67. data/examples/bounding_box/russian_boxes.rb +36 -0
  68. data/examples/bounding_box/stretched_nesting.rb +67 -0
  69. data/examples/builder/simple.rb +28 -0
  70. data/examples/example_helper.rb +4 -0
  71. data/examples/general/background.rb +23 -0
  72. data/examples/general/canvas.rb +15 -0
  73. data/examples/general/context_sensitive_headers.rb +37 -0
  74. data/examples/general/float.rb +11 -0
  75. data/examples/general/margin.rb +36 -0
  76. data/examples/general/measurement_units.rb +51 -0
  77. data/examples/general/metadata-info.rb +16 -0
  78. data/examples/general/multi_page_layout.rb +18 -0
  79. data/examples/general/outlines.rb +50 -0
  80. data/examples/general/page_geometry.rb +31 -0
  81. data/examples/general/page_numbering.rb +15 -0
  82. data/examples/general/repeaters.rb +47 -0
  83. data/examples/general/stamp.rb +41 -0
  84. data/examples/general/templates.rb +13 -0
  85. data/examples/graphics/basic_images.rb +23 -0
  86. data/examples/graphics/chunkable.rb +38 -0
  87. data/examples/graphics/cmyk.rb +12 -0
  88. data/examples/graphics/curves.rb +11 -0
  89. data/examples/graphics/hexagon.rb +13 -0
  90. data/examples/graphics/image_fit.rb +15 -0
  91. data/examples/graphics/image_flow.rb +37 -0
  92. data/examples/graphics/image_position.rb +17 -0
  93. data/examples/graphics/line.rb +32 -0
  94. data/examples/graphics/png_types.rb +22 -0
  95. data/examples/graphics/polygons.rb +16 -0
  96. data/examples/graphics/remote_images.rb +12 -0
  97. data/examples/graphics/rounded_polygons.rb +19 -0
  98. data/examples/graphics/rounded_rectangle.rb +20 -0
  99. data/examples/graphics/ruport_style_helpers.rb +19 -0
  100. data/examples/graphics/stroke_bounds.rb +20 -0
  101. data/examples/graphics/stroke_cap_and_join.rb +45 -0
  102. data/examples/graphics/stroke_dash.rb +42 -0
  103. data/examples/graphics/transformations.rb +52 -0
  104. data/examples/graphics/transparency.rb +26 -0
  105. data/examples/m17n/chinese_text_wrapping.rb +17 -0
  106. data/examples/m17n/euro.rb +15 -0
  107. data/examples/m17n/sjis.rb +28 -0
  108. data/examples/m17n/utf8.rb +13 -0
  109. data/examples/m17n/win_ansi_charset.rb +54 -0
  110. data/examples/security/hello_foo.rb +8 -0
  111. data/examples/table/bill.rb +53 -0
  112. data/examples/table/cell.rb +12 -0
  113. data/examples/table/checkerboard.rb +22 -0
  114. data/examples/table/header.rb +14 -0
  115. data/examples/table/inline_format_table.rb +12 -0
  116. data/examples/table/multi_page_table.rb +9 -0
  117. data/examples/table/simple_table.rb +24 -0
  118. data/examples/table/subtable.rb +12 -0
  119. data/examples/table/widths.rb +20 -0
  120. data/examples/text/alignment.rb +18 -0
  121. data/examples/text/character_spacing.rb +12 -0
  122. data/examples/text/dfont.rb +48 -0
  123. data/examples/text/family_based_styling.rb +24 -0
  124. data/examples/text/font_calculations.rb +91 -0
  125. data/examples/text/font_size.rb +33 -0
  126. data/examples/text/hyphenation.rb +45 -0
  127. data/examples/text/indent_paragraphs.rb +22 -0
  128. data/examples/text/inline_format.rb +103 -0
  129. data/examples/text/kerning.rb +30 -0
  130. data/examples/text/rotated.rb +98 -0
  131. data/examples/text/shaped_text_box.rb +31 -0
  132. data/examples/text/simple_text.rb +17 -0
  133. data/examples/text/simple_text_ttf.rb +17 -0
  134. data/examples/text/text_box.rb +88 -0
  135. data/examples/text/text_box_returning_excess.rb +51 -0
  136. data/examples/text/text_flow.rb +67 -0
  137. data/lib/prawn.rb +27 -0
  138. data/lib/prawn/canvas.rb +119 -0
  139. data/lib/prawn/chunkable.rb +37 -0
  140. data/lib/prawn/compatibility.rb +51 -0
  141. data/lib/prawn/core.rb +85 -0
  142. data/lib/prawn/core/annotations.rb +61 -0
  143. data/lib/prawn/core/byte_string.rb +9 -0
  144. data/lib/prawn/core/chunk.rb +36 -0
  145. data/lib/prawn/core/destinations.rb +90 -0
  146. data/lib/prawn/core/document_state.rb +78 -0
  147. data/lib/prawn/core/literal_string.rb +16 -0
  148. data/lib/prawn/core/name_tree.rb +165 -0
  149. data/lib/prawn/core/object_store.rb +236 -0
  150. data/lib/prawn/core/page.rb +179 -0
  151. data/lib/prawn/core/pdf_object.rb +108 -0
  152. data/lib/prawn/core/reference.rb +112 -0
  153. data/lib/prawn/core/text.rb +140 -0
  154. data/lib/prawn/core/text/formatted/arranger.rb +266 -0
  155. data/lib/prawn/core/text/formatted/line_wrap.rb +127 -0
  156. data/lib/prawn/core/text/formatted/wrap.rb +112 -0
  157. data/lib/prawn/core/text/line_wrap.rb +209 -0
  158. data/lib/prawn/core/text/wrap.rb +80 -0
  159. data/lib/prawn/document.rb +573 -0
  160. data/lib/prawn/document/bounding_box.rb +425 -0
  161. data/lib/prawn/document/graphics_state.rb +48 -0
  162. data/lib/prawn/document/internals.rb +170 -0
  163. data/lib/prawn/document/page_geometry.rb +136 -0
  164. data/lib/prawn/document/snapshot.rb +87 -0
  165. data/lib/prawn/document_builder.rb +51 -0
  166. data/lib/prawn/document_builder/command.rb +38 -0
  167. data/lib/prawn/document_builder/constructs.rb +2 -0
  168. data/lib/prawn/document_builder/constructs/flowing_text_construct.rb +18 -0
  169. data/lib/prawn/document_builder/constructs/path_construct.rb +9 -0
  170. data/lib/prawn/document_builder/layout.rb +25 -0
  171. data/lib/prawn/document_builder/modifications.rb +2 -0
  172. data/lib/prawn/document_builder/modifications/layout_modification.rb +9 -0
  173. data/lib/prawn/document_builder/modifications/path_modification.rb +9 -0
  174. data/lib/prawn/encoding.rb +121 -0
  175. data/lib/prawn/errors.rb +94 -0
  176. data/lib/prawn/font.rb +341 -0
  177. data/lib/prawn/font/afm.rb +225 -0
  178. data/lib/prawn/font/dfont.rb +42 -0
  179. data/lib/prawn/font/ttf.rb +350 -0
  180. data/lib/prawn/graphics.rb +325 -0
  181. data/lib/prawn/graphics/cap_style.rb +38 -0
  182. data/lib/prawn/graphics/color.rb +205 -0
  183. data/lib/prawn/graphics/dash.rb +71 -0
  184. data/lib/prawn/graphics/join_style.rb +38 -0
  185. data/lib/prawn/graphics/transformation.rb +156 -0
  186. data/lib/prawn/graphics/transparency.rb +99 -0
  187. data/lib/prawn/images.rb +348 -0
  188. data/lib/prawn/images/jpg.rb +46 -0
  189. data/lib/prawn/images/png.rb +226 -0
  190. data/lib/prawn/measurement_extensions.rb +46 -0
  191. data/lib/prawn/measurements.rb +71 -0
  192. data/lib/prawn/outline.rb +278 -0
  193. data/lib/prawn/repeater.rb +129 -0
  194. data/lib/prawn/security.rb +262 -0
  195. data/lib/prawn/security/arcfour.rb +51 -0
  196. data/lib/prawn/stamp.rb +126 -0
  197. data/lib/prawn/table.rb +421 -0
  198. data/lib/prawn/table/accessors.rb +180 -0
  199. data/lib/prawn/table/cell.rb +350 -0
  200. data/lib/prawn/table/cell/in_table.rb +27 -0
  201. data/lib/prawn/table/cell/subtable.rb +65 -0
  202. data/lib/prawn/table/cell/text.rb +125 -0
  203. data/lib/prawn/text.rb +449 -0
  204. data/lib/prawn/text/box.rb +392 -0
  205. data/lib/prawn/text/formatted.rb +4 -0
  206. data/lib/prawn/text/formatted/box.rb +228 -0
  207. data/lib/prawn/text/formatted/fragment.rb +181 -0
  208. data/lib/prawn/text/formatted/parser.rb +213 -0
  209. data/spec/annotations_spec.rb +90 -0
  210. data/spec/bounding_box_spec.rb +190 -0
  211. data/spec/cell_spec.rb +348 -0
  212. data/spec/destinations_spec.rb +15 -0
  213. data/spec/document_spec.rb +473 -0
  214. data/spec/font_spec.rb +324 -0
  215. data/spec/formatted_text_arranger_spec.rb +426 -0
  216. data/spec/formatted_text_box_spec.rb +756 -0
  217. data/spec/formatted_text_fragment_spec.rb +211 -0
  218. data/spec/graphics_spec.rb +446 -0
  219. data/spec/images_spec.rb +96 -0
  220. data/spec/inline_formatted_text_parser_spec.rb +502 -0
  221. data/spec/jpg_spec.rb +25 -0
  222. data/spec/line_wrap_spec.rb +341 -0
  223. data/spec/measurement_units_spec.rb +23 -0
  224. data/spec/name_tree_spec.rb +112 -0
  225. data/spec/object_store_spec.rb +160 -0
  226. data/spec/outline_spec.rb +269 -0
  227. data/spec/pdf_object_spec.rb +170 -0
  228. data/spec/png_spec.rb +237 -0
  229. data/spec/reference_spec.rb +82 -0
  230. data/spec/repeater_spec.rb +96 -0
  231. data/spec/security_spec.rb +120 -0
  232. data/spec/snapshot_spec.rb +138 -0
  233. data/spec/spec_helper.rb +26 -0
  234. data/spec/stamp_spec.rb +108 -0
  235. data/spec/stroke_styles_spec.rb +163 -0
  236. data/spec/table_spec.rb +598 -0
  237. data/spec/template_spec.rb +158 -0
  238. data/spec/text_at_spec.rb +119 -0
  239. data/spec/text_box_spec.rb +742 -0
  240. data/spec/text_spacing_spec.rb +75 -0
  241. data/spec/text_spec.rb +333 -0
  242. data/spec/text_with_inline_formatting_spec.rb +193 -0
  243. data/spec/transparency_spec.rb +89 -0
  244. metadata +331 -0
@@ -0,0 +1,421 @@
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/accessors'
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
+
15
+ module Prawn
16
+
17
+ class Document
18
+
19
+ # Set up and draw a table on this document. A block can be given, which will
20
+ # be run after cell setup but before layout and drawing.
21
+ #
22
+ # See the documentation on Prawn::Table for details on the arguments.
23
+ #
24
+ def table(data, options={}, &block)
25
+ t = Table.new(data, self, options, &block)
26
+ t.draw
27
+ t
28
+ end
29
+
30
+ # Set up, but do not draw, a table. Useful for creating subtables to be
31
+ # inserted into another Table. Call +draw+ on the resulting Table to ink it.
32
+ #
33
+ # See the documentation on Prawn::Table for details on the arguments.
34
+ #
35
+ def make_table(data, options={}, &block)
36
+ Table.new(data, self, options, &block)
37
+ end
38
+
39
+ end
40
+
41
+ # Next-generation table drawing for Prawn.
42
+ #
43
+ # = Data
44
+ #
45
+ # Data, for a Prawn table, is a two-dimensional array of objects that can be
46
+ # converted to cells ("cellable" objects). Cellable objects can be:
47
+ #
48
+ # String::
49
+ # Produces a text cell. This is the most common usage.
50
+ # Prawn::Table::Cell::
51
+ # If you have already built a Cell or have a custom subclass of Cell you
52
+ # want to use in a table, you can pass through Cell objects.
53
+ # Prawn::Table::
54
+ # Creates a subtable (a table within a cell). You can use
55
+ # Prawn::Document#make_table to create a table for use as a subtable
56
+ # without immediately drawing it. See examples/table/bill.rb for a
57
+ # somewhat complex use of subtables.
58
+ # Array::
59
+ # Creates a simple subtable. Create a Table object using make_table (see
60
+ # above) if you need more control over the subtable's styling.
61
+ #
62
+ # = Options
63
+ #
64
+ # Prawn/Layout provides many options to control style and layout of your
65
+ # table. These options are implemented with a uniform interface: the +:foo+
66
+ # option always sets the +foo=+ accessor. See the accessor and method
67
+ # documentation for full details on the options you can pass. Some
68
+ # highlights:
69
+ #
70
+ # +cell_style+::
71
+ # A hash of style options to style all cells. See the documentation on
72
+ # Prawn::Table::Cell for all cell style options.
73
+ # +header+::
74
+ # If set to +true+, the first row will be repeated on every page. The
75
+ # header must be included as the first row of your data. Row numbering
76
+ # (for styling and other row-specific options) always indexes based on
77
+ # your data array. Whether or not you have a header, row(n) always refers
78
+ # to the nth element (starting from 0) of the +data+ array.
79
+ # +column_widths+::
80
+ # Sets widths for individual columns. Manually setting widths can give
81
+ # better results than letting Prawn guess at them, as Prawn's algorithm
82
+ # for defaulting widths is currently pretty boneheaded. If you experience
83
+ # problems like weird column widths or CannotFit errors, try manually
84
+ # setting widths on more columns.
85
+ #
86
+ # = Initializer Block
87
+ #
88
+ # If a block is passed to methods that initialize a table
89
+ # (Prawn::Table.new, Prawn::Document#table, Prawn::Document#make_table), it
90
+ # will be called after cell setup but before layout. This is a very flexible
91
+ # way to specify styling and layout constraints. This code sets up a table
92
+ # where the second through the fourth rows (1-3, indexed from 0) are each one
93
+ # inch (72 pt) wide:
94
+ #
95
+ # pdf.table(data) do |table|
96
+ # table.rows(1..3).width = 72
97
+ # end
98
+ #
99
+ # As with Prawn::Document#initialize, if the block has no arguments, it will
100
+ # be evaluated in the context of the object itself. The above code could be
101
+ # rewritten as:
102
+ #
103
+ # pdf.table(data) do
104
+ # rows(1..3).width = 72
105
+ # end
106
+ #
107
+ class Table
108
+
109
+ # Set up a table on the given document. Arguments:
110
+ #
111
+ # +data+::
112
+ # A two-dimensional array of cell-like objects. See the "Data" section
113
+ # above for the types of objects that can be put in a table.
114
+ # +document+::
115
+ # The Prawn::Document instance on which to draw the table.
116
+ # +options+::
117
+ # A hash of attributes and values for the table. See the "Options" block
118
+ # above for details on available options.
119
+ #
120
+ def initialize(data, document, options={}, &block)
121
+ @pdf = document
122
+ @cells = make_cells(data)
123
+ @header = false
124
+ options.each { |k, v| send("#{k}=", v) }
125
+
126
+ if block
127
+ block.arity < 1 ? instance_eval(&block) : block[self]
128
+ end
129
+
130
+ set_column_widths
131
+ set_row_heights
132
+ position_cells
133
+ end
134
+
135
+ # Number of rows in the table.
136
+ #
137
+ attr_reader :row_length
138
+
139
+ # Number of columns in the table.
140
+ #
141
+ attr_reader :column_length
142
+
143
+ # Manually set the width of the table.
144
+ #
145
+ attr_writer :width
146
+
147
+ # Returns the width of the table in PDF points.
148
+ #
149
+ def width
150
+ @width ||= [natural_width, @pdf.bounds.width].min
151
+ end
152
+
153
+ # Sets column widths for the table. The argument can be one of the following
154
+ # types:
155
+ #
156
+ # +Array+::
157
+ # <tt>[w0, w1, w2, ...]</tt> (specify a width for each column)
158
+ # +Hash+::
159
+ # <tt>{0 => w0, 1 => w1, ...}</tt> (keys are column names, values are
160
+ # widths)
161
+ # +Numeric+::
162
+ # +72+ (sets width for all columns)
163
+ #
164
+ def column_widths=(widths)
165
+ case widths
166
+ when Array
167
+ widths.each_with_index { |w, i| column(i).width = w }
168
+ when Hash
169
+ widths.each { |i, w| column(i).width = w }
170
+ when Numeric
171
+ columns.width = widths
172
+ else
173
+ raise ArgumentError, "cannot interpret column widths"
174
+ end
175
+ end
176
+
177
+ # Returns the height of the table in PDF points.
178
+ #
179
+ def height
180
+ cells.height
181
+ end
182
+
183
+ # If +true+, designates the first row as a header row to be repeated on
184
+ # every page. Does not change row numbering -- row numbers always index into
185
+ # the data array provided, with no modification.
186
+ #
187
+ attr_writer :header
188
+
189
+ # Accepts an Array of alternating row colors to stripe the table.
190
+ #
191
+ attr_writer :row_colors
192
+
193
+ # Sets styles for all cells.
194
+ #
195
+ # pdf.table(data, :cell_style => { :borders => [:left, :right] })
196
+ #
197
+ def cell_style=(style_hash)
198
+ cells.style(style_hash)
199
+ end
200
+
201
+ # Allows generic stylable content. This is an alternate syntax that some
202
+ # prefer to the attribute-based syntax. This code using style:
203
+ #
204
+ # pdf.table(data) do
205
+ # style(row(0), :background_color => 'ff00ff')
206
+ # style(column(0)) { |c| c.border_width += 1 }
207
+ # end
208
+ #
209
+ # is equivalent to:
210
+ #
211
+ # pdf.table(data) do
212
+ # row(0).style :background_color => 'ff00ff'
213
+ # column(0).style { |c| c.border_width += 1 }
214
+ # end
215
+ #
216
+ def style(stylable, style_hash={}, &block)
217
+ stylable.style(style_hash, &block)
218
+ end
219
+
220
+ # Draws the table onto the document at the document's current y-position.
221
+ #
222
+ def draw
223
+ # The cell y-positions are based on an infinitely long canvas. The offset
224
+ # keeps track of how much we have to add to the original, theoretical
225
+ # y-position to get to the actual position on the current page.
226
+ offset = @pdf.y
227
+
228
+ # Reference bounds are the non-stretchy bounds used to decide when to
229
+ # flow to a new column / page.
230
+ ref_bounds = @pdf.bounds.stretchy? ? @pdf.margin_box : @pdf.bounds
231
+
232
+ last_y = @pdf.y
233
+ @cells.each do |cell|
234
+ if cell.height > (cell.y + offset) - ref_bounds.absolute_bottom
235
+ # start a new page or column
236
+ @pdf.bounds.move_past_bottom
237
+ draw_header
238
+ offset = @pdf.y - cell.y
239
+ end
240
+
241
+ # Don't modify cell.x / cell.y here, as we want to reuse the original
242
+ # values when re-inking the table. #draw should be able to be called
243
+ # multiple times.
244
+ x, y = cell.x, cell.y
245
+ y += offset
246
+
247
+ # Translate coordinates to the bounds we are in, since drawing is
248
+ # relative to the cursor, not ref_bounds.
249
+ x += @pdf.bounds.left_side - @pdf.bounds.absolute_left
250
+ y -= @pdf.bounds.absolute_bottom
251
+
252
+ # Set background color, if any.
253
+ if @row_colors && (!@header || cell.row > 0)
254
+ index = @header ? (cell.row - 1) : cell.row
255
+ cell.background_color = @row_colors[index % @row_colors.length]
256
+ end
257
+
258
+ cell.draw([x, y])
259
+ last_y = y
260
+ end
261
+
262
+ @pdf.move_cursor_to(last_y - @cells.last.height)
263
+ end
264
+
265
+ # Calculate and return the constrained column widths, taking into account
266
+ # each cell's min_width, max_width, and any user-specified constraints on
267
+ # the table or column size.
268
+ #
269
+ # Because the natural widths can be silly, this does not always work so well
270
+ # at guessing a good size for columns that have vastly different content. If
271
+ # you see weird problems like CannotFit errors or shockingly bad column
272
+ # sizes, you should specify more column widths manually.
273
+ #
274
+ def column_widths
275
+ @column_widths ||= begin
276
+ if width < cells.min_width
277
+ raise Errors::CannotFit,
278
+ "Table's width was set too small to contain its contents"
279
+ end
280
+
281
+ if width > cells.max_width
282
+ raise Errors::CannotFit,
283
+ "Table's width was set larger than its contents' maximum width"
284
+ end
285
+
286
+ if width < natural_width
287
+ # Shrink the table to fit the requested width.
288
+ f = (width - cells.min_width).to_f / (natural_width - cells.min_width)
289
+
290
+ (0...column_length).map do |c|
291
+ min, nat = column(c).min_width, column(c).width
292
+ (f * (nat - min)) + min
293
+ end
294
+ elsif width > natural_width
295
+ # Expand the table to fit the requested width.
296
+ f = (width - cells.width).to_f / (cells.max_width - cells.width)
297
+
298
+ (0...column_length).map do |c|
299
+ nat, max = column(c).width, column(c).max_width
300
+ (f * (max - nat)) + nat
301
+ end
302
+ else
303
+ natural_column_widths
304
+ end
305
+ end
306
+ end
307
+
308
+ # Returns an array with the height of each row.
309
+ #
310
+ def row_heights
311
+ @natural_row_heights ||= (0...row_length).map{ |r| row(r).height }
312
+ end
313
+
314
+ protected
315
+
316
+ # Converts the array of cellable objects given into instances of
317
+ # Prawn::Table::Cell, and sets up their in-table properties so that they
318
+ # know their own position in the table.
319
+ #
320
+ def make_cells(data)
321
+ assert_proper_table_data(data)
322
+
323
+ cells = []
324
+
325
+ @row_length = data.length
326
+ @column_length = data.map{ |r| r.length }.max
327
+
328
+ data.each_with_index do |row_cells, row_number|
329
+ row_cells.each_with_index do |cell_data, column_number|
330
+ cell = Cell.make(@pdf, cell_data)
331
+ cell.extend(Cell::InTable)
332
+ cell.row = row_number
333
+ cell.column = column_number
334
+ cells << cell
335
+ end
336
+ end
337
+ cells
338
+ end
339
+
340
+ # Raises an error if the data provided cannot be converted into a valid
341
+ # table.
342
+ #
343
+ def assert_proper_table_data(data)
344
+ if data.nil? || data.empty?
345
+ raise Prawn::Errors::EmptyTable,
346
+ "data must be a non-empty, non-nil, two dimensional array " +
347
+ "of cell-convertible objects"
348
+ end
349
+
350
+ unless data.all? { |e| Array === e }
351
+ raise Prawn::Errors::InvalidTableData,
352
+ "data must be a two dimensional array of cellable objects"
353
+ end
354
+ end
355
+
356
+ # If the table has a header, draw it at the current position.
357
+ #
358
+ def draw_header
359
+ if @header
360
+ y = @pdf.cursor
361
+ row(0).each do |cell|
362
+ cell.y = y
363
+ cell.draw
364
+ end
365
+ @pdf.move_cursor_to(y - row(0).height)
366
+ end
367
+ end
368
+
369
+ # Returns an array of each column's natural (unconstrained) width.
370
+ #
371
+ def natural_column_widths
372
+ @natural_column_widths ||= (0...column_length).map { |c| column(c).width }
373
+ end
374
+
375
+ # Returns the "natural" (unconstrained) width of the table. This may be
376
+ # extremely silly; for example, the unconstrained width of a paragraph of
377
+ # text is the width it would assume if it were not wrapped at all. Could be
378
+ # a mile long.
379
+ #
380
+ def natural_width
381
+ @natural_width ||= natural_column_widths.inject(0) { |sum, w| sum + w }
382
+ end
383
+
384
+ # Assigns the calculated column widths to each cell. This ensures that each
385
+ # cell in a column is the same width. After this method is called,
386
+ # subsequent calls to column_widths and width should return the finalized
387
+ # values that will be used to ink the table.
388
+ #
389
+ def set_column_widths
390
+ column_widths.each_with_index do |w, col_num|
391
+ column(col_num).width = w
392
+ end
393
+ end
394
+
395
+ # Assigns the row heights to each cell. This ensures that every cell in a
396
+ # row is the same height.
397
+ #
398
+ def set_row_heights
399
+ row_heights.each_with_index { |h, row_num| row(row_num).height = h }
400
+ end
401
+
402
+ # Set each cell's position based on the widths and heights of cells
403
+ # preceding it.
404
+ #
405
+ def position_cells
406
+ # Calculate x- and y-positions as running sums of widths / heights.
407
+ x_positions = column_widths.inject([0]) { |ary, x|
408
+ ary << (ary.last + x); ary }[0..-2]
409
+ x_positions.each_with_index { |x, i| column(i).x = x }
410
+
411
+ # y-positions assume an infinitely long canvas starting at zero -- this
412
+ # is corrected for in Table#draw, and page breaks are properly inserted.
413
+ y_positions = row_heights.inject([0]) { |ary, y|
414
+ ary << (ary.last - y); ary}[0..-2]
415
+ y_positions.each_with_index { |y, i| row(i).y = y }
416
+ end
417
+
418
+ end
419
+
420
+
421
+ end
@@ -0,0 +1,180 @@
1
+ # encoding: utf-8
2
+
3
+ # accessors.rb: Methods for accessing rows, columns, and cells of a
4
+ # Prawn::Table.
5
+ #
6
+ # Copyright December 2009, Brad Ediger. All Rights Reserved.
7
+ #
8
+ # This is free software. Please see the LICENSE and COPYING files for details.
9
+ module Prawn
10
+
11
+ class Table
12
+
13
+ # Returns a CellProxy that can be used to select and style cells. See the
14
+ # CellProxy documentation for things you can do with cells.
15
+ #
16
+ def cells
17
+ @cell_proxy ||= CellProxy.new(@cells)
18
+ end
19
+
20
+ # Selects the given rows (0-based) for styling. Returns a CellProxy -- see
21
+ # the documentation on CellProxy for things you can do with cells.
22
+ #
23
+ def rows(row_spec)
24
+ cells.rows(row_spec)
25
+ end
26
+ alias_method :row, :rows
27
+
28
+ # Selects the given columns (0-based) for styling. Returns a CellProxy --
29
+ # see the documentation on CellProxy for things you can do with cells.
30
+ #
31
+ def columns(col_spec)
32
+ cells.columns(col_spec)
33
+ end
34
+ alias_method :column, :columns
35
+
36
+ # Represents a selection of cells to be styled. Operations on a CellProxy
37
+ # can be chained, and cell properties can be set one-for-all on the proxy.
38
+ #
39
+ # To set vertical borders only:
40
+ #
41
+ # table.cells.borders = [:left, :right]
42
+ #
43
+ # To highlight a rectangular area of the table:
44
+ #
45
+ # table.rows(1..3).columns(2..4).background_color = 'ff0000'
46
+ #
47
+ class CellProxy
48
+ def initialize(cells) #:nodoc:
49
+ @cells = cells
50
+ end
51
+
52
+ # Iterates over cells in turn.
53
+ #
54
+ def each(&b)
55
+ @cells.each(&b)
56
+ end
57
+
58
+ include Enumerable
59
+
60
+ # Limits selection to the given row or rows. +row_spec+ can be anything
61
+ # that responds to the === operator selecting a set of 0-based row
62
+ # numbers; most commonly a number or a range.
63
+ #
64
+ # table.row(0) # selects first row
65
+ # table.rows(3..4) # selects rows four and five
66
+ #
67
+ def rows(row_spec)
68
+ CellProxy.new(@cells.select { |c| row_spec === c.row })
69
+ end
70
+ alias_method :row, :rows
71
+
72
+ # Limits selection to the given column or columns. +col_spec+ can be
73
+ # anything that responds to the === operator selecting a set of 0-based
74
+ # column numbers; most commonly a number or a range.
75
+ #
76
+ # table.column(0) # selects first column
77
+ # table.columns(3..4) # selects columns four and five
78
+ #
79
+ def columns(col_spec)
80
+ CellProxy.new(@cells.select { |c| col_spec === c.column })
81
+ end
82
+ alias_method :column, :columns
83
+
84
+ # Selects cells based on a block.
85
+ #
86
+ # table.column(4).select { |cell| cell.content =~ /Yes/ }.
87
+ # background_color = 'ff0000'
88
+ #
89
+ def select(&b)
90
+ CellProxy.new(@cells.select(&b))
91
+ end
92
+
93
+ # Retrieves a cell based on its 0-based row and column. Returns a Cell,
94
+ # not a CellProxy.
95
+ #
96
+ # table.cells[0, 0].content # => "First cell content"
97
+ #
98
+ def [](row, col)
99
+ @cells.find { |c| c.row == row && c.column == col }
100
+ end
101
+
102
+ # Supports setting multiple properties at once.
103
+ #
104
+ # table.cells.style(:padding => 0, :border_width => 2)
105
+ #
106
+ # is the same as:
107
+ #
108
+ # table.cells.padding = 0
109
+ # table.cells.border_width = 2
110
+ #
111
+ # You can also pass a block, which will be called for each cell in turn.
112
+ # This allows you to set more complicated properties:
113
+ #
114
+ # table.cells.style { |cell| cell.border_width += 12 }
115
+ #
116
+ def style(options={}, &block)
117
+ @cells.each do |cell|
118
+ options.each { |k, v| cell.send("#{k}=", v) }
119
+ block.call(cell) if block
120
+ end
121
+ end
122
+
123
+ # Returns the total width of all columns in the selected set.
124
+ #
125
+ def width
126
+ column_widths = {}
127
+ @cells.each do |cell|
128
+ column_widths[cell.column] =
129
+ [column_widths[cell.column], cell.width].compact.max
130
+ end
131
+ column_widths.values.inject(0) { |sum, width| sum + width }
132
+ end
133
+
134
+ # Returns minimum width required to contain cells in the set.
135
+ #
136
+ def min_width
137
+ column_min_widths = {}
138
+ @cells.each do |cell|
139
+ column_min_widths[cell.column] =
140
+ [column_min_widths[cell.column], cell.min_width].compact.max
141
+ end
142
+ column_min_widths.values.inject(0) { |sum, width| sum + width }
143
+ end
144
+
145
+ # Returns maximum width that can contain cells in the set.
146
+ #
147
+ def max_width
148
+ column_max_widths = {}
149
+ @cells.each do |cell|
150
+ column_max_widths[cell.column] =
151
+ [column_max_widths[cell.column], cell.max_width].compact.min
152
+ end
153
+ column_max_widths.values.inject(0) { |sum, width| sum + width }
154
+ end
155
+
156
+ # Returns the total height of all rows in the selected set.
157
+ #
158
+ def height
159
+ row_heights = {}
160
+ @cells.each do |cell|
161
+ row_heights[cell.row] =
162
+ [row_heights[cell.row], cell.height].compact.max
163
+ end
164
+ row_heights.values.inject(0) { |sum, width| sum + width }
165
+ end
166
+
167
+ # Supports setting arbitrary properties on a group of cells.
168
+ #
169
+ # table.cells.row(3..6).background_color = 'cc0000'
170
+ #
171
+ def method_missing(id, *args, &block)
172
+ @cells.each { |c| c.send(id, *args, &block) }
173
+ end
174
+ end
175
+
176
+ end
177
+
178
+ end
179
+
180
+