prawn-core 0.5.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 (186) hide show
  1. data/COPYING +340 -0
  2. data/LICENSE +56 -0
  3. data/README +121 -0
  4. data/Rakefile +74 -0
  5. data/data/encodings/win_ansi.txt +29 -0
  6. data/data/fonts/Action Man.dfont +0 -0
  7. data/data/fonts/Activa.ttf +0 -0
  8. data/data/fonts/Chalkboard.ttf +0 -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/DejaVuSans.ttf +0 -0
  14. data/data/fonts/Dustismo_Roman.ttf +0 -0
  15. data/data/fonts/Helvetica-Bold.afm +2827 -0
  16. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  17. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  18. data/data/fonts/Helvetica.afm +3051 -0
  19. data/data/fonts/MustRead.html +19 -0
  20. data/data/fonts/Symbol.afm +213 -0
  21. data/data/fonts/Times-Bold.afm +2588 -0
  22. data/data/fonts/Times-BoldItalic.afm +2384 -0
  23. data/data/fonts/Times-Italic.afm +2667 -0
  24. data/data/fonts/Times-Roman.afm +2419 -0
  25. data/data/fonts/ZapfDingbats.afm +225 -0
  26. data/data/fonts/comicsans.ttf +0 -0
  27. data/data/fonts/gkai00mp.ttf +0 -0
  28. data/data/images/16bit.alpha +0 -0
  29. data/data/images/16bit.dat +0 -0
  30. data/data/images/16bit.png +0 -0
  31. data/data/images/arrow.png +0 -0
  32. data/data/images/arrow2.png +0 -0
  33. data/data/images/barcode_issue.png +0 -0
  34. data/data/images/dice.alpha +0 -0
  35. data/data/images/dice.dat +0 -0
  36. data/data/images/dice.png +0 -0
  37. data/data/images/dice_interlaced.png +0 -0
  38. data/data/images/fractal.jpg +0 -0
  39. data/data/images/letterhead.jpg +0 -0
  40. data/data/images/page_white_text.alpha +0 -0
  41. data/data/images/page_white_text.dat +0 -0
  42. data/data/images/page_white_text.png +0 -0
  43. data/data/images/pigs.jpg +0 -0
  44. data/data/images/rails.dat +0 -0
  45. data/data/images/rails.png +0 -0
  46. data/data/images/ruport.png +0 -0
  47. data/data/images/ruport_data.dat +0 -0
  48. data/data/images/ruport_transparent.png +0 -0
  49. data/data/images/ruport_type0.png +0 -0
  50. data/data/images/stef.jpg +0 -0
  51. data/data/images/tru256.bmp +0 -0
  52. data/data/images/web-links.dat +1 -0
  53. data/data/images/web-links.png +0 -0
  54. data/data/shift_jis_text.txt +1 -0
  55. data/examples/bounding_box/bounding_boxes.rb +44 -0
  56. data/examples/bounding_box/russian_boxes.rb +37 -0
  57. data/examples/column_box/column_box_example.rb +44 -0
  58. data/examples/general/background.rb +20 -0
  59. data/examples/general/canvas.rb +16 -0
  60. data/examples/general/measurement_units.rb +52 -0
  61. data/examples/general/metadata-info.rb +17 -0
  62. data/examples/general/multi_page_layout.rb +17 -0
  63. data/examples/general/page_geometry.rb +32 -0
  64. data/examples/graphics/basic_images.rb +24 -0
  65. data/examples/graphics/cmyk.rb +13 -0
  66. data/examples/graphics/curves.rb +12 -0
  67. data/examples/graphics/hexagon.rb +14 -0
  68. data/examples/graphics/image_fit.rb +16 -0
  69. data/examples/graphics/image_flow.rb +38 -0
  70. data/examples/graphics/image_position.rb +18 -0
  71. data/examples/graphics/line.rb +33 -0
  72. data/examples/graphics/png_types.rb +23 -0
  73. data/examples/graphics/polygons.rb +17 -0
  74. data/examples/graphics/remote_images.rb +12 -0
  75. data/examples/graphics/ruport_style_helpers.rb +20 -0
  76. data/examples/graphics/stroke_bounds.rb +21 -0
  77. data/examples/m17n/chinese_text_wrapping.rb +20 -0
  78. data/examples/m17n/euro.rb +16 -0
  79. data/examples/m17n/sjis.rb +29 -0
  80. data/examples/m17n/utf8.rb +14 -0
  81. data/examples/m17n/win_ansi_charset.rb +55 -0
  82. data/examples/text/alignment.rb +19 -0
  83. data/examples/text/dfont.rb +49 -0
  84. data/examples/text/family_based_styling.rb +25 -0
  85. data/examples/text/font_calculations.rb +92 -0
  86. data/examples/text/font_size.rb +34 -0
  87. data/examples/text/kerning.rb +31 -0
  88. data/examples/text/simple_text.rb +18 -0
  89. data/examples/text/simple_text_ttf.rb +18 -0
  90. data/examples/text/span.rb +30 -0
  91. data/examples/text/text_box.rb +26 -0
  92. data/examples/text/text_flow.rb +68 -0
  93. data/lib/prawn/compatibility.rb +38 -0
  94. data/lib/prawn/core.rb +79 -0
  95. data/lib/prawn/document.rb +399 -0
  96. data/lib/prawn/document/annotations.rb +63 -0
  97. data/lib/prawn/document/bounding_box.rb +377 -0
  98. data/lib/prawn/document/column_box.rb +89 -0
  99. data/lib/prawn/document/destinations.rb +81 -0
  100. data/lib/prawn/document/internals.rb +133 -0
  101. data/lib/prawn/document/page_geometry.rb +149 -0
  102. data/lib/prawn/document/span.rb +55 -0
  103. data/lib/prawn/document/text.rb +186 -0
  104. data/lib/prawn/document/text/box.rb +83 -0
  105. data/lib/prawn/document/text/wrapping.rb +59 -0
  106. data/lib/prawn/encoding.rb +121 -0
  107. data/lib/prawn/errors.rb +49 -0
  108. data/lib/prawn/font.rb +292 -0
  109. data/lib/prawn/font/afm.rb +202 -0
  110. data/lib/prawn/font/dfont.rb +31 -0
  111. data/lib/prawn/font/ttf.rb +327 -0
  112. data/lib/prawn/graphics.rb +257 -0
  113. data/lib/prawn/graphics/color.rb +141 -0
  114. data/lib/prawn/images.rb +339 -0
  115. data/lib/prawn/images/jpg.rb +45 -0
  116. data/lib/prawn/images/png.rb +217 -0
  117. data/lib/prawn/literal_string.rb +14 -0
  118. data/lib/prawn/measurement_extensions.rb +46 -0
  119. data/lib/prawn/measurements.rb +71 -0
  120. data/lib/prawn/name_tree.rb +165 -0
  121. data/lib/prawn/pdf_object.rb +77 -0
  122. data/lib/prawn/reference.rb +59 -0
  123. data/spec/annotations_spec.rb +90 -0
  124. data/spec/bounding_box_spec.rb +141 -0
  125. data/spec/destinations_spec.rb +15 -0
  126. data/spec/document_spec.rb +178 -0
  127. data/spec/font_spec.rb +274 -0
  128. data/spec/graphics_spec.rb +209 -0
  129. data/spec/images_spec.rb +79 -0
  130. data/spec/jpg_spec.rb +25 -0
  131. data/spec/measurement_units_spec.rb +23 -0
  132. data/spec/name_tree_spec.rb +103 -0
  133. data/spec/pdf_object_spec.rb +117 -0
  134. data/spec/png_spec.rb +236 -0
  135. data/spec/reference_spec.rb +42 -0
  136. data/spec/span_spec.rb +45 -0
  137. data/spec/spec_helper.rb +23 -0
  138. data/spec/text_box_spec.rb +83 -0
  139. data/spec/text_spec.rb +178 -0
  140. data/vendor/pdf-inspector/README +18 -0
  141. data/vendor/pdf-inspector/lib/pdf/inspector.rb +25 -0
  142. data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +80 -0
  143. data/vendor/pdf-inspector/lib/pdf/inspector/page.rb +16 -0
  144. data/vendor/pdf-inspector/lib/pdf/inspector/text.rb +31 -0
  145. data/vendor/pdf-inspector/lib/pdf/inspector/xobject.rb +19 -0
  146. data/vendor/ttfunk/data/fonts/DejaVuSans.ttf +0 -0
  147. data/vendor/ttfunk/data/fonts/comicsans.ttf +0 -0
  148. data/vendor/ttfunk/example.rb +45 -0
  149. data/vendor/ttfunk/lib/ttfunk.rb +102 -0
  150. data/vendor/ttfunk/lib/ttfunk/directory.rb +17 -0
  151. data/vendor/ttfunk/lib/ttfunk/encoding/mac_roman.rb +88 -0
  152. data/vendor/ttfunk/lib/ttfunk/encoding/windows_1252.rb +69 -0
  153. data/vendor/ttfunk/lib/ttfunk/reader.rb +44 -0
  154. data/vendor/ttfunk/lib/ttfunk/resource_file.rb +78 -0
  155. data/vendor/ttfunk/lib/ttfunk/subset.rb +18 -0
  156. data/vendor/ttfunk/lib/ttfunk/subset/base.rb +141 -0
  157. data/vendor/ttfunk/lib/ttfunk/subset/mac_roman.rb +46 -0
  158. data/vendor/ttfunk/lib/ttfunk/subset/unicode.rb +48 -0
  159. data/vendor/ttfunk/lib/ttfunk/subset/unicode_8bit.rb +63 -0
  160. data/vendor/ttfunk/lib/ttfunk/subset/windows_1252.rb +51 -0
  161. data/vendor/ttfunk/lib/ttfunk/subset_collection.rb +72 -0
  162. data/vendor/ttfunk/lib/ttfunk/table.rb +46 -0
  163. data/vendor/ttfunk/lib/ttfunk/table/cmap.rb +34 -0
  164. data/vendor/ttfunk/lib/ttfunk/table/cmap/format00.rb +54 -0
  165. data/vendor/ttfunk/lib/ttfunk/table/cmap/format04.rb +126 -0
  166. data/vendor/ttfunk/lib/ttfunk/table/cmap/subtable.rb +79 -0
  167. data/vendor/ttfunk/lib/ttfunk/table/glyf.rb +64 -0
  168. data/vendor/ttfunk/lib/ttfunk/table/glyf/compound.rb +81 -0
  169. data/vendor/ttfunk/lib/ttfunk/table/glyf/simple.rb +37 -0
  170. data/vendor/ttfunk/lib/ttfunk/table/head.rb +44 -0
  171. data/vendor/ttfunk/lib/ttfunk/table/hhea.rb +41 -0
  172. data/vendor/ttfunk/lib/ttfunk/table/hmtx.rb +47 -0
  173. data/vendor/ttfunk/lib/ttfunk/table/kern.rb +79 -0
  174. data/vendor/ttfunk/lib/ttfunk/table/kern/format0.rb +62 -0
  175. data/vendor/ttfunk/lib/ttfunk/table/loca.rb +43 -0
  176. data/vendor/ttfunk/lib/ttfunk/table/maxp.rb +40 -0
  177. data/vendor/ttfunk/lib/ttfunk/table/name.rb +119 -0
  178. data/vendor/ttfunk/lib/ttfunk/table/os2.rb +78 -0
  179. data/vendor/ttfunk/lib/ttfunk/table/post.rb +91 -0
  180. data/vendor/ttfunk/lib/ttfunk/table/post/format10.rb +43 -0
  181. data/vendor/ttfunk/lib/ttfunk/table/post/format20.rb +35 -0
  182. data/vendor/ttfunk/lib/ttfunk/table/post/format25.rb +23 -0
  183. data/vendor/ttfunk/lib/ttfunk/table/post/format30.rb +17 -0
  184. data/vendor/ttfunk/lib/ttfunk/table/post/format40.rb +17 -0
  185. data/vendor/ttfunk/lib/ttfunk/table/simple.rb +14 -0
  186. metadata +245 -0
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ # annotations.rb : Implements low-level annotation support for PDF
4
+ #
5
+ # Copyright November 2008, Jamis Buck. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ require 'prawn/literal_string'
10
+
11
+ module Prawn
12
+ class Document
13
+
14
+ # Provides very low-level support for annotations. Those who are
15
+ # interested should check out the text-format branch of sandal/prawn,
16
+ # which includes much higher level interfaces to this code currently
17
+ # being developed by Jamis Buck to be included in a Prawn release soon.
18
+ #
19
+ # Feedback is welcome!
20
+ #
21
+ module Annotations
22
+ # Adds a new annotation (section 8.4 in PDF spec) to the current page.
23
+ # +options+ must be a Hash describing the annotation.
24
+ def annotate(options)
25
+ @current_page.data[:Annots] ||= []
26
+ options = sanitize_annotation_hash(options)
27
+ @current_page.data[:Annots] << ref(options)
28
+ return options
29
+ end
30
+
31
+ # A convenience method for creating Text annotations. +rect+ must be an array
32
+ # of four numbers, describing the bounds of the annotation. +contents+ should
33
+ # be a string, to be shown when the annotation is activated.
34
+ def text_annotation(rect, contents, options={})
35
+ options = options.merge(:Subtype => :Text, :Rect => rect, :Contents => contents)
36
+ annotate(options)
37
+ end
38
+
39
+ # A convenience method for creating Link annotations. +rect+ must be an array
40
+ # of four numbers, describing the bounds of the annotation. The +options+ hash
41
+ # should include either :Dest (describing the target destination, usually as a
42
+ # string that has been recorded in the document's Dests tree), or :A (describing
43
+ # an action to perform on clicking the link), or :PA (for describing a URL to
44
+ # link to).
45
+ def link_annotation(rect, options={})
46
+ options = options.merge(:Subtype => :Link, :Rect => rect)
47
+ annotate(options)
48
+ end
49
+
50
+ private
51
+
52
+ def sanitize_annotation_hash(options)
53
+ options = options.merge(:Type => :Annot)
54
+
55
+ if options[:Dest].is_a?(String)
56
+ options[:Dest] = Prawn::LiteralString.new(options[:Dest])
57
+ end
58
+
59
+ options
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,377 @@
1
+ # encoding: utf-8
2
+
3
+ # bounding_box.rb : Implements a mechanism for shifting the coordinate space
4
+ #
5
+ # Copyright May 2008, Gregory Brown. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ module Prawn
10
+ class Document
11
+
12
+ # :call-seq:
13
+ # bounding_box(point, options={}, &block)
14
+ #
15
+ # A bounding box serves two important purposes:
16
+ # * Provide bounds for flowing text, starting at a given point
17
+ # * Translate the origin (0,0) for graphics primitives, for the purposes
18
+ # of simplifying coordinate math.
19
+ #
20
+ # ==Positioning
21
+ #
22
+ # Bounding boxes are positioned relative to their top left corner and
23
+ # the width measurement is towards the right and height measurement is
24
+ # downwards.
25
+ #
26
+ # Usage:
27
+ #
28
+ # * Bounding box 100pt x 100pt in the absolutle bottom left of the containing
29
+ # box:
30
+ #
31
+ # pdf.bounding_box([0,100], :width => 100, :height => 100)
32
+ # stroke_bounds
33
+ # end
34
+ #
35
+ # * Bounding box 200pt x 400pt high in the center of the page:
36
+ #
37
+ # x_pos = ((bounds.width / 2) - 150)
38
+ # y_pos = ((bounds.height / 2) + 200)
39
+ # pdf.bounding_box([x_pos, y_pos], :width => 300, :height => 400) do
40
+ # stroke_bounds
41
+ # end
42
+ #
43
+ # ==Flowing Text
44
+ #
45
+ # When flowing text, the usage of a bounding box is simple. Text will
46
+ # begin at the point specified, flowing the width of the bounding box.
47
+ # After the block exits, the cursor position will be moved to
48
+ # the bottom of the bounding box (y - height). If flowing text exceeds
49
+ # the height of the bounding box, the text will be continued on the next
50
+ # page, starting again at the top-left corner of the bounding box.
51
+ #
52
+ # Usage:
53
+ #
54
+ # pdf.bounding_box([100,500], :width => 100, :height => 300) do
55
+ # pdf.text "This text will flow in a very narrow box starting" +
56
+ # "from [100,500]. The pointer will then be moved to [100,200]" +
57
+ # "and return to the margin_box"
58
+ # end
59
+ #
60
+ # ==Translating Coordinates
61
+ #
62
+ # When translating coordinates, the idea is to allow the user to draw
63
+ # relative to the origin, and then translate their drawing to a specified
64
+ # area of the document, rather than adjust all their drawing coordinates
65
+ # to match this new region.
66
+ #
67
+ # Take for example two triangles which share one point, drawn from the
68
+ # origin:
69
+ #
70
+ # pdf.polygon [0,250], [0,0], [150,100]
71
+ # pdf.polygon [100,0], [150,100], [200,0]
72
+ #
73
+ # It would be easy enough to translate these triangles to another point,
74
+ # e.g [200,200]
75
+ #
76
+ # pdf.polygon [200,450], [200,200], [350,300]
77
+ # pdf.polygon [300,200], [350,300], [400,200]
78
+ #
79
+ # However, each time you want to move the drawing, you'd need to alter
80
+ # every point in the drawing calls, which as you might imagine, can become
81
+ # tedious.
82
+ #
83
+ # If instead, we think of the drawing as being bounded by a box, we can
84
+ # see that the image is 200 points wide by 250 points tall.
85
+ #
86
+ # To translate it to a new origin, we simply select a point at (x,y+height)
87
+ #
88
+ # Using the [200,200] example:
89
+ #
90
+ # pdf.bounding_box([200,450], :width => 200, :height => 250) do
91
+ # pdf.stroke do
92
+ # pdf.polygon [0,250], [0,0], [150,100]
93
+ # pdf.polygon [100,0], [150,100], [200,0]
94
+ # end
95
+ # end
96
+ #
97
+ # Notice that the drawing is still relative to the origin. If we want to
98
+ # move this drawing around the document, we simply need to recalculate the
99
+ # top-left corner of the rectangular bounding-box, and all of our graphics
100
+ # calls remain unmodified.
101
+ #
102
+ # ==Nesting Bounding Boxes
103
+ #
104
+ # By default, bounding boxes are specified relative to the document's
105
+ # margin_box (which is itself a bounding box). You can also nest bounding
106
+ # boxes, allowing you to build components which are relative to each other
107
+ #
108
+ # Usage:
109
+ #
110
+ # pdf.bounding_box([200,450], :width => 200, :height => 250) do
111
+ # pdf.stroke_bounds # Show the containing bounding box
112
+ # pdf.bounding_box([50,200], :width => 50, :height => 50) do
113
+ # # a 50x50 bounding box that starts 50 pixels left and 50 pixels down
114
+ # # the parent bounding box.
115
+ # pdf.stroke_bounds
116
+ # end
117
+ # end
118
+ #
119
+ # ==Stretchyness
120
+ #
121
+ # If you do not specify a height to a boundng box, it will become stretchy
122
+ # and it's height will be calculated according to the last drawing position
123
+ # within the bounding box:
124
+ #
125
+ # pdf.bounding_box([100,400], :width => 400) do
126
+ # pdf.text("The height of this box is #{pdf.bounds.height}")
127
+ # pdf.text('this is some text')
128
+ # pdf.text('this is some more text')
129
+ # pdf.text('and finally a bit more')
130
+ # pdf.text("Now the height of this box is #{pdf.bounds.height}")
131
+ # end
132
+ #
133
+ # ==Absolute Positioning
134
+ #
135
+ # If you wish to position the bounding boxes at absolute coordinates rather
136
+ # than relative to the margins or other bounding boxes, you can use canvas()
137
+ #
138
+ # pdf.bounding_box([50,500], :width => 200, :height => 300) do
139
+ # pdf.stroke_bounds
140
+ # pdf.canvas do
141
+ # pdf.bounding_box([300,450], :width => 200, :height => 200) do
142
+ # # Positioned outside the containing box at the 'real' (300,450)
143
+ # pdf.stroke_bounds
144
+ # end
145
+ # end
146
+ # end
147
+ #
148
+ # Of course, if you use canvas, you will be responsible for ensuring that
149
+ # you remain within the printable area of your document.
150
+ #
151
+ def bounding_box(*args, &block)
152
+ init_bounding_box(block) do |_|
153
+ translate!(args[0])
154
+ @bounding_box = BoundingBox.new(self, *args)
155
+ end
156
+ end
157
+
158
+ # A shortcut to produce a bounding box which is mapped to the document's
159
+ # absolute coordinates, regardless of how things are nested or margin sizes.
160
+ #
161
+ # pdf.canvas do
162
+ # pdf.line pdf.bounds.bottom_left, pdf.bounds.top_right
163
+ # end
164
+ #
165
+ def canvas(&block)
166
+ init_bounding_box(block, :hold_position => true) do |_|
167
+ @bounding_box = BoundingBox.new(self, [0,page_dimensions[3]],
168
+ :width => page_dimensions[2],
169
+ :height => page_dimensions[3]
170
+ )
171
+ end
172
+ end
173
+
174
+ private
175
+
176
+ def init_bounding_box(user_block, options={}, &init_block)
177
+ parent_box = @bounding_box
178
+
179
+ init_block.call(parent_box)
180
+
181
+ self.y = @bounding_box.absolute_top
182
+ user_block.call
183
+ self.y = @bounding_box.absolute_bottom unless options[:hold_position]
184
+
185
+ @bounding_box = parent_box
186
+ end
187
+
188
+ class BoundingBox
189
+
190
+ def initialize(parent, point, options={}) #:nodoc:
191
+ @parent = parent
192
+ @x, @y = point
193
+ @width, @height = options[:width], options[:height]
194
+ end
195
+
196
+ # The translated origin (x,y-height) which describes the location
197
+ # of the bottom left corner of the bounding box
198
+ #
199
+ def anchor
200
+ [@x, @y - height]
201
+ end
202
+
203
+ # Relative left x-coordinate of the bounding box. (Always 0)
204
+ #
205
+ # Example, position some text 3 pts from the left of the containing box:
206
+ #
207
+ # text('hello', :at => [(bounds.left + 3), 0])
208
+ #
209
+ def left
210
+ 0
211
+ end
212
+
213
+ # Relative right x-coordinate of the bounding box. (Equal to the box width)
214
+ #
215
+ # Example, position some text 3 pts from the right of the containing box:
216
+ #
217
+ # text('hello', :at => [(bounds.right - 3), 0])
218
+ #
219
+ def right
220
+ @width
221
+ end
222
+
223
+ # Relative top y-coordinate of the bounding box. (Equal to the box height)
224
+ #
225
+ # Example, position some text 3 pts from the top of the containing box:
226
+ #
227
+ # text('hello', :at => [0, (bounds.top - 3)])
228
+ #
229
+ def top
230
+ height
231
+ end
232
+
233
+ # Relative bottom y-coordinate of the bounding box (Always 0)
234
+ #
235
+ # Example, position some text 3 pts from the bottom of the containing box:
236
+ #
237
+ # text('hello', :at => [0, (bounds.bottom + 3)])
238
+ #
239
+ def bottom
240
+ 0
241
+ end
242
+
243
+ # Relative top-left point of the bounding_box
244
+ #
245
+ # Example, draw a line from the top left of the box diagonally to the
246
+ # bottom right:
247
+ #
248
+ # stroke do
249
+ # line(bounds., bounds.bottom_right)
250
+ # end
251
+ #
252
+ def top_left
253
+ [left,top]
254
+ end
255
+
256
+ # Relative top-right point of the bounding box
257
+ #
258
+ # Example, draw a line from the top_right of the box diagonally to the
259
+ # bottom left:
260
+ #
261
+ # stroke do
262
+ # line(bounds.top_right, bounds.bottom_left)
263
+ # end
264
+ #
265
+ def top_right
266
+ [right,top]
267
+ end
268
+
269
+ # Relative bottom-right point of the bounding box
270
+ #
271
+ # Example, draw a line along the right hand side of the page:
272
+ #
273
+ # stroke do
274
+ # line(bounds.bottom_right, bounds.top_right)
275
+ # end
276
+ #
277
+ def bottom_right
278
+ [right,bottom]
279
+ end
280
+
281
+ # Relative bottom-left point of the bounding box
282
+ #
283
+ # Example, draw a line along the left hand side of the page:
284
+ #
285
+ # stroke do
286
+ # line(bounds.bottom_left, bounds.top_left)
287
+ # end
288
+ #
289
+ def bottom_left
290
+ [left,bottom]
291
+ end
292
+
293
+ # Absolute left x-coordinate of the bounding box
294
+ #
295
+ def absolute_left
296
+ @x
297
+ end
298
+
299
+ # Absolute right x-coordinate of the bounding box
300
+ #
301
+ def absolute_right
302
+ @x + width
303
+ end
304
+
305
+ # Absolute top y-coordinate of the bounding box
306
+ #
307
+ def absolute_top
308
+ @y
309
+ end
310
+
311
+ # Absolute bottom y-coordinate of the bottom box
312
+ #
313
+ def absolute_bottom
314
+ @y - height
315
+ end
316
+
317
+ # Absolute top-left point of the bounding box
318
+ #
319
+ def absolute_top_left
320
+ [absolute_left, absolute_top]
321
+ end
322
+
323
+ # Absolute top-right point of the bounding box
324
+ #
325
+ def absolute_top_right
326
+ [absolute_right, absolute_top]
327
+ end
328
+
329
+ # Absolute bottom-left point of the bounding box
330
+ #
331
+ def absolute_bottom_left
332
+ [absolute_left, absolute_bottom]
333
+ end
334
+
335
+ # Absolute bottom-left point of the bounding box
336
+ #
337
+ def absolute_bottom_right
338
+ [absolute_right, absolute_bottom]
339
+ end
340
+
341
+ # Width of the bounding box
342
+ #
343
+ def width
344
+ @width
345
+ end
346
+
347
+ # Height of the bounding box. If the box is 'stretchy' (unspecified
348
+ # height attribute), height is calculated as the distance from the top of
349
+ # the box to the current drawing position.
350
+ #
351
+ def height
352
+ @height || absolute_top - @parent.y
353
+ end
354
+
355
+ # Returns +false+ when the box has a defined height, +true+ when the height
356
+ # is being calculated on the fly based on the current vertical position.
357
+ #
358
+ def stretchy?
359
+ !@height
360
+ end
361
+
362
+ def left_side
363
+ absolute_left
364
+ end
365
+
366
+ def right_side
367
+ absolute_right
368
+ end
369
+
370
+ def move_past_bottom
371
+ @parent.start_new_page
372
+ end
373
+
374
+ end
375
+
376
+ end
377
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+ #
3
+ # column_box.rb: Extends BoundingBox to allow for columns of text
4
+ #
5
+ # Author Paul Ostazeski.
6
+
7
+ require "prawn/document/bounding_box"
8
+
9
+ module Prawn
10
+ class Document
11
+
12
+ # A column box is a bounding box with the additional property that when
13
+ # text flows past the bottom, it will wrap first to another column on the
14
+ # same page, and only flow to the next page when all the columns are
15
+ # filled.
16
+ #
17
+ # column_box accepts the same parameters as bounding_box, as well as the
18
+ # number of :columns and a :spacer (in points) between columns.
19
+ #
20
+ # Defaults are :columns = 3 and :spacer = font_size
21
+ #
22
+ # Under PDF::Writer, "spacer" was known as "gutter"
23
+ def column_box(*args, &block)
24
+ init_column_box(block) do |_|
25
+ translate!(args[0])
26
+ @bounding_box = ColumnBox.new(self, *args)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def init_column_box(user_block, options={}, &init_block)
33
+ parent_box = @bounding_box
34
+
35
+ init_block.call(parent_box)
36
+
37
+ self.y = @bounding_box.absolute_top
38
+ user_block.call
39
+ self.y = @bounding_box.absolute_bottom unless options[:hold_position]
40
+
41
+ @bounding_box = parent_box
42
+ end
43
+
44
+ class ColumnBox < BoundingBox
45
+
46
+ def initialize(parent, point, options={})
47
+ super
48
+ @columns = options[:columns] || 3
49
+ @spacer = options[:spacer] || @parent.font_size
50
+ @current_column = 0
51
+ end
52
+
53
+ # The column width, not the width of the whole box. Used to calculate
54
+ # how long a line of text can be.
55
+ #
56
+ def width
57
+ super / @columns - @spacer
58
+ end
59
+
60
+ def width_of_column
61
+ width + @spacer
62
+ end
63
+
64
+ # x coordinate of the left edge of the current column
65
+ #
66
+ def left_side
67
+ absolute_left + (width_of_column * @current_column)
68
+ end
69
+
70
+ # x co-orordinate of the right edge of the current column
71
+ #
72
+ def right_side
73
+ columns_from_right = @columns - (1 + @current_column)
74
+ absolute_right - (width_of_column * columns_from_right)
75
+ end
76
+
77
+ # Wrap position to the next column, starting a new page if necessary
78
+ #
79
+ def move_past_bottom
80
+ @current_column = (@current_column + 1) % @columns
81
+ @parent.y = @y
82
+ if 0 == @current_column
83
+ @parent.start_new_page
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+ end