prawn-git 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +10 -0
  3. data/COPYING +2 -0
  4. data/GPLv2 +340 -0
  5. data/GPLv3 +674 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE +56 -0
  8. data/Rakefile +55 -0
  9. data/data/fonts/Courier-Bold.afm +342 -0
  10. data/data/fonts/Courier-BoldOblique.afm +342 -0
  11. data/data/fonts/Courier-Oblique.afm +342 -0
  12. data/data/fonts/Courier.afm +342 -0
  13. data/data/fonts/Helvetica-Bold.afm +2827 -0
  14. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  15. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  16. data/data/fonts/Helvetica.afm +3051 -0
  17. data/data/fonts/MustRead.html +19 -0
  18. data/data/fonts/Symbol.afm +213 -0
  19. data/data/fonts/Times-Bold.afm +2588 -0
  20. data/data/fonts/Times-BoldItalic.afm +2384 -0
  21. data/data/fonts/Times-Italic.afm +2667 -0
  22. data/data/fonts/Times-Roman.afm +2419 -0
  23. data/data/fonts/ZapfDingbats.afm +225 -0
  24. data/data/images/16bit.alpha +0 -0
  25. data/data/images/16bit.color +0 -0
  26. data/data/images/16bit.png +0 -0
  27. data/data/images/arrow.png +0 -0
  28. data/data/images/arrow2.png +0 -0
  29. data/data/images/dice.alpha +0 -0
  30. data/data/images/dice.color +0 -0
  31. data/data/images/dice.png +0 -0
  32. data/data/images/dice_interlaced.png +0 -0
  33. data/data/images/fractal.jpg +0 -0
  34. data/data/images/indexed_color.dat +0 -0
  35. data/data/images/indexed_color.png +0 -0
  36. data/data/images/letterhead.jpg +0 -0
  37. data/data/images/license.md +8 -0
  38. data/data/images/page_white_text.alpha +0 -0
  39. data/data/images/page_white_text.color +0 -0
  40. data/data/images/page_white_text.png +0 -0
  41. data/data/images/pal_bk.png +0 -0
  42. data/data/images/pigs.jpg +0 -0
  43. data/data/images/prawn.png +0 -0
  44. data/data/images/ruport.png +0 -0
  45. data/data/images/ruport_data.dat +0 -0
  46. data/data/images/ruport_transparent.png +0 -0
  47. data/data/images/ruport_type0.png +0 -0
  48. data/data/images/stef.jpg +0 -0
  49. data/data/images/tru256.bmp +0 -0
  50. data/data/images/web-links.dat +1 -0
  51. data/data/images/web-links.png +0 -0
  52. data/data/pdfs/complex_template.pdf +0 -0
  53. data/data/pdfs/contains_ttf_font.pdf +0 -0
  54. data/data/pdfs/encrypted.pdf +0 -0
  55. data/data/pdfs/form.pdf +820 -0
  56. data/data/pdfs/hexagon.pdf +61 -0
  57. data/data/pdfs/indirect_reference.pdf +86 -0
  58. data/data/pdfs/multipage_template.pdf +127 -0
  59. data/data/pdfs/nested_pages.pdf +118 -0
  60. data/data/pdfs/page_without_mediabox.pdf +193 -0
  61. data/data/pdfs/resources_as_indirect_object.pdf +83 -0
  62. data/data/pdfs/two_hexagons.pdf +90 -0
  63. data/data/pdfs/version_1_6.pdf +61 -0
  64. data/data/shift_jis_text.txt +1 -0
  65. data/lib/prawn.rb +89 -0
  66. data/lib/prawn/document.rb +706 -0
  67. data/lib/prawn/document/bounding_box.rb +539 -0
  68. data/lib/prawn/document/column_box.rb +144 -0
  69. data/lib/prawn/document/internals.rb +58 -0
  70. data/lib/prawn/document/span.rb +57 -0
  71. data/lib/prawn/encoding.rb +87 -0
  72. data/lib/prawn/errors.rb +80 -0
  73. data/lib/prawn/font.rb +413 -0
  74. data/lib/prawn/font/afm.rb +256 -0
  75. data/lib/prawn/font/dfont.rb +43 -0
  76. data/lib/prawn/font/ttf.rb +355 -0
  77. data/lib/prawn/font_metric_cache.rb +46 -0
  78. data/lib/prawn/graphics.rb +646 -0
  79. data/lib/prawn/graphics/cap_style.rb +47 -0
  80. data/lib/prawn/graphics/color.rb +232 -0
  81. data/lib/prawn/graphics/dash.rb +109 -0
  82. data/lib/prawn/graphics/join_style.rb +49 -0
  83. data/lib/prawn/graphics/patterns.rb +126 -0
  84. data/lib/prawn/graphics/transformation.rb +157 -0
  85. data/lib/prawn/graphics/transparency.rb +101 -0
  86. data/lib/prawn/grid.rb +279 -0
  87. data/lib/prawn/image_handler.rb +44 -0
  88. data/lib/prawn/images.rb +199 -0
  89. data/lib/prawn/images/image.rb +49 -0
  90. data/lib/prawn/images/jpg.rb +91 -0
  91. data/lib/prawn/images/png.rb +290 -0
  92. data/lib/prawn/measurement_extensions.rb +50 -0
  93. data/lib/prawn/measurements.rb +77 -0
  94. data/lib/prawn/outline.rb +289 -0
  95. data/lib/prawn/repeater.rb +124 -0
  96. data/lib/prawn/security.rb +288 -0
  97. data/lib/prawn/security/arcfour.rb +54 -0
  98. data/lib/prawn/soft_mask.rb +94 -0
  99. data/lib/prawn/stamp.rb +136 -0
  100. data/lib/prawn/text.rb +437 -0
  101. data/lib/prawn/text/box.rb +141 -0
  102. data/lib/prawn/text/formatted.rb +7 -0
  103. data/lib/prawn/text/formatted/arranger.rb +290 -0
  104. data/lib/prawn/text/formatted/box.rb +614 -0
  105. data/lib/prawn/text/formatted/fragment.rb +264 -0
  106. data/lib/prawn/text/formatted/line_wrap.rb +277 -0
  107. data/lib/prawn/text/formatted/parser.rb +224 -0
  108. data/lib/prawn/text/formatted/wrap.rb +160 -0
  109. data/lib/prawn/utilities.rb +46 -0
  110. data/lib/prawn/version.rb +5 -0
  111. data/lib/prawn/view.rb +91 -0
  112. data/manual/absolute_position.pdf +0 -0
  113. data/manual/basic_concepts/adding_pages.rb +27 -0
  114. data/manual/basic_concepts/basic_concepts.rb +36 -0
  115. data/manual/basic_concepts/creation.rb +39 -0
  116. data/manual/basic_concepts/cursor.rb +33 -0
  117. data/manual/basic_concepts/measurement.rb +25 -0
  118. data/manual/basic_concepts/origin.rb +38 -0
  119. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  120. data/manual/basic_concepts/view.rb +42 -0
  121. data/manual/bounding_box/bounding_box.rb +39 -0
  122. data/manual/bounding_box/bounds.rb +49 -0
  123. data/manual/bounding_box/canvas.rb +24 -0
  124. data/manual/bounding_box/creation.rb +23 -0
  125. data/manual/bounding_box/indentation.rb +46 -0
  126. data/manual/bounding_box/nesting.rb +45 -0
  127. data/manual/bounding_box/russian_boxes.rb +40 -0
  128. data/manual/bounding_box/stretchy.rb +31 -0
  129. data/manual/contents.rb +29 -0
  130. data/manual/cover.rb +39 -0
  131. data/manual/document_and_page_options/background.rb +27 -0
  132. data/manual/document_and_page_options/document_and_page_options.rb +32 -0
  133. data/manual/document_and_page_options/metadata.rb +23 -0
  134. data/manual/document_and_page_options/page_margins.rb +38 -0
  135. data/manual/document_and_page_options/page_size.rb +34 -0
  136. data/manual/document_and_page_options/print_scaling.rb +20 -0
  137. data/manual/example_helper.rb +7 -0
  138. data/manual/graphics/circle_and_ellipse.rb +22 -0
  139. data/manual/graphics/color.rb +24 -0
  140. data/manual/graphics/common_lines.rb +30 -0
  141. data/manual/graphics/fill_and_stroke.rb +42 -0
  142. data/manual/graphics/fill_rules.rb +37 -0
  143. data/manual/graphics/gradients.rb +37 -0
  144. data/manual/graphics/graphics.rb +58 -0
  145. data/manual/graphics/helper.rb +24 -0
  146. data/manual/graphics/line_width.rb +35 -0
  147. data/manual/graphics/lines_and_curves.rb +41 -0
  148. data/manual/graphics/polygon.rb +29 -0
  149. data/manual/graphics/rectangle.rb +21 -0
  150. data/manual/graphics/rotate.rb +28 -0
  151. data/manual/graphics/scale.rb +41 -0
  152. data/manual/graphics/soft_masks.rb +46 -0
  153. data/manual/graphics/stroke_cap.rb +31 -0
  154. data/manual/graphics/stroke_dash.rb +48 -0
  155. data/manual/graphics/stroke_join.rb +30 -0
  156. data/manual/graphics/translate.rb +29 -0
  157. data/manual/graphics/transparency.rb +35 -0
  158. data/manual/how_to_read_this_manual.rb +40 -0
  159. data/manual/images/absolute_position.rb +23 -0
  160. data/manual/images/fit.rb +21 -0
  161. data/manual/images/horizontal.rb +25 -0
  162. data/manual/images/images.rb +40 -0
  163. data/manual/images/plain_image.rb +18 -0
  164. data/manual/images/scale.rb +22 -0
  165. data/manual/images/vertical.rb +28 -0
  166. data/manual/images/width_and_height.rb +25 -0
  167. data/manual/layout/boxes.rb +27 -0
  168. data/manual/layout/content.rb +25 -0
  169. data/manual/layout/layout.rb +28 -0
  170. data/manual/layout/simple_grid.rb +23 -0
  171. data/manual/outline/add_subsection_to.rb +61 -0
  172. data/manual/outline/insert_section_after.rb +47 -0
  173. data/manual/outline/outline.rb +32 -0
  174. data/manual/outline/sections_and_pages.rb +67 -0
  175. data/manual/repeatable_content/alternate_page_numbering.rb +32 -0
  176. data/manual/repeatable_content/page_numbering.rb +54 -0
  177. data/manual/repeatable_content/repeatable_content.rb +32 -0
  178. data/manual/repeatable_content/repeater.rb +55 -0
  179. data/manual/repeatable_content/stamp.rb +41 -0
  180. data/manual/security/encryption.rb +31 -0
  181. data/manual/security/permissions.rb +38 -0
  182. data/manual/security/security.rb +28 -0
  183. data/manual/table.rb +16 -0
  184. data/manual/text/alignment.rb +44 -0
  185. data/manual/text/color.rb +24 -0
  186. data/manual/text/column_box.rb +32 -0
  187. data/manual/text/fallback_fonts.rb +37 -0
  188. data/manual/text/font.rb +41 -0
  189. data/manual/text/font_size.rb +45 -0
  190. data/manual/text/font_style.rb +23 -0
  191. data/manual/text/formatted_callbacks.rb +60 -0
  192. data/manual/text/formatted_text.rb +50 -0
  193. data/manual/text/free_flowing_text.rb +51 -0
  194. data/manual/text/inline.rb +41 -0
  195. data/manual/text/kerning_and_character_spacing.rb +39 -0
  196. data/manual/text/leading.rb +25 -0
  197. data/manual/text/line_wrapping.rb +41 -0
  198. data/manual/text/paragraph_indentation.rb +34 -0
  199. data/manual/text/positioned_text.rb +38 -0
  200. data/manual/text/registering_families.rb +48 -0
  201. data/manual/text/rendering_and_color.rb +37 -0
  202. data/manual/text/right_to_left_text.rb +47 -0
  203. data/manual/text/rotation.rb +43 -0
  204. data/manual/text/single_usage.rb +37 -0
  205. data/manual/text/text.rb +73 -0
  206. data/manual/text/text_box_excess.rb +32 -0
  207. data/manual/text/text_box_extensions.rb +45 -0
  208. data/manual/text/text_box_overflow.rb +48 -0
  209. data/manual/text/utf8.rb +28 -0
  210. data/manual/text/win_ansi_charset.rb +60 -0
  211. data/prawn.gemspec +45 -0
  212. data/spec/acceptance/png.rb +25 -0
  213. data/spec/annotations_spec.rb +74 -0
  214. data/spec/bounding_box_spec.rb +510 -0
  215. data/spec/column_box_spec.rb +65 -0
  216. data/spec/data/curves.pdf +66 -0
  217. data/spec/destinations_spec.rb +15 -0
  218. data/spec/document_spec.rb +748 -0
  219. data/spec/extensions/encoding_helpers.rb +11 -0
  220. data/spec/extensions/mocha.rb +46 -0
  221. data/spec/font_metric_cache_spec.rb +52 -0
  222. data/spec/font_spec.rb +474 -0
  223. data/spec/formatted_text_arranger_spec.rb +421 -0
  224. data/spec/formatted_text_box_spec.rb +705 -0
  225. data/spec/formatted_text_fragment_spec.rb +298 -0
  226. data/spec/graphics_spec.rb +683 -0
  227. data/spec/grid_spec.rb +96 -0
  228. data/spec/image_handler_spec.rb +54 -0
  229. data/spec/images_spec.rb +153 -0
  230. data/spec/inline_formatted_text_parser_spec.rb +564 -0
  231. data/spec/jpg_spec.rb +25 -0
  232. data/spec/line_wrap_spec.rb +367 -0
  233. data/spec/measurement_units_spec.rb +25 -0
  234. data/spec/outline_spec.rb +430 -0
  235. data/spec/png_spec.rb +245 -0
  236. data/spec/reference_spec.rb +25 -0
  237. data/spec/repeater_spec.rb +160 -0
  238. data/spec/security_spec.rb +158 -0
  239. data/spec/soft_mask_spec.rb +79 -0
  240. data/spec/span_spec.rb +44 -0
  241. data/spec/spec_helper.rb +54 -0
  242. data/spec/stamp_spec.rb +160 -0
  243. data/spec/stroke_styles_spec.rb +211 -0
  244. data/spec/text_at_spec.rb +143 -0
  245. data/spec/text_box_spec.rb +1043 -0
  246. data/spec/text_rendering_mode_spec.rb +45 -0
  247. data/spec/text_spacing_spec.rb +93 -0
  248. data/spec/text_spec.rb +557 -0
  249. data/spec/text_with_inline_formatting_spec.rb +35 -0
  250. data/spec/transparency_spec.rb +91 -0
  251. data/spec/view_spec.rb +43 -0
  252. metadata +509 -0
