prawn 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (282) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +2 -2
  3. data/Gemfile +18 -0
  4. data/LICENSE +1 -1
  5. data/README.md +17 -4
  6. data/Rakefile +18 -22
  7. data/data/images/indexed_color.dat +0 -0
  8. data/data/images/indexed_color.png +0 -0
  9. data/data/pdfs/nested_pages.pdf +13 -13
  10. data/lib/pdf/core.rb +35 -0
  11. data/lib/{prawn → pdf}/core/annotations.rb +6 -7
  12. data/lib/{prawn → pdf}/core/byte_string.rb +1 -1
  13. data/lib/{prawn → pdf}/core/destinations.rb +23 -23
  14. data/lib/{prawn → pdf}/core/document_state.rb +8 -8
  15. data/lib/pdf/core/filter_list.rb +51 -0
  16. data/lib/pdf/core/filters.rb +36 -0
  17. data/lib/pdf/core/graphics_state.rb +68 -0
  18. data/lib/{prawn → pdf}/core/literal_string.rb +1 -1
  19. data/lib/{prawn → pdf}/core/name_tree.rb +14 -2
  20. data/lib/{prawn → pdf}/core/object_store.rb +80 -24
  21. data/lib/pdf/core/outline.rb +315 -0
  22. data/lib/{prawn → pdf}/core/page.rb +23 -26
  23. data/lib/{prawn/document → pdf/core}/page_geometry.rb +11 -21
  24. data/lib/{prawn → pdf}/core/pdf_object.rb +48 -32
  25. data/lib/{prawn → pdf}/core/reference.rb +35 -44
  26. data/lib/pdf/core/stream.rb +98 -0
  27. data/lib/{prawn → pdf}/core/text.rb +24 -17
  28. data/lib/prawn.rb +95 -17
  29. data/lib/prawn/compatibility.rb +66 -26
  30. data/lib/prawn/document.rb +48 -30
  31. data/lib/prawn/document/bounding_box.rb +3 -3
  32. data/lib/prawn/document/column_box.rb +46 -8
  33. data/lib/prawn/document/graphics_state.rb +10 -73
  34. data/lib/prawn/document/internals.rb +24 -23
  35. data/lib/prawn/document/snapshot.rb +6 -7
  36. data/lib/prawn/document/span.rb +10 -10
  37. data/lib/prawn/encoding.rb +7 -7
  38. data/lib/prawn/errors.rb +18 -29
  39. data/lib/prawn/font.rb +64 -28
  40. data/lib/prawn/font/afm.rb +32 -74
  41. data/lib/prawn/font/dfont.rb +2 -2
  42. data/lib/prawn/font/ttf.rb +28 -57
  43. data/lib/prawn/font_metric_cache.rb +45 -0
  44. data/lib/prawn/graphics.rb +307 -41
  45. data/lib/prawn/graphics/cap_style.rb +3 -3
  46. data/lib/prawn/graphics/color.rb +12 -5
  47. data/lib/prawn/graphics/dash.rb +52 -31
  48. data/lib/prawn/graphics/join_style.rb +7 -7
  49. data/lib/prawn/graphics/patterns.rb +137 -0
  50. data/lib/prawn/graphics/transformation.rb +9 -9
  51. data/lib/prawn/graphics/transparency.rb +1 -1
  52. data/lib/prawn/image_handler.rb +30 -0
  53. data/lib/prawn/images.rb +86 -105
  54. data/lib/prawn/images/image.rb +48 -0
  55. data/lib/prawn/images/jpg.rb +14 -10
  56. data/lib/prawn/images/png.rb +50 -37
  57. data/lib/prawn/layout.rb +2 -2
  58. data/lib/prawn/layout/grid.rb +51 -51
  59. data/lib/prawn/measurement_extensions.rb +5 -5
  60. data/lib/prawn/measurements.rb +25 -21
  61. data/lib/prawn/outline.rb +4 -308
  62. data/lib/prawn/repeater.rb +8 -8
  63. data/lib/prawn/security.rb +50 -36
  64. data/lib/prawn/soft_mask.rb +94 -0
  65. data/lib/prawn/stamp.rb +3 -3
  66. data/lib/prawn/table.rb +292 -118
  67. data/lib/prawn/table/cell.rb +272 -45
  68. data/lib/prawn/table/cell/image.rb +70 -0
  69. data/lib/prawn/table/cell/in_table.rb +2 -2
  70. data/lib/prawn/table/cell/span_dummy.rb +92 -0
  71. data/lib/prawn/table/cell/subtable.rb +2 -2
  72. data/lib/prawn/table/cell/text.rb +42 -24
  73. data/lib/prawn/table/cells.rb +137 -48
  74. data/lib/prawn/text.rb +35 -23
  75. data/lib/prawn/text/box.rb +18 -5
  76. data/lib/prawn/text/formatted.rb +5 -4
  77. data/lib/prawn/text/formatted/arranger.rb +292 -0
  78. data/lib/prawn/text/formatted/box.rb +52 -13
  79. data/lib/prawn/text/formatted/fragment.rb +37 -22
  80. data/lib/prawn/text/formatted/line_wrap.rb +286 -0
  81. data/lib/prawn/text/formatted/parser.rb +14 -6
  82. data/lib/prawn/text/formatted/wrap.rb +151 -0
  83. data/lib/prawn/utilities.rb +44 -0
  84. data/manual/basic_concepts/adding_pages.rb +27 -0
  85. data/manual/basic_concepts/basic_concepts.rb +34 -0
  86. data/manual/basic_concepts/creation.rb +39 -0
  87. data/manual/basic_concepts/cursor.rb +33 -0
  88. data/manual/basic_concepts/measurement.rb +25 -0
  89. data/manual/basic_concepts/origin.rb +38 -0
  90. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  91. data/manual/bounding_box/bounding_box.rb +39 -0
  92. data/manual/bounding_box/bounds.rb +49 -0
  93. data/manual/bounding_box/canvas.rb +24 -0
  94. data/manual/bounding_box/creation.rb +23 -0
  95. data/manual/bounding_box/indentation.rb +46 -0
  96. data/manual/bounding_box/nesting.rb +45 -0
  97. data/manual/bounding_box/russian_boxes.rb +40 -0
  98. data/manual/bounding_box/stretchy.rb +31 -0
  99. data/manual/document_and_page_options/background.rb +27 -0
  100. data/manual/document_and_page_options/document_and_page_options.rb +31 -0
  101. data/manual/document_and_page_options/metadata.rb +23 -0
  102. data/manual/document_and_page_options/page_margins.rb +38 -0
  103. data/manual/document_and_page_options/page_size.rb +34 -0
  104. data/manual/example_file.rb +116 -0
  105. data/manual/example_helper.rb +411 -0
  106. data/manual/example_package.rb +53 -0
  107. data/manual/example_section.rb +46 -0
  108. data/manual/graphics/circle_and_ellipse.rb +22 -0
  109. data/manual/graphics/color.rb +24 -0
  110. data/manual/graphics/common_lines.rb +28 -0
  111. data/manual/graphics/fill_and_stroke.rb +42 -0
  112. data/manual/graphics/fill_rules.rb +37 -0
  113. data/manual/graphics/gradients.rb +37 -0
  114. data/manual/graphics/graphics.rb +58 -0
  115. data/manual/graphics/helper.rb +24 -0
  116. data/manual/graphics/line_width.rb +35 -0
  117. data/manual/graphics/lines_and_curves.rb +41 -0
  118. data/manual/graphics/polygon.rb +29 -0
  119. data/manual/graphics/rectangle.rb +21 -0
  120. data/manual/graphics/rotate.rb +28 -0
  121. data/manual/graphics/scale.rb +41 -0
  122. data/manual/graphics/soft_masks.rb +46 -0
  123. data/manual/graphics/stroke_cap.rb +31 -0
  124. data/manual/graphics/stroke_dash.rb +48 -0
  125. data/manual/graphics/stroke_join.rb +30 -0
  126. data/manual/graphics/translate.rb +29 -0
  127. data/manual/graphics/transparency.rb +35 -0
  128. data/manual/images/absolute_position.rb +23 -0
  129. data/manual/images/fit.rb +21 -0
  130. data/manual/images/horizontal.rb +25 -0
  131. data/manual/images/images.rb +40 -0
  132. data/manual/images/plain_image.rb +18 -0
  133. data/manual/images/scale.rb +22 -0
  134. data/manual/images/vertical.rb +28 -0
  135. data/manual/images/width_and_height.rb +25 -0
  136. data/manual/layout/boxes.rb +27 -0
  137. data/manual/layout/content.rb +25 -0
  138. data/manual/layout/layout.rb +28 -0
  139. data/manual/layout/simple_grid.rb +23 -0
  140. data/manual/manual/cover.rb +35 -0
  141. data/manual/manual/foreword.rb +85 -0
  142. data/manual/manual/how_to_read_this_manual.rb +41 -0
  143. data/manual/manual/manual.rb +35 -0
  144. data/manual/outline/add_subsection_to.rb +61 -0
  145. data/manual/outline/insert_section_after.rb +47 -0
  146. data/manual/outline/outline.rb +32 -0
  147. data/manual/outline/sections_and_pages.rb +67 -0
  148. data/manual/repeatable_content/page_numbering.rb +54 -0
  149. data/manual/repeatable_content/repeatable_content.rb +31 -0
  150. data/manual/repeatable_content/repeater.rb +55 -0
  151. data/manual/repeatable_content/stamp.rb +41 -0
  152. data/manual/security/encryption.rb +31 -0
  153. data/manual/security/permissions.rb +38 -0
  154. data/manual/security/security.rb +28 -0
  155. data/manual/syntax_highlight.rb +52 -0
  156. data/manual/table/basic_block.rb +53 -0
  157. data/manual/table/before_rendering_page.rb +26 -0
  158. data/manual/table/cell_border_lines.rb +24 -0
  159. data/manual/table/cell_borders_and_bg.rb +31 -0
  160. data/manual/table/cell_dimensions.rb +30 -0
  161. data/manual/table/cell_text.rb +38 -0
  162. data/manual/table/column_widths.rb +30 -0
  163. data/manual/table/content_and_subtables.rb +39 -0
  164. data/manual/table/creation.rb +27 -0
  165. data/manual/table/filtering.rb +36 -0
  166. data/manual/table/flow_and_header.rb +17 -0
  167. data/manual/table/image_cells.rb +33 -0
  168. data/manual/table/position.rb +29 -0
  169. data/manual/table/row_colors.rb +20 -0
  170. data/manual/table/span.rb +30 -0
  171. data/manual/table/style.rb +22 -0
  172. data/manual/table/table.rb +52 -0
  173. data/manual/table/width.rb +27 -0
  174. data/manual/templates/full_template.rb +25 -0
  175. data/manual/templates/page_template.rb +48 -0
  176. data/manual/templates/templates.rb +27 -0
  177. data/manual/text/alignment.rb +44 -0
  178. data/manual/text/color.rb +24 -0
  179. data/manual/text/column_box.rb +32 -0
  180. data/manual/text/fallback_fonts.rb +37 -0
  181. data/manual/text/font.rb +41 -0
  182. data/manual/text/font_size.rb +45 -0
  183. data/manual/text/font_style.rb +23 -0
  184. data/manual/text/formatted_callbacks.rb +60 -0
  185. data/manual/text/formatted_text.rb +54 -0
  186. data/manual/text/free_flowing_text.rb +51 -0
  187. data/manual/text/group.rb +29 -0
  188. data/manual/text/inline.rb +43 -0
  189. data/manual/text/kerning_and_character_spacing.rb +39 -0
  190. data/manual/text/leading.rb +25 -0
  191. data/manual/text/line_wrapping.rb +41 -0
  192. data/manual/text/paragraph_indentation.rb +26 -0
  193. data/manual/text/positioned_text.rb +38 -0
  194. data/manual/text/registering_families.rb +48 -0
  195. data/manual/text/rendering_and_color.rb +37 -0
  196. data/manual/text/right_to_left_text.rb +43 -0
  197. data/manual/text/rotation.rb +43 -0
  198. data/manual/text/single_usage.rb +37 -0
  199. data/manual/text/text.rb +75 -0
  200. data/manual/text/text_box_excess.rb +32 -0
  201. data/manual/text/text_box_extensions.rb +45 -0
  202. data/manual/text/text_box_overflow.rb +44 -0
  203. data/manual/text/utf8.rb +28 -0
  204. data/{examples/m17n → manual/text}/win_ansi_charset.rb +14 -10
  205. data/prawn.gemspec +18 -12
  206. data/spec/acceptance/png.rb +23 -0
  207. data/spec/annotations_spec.rb +16 -32
  208. data/spec/bounding_box_spec.rb +128 -15
  209. data/spec/cell_spec.rb +169 -38
  210. data/spec/column_box_spec.rb +33 -0
  211. data/spec/destinations_spec.rb +5 -5
  212. data/spec/document_spec.rb +150 -104
  213. data/spec/extensions/encoding_helpers.rb +10 -0
  214. data/spec/extensions/mocha.rb +1 -0
  215. data/spec/filters_spec.rb +34 -0
  216. data/spec/font_metric_cache_spec.rb +52 -0
  217. data/spec/font_spec.rb +183 -97
  218. data/spec/formatted_text_arranger_spec.rb +43 -43
  219. data/spec/formatted_text_box_spec.rb +30 -20
  220. data/spec/formatted_text_fragment_spec.rb +8 -8
  221. data/spec/graphics_spec.rb +158 -69
  222. data/spec/grid_spec.rb +15 -15
  223. data/spec/image_handler_spec.rb +42 -0
  224. data/spec/images_spec.rb +49 -24
  225. data/spec/inline_formatted_text_parser_spec.rb +73 -19
  226. data/spec/jpg_spec.rb +4 -4
  227. data/spec/line_wrap_spec.rb +26 -26
  228. data/spec/measurement_units_spec.rb +6 -6
  229. data/spec/name_tree_spec.rb +21 -21
  230. data/spec/object_store_spec.rb +39 -39
  231. data/spec/outline_spec.rb +93 -53
  232. data/spec/pdf_object_spec.rb +88 -86
  233. data/spec/png_spec.rb +31 -28
  234. data/spec/reference_spec.rb +32 -32
  235. data/spec/repeater_spec.rb +25 -11
  236. data/spec/security_spec.rb +44 -12
  237. data/spec/snapshot_spec.rb +8 -9
  238. data/spec/soft_mask_spec.rb +117 -0
  239. data/spec/span_spec.rb +10 -15
  240. data/spec/spec_helper.rb +25 -8
  241. data/spec/stamp_spec.rb +29 -30
  242. data/spec/stream_spec.rb +58 -0
  243. data/spec/stroke_styles_spec.rb +36 -18
  244. data/spec/table/span_dummy_spec.rb +17 -0
  245. data/spec/table_spec.rb +697 -105
  246. data/spec/template_spec.rb +108 -54
  247. data/spec/text_at_spec.rb +18 -17
  248. data/spec/text_box_spec.rb +111 -62
  249. data/spec/text_rendering_mode_spec.rb +5 -5
  250. data/spec/text_spacing_spec.rb +4 -4
  251. data/spec/text_spec.rb +57 -49
  252. data/spec/transparency_spec.rb +5 -5
  253. metadata +421 -213
  254. data/data/fonts/Action Man.dfont +0 -0
  255. data/data/fonts/Activa.ttf +0 -0
  256. data/data/fonts/Chalkboard.ttf +0 -0
  257. data/data/fonts/DejaVuSans.ttf +0 -0
  258. data/data/fonts/Dustismo_Roman.ttf +0 -0
  259. data/data/fonts/comicsans.ttf +0 -0
  260. data/data/fonts/gkai00mp.ttf +0 -0
  261. data/data/images/rails.dat +0 -0
  262. data/data/images/rails.png +0 -0
  263. data/examples/bounding_box/russian_boxes.rb +0 -37
  264. data/examples/example_helper.rb +0 -11
  265. data/examples/general/context_sensitive_headers.rb +0 -38
  266. data/examples/graphics/cmyk.rb +0 -13
  267. data/examples/graphics/gradient.rb +0 -23
  268. data/examples/graphics/png_types.rb +0 -23
  269. data/examples/graphics/remote_images.rb +0 -13
  270. data/examples/m17n/full_win_ansi_character_list.rb +0 -20
  271. data/examples/m17n/sjis.rb +0 -29
  272. data/examples/table/bill.rb +0 -54
  273. data/examples/table/header.rb +0 -15
  274. data/examples/text/font_calculations.rb +0 -92
  275. data/examples/text/hyphenation.rb +0 -45
  276. data/examples/text/indent_paragraphs.rb +0 -24
  277. data/lib/prawn/core.rb +0 -85
  278. data/lib/prawn/core/text/formatted/arranger.rb +0 -294
  279. data/lib/prawn/core/text/formatted/line_wrap.rb +0 -273
  280. data/lib/prawn/core/text/formatted/wrap.rb +0 -153
  281. data/lib/prawn/graphics/gradient.rb +0 -84
  282. data/lib/prawn/security/arcfour.rb +0 -51
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # encoding: utf-8
2
2
 
