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
@@ -0,0 +1,70 @@
1
+ # encoding: utf-8
2
+
3
+ # image.rb: Table image cells.
4
+ #
5
+ # Copyright September 2010, Brad Ediger. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ module Prawn
9
+ class Table
10
+ class Cell
11
+
12
+ # A Cell that contains another table.
13
+ #
14
+ class Image < Cell
15
+
16
+ def initialize(pdf, point, options={})
17
+ @image_options = {}
18
+ super
19
+
20
+ @pdf_object, @image_info = @pdf.build_image_object(@file)
21
+ @natural_width, @natural_height = @image_info.calc_image_dimensions(
22
+ @image_options)
23
+ end
24
+
25
+ def image=(file)
26
+ @file = file
27
+ end
28
+
29
+ def scale=(s)
30
+ @image_options[:scale] = s
31
+ end
32
+
33
+ def fit=(f)
34
+ @image_options[:fit] = f
35
+ end
36
+
37
+ def image_height=(h)
38
+ @image_options[:height] = h
39
+ end
40
+
41
+ def image_width=(w)
42
+ @image_options[:width] = w
43
+ end
44
+
45
+ def position=(p)
46
+ @image_options[:position] = p
47
+ end
48
+
49
+ def vposition=(vp)
50
+ @image_options[:vposition] = vp
51
+ end
52
+
53
+ def natural_content_width
54
+ @natural_width
55
+ end
56
+
57
+ def natural_content_height
58
+ @natural_height
59
+ end
60
+
61
+ # Draw the image on the page.
62
+ #
63
+ def draw_content
64
+ @pdf.embed_image(@pdf_object, @image_info, @image_options)
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+ end
@@ -3,7 +3,7 @@
3
3
  # Accessors for using a Cell inside a Table.
4
4
 
5
5
  module Prawn
6
- class Table
6
+ class Table
7
7
  class Cell
8
8
 
9
9
  # This module extends Cell objects when they are used in a table (as
@@ -15,7 +15,7 @@ module Prawn
15
15
  # Row number (0-based).
16
16
  #
17
17
  attr_accessor :row
18
-
18
+
19
19
  # Column number (0-based).
20
20
  #
21
21
  attr_accessor :column
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+
3
+ # span_dummy.rb: Placeholder for non-master spanned cells.
4
+ #
5
+ # Copyright December 2011, Brad Ediger. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ module Prawn
9
+ class Table
10
+ class Cell
11
+
12
+ # A Cell object used to represent all but the topmost cell in a span
13
+ # group.
14
+ #
15
+ class SpanDummy < Cell
16
+ def initialize(pdf, master_cell)
17
+ super(pdf, [0, pdf.cursor])
18
+ @master_cell = master_cell
19
+ @padding = [0, 0, 0, 0]
20
+ end
21
+
22
+ # By default, a span dummy will never increase the height demand.
23
+ #
24
+ def natural_content_height
25
+ 0
26
+ end
27
+
28
+ # By default, a span dummy will never increase the width demand.
29
+ #
30
+ def natural_content_width
31
+ 0
32
+ end
33
+
34
+ def avg_spanned_min_width
35
+ @master_cell.avg_spanned_min_width
36
+ end
37
+
38
+ # Dummy cells have nothing to draw.
39
+ #
40
+ def draw_borders(pt)
41
+ end
42
+
43
+ # Dummy cells have nothing to draw.
44
+ #
45
+ def draw_bounded_content(pt)
46
+ end
47
+
48
+ def padding_right=(val)
49
+ @master_cell.padding_right = val if rightmost?
50
+ end
51
+
52
+ def padding_bottom=(val)
53
+ @master_cell.padding_bottom = val if bottommost?
54
+ end
55
+
56
+ def border_right_color=(val)
57
+ @master_cell.border_right_color = val if rightmost?
58
+ end
59
+
60
+ def border_bottom_color=(val)
61
+ @master_cell.border_bottom_color = val if bottommost?
62
+ end
63
+
64
+ def border_right_width=(val)
65
+ @master_cell.border_right_width = val if rightmost?
66
+ end
67
+
68
+ def border_bottom_width=(val)
69
+ @master_cell.border_bottom_width = val if bottommost?
70
+ end
71
+
72
+ def background_color
73
+ @master_cell.background_color
74
+ end
75
+
76
+ private
77
+
78
+ # Are we on the right border of the span?
79
+ #
80
+ def rightmost?
81
+ @column == @master_cell.column + @master_cell.colspan - 1
82
+ end
83
+
84
+ # Are we on the bottom border of the span?
85
+ #
86
+ def bottommost?
87
+ @row == @master_cell.row + @master_cell.rowspan - 1
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # encoding: utf-8
2
2
 
