prawn 0.11.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (353) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +10 -0
  3. data/COPYING +2 -2
  4. data/GPLv2 +340 -0
  5. data/GPLv3 +674 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE +1 -1
  8. data/Rakefile +29 -38
  9. data/data/images/16bit.alpha +0 -0
  10. data/data/images/16bit.color +0 -0
  11. data/data/images/dice.alpha +0 -0
  12. data/data/images/dice.color +0 -0
  13. data/data/images/indexed_color.dat +0 -0
  14. data/data/images/indexed_color.png +0 -0
  15. data/data/images/page_white_text.alpha +0 -0
  16. data/data/images/page_white_text.color +0 -0
  17. data/data/pdfs/nested_pages.pdf +13 -13
  18. data/lib/prawn/document/bounding_box.rb +87 -12
  19. data/lib/prawn/document/column_box.rb +57 -28
  20. data/lib/prawn/document/graphics_state.rb +11 -74
  21. data/lib/prawn/document/internals.rb +25 -23
  22. data/lib/prawn/document/snapshot.rb +11 -8
  23. data/lib/prawn/document/span.rb +12 -10
  24. data/lib/prawn/document.rb +250 -194
  25. data/lib/prawn/encoding.rb +9 -10
  26. data/lib/prawn/errors.rb +18 -29
  27. data/lib/prawn/font/afm.rb +52 -41
  28. data/lib/prawn/font/dfont.rb +4 -3
  29. data/lib/prawn/font/ttf.rb +44 -48
  30. data/lib/prawn/font.rb +138 -88
  31. data/lib/prawn/font_metric_cache.rb +47 -0
  32. data/lib/prawn/graphics/cap_style.rb +4 -3
  33. data/lib/prawn/graphics/color.rb +13 -5
  34. data/lib/prawn/graphics/dash.rb +53 -31
  35. data/lib/prawn/graphics/join_style.rb +9 -7
  36. data/lib/prawn/graphics/patterns.rb +138 -0
  37. data/lib/prawn/graphics/transformation.rb +10 -9
  38. data/lib/prawn/graphics/transparency.rb +3 -1
  39. data/lib/prawn/graphics.rb +316 -61
  40. data/lib/prawn/image_handler.rb +36 -0
  41. data/lib/prawn/images/image.rb +49 -0
  42. data/lib/prawn/images/jpg.rb +21 -15
  43. data/lib/prawn/images/png.rb +62 -119
  44. data/lib/prawn/images.rb +89 -108
  45. data/lib/prawn/layout/grid.rb +66 -54
  46. data/lib/prawn/layout.rb +10 -15
  47. data/lib/prawn/measurement_extensions.rb +10 -6
  48. data/lib/prawn/measurements.rb +27 -21
  49. data/lib/prawn/outline.rb +6 -308
  50. data/lib/prawn/repeater.rb +11 -9
  51. data/lib/prawn/security/arcfour.rb +1 -0
  52. data/lib/prawn/security.rb +55 -33
  53. data/lib/prawn/soft_mask.rb +96 -0
  54. data/lib/prawn/stamp.rb +5 -3
  55. data/lib/prawn/table/cell/image.rb +69 -0
  56. data/lib/prawn/table/cell/in_table.rb +4 -2
  57. data/lib/prawn/table/cell/span_dummy.rb +93 -0
  58. data/lib/prawn/table/cell/subtable.rb +2 -2
  59. data/lib/prawn/table/cell/text.rb +44 -26
  60. data/lib/prawn/table/cell.rb +302 -50
  61. data/lib/prawn/table/cells.rb +147 -49
  62. data/lib/prawn/table/column_width_calculator.rb +61 -0
  63. data/lib/prawn/table.rb +297 -118
  64. data/lib/prawn/text/box.rb +21 -5
  65. data/lib/prawn/text/formatted/arranger.rb +290 -0
  66. data/lib/prawn/text/formatted/box.rb +103 -59
  67. data/lib/prawn/text/formatted/fragment.rb +34 -23
  68. data/lib/prawn/text/formatted/line_wrap.rb +266 -0
  69. data/lib/prawn/text/formatted/parser.rb +15 -5
  70. data/lib/prawn/text/formatted/wrap.rb +150 -0
  71. data/lib/prawn/text/formatted.rb +5 -4
  72. data/lib/prawn/text.rb +38 -24
  73. data/lib/prawn/utilities.rb +46 -0
  74. data/lib/prawn.rb +85 -20
  75. data/manual/basic_concepts/adding_pages.rb +27 -0
  76. data/manual/basic_concepts/basic_concepts.rb +34 -0
  77. data/manual/basic_concepts/creation.rb +39 -0
  78. data/manual/basic_concepts/cursor.rb +33 -0
  79. data/manual/basic_concepts/measurement.rb +25 -0
  80. data/manual/basic_concepts/origin.rb +38 -0
  81. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  82. data/manual/bounding_box/bounding_box.rb +39 -0
  83. data/manual/bounding_box/bounds.rb +49 -0
  84. data/manual/bounding_box/canvas.rb +24 -0
  85. data/manual/bounding_box/creation.rb +23 -0
  86. data/manual/bounding_box/indentation.rb +46 -0
  87. data/manual/bounding_box/nesting.rb +45 -0
  88. data/manual/bounding_box/russian_boxes.rb +40 -0
  89. data/manual/bounding_box/stretchy.rb +31 -0
  90. data/manual/document_and_page_options/background.rb +27 -0
  91. data/manual/document_and_page_options/document_and_page_options.rb +32 -0
  92. data/manual/document_and_page_options/metadata.rb +23 -0
  93. data/manual/document_and_page_options/page_margins.rb +38 -0
  94. data/manual/document_and_page_options/page_size.rb +34 -0
  95. data/manual/document_and_page_options/print_scaling.rb +20 -0
  96. data/manual/example_file.rb +111 -0
  97. data/manual/example_helper.rb +411 -0
  98. data/manual/example_package.rb +53 -0
  99. data/manual/example_section.rb +46 -0
  100. data/manual/graphics/circle_and_ellipse.rb +22 -0
  101. data/manual/graphics/color.rb +24 -0
  102. data/manual/graphics/common_lines.rb +30 -0
  103. data/manual/graphics/fill_and_stroke.rb +42 -0
  104. data/manual/graphics/fill_rules.rb +37 -0
  105. data/manual/graphics/gradients.rb +37 -0
  106. data/manual/graphics/graphics.rb +58 -0
  107. data/manual/graphics/helper.rb +24 -0
  108. data/manual/graphics/line_width.rb +35 -0
  109. data/manual/graphics/lines_and_curves.rb +41 -0
  110. data/manual/graphics/polygon.rb +29 -0
  111. data/manual/graphics/rectangle.rb +21 -0
  112. data/manual/graphics/rotate.rb +28 -0
  113. data/manual/graphics/scale.rb +41 -0
  114. data/manual/graphics/soft_masks.rb +46 -0
  115. data/manual/graphics/stroke_cap.rb +31 -0
  116. data/manual/graphics/stroke_dash.rb +48 -0
  117. data/manual/graphics/stroke_join.rb +30 -0
  118. data/manual/graphics/translate.rb +29 -0
  119. data/manual/graphics/transparency.rb +35 -0
  120. data/manual/images/absolute_position.rb +23 -0
  121. data/manual/images/fit.rb +21 -0
  122. data/manual/images/horizontal.rb +25 -0
  123. data/manual/images/images.rb +40 -0
  124. data/manual/images/plain_image.rb +18 -0
  125. data/manual/images/scale.rb +22 -0
  126. data/manual/images/vertical.rb +28 -0
  127. data/manual/images/width_and_height.rb +25 -0
  128. data/manual/layout/boxes.rb +27 -0
  129. data/manual/layout/content.rb +25 -0
  130. data/manual/layout/layout.rb +28 -0
  131. data/manual/layout/simple_grid.rb +23 -0
  132. data/manual/manual/cover.rb +36 -0
  133. data/manual/manual/foreword.rb +85 -0
  134. data/manual/manual/how_to_read_this_manual.rb +41 -0
  135. data/manual/manual/manual.rb +34 -0
  136. data/manual/outline/add_subsection_to.rb +61 -0
  137. data/manual/outline/insert_section_after.rb +47 -0
  138. data/manual/outline/outline.rb +32 -0
  139. data/manual/outline/sections_and_pages.rb +67 -0
  140. data/manual/repeatable_content/page_numbering.rb +54 -0
  141. data/manual/repeatable_content/repeatable_content.rb +31 -0
  142. data/manual/repeatable_content/repeater.rb +55 -0
  143. data/manual/repeatable_content/stamp.rb +41 -0
  144. data/manual/security/encryption.rb +31 -0
  145. data/manual/security/permissions.rb +38 -0
  146. data/manual/security/security.rb +28 -0
  147. data/manual/syntax_highlight.rb +52 -0
  148. data/manual/table/basic_block.rb +53 -0
  149. data/manual/table/before_rendering_page.rb +26 -0
  150. data/manual/table/cell_border_lines.rb +24 -0
  151. data/manual/table/cell_borders_and_bg.rb +31 -0
  152. data/manual/table/cell_dimensions.rb +30 -0
  153. data/manual/table/cell_text.rb +38 -0
  154. data/manual/table/column_widths.rb +30 -0
  155. data/manual/table/content_and_subtables.rb +39 -0
  156. data/manual/table/creation.rb +27 -0
  157. data/manual/table/filtering.rb +36 -0
  158. data/manual/table/flow_and_header.rb +17 -0
  159. data/manual/table/image_cells.rb +33 -0
  160. data/manual/table/position.rb +29 -0
  161. data/manual/table/row_colors.rb +20 -0
  162. data/manual/table/span.rb +30 -0
  163. data/manual/table/style.rb +22 -0
  164. data/manual/table/table.rb +52 -0
  165. data/manual/table/width.rb +27 -0
  166. data/manual/text/alignment.rb +44 -0
  167. data/manual/text/color.rb +24 -0
  168. data/manual/text/column_box.rb +32 -0
  169. data/manual/text/fallback_fonts.rb +37 -0
  170. data/manual/text/font.rb +41 -0
  171. data/manual/text/font_size.rb +45 -0
  172. data/manual/text/font_style.rb +23 -0
  173. data/manual/text/formatted_callbacks.rb +60 -0
  174. data/manual/text/formatted_text.rb +54 -0
  175. data/manual/text/free_flowing_text.rb +51 -0
  176. data/manual/text/group.rb +31 -0
  177. data/manual/text/inline.rb +43 -0
  178. data/manual/text/kerning_and_character_spacing.rb +39 -0
  179. data/manual/text/leading.rb +25 -0
  180. data/manual/text/line_wrapping.rb +41 -0
  181. data/manual/text/paragraph_indentation.rb +26 -0
  182. data/manual/text/positioned_text.rb +38 -0
  183. data/manual/text/registering_families.rb +48 -0
  184. data/manual/text/rendering_and_color.rb +37 -0
  185. data/manual/text/right_to_left_text.rb +43 -0
  186. data/manual/text/rotation.rb +43 -0
  187. data/manual/text/single_usage.rb +37 -0
  188. data/manual/text/text.rb +75 -0
  189. data/manual/text/text_box_excess.rb +32 -0
  190. data/manual/text/text_box_extensions.rb +45 -0
  191. data/manual/text/text_box_overflow.rb +44 -0
  192. data/manual/text/utf8.rb +28 -0
  193. data/{examples/m17n → manual/text}/win_ansi_charset.rb +14 -10
  194. data/prawn.gemspec +27 -17
  195. data/spec/acceptance/png.rb +23 -0
  196. data/spec/annotations_spec.rb +16 -32
  197. data/spec/bounding_box_spec.rb +284 -2
  198. data/spec/cell_spec.rb +169 -38
  199. data/spec/column_box_spec.rb +65 -0
  200. data/spec/data/curves.pdf +66 -0
  201. data/spec/destinations_spec.rb +5 -5
  202. data/spec/document_spec.rb +212 -113
  203. data/spec/extensions/encoding_helpers.rb +9 -0
  204. data/spec/extensions/mocha.rb +2 -3
  205. data/spec/font_metric_cache_spec.rb +52 -0
  206. data/spec/font_spec.rb +205 -95
  207. data/spec/formatted_text_arranger_spec.rb +43 -43
  208. data/spec/formatted_text_box_spec.rb +63 -24
  209. data/spec/formatted_text_fragment_spec.rb +8 -8
  210. data/spec/graphics_spec.rb +175 -68
  211. data/spec/grid_spec.rb +26 -15
  212. data/spec/image_handler_spec.rb +54 -0
  213. data/spec/images_spec.rb +58 -30
  214. data/spec/inline_formatted_text_parser_spec.rb +73 -19
  215. data/spec/jpg_spec.rb +4 -4
  216. data/spec/line_wrap_spec.rb +28 -28
  217. data/spec/measurement_units_spec.rb +6 -6
  218. data/spec/object_store_spec.rb +17 -106
  219. data/spec/outline_spec.rb +103 -63
  220. data/spec/png_spec.rb +25 -25
  221. data/spec/reference_spec.rb +8 -65
  222. data/spec/repeater_spec.rb +25 -11
  223. data/spec/security_spec.rb +44 -12
  224. data/spec/snapshot_spec.rb +38 -6
  225. data/spec/soft_mask_spec.rb +117 -0
  226. data/spec/span_spec.rb +10 -15
  227. data/spec/spec_helper.rb +32 -8
  228. data/spec/stamp_spec.rb +29 -30
  229. data/spec/stroke_styles_spec.rb +36 -18
  230. data/spec/table/span_dummy_spec.rb +17 -0
  231. data/spec/table_spec.rb +850 -104
  232. data/spec/text_at_spec.rb +19 -33
  233. data/spec/text_box_spec.rb +117 -64
  234. data/spec/text_rendering_mode_spec.rb +5 -5
  235. data/spec/text_spacing_spec.rb +20 -2
  236. data/spec/text_spec.rb +111 -59
  237. data/spec/transparency_spec.rb +5 -5
  238. metadata +477 -328
  239. data/HACKING +0 -50
  240. data/README +0 -141
  241. data/data/fonts/Action Man.dfont +0 -0
  242. data/data/fonts/Activa.ttf +0 -0
  243. data/data/fonts/Chalkboard.ttf +0 -0
  244. data/data/fonts/DejaVuSans.ttf +0 -0
  245. data/data/fonts/Dustismo_Roman.ttf +0 -0
  246. data/data/fonts/comicsans.ttf +0 -0
  247. data/data/fonts/gkai00mp.ttf +0 -0
  248. data/data/images/16bit.dat +0 -0
  249. data/data/images/dice.dat +0 -0
  250. data/data/images/page_white_text.dat +0 -0
  251. data/data/images/rails.dat +0 -0
  252. data/data/images/rails.png +0 -0
  253. data/examples/bounding_box/bounding_boxes.rb +0 -44
  254. data/examples/bounding_box/indentation.rb +0 -35
  255. data/examples/bounding_box/russian_boxes.rb +0 -37
  256. data/examples/bounding_box/stretched_nesting.rb +0 -68
  257. data/examples/example_helper.rb +0 -8
  258. data/examples/general/background.rb +0 -24
  259. data/examples/general/canvas.rb +0 -16
  260. data/examples/general/context_sensitive_headers.rb +0 -38
  261. data/examples/general/float.rb +0 -12
  262. data/examples/general/margin.rb +0 -37
  263. data/examples/general/measurement_units.rb +0 -52
  264. data/examples/general/metadata-info.rb +0 -17
  265. data/examples/general/multi_page_layout.rb +0 -19
  266. data/examples/general/outlines.rb +0 -67
  267. data/examples/general/page_geometry.rb +0 -32
  268. data/examples/general/page_numbering.rb +0 -40
  269. data/examples/general/page_templates.rb +0 -20
  270. data/examples/general/repeaters.rb +0 -48
  271. data/examples/general/stamp.rb +0 -42
  272. data/examples/general/templates.rb +0 -14
  273. data/examples/graphics/basic_images.rb +0 -24
  274. data/examples/graphics/cmyk.rb +0 -13
  275. data/examples/graphics/curves.rb +0 -12
  276. data/examples/graphics/gradient.rb +0 -23
  277. data/examples/graphics/hexagon.rb +0 -14
  278. data/examples/graphics/image_fit.rb +0 -16
  279. data/examples/graphics/image_flow.rb +0 -38
  280. data/examples/graphics/image_position.rb +0 -18
  281. data/examples/graphics/line.rb +0 -33
  282. data/examples/graphics/png_types.rb +0 -23
  283. data/examples/graphics/polygons.rb +0 -17
  284. data/examples/graphics/remote_images.rb +0 -13
  285. data/examples/graphics/rounded_polygons.rb +0 -20
  286. data/examples/graphics/rounded_rectangle.rb +0 -21
  287. data/examples/graphics/ruport_style_helpers.rb +0 -20
  288. data/examples/graphics/stroke_bounds.rb +0 -21
  289. data/examples/graphics/stroke_cap_and_join.rb +0 -46
  290. data/examples/graphics/stroke_dash.rb +0 -43
  291. data/examples/graphics/transformations.rb +0 -53
  292. data/examples/graphics/transparency.rb +0 -27
  293. data/examples/grid/bounding_boxes.rb +0 -22
  294. data/examples/grid/column_gutter_grid.rb +0 -21
  295. data/examples/grid/multi_boxes.rb +0 -52
  296. data/examples/grid/show_grid.rb +0 -14
  297. data/examples/grid/simple_grid.rb +0 -21
  298. data/examples/m17n/chinese_text_wrapping.rb +0 -18
  299. data/examples/m17n/euro.rb +0 -16
  300. data/examples/m17n/full_win_ansi_character_list.rb +0 -20
  301. data/examples/m17n/sjis.rb +0 -29
  302. data/examples/m17n/utf8.rb +0 -14
  303. data/examples/security/hello_foo.rb +0 -9
  304. data/examples/table/bill.rb +0 -54
  305. data/examples/table/borders.rb +0 -25
  306. data/examples/table/cell.rb +0 -13
  307. data/examples/table/checkerboard.rb +0 -23
  308. data/examples/table/header.rb +0 -15
  309. data/examples/table/inline_format_table.rb +0 -13
  310. data/examples/table/multi_page_table.rb +0 -10
  311. data/examples/table/simple_table.rb +0 -25
  312. data/examples/table/subtable.rb +0 -13
  313. data/examples/table/widths.rb +0 -21
  314. data/examples/text/alignment.rb +0 -19
  315. data/examples/text/character_spacing.rb +0 -13
  316. data/examples/text/dfont.rb +0 -49
  317. data/examples/text/family_based_styling.rb +0 -25
  318. data/examples/text/font_calculations.rb +0 -92
  319. data/examples/text/font_size.rb +0 -34
  320. data/examples/text/hyphenation.rb +0 -45
  321. data/examples/text/indent_paragraphs.rb +0 -24
  322. data/examples/text/inline_format.rb +0 -104
  323. data/examples/text/kerning.rb +0 -31
  324. data/examples/text/rendering_mode.rb +0 -21
  325. data/examples/text/rotated.rb +0 -99
  326. data/examples/text/shaped_text_box.rb +0 -32
  327. data/examples/text/simple_text.rb +0 -18
  328. data/examples/text/simple_text_ttf.rb +0 -18
  329. data/examples/text/span.rb +0 -30
  330. data/examples/text/text_box.rb +0 -90
  331. data/examples/text/text_box_returning_excess.rb +0 -52
  332. data/examples/text/text_flow.rb +0 -68
  333. data/lib/prawn/compatibility.rb +0 -51
  334. data/lib/prawn/core/annotations.rb +0 -61
  335. data/lib/prawn/core/byte_string.rb +0 -9
  336. data/lib/prawn/core/destinations.rb +0 -90
  337. data/lib/prawn/core/document_state.rb +0 -78
  338. data/lib/prawn/core/literal_string.rb +0 -16
  339. data/lib/prawn/core/name_tree.rb +0 -165
  340. data/lib/prawn/core/object_store.rb +0 -264
  341. data/lib/prawn/core/page.rb +0 -213
  342. data/lib/prawn/core/pdf_object.rb +0 -108
  343. data/lib/prawn/core/reference.rb +0 -112
  344. data/lib/prawn/core/text/formatted/arranger.rb +0 -293
  345. data/lib/prawn/core/text/formatted/line_wrap.rb +0 -272
  346. data/lib/prawn/core/text/formatted/wrap.rb +0 -149
  347. data/lib/prawn/core/text.rb +0 -268
  348. data/lib/prawn/core.rb +0 -85
  349. data/lib/prawn/document/page_geometry.rb +0 -136
  350. data/lib/prawn/graphics/gradient.rb +0 -84
  351. data/spec/name_tree_spec.rb +0 -112
  352. data/spec/pdf_object_spec.rb +0 -170
  353. data/spec/template_spec.rb +0 -291