3
3
  # cell.rb: Table cell drawing.
4
4
  #
@@ -6,10 +6,11 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
+ require 'date'
9
10
  module Prawn
10
11
  class Document
11
12
 
12
- # Instantiates and draws a cell on the document.
13
+ # Instantiates and draws a cell on the document.
13
14
  #
14
15
  # cell(:content => "Hello world!", :at => [12, 34])
15
16
  #
@@ -20,9 +21,9 @@ module Prawn
20
21
  cell.draw
21
22
  cell
22
23
  end
23
-
24
- # Set up, but do not draw, a cell. Useful for creating cells with
25
- # formatting options to be inserted into a Table. Call +draw+ on the
24
+
25
+ # Set up, but do not draw, a cell. Useful for creating cells with
26
+ # formatting options to be inserted into a Table. Call +draw+ on the
26
27
  # resulting Cell to ink it.
27
28
  #
28
29
  # See the documentation on Prawn::Cell for details on the arguments.
@@ -34,7 +35,7 @@ module Prawn
34
35
  end
35
36
 
36
37
  class Table
37
-
38
+
38
39
  # A Cell is a rectangular area of the page into which content is drawn. It
39
40
  # has a framework for sizing itself and adding padding and simple styling.
40
41
  # There are several standard Cell subclasses that handle things like text,