3
3
  # subtable.rb: Yo dawg.
4
4
  #
@@ -47,7 +47,7 @@ module Prawn
47
47
  @subtable.cells.max_width
48
48
  end
49
49
 
50
- # Proxied to subtable.
50
+ # Proxied to subtable.
51
51
  #
52
52
  def natural_content_height
53
53
  @subtable.cells.height
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # encoding: utf-8
2
2
 
3
3
  # text.rb: Text table cells.
4
4
  #
@@ -47,7 +47,7 @@ module Prawn
47
47
  # from the final width if the text is long.
48
48
  #
49
49
  def natural_content_width
50
- [styled_width_of(@content), @pdf.bounds.width].min
50
+ @natural_content_width ||= [styled_width_of(@content), @pdf.bounds.width].min
51
51
  end
52
52
 
53
53
  # Returns the natural height of this block of text, wrapped to the
@@ -55,7 +55,7 @@ module Prawn
55
55
  #
56
56
  def natural_content_height
57
57
  with_font do
58
- b = text_box(:width => content_width + FPTolerance)
58
+ b = text_box(:width => spanned_content_width + FPTolerance)
59
59
  b.render(:dry_run => true)
60
60
  b.height + b.line_gap
61
61
  end
@@ -64,11 +64,11 @@ module Prawn
64
64
  # Draws the text content into its bounding box.
65
65
  #
66
66
  def draw_content
67
- with_font do
67
+ with_font do
68
68
  @pdf.move_down((@pdf.font.line_gap + @pdf.font.descender)/2)
69
69
  with_text_color do