data/lib/prawn/table.rb CHANGED
@@ -6,16 +6,21 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
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'
9
+ require_relative 'table/column_width_calculator'
10
+ require_relative 'table/cells'
11
+ require_relative 'table/cell'
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'
14
17
 
15
18
  module Prawn
16
19
 
17
20
  class Document
18
-
21
+
22
+ # @group Experimental API
23
+
19
24
  # Set up and draw a table on this document. A block can be given, which will
20
25
  # be run after cell setup but before layout and drawing.
21
26
  #
@@ -49,7 +54,7 @@ module Prawn
49
54
  # Produces a text cell. This is the most common usage.
50
55
  # Prawn::Table::Cell::
51
56
  # 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.
57
+ # want to use in a table, you can pass through Cell objects.
53
58
  # Prawn::Table::
54
59
  # Creates a subtable (a table within a cell). You can use
55
60
  # Prawn::Document#make_table to create a table for use as a subtable
@@ -71,17 +76,21 @@ module Prawn
71
76
  # A hash of style options to style all cells. See the documentation on
72
77
  # Prawn::Table::Cell for all cell style options.
73
78
  # +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+::
79
+ # If set to +true+, the first row will be repeated on every page. If set
80
+ # to an Integer, the first +x+ rows will be repeated on every page. Row
81
+ # numbering (for styling and other row-specific options) always indexes
82
+ # based on your data array. Whether or not you have a header, row(n) always
83
+ # refers to the nth element (starting from 0) of the +data+ array.
84
+ # +column_widths+::
80
85
  # Sets widths for individual columns. Manually setting widths can give