@@ -53,20 +54,58 @@ module Prawn
53
54
  #
54
55
  attr_reader :padding
55
56
 
56
- # If provided, the minimum width that this cell will permit.
57
- #
58
- def min_width
57
+ # If provided, the minimum width that this cell in its column will permit.
58
+ #
59
+ def min_width_ignoring_span
59
60
  set_width_constraints
60
61
  @min_width
61
62
  end
62
-
63
- # If provided, the maximum width that this cell can be drawn in.
63
+
64
+ # Minimum width of the entire span group this cell controls.
64
65
  #
65
- def max_width
66
+ def min_width
67
+ return min_width_ignoring_span if @colspan == 1
68
+
69
+ # Sum up the largest min-width from each column, including myself.
70
+ min_widths = Hash.new(0)
71
+ dummy_cells.each do |cell|
72
+ min_widths[cell.column] =
73
+ [min_widths[cell.column], cell.min_width].max
74
+ end
75
+ min_widths[column] = [min_widths[column], min_width_ignoring_span].max
76
+ min_widths.values.inject(0, &:+)
77
+ end
78
+
79
+ # Min-width of the span divided by the number of columns.
80
+ #
81
+ def avg_spanned_min_width
82
+ min_width.to_f / colspan
83
+ end
84
+
85
+ # If provided, the maximum width that this cell can be drawn in, within
86
+ # its column.
87
+ #
88
+ def max_width_ignoring_span
66
89
  set_width_constraints