@@ -0,0 +1,157 @@
1
+ # encoding: utf-8
2
+ #
3
+ # transformation.rb: Implements rotate, translate, skew, scale and a generic
4
+ # transformation_matrix
5
+ #
6
+ # Copyright January 2010, Michael Witrant. All Rights Reserved.
7
+ #
8
+ # This is free software. Please see the LICENSE and COPYING files for details.
9
+
10
+ module Prawn
11
+ module Graphics
12
+ module Transformation
13
+ # @group Stable API
14
+
15
+ # Rotate the user space. If a block is not provided, then you must save
16
+ # and restore the graphics state yourself.
17
+ #
18
+ # == Options
19
+ # <tt>:origin</tt>:: <tt>[number, number]</tt>. The point around which to
20
+ # rotate. A block must be provided if using the :origin
21
+ #
22
+ # raises <tt>Prawn::Errors::BlockRequired</tt> if an :origin option is
23
+ # provided, but no block is given
24
+ #
25
+ # Example without a block:
26
+ #
27
+ # save_graphics_state
28
+ # rotate 30
29
+ # text "rotated text"
30
+ # restore_graphics_state
31
+ #
32
+ # Example with a block: rotating a rectangle around its upper-left corner
33
+ #
34
+ # x = 300
35
+ # y = 300
36
+ # width = 150
37
+ # height = 200
38
+ # angle = 30
39
+ # pdf.rotate(angle, :origin => [x, y]) do
40
+ # pdf.stroke_rectangle([x, y], width, height)
41
+ # end
42
+ #
43
+ def rotate(angle, options={}, &block)
44
+ Prawn.verify_options(:origin, options)
45
+ rad = degree_to_rad(angle)
46
+ cos = Math.cos(rad)
47
+ sin = Math.sin(rad)
48
+ if options[:origin].nil?
49
+ transformation_matrix(cos, sin, -sin, cos, 0, 0, &block)
50
+ else
51
+ raise Prawn::Errors::BlockRequired unless block_given?
52
+ x = options[:origin][0] + bounds.absolute_left
53
+ y = options[:origin][1] + bounds.absolute_bottom
54
+ x_prime = x * cos - y * sin
55
+ y_prime = x * sin + y * cos
56
+ translate(x - x_prime, y - y_prime) do
57
+ transformation_matrix(cos, sin, -sin, cos, 0, 0, &block)
58
+ end
59
+ end
60
+ end
61
+
62
+ # Translate the user space. If a block is not provided, then you must save
63
+ # and restore the graphics state yourself.
64
+ #
65
+ # Example without a block: move the text up and over 10
66
+ #
67
+ # save_graphics_state
68
+ # translate(10, 10)
69
+ # text "scaled text"
70
+ # restore_graphics_state
71
+ #
72
+ # Example with a block: draw a rectangle with its upper-left corner at
73
+ # x + 10, y + 10
74
+ #
75
+ # x = 300
76
+ # y = 300
77
+ # width = 150
78
+ # height = 200
79
+ # pdf.translate(10, 10) do
80
+ # pdf.stroke_rectangle([x, y], width, height)
81
+ # end
82
+ #
83
+ def translate(x, y, &block)
84
+ transformation_matrix(1, 0, 0, 1, x, y, &block)
85
+ end
86
+
87
+ # Scale the user space. If a block is not provided, then you must save
88
+ # and restore the graphics state yourself.
89
+ #
90
+ # == Options
91
+ # <tt>:origin</tt>:: <tt>[number, number]</tt>. The point from which to
92
+ # scale. A block must be provided if using the :origin
93
+ #
94
+ # raises <tt>Prawn::Errors::BlockRequired</tt> if an :origin option is
95
+ # provided, but no block is given
96
+ #
97
+ # Example without a block:
98
+ #
99
+ # save_graphics_state
100
+ # scale 1.5
101
+ # text "scaled text"
102
+ # restore_graphics_state
103
+ #
104
+ # Example with a block: scale a rectangle from its upper-left corner
105
+ #
106
+ # x = 300
107
+ # y = 300
108
+ # width = 150
109
+ # height = 200
110
+ # factor = 1.5
111
+ # pdf.scale(angle, :origin => [x, y]) do
112
+ # pdf.stroke_rectangle([x, y], width, height)
113
+ # end
114
+ #
115
+ def scale(factor, options={}, &block)
116
+ Prawn.verify_options(:origin, options)
117
+ if options[:origin].nil?
118
+ transformation_matrix(factor, 0, 0, factor, 0, 0, &block)
119
+ else
120
+ raise Prawn::Errors::BlockRequired unless block_given?
121
+ x = options[:origin][0] + bounds.absolute_left
122
+ y = options[:origin][1] + bounds.absolute_bottom
123
+ x_prime = factor * x
124
+ y_prime = factor * y
125
+ translate(x - x_prime, y - y_prime) do
126
+ transformation_matrix(factor, 0, 0, factor, 0, 0, &block)
127
+ end
128
+ end
129
+ end
130
+
131
+ # The following definition of skew would only work in a clearly
132
+ # predicatable manner when if the document had no margin. don't provide
133
+ # this shortcut until it behaves in a clearly understood manner
134
+ #
135
+ # def skew(a, b, &block)
136
+ # transformation_matrix(1,
137
+ # Math.tan(degree_to_rad(a)),
138
+ # Math.tan(degree_to_rad(b)),
139
+ # 1, 0, 0, &block)
140
+ # end
141
+
142
+ # Transform the user space (see notes for rotate regarding graphics state)
143
+ # Generally, one would use the rotate, scale, translate, and skew
144
+ # convenience methods instead of calling transformation_matrix directly
145
+ def transformation_matrix(a, b, c, d, e, f)
146
+ values = [a, b, c, d, e, f].map { |x| "%.5f" % x }.join(" ")
147
+ save_graphics_state if block_given?
148
+ renderer.add_content "#{values} cm"
149
+ if block_given?
150
+ yield
151
+ restore_graphics_state
152
+ end
153
+ end
154
+
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,101 @@
1
+ # encoding: utf-8
2
+ #
3
+ # transparency.rb : Implements transparency
4
+ #
5
+ # Copyright October 2009, Daniel Nelson. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+
10
+ module Prawn
11
+ module Graphics
12
+
13
+ # The Prawn::Transparency module is used to place transparent
14
+ # content on the page. It has the capacity for separate
15
+ # transparency values for stroked content and all other content.
16
+ #
17
+ # Example:
18
+ # # both the fill and stroke will be at 50% opacity
19
+ # pdf.transparent(0.5) do
20
+ # pdf.text("hello world")
21
+ # pdf.fill_and_stroke_circle([x, y], 25)
22
+ # end
23
+ #
24
+ # # the fill will be at 50% opacity, but the stroke will
25
+ # # be at 75% opacity
26
+ # pdf.transparent(0.5, 0.75) do
27
+ # pdf.text("hello world")
28
+ # pdf.fill_and_stroke_circle([x, y], 25)
29
+ # end
30
+ #
31
+ module Transparency
32
+
33
+ # @group Stable API
34
+
35
+ # Sets the <tt>opacity</tt> and <tt>stroke_opacity</tt> for all
36
+ # the content within the <tt>block</tt>
37
+ # If <tt>stroke_opacity</tt> is not provided, then it takes on
38
+ # the same value as <tt>opacity</tt>
39
+ #
40
+ # Valid ranges for both paramters are 0.0 to 1.0
41
+ #
42
+ # Example:
43
+ # # both the fill and stroke will be at 50% opacity
44
+ # pdf.transparent(0.5) do
45
+ # pdf.text("hello world")
46
+ # pdf.fill_and_stroke_circle([x, y], 25)
47
+ # end
48
+ #
49
+ # # the fill will be at 50% opacity, but the stroke will
50
+ # # be at 75% opacity
51
+ # pdf.transparent(0.5, 0.75) do
52
+ # pdf.text("hello world")
53
+ # pdf.fill_and_stroke_circle([x, y], 25)
54
+ # end
55
+ #
56
+ def transparent(opacity, stroke_opacity=opacity, &block)
57
+ renderer.min_version(1.4)
58
+
59
+ opacity = [[opacity, 0.0].max, 1.0].min
60
+ stroke_opacity = [[stroke_opacity, 0.0].max, 1.0].min
61
+
62
+ save_graphics_state
63
+ renderer.add_content "/#{opacity_dictionary_name(opacity, stroke_opacity)} gs"
64
+ yield
65
+ restore_graphics_state
66
+ end
67
+
68
+ private
69
+
70
+ def opacity_dictionary_registry
71
+ @opacity_dictionary_registry ||= {}
72
+ end
73
+
74
+ def next_opacity_dictionary_id
75
+ opacity_dictionary_registry.length + 1
76
+ end
77
+
78
+ def opacity_dictionary_name(opacity, stroke_opacity)
79
+ key = "#{opacity}_#{stroke_opacity}"
80
+
81
+ if opacity_dictionary_registry[key]
82
+ dictionary = opacity_dictionary_registry[key][:obj]
83
+ dictionary_name = opacity_dictionary_registry[key][:name]
84
+ else
85
+ dictionary = ref!(:Type => :ExtGState,
86
+ :CA => stroke_opacity,
87
+ :ca => opacity
88
+ )
89
+
90
+ dictionary_name = "Tr#{next_opacity_dictionary_id}"
91
+ opacity_dictionary_registry[key] = { :name => dictionary_name,
92
+ :obj => dictionary }
93
+ end
94
+
95
+ page.ext_gstates.merge!(dictionary_name => dictionary)
96
+ dictionary_name
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,279 @@
1
+ # encoding: utf-8
2
+
3
+ # grid.rb: Provides a basic grid layout system for Prawn
4
+ #
5
+ # Contributed by Andrew O'Brien in March 2009
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ module Prawn
10
+ class Document
11
+ # @group Experimental API
12
+
13
+ # Defines the grid system for a particular document. Takes the number of
14
+ # rows and columns and the width to use for the gutter as the
15
+ # keys :rows, :columns, :gutter, :row_gutter, :column_gutter
16
+ #
17
+ # Note that a completely new grid object is built each time define_grid()
18
+ # is called. This means that all subsequent calls to grid() will use
19
+ # the newly defined Grid object -- grids are not nestable like
20
+ # bounding boxes are.
21
+
22
+ def define_grid(options = {})
23
+ @boxes = nil
24
+ @grid = Grid.new(self, options)
25
+ end
26
+
27
+ # A method that can either be used to access a particular grid on the page
28
+ # or work with the grid system directly.
29
+ #
30
+ # @pdf.grid # Get the Grid directly
31
+ # @pdf.grid([0,1]) # Get the GridBox at [0,1]
32
+ # @pdf.grid([0,1], [1,2]) # Get a multi-box spanning from [0,1] to [1,2]
33
+ #
34
+ def grid(*args)
35
+ @boxes ||= {}
36
+ @boxes[args] ||= if args.empty?
37
+ @grid
38
+ else
39
+ g1, g2 = args
40
+ if(g1.class == Array && g2.class == Array &&
41
+ g1.length == 2 && g2.length == 2)
42
+ multi_box(single_box(*g1), single_box(*g2))
43
+ else
44
+ single_box(g1, g2)
45
+ end
46
+ end
47
+ end
48
+
49
+ # A Grid represents the entire grid system of a Page and calculates
50
+ # the column width and row height of the base box.
51
+ #
52
+ # @group Experimental API
53
+ class Grid
54
+ attr_reader :pdf, :columns, :rows, :gutter, :row_gutter, :column_gutter
55
+ def initialize(pdf, options = {}) # :nodoc:
56
+ valid_options = [:columns, :rows, :gutter, :row_gutter, :column_gutter]
57
+ Prawn.verify_options valid_options, options
58
+
59
+ @pdf = pdf
60
+ @columns = options[:columns]
61
+ @rows = options[:rows]
62
+ set_gutter(options)
63
+ end
64
+
65
+ # Calculates the base width of boxes.
66
+ def column_width
67
+ @column_width ||= subdivide(pdf.bounds.width, columns, column_gutter)
68
+ end
69
+
70
+ # Calculates the base height of boxes.
71
+ def row_height
72
+ @row_height ||= subdivide(pdf.bounds.height, rows, row_gutter)
73
+ end
74
+
75
+ # Diagnostic tool to show all of the grids. Defaults to gray.
76
+ def show_all(color = "CCCCCC")
77
+ self.rows.times do |i|
78
+ self.columns.times do |j|
79
+ pdf.grid(i,j).show(color)
80
+ end
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def subdivide(total, num, gutter)
87
+ (total.to_f - (gutter * (num - 1).to_f)) / num.to_f
88
+ end
89
+
90
+ def set_gutter(options)
91
+ if options.has_key?(:gutter)
92
+ @gutter = options[:gutter].to_f
93
+ @row_gutter, @column_gutter = @gutter, @gutter
94
+ else
95
+ @row_gutter = options[:row_gutter].to_f
96
+ @column_gutter = options[:column_gutter].to_f
97
+ @gutter = 0
98
+ end
99
+ end
100
+ end
101
+
102
+ # A Box is a class that represents a bounded area of a page.
103
+ # A Grid object has methods that allow easy access to the coordinates of
104
+ # its corners, which can be plugged into most existing prawnmethods.
105
+ #
106
+ # @group Experimental API
107
+ class GridBox
108
+ attr_reader :pdf
109
+
110
+ def initialize(pdf, i, j)
111
+ @pdf = pdf
112
+ @i = i
113
+ @j = j
114
+ end
115
+
116
+ # Mostly diagnostic method that outputs the name of a box as
117
+ # col_num, row_num
118
+ #
119
+ def name
120
+ "#{@i.to_s},#{@j.to_s}"
121
+ end
122
+
123
+ # :nodoc
124
+ def total_height
125
+ pdf.bounds.height.to_f
126
+ end
127
+
128
+ # Width of a box
129
+ def width
130
+ grid.column_width.to_f
131
+ end
132
+
133
+ # Height of a box
134
+ def height
135
+ grid.row_height.to_f
136
+ end
137
+
138
+ # Width of the gutter
139
+ def gutter
140
+ grid.gutter.to_f
141
+ end
142
+
143
+ # x-coordinate of left side
144
+ def left
145
+ @left ||= (width + grid.column_gutter) * @j.to_f
146
+ end
147
+
148
+ # x-coordinate of right side
149
+ def right
150
+ @right ||= left + width
151
+ end
152
+
153
+ # y-coordinate of the top
154
+ def top
155
+ @top ||= total_height - ((height + grid.row_gutter) * @i.to_f)
156
+ end
157
+
158
+ # y-coordinate of the bottom
159
+ def bottom
160
+ @bottom ||= top - height
161
+ end
162
+
163
+ # x,y coordinates of top left corner
164
+ def top_left
165
+ [left, top]
166
+ end
167
+
168
+ # x,y coordinates of top right corner
169
+ def top_right
170
+ [right, top]
171
+ end
172
+
173
+ # x,y coordinates of bottom left corner
174
+ def bottom_left
175
+ [left, bottom]
176
+ end
177
+
178
+ # x,y coordinates of bottom right corner
179
+ def bottom_right
180
+ [right, bottom]
181
+ end
182
+
183
+ # Creates a standard bounding box based on the grid box.
184
+ def bounding_box(&blk)
185
+ pdf.bounding_box(top_left, :width => width, :height => height, &blk)
186
+ end
187
+
188
+ # Diagnostic method
189
+ def show(grid_color = "CCCCCC")
190
+ self.bounding_box do
191
+ original_stroke_color = pdf.stroke_color
192
+
193
+ pdf.stroke_color = grid_color
194
+ pdf.text self.name
195
+ pdf.stroke_bounds
196
+
197
+ pdf.stroke_color = original_stroke_color
198
+ end
199
+ end
200
+
201
+ private
202
+ def grid
203
+ pdf.grid
204
+ end
205
+ end
206
+
207
+ # A MultiBox is specified by 2 Boxes and spans the areas between.
208
+ #
209
+ # @group Experimental API
210
+ class MultiBox < GridBox
211
+ def initialize(pdf, b1, b2)
212
+ @pdf = pdf
213
+ @bs = [b1, b2]
214
+ end
215
+
216
+ def name
217
+ @bs.map {|b| b.name}.join(":")
218
+ end
219
+
220
+ def total_height
221
+ @bs[0].total_height
222
+ end
223
+
224
+ def width
225
+ right_box.right - left_box.left
226
+ end
227
+
228
+ def height
229
+ top_box.top - bottom_box.bottom
230
+ end
231
+
232
+ def gutter
233
+ @bs[0].gutter
234
+ end
235
+
236
+ def left
237
+ left_box.left
238
+ end
239
+
240
+ def right
241
+ right_box.right
242
+ end
243
+
244
+ def top
245
+ top_box.top
246
+ end
247
+
248
+ def bottom
249
+ bottom_box.bottom
250
+ end
251
+
252
+ private
253
+ def left_box
254
+ @left_box ||= @bs.min {|a,b| a.left <=> b.left}
255
+ end
256
+
257
+ def right_box
258
+ @right_box ||= @bs.max {|a,b| a.right <=> b.right}
259
+ end
260
+
261
+ def top_box
262
+ @top_box ||= @bs.max {|a,b| a.top <=> b.top}
263
+ end
264
+
265
+ def bottom_box
266
+ @bottom_box ||= @bs.min {|a,b| a.bottom <=> b.bottom}
267
+ end
268
+ end
269
+
270
+ private
271
+ def single_box(i, j)
272
+ GridBox.new(self, i, j)
273
+ end
274
+
275
+ def multi_box(b1, b2)
276
+ MultiBox.new(self, b1, b2)
277
+ end
278
+ end
279
+ end