81
86
  # better results than letting Prawn guess at them, as Prawn's algorithm
82
87
  # for defaulting widths is currently pretty boneheaded. If you experience
83
88
  # problems like weird column widths or CannotFit errors, try manually
84
89
  # setting widths on more columns.
90
+ # +position+::
91
+ # Either :left (the default), :center, :right, or a number. Specifies the
92
+ # horizontal position of the table within its bounding box. If a number is
93
+ # provided, it specifies the distance in points from the left edge.
85
94
  #
86
95
  # = Initializer Block
87
96
  #
@@ -95,7 +104,7 @@ module Prawn
95
104
  # pdf.table(data) do |table|
96
105
  # table.rows(1..3).width = 72
97
106
  # end
98
- #
107
+ #
99
108
  # As with Prawn::Document#initialize, if the block has no arguments, it will
100
109
  # be evaluated in the context of the object itself. The above code could be
101
110
  # rewritten as:
@@ -104,7 +113,7 @@ module Prawn
104
113
  # rows(1..3).width = 72
105
114
  # end
106
115
  #
107
- class Table
116
+ class Table
108
117
 
109
118
  # Set up a table on the given document. Arguments:
110
119
  #
@@ -121,6 +130,7 @@ module Prawn
121
130
  @pdf = document
122
131
  @cells = make_cells(data)