67
90
  @max_width
68
91
  end
69
92
 
93
+ # Maximum width of the entire span group this cell controls.
94
+ #
95
+ def max_width
96
+ return max_width_ignoring_span if @colspan == 1
97
+
98
+ # Sum the smallest max-width from each column in the group, including
99
+ # myself.
100
+ max_widths = Hash.new(0)
101
+ dummy_cells.each do |cell|
102
+ max_widths[cell.column] =
103
+ [max_widths[cell.column], cell.max_width].min
104
+ end
105
+ max_widths[column] = [max_widths[column], max_width_ignoring_span].min
106
+ max_widths.values.inject(0, &:+)
107
+ end
108
+
70
109
  # Manually specify the cell's height.
71
110
  #
72
111
  attr_writer :height
@@ -84,11 +123,15 @@ module Prawn
84
123
  #
85
124
  attr_reader :border_colors
86
125
 
126
+ # Line style
127
+ #
128
+ attr_reader :border_lines
129
+
87
130
  # Specifies the content for the cell. Must be a "cellable" object. See the
88
131
  # "Data" section of the Prawn::Table documentation for details on cellable
89
132
  # objects.
90
133
  #
91
- attr_accessor :content
134
+ attr_accessor :content
92
135
 
93
136
  # The background color, if any, for this cell. Specified in HTML RGB