70
- text_box(:width => content_width + FPTolerance,
71
- :height => content_height + FPTolerance,
70
+ text_box(:width => spanned_content_width + FPTolerance,
71
+ :height => spanned_content_height + FPTolerance,
72
72
  :at => [0, @pdf.cursor]).render
73
73
  end
74
74
  end
@@ -78,9 +78,11 @@ module Prawn
78
78
  # Sets a reasonable minimum width. If the cell has any content, make
79
79
  # sure we have enough width to be at least one character wide. This is
80
80
  # a bit of a hack, but it should work well enough.
81
- min_content_width = [natural_content_width, styled_width_of("M")].min
82
- @min_width ||= padding_left + padding_right + min_content_width
83
- super
81
+ unless defined?(@min_width) && @min_width
82
+ min_content_width = [natural_content_width, styled_width_of_single_character].min
83
+ @min_width = padding_left + padding_right + min_content_width
84
+ super
85
+ end
84
86
  end
85
87
 
86
88
  protected
@@ -89,27 +91,37 @@ module Prawn
89
91
  @pdf.save_font do
90
92
  options = {}
91
93
  options[:style] = @text_options[:style] if @text_options[:style]
94
+ options[:style] ||= @pdf.font.options[:style] if @pdf.font.options[:style]
92
95
 
93
- @pdf.font(@font || @pdf.font.name, options)
96
+ @pdf.font(defined?(@font) && @font || @pdf.font.family, options)
94
97
 
95
98
  yield
96
99
  end
97
100
  end
98
101
 
99
102
  def with_text_color
100
- old_color = @pdf.fill_color || '000000'
101
- @pdf.fill_color(@text_color) if @text_color
102
- yield
103
- ensure
104
- @pdf.fill_color(old_color)
103
+ if defined?(@text_color) && @text_color
104
+ begin
105
+ old_color = @pdf.fill_color || '000000'
106
+ @pdf.fill_color(@text_color)
107
+ yield
108
+ ensure
109
+ @pdf.fill_color(old_color)
110
+ end
111
+ else
112
+ yield
113
+ end
105
114
  end
106
-
115
+
107
116
  def text_box(extra_options={})
108
- if @text_options[:inline_format]
117
+ if p = @text_options[:inline_format]
118
+ p = [] unless p.is_a?(Array)
109
119
  options = @text_options.dup
110
120
  options.delete(:inline_format)
121
+ options.merge!(extra_options)
122
+ options[:document] = @pdf
111
123
 
112
- array = ::Prawn::Text::Formatted::Parser.to_array(@content)
124
+ array = @pdf.text_formatter.format(@content, *p)
113
125
  ::Prawn::Text::Formatted::Box.new(array,
114
126
  options.merge(extra_options).merge(:document => @pdf))
115
127
  else
@@ -121,14 +133,20 @@ module Prawn
121
133
  # Returns the width of +text+ under the given text options.
122
134
  #
123
135
  def styled_width_of(text)
124
- with_font do
125
- options = {}
126
- options[:size] = @text_options[:size] if @text_options[:size]
127
-
128
- @pdf.font.compute_width_of(text, options)
129
- end
136
+ @pdf.width_of(text, @text_options)
130
137
  end
131
138
 
139
+ private
140
+
141
+ # Returns the greatest possible width of any single character
142
+ # under the given text options.
143
+ # (We use this to determine the minimum width of a table cell)
144
+ # (Although we currently determine this by measuring "M", it should really
145
+ # use whichever character is widest under the current font)
146
+ #
147
+ def styled_width_of_single_character
148
+ styled_width_of("M")
149
+ end
132
150
  end
133
151
  end
134
152
  end
@@ -9,13 +9,6 @@
9
9
  module Prawn
10
10
  class Table
11
11
 
12
- # Returns a Cells object that can be used to select and style cells. See
13
- # the Cells documentation for things you can do with cells.
14
- #
15
- def cells
16
- @cell_proxy ||= Cells.new(@cells)
17
- end
18
-
19
12
  # Selects the given rows (0-based) for styling. Returns a Cells object --
20
13
  # see the documentation on Cells for things you can do with cells.
21
14
  #
@@ -53,12 +46,21 @@ module Prawn
53
46
  # table.rows(3..4) # selects rows four and five
54
47
  #
55
48
  def rows(row_spec)
56
- index_cells unless @indexed
57
- row_spec = transform_spec(row_spec, @row_count)
58
- Cells.new(@rows[row_spec] ||= select{ |c| row_spec === c.row })
49
+ index_cells unless defined?(@indexed) && @indexed
50
+ row_spec = transform_spec(row_spec, @first_row, @row_count)
51
+ Cells.new(@rows[row_spec] ||= select { |c|
52
+ row_spec.respond_to?(:include?) ?
53
+ row_spec.include?(c.row) : row_spec === c.row })
59
54
  end
60
55
  alias_method :row, :rows
61
-
56
+
57
+ # Returns the number of rows in the list.
58
+ #
59
+ def row_count
60
+ index_cells unless defined?(@indexed) && @indexed
61
+ @row_count
62
+ end
63
+
62
64
  # Limits selection to the given column or columns. +col_spec+ can be
63
65
  # anything that responds to the === operator selecting a set of 0-based
64
66
  # column numbers; most commonly a number or a range.
@@ -67,12 +69,21 @@ module Prawn
67
69
  # table.columns(3..4) # selects columns four and five
68
70
  #
69
71
  def columns(col_spec)
70
- index_cells unless @indexed
71
- col_spec = transform_spec(col_spec, @column_count)
72
- Cells.new(@columns[col_spec] ||= select{ |c| col_spec === c.column })
72
+ index_cells unless defined?(@indexed) && @indexed
73
+ col_spec = transform_spec(col_spec, @first_column, @column_count)
74
+ Cells.new(@columns[col_spec] ||= select { |c|
75
+ col_spec.respond_to?(:include?) ?
76
+ col_spec.include?(c.column) : col_spec === c.column })
73
77
  end
74
78
  alias_method :column, :columns
75
79
 
80
+ # Returns the number of columns in the list.
81
+ #
82
+ def column_count
83
+ index_cells unless defined?(@indexed) && @indexed
84
+ @column_count
85
+ end
86
+
76
87
  # Allows you to filter the given cells by arbitrary properties.
77
88
  #
78
89
  # table.column(4).filter { |cell| cell.content =~ /Yes/ }.
@@ -84,11 +95,37 @@ module Prawn
84
95
 
85
96
  # Retrieves a cell based on its 0-based row and column. Returns an
86
97
  # individual Cell, not a Cells collection.
87
- #
98
+ #
88
99
  # table.cells[0, 0].content # => "First cell content"
89
100
  #
90
101
  def [](row, col)
91
- find { |c| c.row == row && c.column == col }
102
+ return nil if empty?
103
+ index_cells unless defined?(@indexed) && @indexed
104
+ row_array, col_array = @rows[@first_row + row] || [], @columns[@first_column + col] || []
105
+ if row_array.length < col_array.length
106
+ row_array.find { |c| c.column == @first_column + col }
107
+ else
108
+ col_array.find { |c| c.row == @first_row + row }
109
+ end
110
+ end
111
+
112
+ # Puts a cell in the collection at the given position. Internal use only.
113
+ #
114
+ def []=(row, col, cell) # :nodoc:
115
+ cell.extend(Cell::InTable)
116
+ cell.row = row
117
+ cell.column = col
118
+
119
+ if defined?(@indexed) && @indexed
120
+ (@rows[row] ||= []) << cell
121
+ (@columns[col] ||= []) << cell
122
+ @first_row = row if !@first_row || row < @first_row
123
+ @first_column = col if !@first_column || col < @first_column
124
+ @row_count = @rows.size
125
+ @column_count = @columns.size
126
+ end
127
+
128
+ self << cell
92
129
  end
93
130
 
94
131
  # Supports setting multiple properties at once.
@@ -106,51 +143,42 @@ module Prawn
106
143
  # table.cells.style { |cell| cell.border_width += 12 }
107
144
  #
108
145
  def style(options={}, &block)
109
- each { |cell| cell.style(options, &block) }
146
+ each do |cell|
147
+ next if cell.is_a?(Cell::SpanDummy)
148
+ cell.style(options, &block)
149
+ end
110
150
  end
111
151
 
112
152
  # Returns the total width of all columns in the selected set.
113
153
  #
114
154
  def width
115
- column_widths = {}
116
- each do |cell|
117
- column_widths[cell.column] =
118
- [column_widths[cell.column], cell.width].compact.max
155
+ widths = {}
156
+ each do |cell|
157
+ per_cell_width = cell.width_ignoring_span.to_f / cell.colspan
158
+ cell.colspan.times do |n|
159
+ widths[cell.column+n] = [widths[cell.column+n], per_cell_width].
160
+ compact.max
161
+ end
119
162
  end
120
- column_widths.values.inject(0) { |sum, width| sum + width }
163
+ widths.values.inject(0, &:+)
121
164
  end
122
165
 
123
166
  # Returns minimum width required to contain cells in the set.
124
167
  #
125
168
  def min_width
126
- column_min_widths = {}
127
- each do |cell|
128
- column_min_widths[cell.column] =
129
- [column_min_widths[cell.column], cell.min_width].compact.max
130
- end
131
- column_min_widths.values.inject(0) { |sum, width| sum + width }
169
+ aggregate_cell_values(:column, :avg_spanned_min_width, :max)
132
170
  end
133
171
 
134
172
  # Returns maximum width that can contain cells in the set.
135
173
  #
136
174
  def max_width
137
- column_max_widths = {}
138
- each do |cell|
139
- column_max_widths[cell.column] =
140
- [column_max_widths[cell.column], cell.max_width].compact.min
141
- end
142
- column_max_widths.values.inject(0) { |sum, width| sum + width }
175
+ aggregate_cell_values(:column, :max_width_ignoring_span, :max)
143
176
  end
144
177
 
145
178
  # Returns the total height of all rows in the selected set.
146
179
  #
147
180
  def height
148
- row_heights = {}
149
- each do |cell|
150
- row_heights[cell.row] =
151
- [row_heights[cell.row], cell.height].compact.max
152
- end
153
- row_heights.values.inject(0) { |sum, width| sum + width }
181
+ aggregate_cell_values(:row, :height_ignoring_span, :max)
154
182
  end
155
183
 
156
184
  # Supports setting arbitrary properties on a group of cells.
@@ -158,17 +186,21 @@ module Prawn
158
186
  # table.cells.row(3..6).background_color = 'cc0000'
159
187
  #
160
188
  def method_missing(id, *args, &block)
161
- each { |c| c.send(id, *args, &block) }
189
+ if id.to_s =~ /=\z/
190
+ each { |c| c.send(id, *args, &block) if c.respond_to?(id) }
191
+ else
192
+ super
193
+ end
162
194
  end
163
195
 
164
196
  protected
165
-
197
+
166
198
  # Defers indexing until rows() or columns() is actually called on the
167
199
  # Cells object. Without this, we would needlessly index the leaf nodes of
168
200
  # the object graph, the ones that are only there to be iterated over.
169
201
  #
170
202
  # Make sure to call this before using @rows or @columns.
171
- #
203
+ #
172
204
  def index_cells
173
205
  @rows = {}
174
206
  @columns = {}
@@ -181,24 +213,81 @@ module Prawn
181
213
  @columns[cell.column] << cell
182
214
  end
183
215
 
216
+ @first_row = @rows.keys.min
217
+ @first_column = @columns.keys.min
218
+
184
219
  @row_count = @rows.size
185
220
  @column_count = @columns.size
186
221
 
187
222
  @indexed = true
188
223
  end
189
224
 
225
+ # Sum up a min/max value over rows or columns in the cells selected.
226
+ # Takes the min/max (per +aggregate+) of the result of sending +meth+ to
227
+ # each cell, grouped by +row_or_column+.
228
+ #
229
+ def aggregate_cell_values(row_or_column, meth, aggregate)
230
+ values = {}
231
+
232
+ #calculate values for all cells that do not span accross multiple cells
233
+ #this ensures that we don't have a problem if the first line includes
234
+ #a cell that spans across multiple cells
235
+ each do |cell|
236
+ #don't take spanned cells
237
+ if cell.colspan == 1 and cell.class != Prawn::Table::Cell::SpanDummy
238
+ index = cell.send(row_or_column)
239
+ values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
240
+ end
241
+ end
242
+
243
+ each do |cell|
244
+ index = cell.send(row_or_column)
245
+ if cell.colspan > 1
246
+ #calculate current (old) return value before we do anything
247
+ old_sum = 0
248
+ cell.colspan.times { |i|
249
+ old_sum += values[index+i] unless values[index+i].nil?
250
+ }
251
+
252
+ #calculate future return value
253
+ new_sum = cell.send(meth) * cell.colspan
254
+
255
+ if new_sum >= old_sum
256
+ #not entirely sure why we need this line, but with it the tests pass
257
+ values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
258
+ #overwrite the old values with the new ones, but only if all entries existed
259
+ entries_exist = true
260
+ cell.colspan.times { |i| entries_exist = false if values[index+i].nil? }
261
+ cell.colspan.times { |i|
262
+ values[index+i] = cell.send(meth) if entries_exist
263
+ }
264
+ end
265
+ else
266
+ if cell.class == Prawn::Table::Cell::SpanDummy
267
+ values[index] = [values[index], cell.send(meth)].compact.send(aggregate)
268
+ end
269
+ end
270
+ end
271
+ values.values.inject(0, &:+)
272
+ end
273
+
190
274
  # Transforms +spec+, a column / row specification, into an object that
191
275
  # can be compared against a row or column number using ===. Normalizes
192
- # negative indices to be positive, given a total size of +total+.
276
+ # negative indices to be positive, given a total size of +total+. The
277
+ # first row/column is indicated by +first+; this value is considered row
278
+ # or column 0.
193
279
  #
194
- def transform_spec(spec, total)
280
+ def transform_spec(spec, first, total)
195
281
  case spec
196
282
  when Range
197
- transform_spec(spec.begin, total)..transform_spec(spec.end, total)
283
+ transform_spec(spec.begin, first, total) ..
284
+ transform_spec(spec.end, first, total)
198
285
  when Integer
199
- spec < 0 ? (total + spec) : spec
286
+ spec < 0 ? (first + total + spec) : first + spec
287
+ when Enumerable
288
+ spec.map { |x| first + x }
200
289
  else # pass through
201
- spec
290
+ raise "Don't understand spec #{spec.inspect}"
202
291
  end
203
292
  end
204
293
  end