123
132
  @header = false
133
+ @epsilon = 1e-9
124
134
  options.each { |k, v| send("#{k}=", v) }
125
135
 
126
136
  if block
@@ -130,7 +140,7 @@ module Prawn
130
140
  set_column_widths
131
141
  set_row_heights
132
142
  position_cells
133
- end
143
+ end
134
144
 
135
145
  # Number of rows in the table.
136
146
  #
@@ -144,6 +154,25 @@ module Prawn
144
154
  #
145
155
  attr_writer :width
146
156
 
157
+ # Position (:left, :right, :center, or a number indicating distance in
158
+ # points from the left edge) of the table within its parent bounds.
159
+ #
160
+ attr_writer :position
161
+
162
+ # Returns a Prawn::Table::Cells object representing all of the cells in
163
+ # this table.
164
+ #
165
+ attr_reader :cells
166
+
167
+ # Specify a callback to be called before each page of cells is rendered.
168
+ # The block is passed a Cells object containing all cells to be rendered on
169
+ # that page. You can change styling of the cells in this block, but keep in
170
+ # mind that the cells have already been positioned and sized.
171
+ #
172
+ def before_rendering_page(&block)
173
+ @before_rendering_page = block
174
+ end
175
+
147
176
  # Returns the width of the table in PDF points.