94
137
  # format, e.g., "ccffff". The background is drawn under the whole cell,
@@ -96,6 +139,20 @@ module Prawn
96
139
  #
97
140
  attr_accessor :background_color
98
141
 
142
+ # Number of columns this cell spans. Defaults to 1.
143
+ #
144
+ attr_reader :colspan
145
+
146
+ # Number of rows this cell spans. Defaults to 1.
147
+ #
148
+ attr_reader :rowspan
149
+
150
+ # Array of SpanDummy cells (if any) that represent the other cells in
151
+ # this span group. They know their own width / height, but do not draw
152
+ # anything.
153
+ #
154
+ attr_reader :dummy_cells # :nodoc:
155
+
99
156
  # Instantiates a Cell based on the given options. The particular class of
100
157
  # cell returned depends on the :content argument. See the Prawn::Table
101
158
  # documentation under "Data" for allowable content types.
@@ -104,14 +161,19 @@ module Prawn
104
161
  at = options.delete(:at) || [0, pdf.cursor]
105
162
  content = content.to_s if content.nil? || content.kind_of?(Numeric) ||
106
163
  content.kind_of?(Date)
107
-
164
+
108
165
  if content.is_a?(Hash)
166
+ if content[:image]
167
+ return Cell::Image.new(pdf, at, content)
168
+ end
109
169
  options.update(content)
110
170
  content = options[:content]
111
171
  else
112
172
  options[:content] = content
113
173
  end