148
177
  #
149
178
  def width
@@ -153,9 +182,9 @@ module Prawn
153
182
  # Sets column widths for the table. The argument can be one of the following
154
183
  # types:
155
184
  #
156
- # +Array+::
185
+ # +Array+::
157
186
  # <tt>[w0, w1, w2, ...]</tt> (specify a width for each column)
158
- # +Hash+::
187
+ # +Hash+::
159
188
  # <tt>{0 => w0, 1 => w1, ...}</tt> (keys are column names, values are
160
189
  # widths)
161
190
  # +Numeric+::
@@ -168,7 +197,7 @@ module Prawn
168
197
  when Hash
169
198
  widths.each { |i, w| column(i).width = w }
170
199
  when Numeric
171
- columns.width = widths
200
+ cells.width = widths
172
201
  else
173
202
  raise ArgumentError, "cannot interpret column widths"
174
203
  end
@@ -181,8 +210,9 @@ module Prawn
181
210
  end
182
211
 
183
212
  # 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.
213
+ # every page. If an integer, designates the number of rows to be treated
214
+ # as a header Does not change row numbering -- row numbers always index
215
+ # into the data array provided, with no modification.
186
216
  #
187
217
  attr_writer :header
188
218
 
@@ -220,76 +250,139 @@ module Prawn
220
250
  # Draws the table onto the document at the document's current y-position.
221
251
  #
222
252
  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
-
234
- # Determine whether we're at the top of the current bounds (margin box or
235
- # bounding box). If we're at the top, we couldn't gain any more room by
236
- # breaking to the next page -- this means, in particular, that if the
237
- # first row is taller than the margin box, we will only move to the next
238
- # page if we're below the top. Some floating-point tolerance is added to
239
- # the calculation.
240
- #
241
- # Note that we use the actual bounds, not the reference bounds. This is
242
- # because even if we are in a stretchy bounding box, flowing to the next
243
- # page will not buy us any space if we are at the top.
244
- if @pdf.y > @pdf.bounds.height + @pdf.bounds.absolute_bottom - 0.001
245
- # we're at the top of our bounds
246
- started_new_page_at_row = 0
247
- else
248
- started_new_page_at_row = -1
249
-
250
- # If there isn't enough room left on the page to fit the first data row
251
- # (excluding the header), start the table on the next page.
252
- needed_height = row(0).height
253
- needed_height += row(1).height if @header
254
- if needed_height > @pdf.y - ref_bounds.absolute_bottom
255
- @pdf.bounds.move_past_bottom
256
- offset = @pdf.y
253
+ with_position do
254
+ # The cell y-positions are based on an infinitely long canvas. The offset
255
+ # keeps track of how much we have to add to the original, theoretical
256
+ # y-position to get to the actual position on the current page.
257
+ offset = @pdf.y
258
+
259
+ # Reference bounds are the non-stretchy bounds used to decide when to
260
+ # flow to a new column / page.
261
+ ref_bounds = @pdf.reference_bounds
262
+
263
+ last_y = @pdf.y
264
+
265
+ # Determine whether we're at the top of the current bounds (margin box or
266
+ # bounding box). If we're at the top, we couldn't gain any more room by
267
+ # breaking to the next page -- this means, in particular, that if the
268
+ # first row is taller than the margin box, we will only move to the next
269
+ # page if we're below the top. Some floating-point tolerance is added to
270
+ # the calculation.
271
+ #
272
+ # Note that we use the actual bounds, not the reference bounds. This is
273
+ # because even if we are in a stretchy bounding box, flowing to the next
274
+ # page will not buy us any space if we are at the top.
275
+ if @pdf.y > @pdf.bounds.height + @pdf.bounds.absolute_bottom - 0.001
276
+ # we're at the top of our bounds
257
277
  started_new_page_at_row = 0
278
+ else
279
+ started_new_page_at_row = -1
280
+
281
+ # If there isn't enough room left on the page to fit the first data row
282
+ # (excluding the header), start the table on the next page.
283
+ needed_height = row(0).height
284
+ if @header
285
+ if @header.is_a? Integer
286
+ needed_height += row(1..@header).height
287
+ else
288
+ needed_height += row(1).height
289
+ end
290
+ end
291
+ if needed_height > @pdf.y - ref_bounds.absolute_bottom
292
+ @pdf.bounds.move_past_bottom
293
+ offset = @pdf.y
294
+ started_new_page_at_row = 0
295
+ end
258
296
  end
259
- end
260
297
 
261
- @cells.each do |cell|
262
- if cell.height > (cell.y + offset) - ref_bounds.absolute_bottom &&
263
- cell.row > started_new_page_at_row
264
- # start a new page or column
265
- @pdf.bounds.move_past_bottom
266
- draw_header unless cell.row == 0
267
- offset = @pdf.y - cell.y
268
- started_new_page_at_row = cell.row
298
+ # Duplicate each cell of the header row into @header_row so it can be
299
+ # modified in before_rendering_page callbacks.
300
+ if @header
301
+ @header_row = Cells.new
302
+ if @header.is_a? Integer
303
+ @header.times do |r|
304
+ row(r).each { |cell| @header_row[cell.row, cell.column] = cell.dup }
305
+ end
306
+ else
307
+ row(0).each { |cell| @header_row[cell.row, cell.column] = cell.dup }
308
+ end
269
309
  end
270
-
271
- # Don't modify cell.x / cell.y here, as we want to reuse the original
272
- # values when re-inking the table. #draw should be able to be called
273
- # multiple times.
274
- x, y = cell.x, cell.y
275
- y += offset
276
-
277
- # Translate coordinates to the bounds we are in, since drawing is
278
- # relative to the cursor, not ref_bounds.
279
- x += @pdf.bounds.left_side - @pdf.bounds.absolute_left
280
- y -= @pdf.bounds.absolute_bottom
281
-
282
- # Set background color, if any.
283
- if @row_colors && (!@header || cell.row > 0)
284
- index = @header ? (cell.row - 1) : cell.row
285
- cell.background_color = @row_colors[index % @row_colors.length]
310
+
311
+ # Track cells to be drawn on this page. They will all be drawn when this
312
+ # page is finished.
313
+ cells_this_page = []
314
+
315
+ @cells.each do |cell|
316
+ if cell.height > (cell.y + offset) - ref_bounds.absolute_bottom &&
317
+ cell.row > started_new_page_at_row
318
+ # Ink all cells on the current page
319
+ if defined?(@before_rendering_page) && @before_rendering_page
320
+ c = Cells.new(cells_this_page.map { |ci, _| ci })
321
+ @before_rendering_page.call(c)
322
+ end
323
+ Cell.draw_cells(cells_this_page)
324
+ cells_this_page = []
325
+
326
+ # start a new page or column
327
+ @pdf.bounds.move_past_bottom
328
+ x_offset = @pdf.bounds.left_side - @pdf.bounds.absolute_left
329
+ if cell.row > 0 && @header
330
+ if @header.is_a? Integer
331
+ header_height = 0
332
+ y_coord = @pdf.cursor
333
+ @header.times do |h|
334
+ additional_header_height = add_header(cells_this_page, x_offset, y_coord-header_height, cell.row-1, h)
335
+ header_height += additional_header_height
336
+ end
337
+ else
338
+ header_height = add_header(cells_this_page, x_offset, @pdf.cursor, cell.row-1)
339
+ end
340
+ else
341
+ header_height = 0
342
+ end
343
+ offset = @pdf.y - cell.y - header_height
344
+ started_new_page_at_row = cell.row
345
+ end
346
+
347
+ # Don't modify cell.x / cell.y here, as we want to reuse the original
348
+ # values when re-inking the table. #draw should be able to be called
349
+ # multiple times.
350
+ x, y = cell.x, cell.y
351
+ y += offset
352
+
353
+ # Translate coordinates to the bounds we are in, since drawing is
354
+ # relative to the cursor, not ref_bounds.
355
+ x += @pdf.bounds.left_side - @pdf.bounds.absolute_left
356
+ y -= @pdf.bounds.absolute_bottom
357
+
358
+ # Set background color, if any.
359
+ if defined?(@row_colors) && @row_colors && (!@header || cell.row > 0)
360
+ # Ensure coloring restarts on every page (to make sure the header
361
+ # and first row of a page are not colored the same way).
362
+ if @header.is_a? Integer
363
+ rows = @header
364
+ elsif @header
365
+ rows = 1
366
+ else
367
+ rows = 0
368
+ end
369
+ index = cell.row - [started_new_page_at_row, rows].max
370
+
371
+ cell.background_color ||= @row_colors[index % @row_colors.length]
372
+ end
373
+
374
+ cells_this_page << [cell, [x, y]]
375
+ last_y = y
376
+ end
377
+ # Draw the last page of cells
378
+ if defined?(@before_rendering_page) && @before_rendering_page
379
+ c = Cells.new(cells_this_page.map { |ci, _| ci })
380
+ @before_rendering_page.call(c)
286
381
  end
382
+ Cell.draw_cells(cells_this_page)
287
383
 
288
- cell.draw([x, y])
289
- last_y = y
384
+ @pdf.move_cursor_to(last_y - @cells.last.height)
290
385
  end
291
-
292
- @pdf.move_cursor_to(last_y - @cells.last.height)
293
386
  end
294
387
 
295
388
  # Calculate and return the constrained column widths, taking into account
@@ -303,32 +396,32 @@ module Prawn
303
396
  #
304
397
  def column_widths
305
398
  @column_widths ||= begin