114
174
 
175
+ options[:content] = content = "" if content.nil?
176
+
115
177
  case content
116
178
  when Prawn::Table::Cell
117
179
  content
@@ -123,8 +185,7 @@ module Prawn
123
185
  subtable = Prawn::Table.new(options[:content], pdf, {})
124
186
  Cell::Subtable.new(pdf, at, options.merge(:content => subtable))
125
187
  else
126
- # TODO: other types of content
127
- raise ArgumentError, "Content type not recognized: #{content.inspect}"
188
+ raise Errors::UnrecognizedTableContent
128
189
  end
129
190
  end
130
191
 
@@ -151,8 +212,14 @@ module Prawn
151
212
  @borders = [:top, :bottom, :left, :right]
152
213
  @border_widths = [1] * 4
153
214
  @border_colors = ['000000'] * 4
215
+ @border_lines = [:solid] * 4
216
+ @colspan = 1
217
+ @rowspan = 1
218
+ @dummy_cells = []
154
219
 
155
220
  options.each { |k, v| send("#{k}=", v) }
221
+
222
+ @initializer_run = true
156
223
  end
157
224
 
158
225
  # Supports setting multiple properties at once.
@@ -165,19 +232,40 @@ module Prawn
165
232
  # cell.border_width = 2
166
233
  #
167
234
  def style(options={}, &block)
168
- options.each { |k, v| send("#{k}=", v) }
235
+ options.each do |k, v|
236
+ send("#{k}=", v) if respond_to?("#{k}=")
237
+ end
169
238
 
170
239
  # The block form supports running a single block for multiple cells, as
171
240
  # in Cells#style.
172
241
  block.call(self) if block
173
242
  end
174
243
 
175
- # Returns the cell's width in points, inclusive of padding.
244
+ # Returns the width of the cell in its first column alone, ignoring any
245
+ # colspans.
176
246
  #
177
- def width
247
+ def width_ignoring_span
178
248
  # We can't ||= here because the FP error accumulates on the round-trip
179
249
  # from #content_width.
180
- @width || (content_width + padding_left + padding_right)
250
+ defined?(@width) && @width || (content_width + padding_left + padding_right)
251
+ end
252
+
253
+ # Returns the cell's width in points, inclusive of padding. If the cell is
254
+ # the master cell of a colspan, returns the width of the entire span
255
+ # group.
256
+ #
257
+ def width
258
+ return width_ignoring_span if @colspan == 1 && @rowspan == 1
259
+
260
+ # We're in a span group; get the maximum width per column (including
261
+ # the master cell) and sum each column.
262
+ column_widths = Hash.new(0)
263
+ dummy_cells.each do |cell|
264
+ column_widths[cell.column] =
265
+ [column_widths[cell.column], cell.width].max
266
+ end
267
+ column_widths[column] = [column_widths[column], width_ignoring_span].max
268
+ column_widths.values.inject(0, &:+)
181
269
  end
182
270
 
183
271
  # Manually sets the cell's width, inclusive of padding.
@@ -189,47 +277,120 @@ module Prawn
189
277
  # Returns the width of the bare content in the cell, excluding padding.
190
278
  #
191
279
  def content_width
192
- if @width # manually set
280
+ if defined?(@width) && @width # manually set
193
281
  return @width - padding_left - padding_right
194
282
  end
195
283
 
196
284
  natural_content_width
197
285
  end
198
286
 
287
+ # Width of the entire span group.
288
+ #
289
+ def spanned_content_width
290
+ width - padding_left - padding_right
291
+ end
292
+
199
293
  # Returns the width this cell would naturally take on, absent other
200
294
  # constraints. Must be implemented in subclasses.
201
295
  #
202
296
  def natural_content_width
203
- raise NotImplementedError,
297
+ raise NotImplementedError,
204
298
  "subclasses must implement natural_content_width"
205
299
  end
206
300
 
207
- # Returns the cell's height in points, inclusive of padding.
301
+ # Returns the cell's height in points, inclusive of padding, in its first
302
+ # row only.
208
303
  #
209
- def height
304
+ def height_ignoring_span
210
305
  # We can't ||= here because the FP error accumulates on the round-trip
211
306
  # from #content_height.