306
- if width < cells.min_width
399
+ if width - cells.min_width < -epsilon
307
400
  raise Errors::CannotFit,
308
401
  "Table's width was set too small to contain its contents " +
309
402
  "(min width #{cells.min_width}, requested #{width})"
310
403
  end
311
404
 
312
- if width > cells.max_width
405
+ if width - cells.max_width > epsilon
313
406
  raise Errors::CannotFit,
314
407
  "Table's width was set larger than its contents' maximum width " +
315
408
  "(max width #{cells.max_width}, requested #{width})"
316
409
  end
317
410
 
318
- if width < natural_width
411
+ if width - natural_width < -epsilon
319
412
  # Shrink the table to fit the requested width.
320
413
  f = (width - cells.min_width).to_f / (natural_width - cells.min_width)
321
414
 
322
415
  (0...column_length).map do |c|
323
- min, nat = column(c).min_width, column(c).width
416
+ min, nat = column(c).min_width, natural_column_widths[c]
324
417
  (f * (nat - min)) + min
325
418
  end
326
- elsif width > natural_width
419
+ elsif width - natural_width > epsilon
327
420
  # Expand the table to fit the requested width.
328
421
  f = (width - cells.width).to_f / (cells.max_width - cells.width)
329
422
 
330
423
  (0...column_length).map do |c|
331
- nat, max = column(c).width, column(c).max_width
424
+ nat, max = natural_column_widths[c], column(c).max_width
332
425
  (f * (max - nat)) + nat
333
426
  end
334
427
  else
@@ -340,7 +433,21 @@ module Prawn
340
433
  # Returns an array with the height of each row.
341
434
  #
342
435
  def row_heights
343
- @natural_row_heights ||= (0...row_length).map{ |r| row(r).height }
436
+ @natural_row_heights ||=
437
+ begin
438
+ heights_by_row = Hash.new(0)
439
+ cells.each do |cell|
440
+ next if cell.is_a?(Cell::SpanDummy)
441
+
442
+ # Split the height of row-spanned cells evenly by rows
443
+ height_per_row = cell.height.to_f / cell.rowspan
444
+ cell.rowspan.times do |i|
445
+ heights_by_row[cell.row + i] =
446
+ [heights_by_row[cell.row + i], height_per_row].max
447
+ end
448
+ end
449
+ heights_by_row.sort_by { |row, _| row }.map { |_, h| h }
450
+ end
344
451
  end
345
452
 
346
453
  protected
@@ -352,23 +459,80 @@ module Prawn
352
459
  def make_cells(data)
353
460
  assert_proper_table_data(data)
354
461
 
355
- cells = []
356
-
357
- @row_length = data.length
358
- @column_length = data.map{ |r| r.length }.max
462
+ cells = Cells.new
359
463
 
360
- data.each_with_index do |row_cells, row_number|
361
- row_cells.each_with_index do |cell_data, column_number|
464
+ row_number = 0
465
+ data.each do |row_cells|
466
+ column_number = 0
467
+ row_cells.each do |cell_data|
468
+ # If we landed on a spanned cell (from a rowspan above), continue
469
+ # until we find an empty spot.
470
+ column_number += 1 until cells[row_number, column_number].nil?
471
+
472
+ # Build the cell and store it in the Cells collection.
362
473
  cell = Cell.make(@pdf, cell_data)
363
- cell.extend(Cell::InTable)
364
- cell.row = row_number
365
- cell.column = column_number
366
- cells << cell
474
+ cells[row_number, column_number] = cell
475
+
476
+ # Add dummy cells for the rest of the cells in the span group. This
477
+ # allows Prawn to keep track of the horizontal and vertical space
478
+ # occupied in each column and row spanned by this cell, while still
479
+ # leaving the master (top left) cell in the group responsible for
480
+ # drawing. Dummy cells do not put ink on the page.
481
+ cell.rowspan.times do |i|
482
+ cell.colspan.times do |j|
483
+ next if i == 0 && j == 0
484
+
485
+ # It is an error to specify spans that overlap; catch this here
486
+ if cells[row_number + i, column_number + j]
487
+ raise Prawn::Errors::InvalidTableSpan,
488
+ "Spans overlap at row #{row_number + i}, " +
489
+ "column #{column_number + j}."
490
+ end
491
+
492
+ dummy = Cell::SpanDummy.new(@pdf, cell)
493
+ cells[row_number + i, column_number + j] = dummy
494
+ cell.dummy_cells << dummy
495
+ end
496
+ end
497
+
498
+ column_number += cell.colspan
367
499
  end
500
+
501
+ row_number += 1
368
502
  end
503
+
504
+ # Calculate the number of rows and columns in the table, taking into
505
+ # account that some cells may span past the end of the physical cells we
506
+ # have.
507
+ @row_length = cells.map do |cell|
508
+ cell.row + cell.rowspan
509
+ end.max
510
+
511
+ @column_length = cells.map do |cell|
512
+ cell.column + cell.colspan
513
+ end.max
514
+
369
515
  cells
370
516
  end
371
517
 
518
+ # Add the header row(s) to the given array of cells at the given y-position.
519
+ # Number the row with the given +row+ index, so that the header appears (in
520
+ # any Cells built for this page) immediately prior to the first data row on
521
+ # this page.
522
+ #
523
+ # Return the height of the header.
524
+ #
525
+ def add_header(page_of_cells, x_offset, y, row, row_of_header=nil)
526
+ rows_to_operate_on = @header_row
527
+ rows_to_operate_on = @header_row.rows(row_of_header) if row_of_header
528
+ rows_to_operate_on.each do |cell|
529
+ cell.row = row
530
+ cell.dummy_cells.each {|c| c.row = row }
531
+ page_of_cells << [cell, [cell.x + x_offset, y]]
532
+ end
533
+ rows_to_operate_on.height
534
+ end
535
+
372
536
  # Raises an error if the data provided cannot be converted into a valid
373
537
  # table.
374
538
  #
@@ -385,23 +549,10 @@ module Prawn
385
549
  end
386
550
  end
387
551
 
388
- # If the table has a header, draw it at the current position.
389
- #
390
- def draw_header
391
- if @header
392
- y = @pdf.cursor
393
- row(0).each do |cell|
394
- cell.y = y
395
- cell.draw
396
- end
397
- @pdf.move_cursor_to(y - row(0).height)
398
- end
399
- end
400
-
401
552
  # Returns an array of each column's natural (unconstrained) width.
402
553
  #
403
554
  def natural_column_widths
404
- @natural_column_widths ||= (0...column_length).map { |c| column(c).width }
555
+ @natural_column_widths ||= ColumnWidthCalculator.new(cells).natural_widths
405
556
  end
406
557
 
407
558
  # Returns the "natural" (unconstrained) width of the table. This may be
@@ -410,7 +561,7 @@ module Prawn
410
561
  # a mile long.
411
562
  #
412
563
  def natural_width
413
- @natural_width ||= natural_column_widths.inject(0) { |sum, w| sum + w }
564
+ @natural_width ||= natural_column_widths.inject(0, &:+)
414
565
  end
415
566
 
416
567
  # Assigns the calculated column widths to each cell. This ensures that each
@@ -419,7 +570,7 @@ module Prawn
419
570
  # values that will be used to ink the table.
420
571
  #
421
572
  def set_column_widths
422
- column_widths.each_with_index do |w, col_num|
573
+ column_widths.each_with_index do |w, col_num|
423
574
  column(col_num).width = w
424
575
  end
425
576
  end
@@ -436,7 +587,7 @@ module Prawn
436
587
  #
437
588
  def position_cells
438
589
  # Calculate x- and y-positions as running sums of widths / heights.
439
- x_positions = column_widths.inject([0]) { |ary, x|
590
+ x_positions = column_widths.inject([0]) { |ary, x|
440
591
  ary << (ary.last + x); ary }[0..-2]
441
592
  x_positions.each_with_index { |x, i| column(i).x = x }
442
593
 
@@ -447,6 +598,34 @@ module Prawn
447
598
  y_positions.each_with_index { |y, i| row(i).y = y }
448
599
  end
449
600
 
601
+ # Sets up a bounding box to position the table according to the specified
602
+ # :position option, and yields.
603
+ #
604
+ def with_position
605
+ x = case defined?(@position) && @position || :left
606
+ when :left then return yield
607
+ when :center then (@pdf.bounds.width - width) / 2.0
608
+ when :right then @pdf.bounds.width - width
609
+ when Numeric then @position
610
+ else raise ArgumentError, "unknown position #{@position.inspect}"
611
+ end
612
+ dy = @pdf.bounds.absolute_top - @pdf.y
613
+ final_y = nil
614
+
615
+ @pdf.bounding_box([x, @pdf.bounds.top], :width => width) do
616
+ @pdf.move_down dy
617
+ yield
618
+ final_y = @pdf.y
619
+ end
620
+
621
+ @pdf.y = final_y
622
+ end
623
+
624
+ private
625
+
626
+ def epsilon
627
+ @epsilon
628
+ end
450
629
  end
451
630
 
452
631
 
@@ -7,8 +7,11 @@
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
  #
9
9
 
10
+ require_relative "formatted/box"
11
+
10
12
  module Prawn
11
13
  module Text
14
+ # @group Stable API
12
15
 
13
16
  # Draws the requested text into a box. When the text overflows
14
17
  # the rectangle, you shrink to fit, or truncate the text. Text
@@ -17,14 +20,14 @@ module Prawn
17
20
  # == Encoding
18
21
  #
19
22
  # Note that strings passed to this function should be encoded as UTF-8.
20
- # If you get unexpected characters appearing in your rendered document,
23
+ # If you get unexpected characters appearing in your rendered document,
21
24
  # check this.
22
25
  #
23
26
  # If the current font is a built-in one, although the string must be
24
27
  # encoded as UTF-8, only characters that are available in WinAnsi
25
28
  # are allowed.
26
29
  #
27
- # If an empty box is rendered to your PDF instead of the character you
30
+ # If an empty box is rendered to your PDF instead of the character you
28
31
  # wanted it usually means the current font doesn't include that character.
29
32
  #
30
33
  # == Options (default values marked in [])
@@ -67,7 +70,7 @@ module Prawn
67
70
  # <tt>:valign</tt>::
68
71
  # <tt>:top</tt>, <tt>:center</tt>, or <tt>:bottom</tt>. Vertical
69
72
  # alignment within the bounding box [:top]
70
- #
73
+ #
71
74
  # <tt>:rotate</tt>::
72
75
  # <tt>number</tt>. The angle to rotate the text
73
76
  # <tt>:rotate_around</tt>::
@@ -105,11 +108,24 @@ module Prawn
105
108
  # any text
106
109
  #
107
110
  def text_box(string, options={})
108
- Text::Box.new(string, options.merge(:document => self)).render
111
+ options = options.dup
112
+ options[:document] = self
113
+
114
+ box = if p = options.delete(:inline_format)
115
+ p = [] unless p.is_a?(Array)
116
+ array = self.text_formatter.format(string, *p)
117
+ Text::Formatted::Box.new(array, options)
118
+ else
119
+ Text::Box.new(string, options)
120
+ end
121
+
122
+ box.render
109
123
  end
110
124
 
125
+ # @group Experimental API
126
+
111
127
  # Generally, one would use the Prawn::Text#text_box convenience
112
- # method. However, using Text::Box.new in conjunction with
128
+ # method. However, using Text::Box.new in conjunction with
113
129
  # #render(:dry_run=> true) enables one to do look-ahead calculations prior
114
130
  # to placing text on the page, or to determine how much vertical space was
115
131
  # consumed by the printed text