212
- @height || (content_height + padding_top + padding_bottom)
307
+ defined?(@height) && @height || (content_height + padding_top + padding_bottom)
308
+ end
309
+
310
+ # Returns the cell's height in points, inclusive of padding. If the cell
311
+ # is the master cell of a rowspan, returns the width of the entire span
312
+ # group.
313
+ #
314
+ def height
315
+ return height_ignoring_span if @colspan == 1 && @rowspan == 1
316
+
317
+ # We're in a span group; get the maximum height per row (including the
318
+ # master cell) and sum each row.
319
+ row_heights = Hash.new(0)
320
+ dummy_cells.each do |cell|
321
+ row_heights[cell.row] = [row_heights[cell.row], cell.height].max
322
+ end
323
+ row_heights[row] = [row_heights[row], height_ignoring_span].max
324
+ row_heights.values.inject(0, &:+)
213
325
  end
214
326
 
215
327
  # Returns the height of the bare content in the cell, excluding padding.
216
328
  #
217
329
  def content_height
218
- if @height # manually set
330
+ if defined?(@height) && @height # manually set
219
331
  return @height - padding_top - padding_bottom
220
332
  end
221
-
333
+
222
334
  natural_content_height
223
335
  end
224
336
 
337
+ # Height of the entire span group.
338
+ #
339
+ def spanned_content_height
340
+ height - padding_top - padding_bottom
341
+ end
342
+
225
343
  # Returns the height this cell would naturally take on, absent
226
344
  # constraints. Must be implemented in subclasses.
227
345
  #
228
346
  def natural_content_height
229
- raise NotImplementedError,
347
+ raise NotImplementedError,
230
348
  "subclasses must implement natural_content_height"
231
349
  end
232
350
 
351
+ # Indicates the number of columns that this cell is to span. Defaults to
352
+ # 1.
353
+ #
354
+ # This must be provided as part of the table data, like so:
355
+ #
356
+ # pdf.table([["foo", {:content => "bar", :colspan => 2}]])
357
+ #
358
+ # Setting colspan from the initializer block is invalid because layout
359
+ # has already run. For example, this will NOT work:
360
+ #
361
+ # pdf.table([["foo", "bar"]]) { cells[0, 1].colspan = 2 }
362
+ #
363
+ def colspan=(span)
364
+ if defined?(@initializer_run) && @initializer_run
365
+ raise Prawn::Errors::InvalidTableSpan,
366
+ "colspan must be provided in the table's structure, never in the " +
367
+ "initialization block. See Prawn's documentation for details."
368
+ end
369
+
370
+ @colspan = span
371
+ end
372
+
373
+ # Indicates the number of rows that this cell is to span. Defaults to 1.
374
+ #
375
+ # This must be provided as part of the table data, like so:
376
+ #
377
+ # pdf.table([["foo", {:content => "bar", :rowspan => 2}], ["baz"]])
378
+ #
379
+ # Setting rowspan from the initializer block is invalid because layout
380
+ # has already run. For example, this will NOT work:
381
+ #
382
+ # pdf.table([["foo", "bar"], ["baz"]]) { cells[0, 1].rowspan = 2 }
383
+ #
384
+ def rowspan=(span)
385
+ if defined?(@initializer_run) && @initializer_run
386
+ raise Prawn::Errors::InvalidTableSpan,
387
+ "rowspan must be provided in the table's structure, never in the " +
388
+ "initialization block. See Prawn's documentation for details."
389
+ end
390
+
391
+ @rowspan = span
392
+ end
393
+
233
394
  # Draws the cell onto the document. Pass in a point [x,y] to override the
234
395
  # location at which the cell is drawn.
235
396
  #
@@ -238,7 +399,7 @@ module Prawn
238
399
  # content are all drawn in correct order so as not to overlap.
239
400
  #
240
401
  def draw(pt=[x, y])
241
- self.class.draw_cells([[self, pt]])
402
+ Prawn::Table::Cell.draw_cells([[self, pt]])
242
403
  end
243
404
 
244
405
  # Given an array of pairs [cell, pt], draws each cell at its
@@ -261,9 +422,9 @@ module Prawn
261
422
  #
262
423
  def draw_bounded_content(pt)
263
424
  @pdf.float do
264
- @pdf.bounding_box([pt[0] + padding_left, pt[1] - padding_top],
265
- :width => content_width + FPTolerance,
266
- :height => content_height + FPTolerance) do
425
+ @pdf.bounding_box([pt[0] + padding_left, pt[1] - padding_top],
426
+ :width => spanned_content_width + FPTolerance,
427
+ :height => spanned_content_height + FPTolerance) do
267
428
  draw_content
268
429
  end
269
430
  end
@@ -321,7 +482,7 @@ module Prawn
321
482
  def padding_top
322
483
  @padding[0]
323
484
  end
324
-
485
+
325
486
  def padding_top=(val)
326
487
  @padding[0] = val
327
488
  end
@@ -329,7 +490,7 @@ module Prawn
329
490
  def padding_right
330
491
  @padding[1]
331
492
  end
332
-
493
+
333
494
  def padding_right=(val)
334
495
  @padding[1] = val
335
496
  end
@@ -337,7 +498,7 @@ module Prawn
337
498
  def padding_bottom
338
499
  @padding[2]
339
500
  end
340
-
501
+
341
502
  def padding_bottom=(val)
342
503
  @padding[2] = val
343
504
  end
@@ -345,7 +506,7 @@ module Prawn
345
506
  def padding_left
346
507
  @padding[3]
347
508
  end
348
-
509
+
349
510
  def padding_left=(val)
350
511
  @padding[3] = val
351
512
  end
@@ -384,14 +545,6 @@ module Prawn
384
545
  @border_colors[0] = val
385
546
  end
386
547
 
387
- def border_top_color
388
- @border_colors[0]
389
- end
390
-
391
- def border_top_color=(val)
392
- @border_colors[0] = val
393
- end
394
-
395
548
  def border_right_color
396
549
  @border_colors[1]
397
550
  end
@@ -482,10 +635,70 @@ module Prawn
482
635
  @max_width ||= @pdf.bounds.width
483
636
  end
484
637
 
638
+ # Sets border line style on this cell. The argument can be one of:
639
+ #
640
+ # Possible values are: :solid, :dashed, :dotted
641
+ #
642
+ # * one value (sets all lines)
643
+ # * a two-element array [vertical, horizontal]
644
+ # * a three-element array [top, horizontal, bottom]
645
+ # * a four-element array [top, right, bottom, left]
646
+ #
647
+ def border_line=(line)
648
+ @border_lines = case
649
+ when line.nil?
650
+ [:solid] * 4
651
+ when line.length == 1 # all lines
652
+ [line[0]] * 4
653
+ when line.length == 2
654
+ [line[0], line[1], line[0], line[1]]
655
+ when line.length == 3
656
+ [line[0], line[1], line[2], line[1]]
657
+ when line.length == 4
658
+ [line[0], line[1], line[2], line[3]]
659
+ else
660
+ raise ArgumentError, "border_line must be one of :solid, :dashed, "
661
+ ":dotted or an array [v,h] or [t,r,b,l]"
662
+ end
663
+ end
664
+ alias_method :border_lines=, :border_line=
665
+
666
+ def border_top_line
667
+ @borders.include?(:top) ? @border_lines[0] : 0
668
+ end
669
+
670
+ def border_top_line=(val)
671
+ @border_lines[0] = val
672
+ end
673
+
674
+ def border_right_line
675
+ @borders.include?(:right) ? @border_lines[1] : 0
676
+ end
677
+
678
+ def border_right_line=(val)
679
+ @border_lines[1] = val
680
+ end
681
+
682
+ def border_bottom_line
683
+ @borders.include?(:bottom) ? @border_lines[2] : 0
684
+ end
685
+
686
+ def border_bottom_line=(val)
687
+ @border_lines[2] = val
688
+ end
689
+
690
+ def border_left_line
691
+ @borders.include?(:left) ? @border_lines[3] : 0
692
+ end
693
+
694
+ def border_left_line=(val)
695
+ @border_lines[3] = val
696
+ end
697
+
485
698
  # Draws the cell's background color.
486
699
  #
487
700
  def draw_background(pt)
488
- if @background_color
701
+ if defined?(@background_color) && @background_color
489
702
  @pdf.mask(:fill_color) do
490
703
  @pdf.fill_color @background_color
491
704
  @pdf.fill_rectangle pt, width, height
@@ -506,6 +719,7 @@ module Prawn
506
719
  idx = {:top => 0, :right => 1, :bottom => 2, :left => 3}[border]
507
720
  border_color = @border_colors[idx]
508
721
  border_width = @border_widths[idx]
722
+ border_line = @border_lines[idx]
509
723
 
510
724
  next if border_width <= 0
511
725
 
@@ -524,9 +738,22 @@ module Prawn
524
738
  [x+width, y - height - (border_bottom_width / 2.0)]]
525
739
  end
526
740
 
741
+ case border_line
742
+ when :dashed
743
+ @pdf.dash border_width * 4
744
+ when :dotted
745
+ @pdf.dash border_width, :space => border_width * 2
746
+ when :solid
747
+ # normal line style
748
+ else
749
+ raise ArgumentError, "border_line must be :solid, :dotted or" +
750
+ " :dashed"
751
+ end
752
+
527
753
  @pdf.line_width = border_width
528
754
  @pdf.stroke_color = border_color
529
755
  @pdf.stroke_line(from, to)
756
+ @pdf.undash
530
757
  end
531
758
  end
532
759
  end