prawn 1.1.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -0
  3. data/lib/prawn.rb +2 -2
  4. data/lib/prawn/text/box.rb +2 -0
  5. data/lib/prawn/text/formatted/box.rb +46 -21
  6. data/lib/prawn/text/formatted/line_wrap.rb +8 -2
  7. data/lib/prawn/text/formatted/wrap.rb +3 -1
  8. data/manual/contents.rb +1 -1
  9. data/manual/example_helper.rb +0 -1
  10. data/manual/table.rb +16 -0
  11. data/manual/text/text_box_overflow.rb +4 -0
  12. data/prawn.gemspec +2 -3
  13. data/spec/formatted_text_box_spec.rb +22 -0
  14. data/spec/line_wrap_spec.rb +4 -0
  15. data/spec/text_box_spec.rb +28 -0
  16. data/spec/text_spec.rb +19 -0
  17. metadata +6 -50
  18. data/lib/prawn/table.rb +0 -644
  19. data/lib/prawn/table/cell.rb +0 -772
  20. data/lib/prawn/table/cell/image.rb +0 -69
  21. data/lib/prawn/table/cell/in_table.rb +0 -33
  22. data/lib/prawn/table/cell/span_dummy.rb +0 -93
  23. data/lib/prawn/table/cell/subtable.rb +0 -66
  24. data/lib/prawn/table/cell/text.rb +0 -154
  25. data/lib/prawn/table/cells.rb +0 -255
  26. data/lib/prawn/table/column_width_calculator.rb +0 -182
  27. data/manual/table/basic_block.rb +0 -53
  28. data/manual/table/before_rendering_page.rb +0 -26
  29. data/manual/table/cell_border_lines.rb +0 -24
  30. data/manual/table/cell_borders_and_bg.rb +0 -31
  31. data/manual/table/cell_dimensions.rb +0 -30
  32. data/manual/table/cell_text.rb +0 -38
  33. data/manual/table/column_widths.rb +0 -30
  34. data/manual/table/content_and_subtables.rb +0 -39
  35. data/manual/table/creation.rb +0 -27
  36. data/manual/table/filtering.rb +0 -36
  37. data/manual/table/flow_and_header.rb +0 -17
  38. data/manual/table/image_cells.rb +0 -33
  39. data/manual/table/position.rb +0 -29
  40. data/manual/table/row_colors.rb +0 -20
  41. data/manual/table/span.rb +0 -30
  42. data/manual/table/style.rb +0 -22
  43. data/manual/table/table.rb +0 -52
  44. data/manual/table/width.rb +0 -27
  45. data/spec/cell_spec.rb +0 -629
  46. data/spec/table/span_dummy_spec.rb +0 -17
  47. data/spec/table_spec.rb +0 -1527
@@ -1,644 +0,0 @@
1
- # encoding: utf-8
2
- #
3
- # table.rb: Table drawing functionality.
4
- #
5
- # Copyright December 2009, Brad Ediger. All rights reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
-
9
- require_relative 'table/column_width_calculator'
10
- require_relative 'table/cell'
11
- require_relative 'table/cells'
12
- require_relative 'table/cell/in_table'
13
- require_relative 'table/cell/text'
14
- require_relative 'table/cell/subtable'
15
- require_relative 'table/cell/image'
16
- require_relative 'table/cell/span_dummy'
17
-
18
- module Prawn
19
-
20
- class Document
21
-
22
- # @group Experimental API
23
-
24
- # Set up and draw a table on this document. A block can be given, which will
25
- # be run after cell setup but before layout and drawing.
26
- #
27
- # See the documentation on Prawn::Table for details on the arguments.
28
- #
29
- def table(data, options={}, &block)
30
- t = Table.new(data, self, options, &block)
31
- t.draw
32
- t
33
- end
34
-
35
- # Set up, but do not draw, a table. Useful for creating subtables to be
36
- # inserted into another Table. Call +draw+ on the resulting Table to ink it.
37
- #
38
- # See the documentation on Prawn::Table for details on the arguments.
39
- #
40
- def make_table(data, options={}, &block)
41
- Table.new(data, self, options, &block)
42
- end
43
-
44
- end
45
-
46
- module Errors
47
- # This error is raised when table data is malformed
48
- #
49
- InvalidTableData = Class.new(StandardError)
50
-
51
- # This error is raised when an empty or nil table is rendered
52
- #
53
- EmptyTable = Class.new(StandardError)
54
- end
55
-
56
- # Next-generation table drawing for Prawn.
57
- #
58
- # = Data
59
- #
60
- # Data, for a Prawn table, is a two-dimensional array of objects that can be
61
- # converted to cells ("cellable" objects). Cellable objects can be:
62
- #
63
- # String::
64
- # Produces a text cell. This is the most common usage.
65
- # Prawn::Table::Cell::
66
- # If you have already built a Cell or have a custom subclass of Cell you
67
- # want to use in a table, you can pass through Cell objects.
68
- # Prawn::Table::
69
- # Creates a subtable (a table within a cell). You can use
70
- # Prawn::Document#make_table to create a table for use as a subtable
71
- # without immediately drawing it. See examples/table/bill.rb for a
72
- # somewhat complex use of subtables.
73
- # Array::
74
- # Creates a simple subtable. Create a Table object using make_table (see
75
- # above) if you need more control over the subtable's styling.
76
- #
77
- # = Options
78
- #
79
- # Prawn/Layout provides many options to control style and layout of your
80
- # table. These options are implemented with a uniform interface: the +:foo+
81
- # option always sets the +foo=+ accessor. See the accessor and method
82
- # documentation for full details on the options you can pass. Some
83
- # highlights:
84
- #
85
- # +cell_style+::
86
- # A hash of style options to style all cells. See the documentation on
87
- # Prawn::Table::Cell for all cell style options.
88
- # +header+::
89
- # If set to +true+, the first row will be repeated on every page. If set
90
- # to an Integer, the first +x+ rows will be repeated on every page. Row
91
- # numbering (for styling and other row-specific options) always indexes
92
- # based on your data array. Whether or not you have a header, row(n) always
93
- # refers to the nth element (starting from 0) of the +data+ array.
94
- # +column_widths+::
95
- # Sets widths for individual columns. Manually setting widths can give
96
- # better results than letting Prawn guess at them, as Prawn's algorithm
97
- # for defaulting widths is currently pretty boneheaded. If you experience
98
- # problems like weird column widths or CannotFit errors, try manually
99
- # setting widths on more columns.
100
- # +position+::
101
- # Either :left (the default), :center, :right, or a number. Specifies the
102
- # horizontal position of the table within its bounding box. If a number is
103
- # provided, it specifies the distance in points from the left edge.
104
- #
105
- # = Initializer Block
106
- #
107
- # If a block is passed to methods that initialize a table
108
- # (Prawn::Table.new, Prawn::Document#table, Prawn::Document#make_table), it
109
- # will be called after cell setup but before layout. This is a very flexible
110
- # way to specify styling and layout constraints. This code sets up a table
111
- # where the second through the fourth rows (1-3, indexed from 0) are each one
112
- # inch (72 pt) wide:
113
- #
114
- # pdf.table(data) do |table|
115
- # table.rows(1..3).width = 72
116
- # end
117
- #
118
- # As with Prawn::Document#initialize, if the block has no arguments, it will
119
- # be evaluated in the context of the object itself. The above code could be
120
- # rewritten as:
121
- #
122
- # pdf.table(data) do
123
- # rows(1..3).width = 72
124
- # end
125
- #
126
- class Table
127
-
128
- # Set up a table on the given document. Arguments:
129
- #
130
- # +data+::
131
- # A two-dimensional array of cell-like objects. See the "Data" section
132
- # above for the types of objects that can be put in a table.
133
- # +document+::
134
- # The Prawn::Document instance on which to draw the table.
135
- # +options+::
136
- # A hash of attributes and values for the table. See the "Options" block
137
- # above for details on available options.
138
- #
139
- def initialize(data, document, options={}, &block)
140
- @pdf = document
141
- @cells = make_cells(data)
142
- @header = false
143
- @epsilon = 1e-9
144
- options.each { |k, v| send("#{k}=", v) }
145
-
146
- if block
147
- block.arity < 1 ? instance_eval(&block) : block[self]
148
- end
149
-
150
- set_column_widths
151
- set_row_heights
152
- position_cells
153
- end
154
-
155
- # Number of rows in the table.
156
- #
157
- attr_reader :row_length
158
-
159
- # Number of columns in the table.
160
- #
161
- attr_reader :column_length
162
-
163
- # Manually set the width of the table.
164
- #
165
- attr_writer :width
166
-
167
- # Position (:left, :right, :center, or a number indicating distance in
168
- # points from the left edge) of the table within its parent bounds.
169
- #
170
- attr_writer :position
171
-
172
- # Returns a Prawn::Table::Cells object representing all of the cells in
173
- # this table.
174
- #
175
- attr_reader :cells
176
-
177
- # Specify a callback to be called before each page of cells is rendered.
178
- # The block is passed a Cells object containing all cells to be rendered on
179
- # that page. You can change styling of the cells in this block, but keep in
180
- # mind that the cells have already been positioned and sized.
181
- #
182
- def before_rendering_page(&block)
183
- @before_rendering_page = block
184
- end
185
-
186
- # Returns the width of the table in PDF points.
187
- #
188
- def width
189
- @width ||= [natural_width, @pdf.bounds.width].min
190
- end
191
-
192
- # Sets column widths for the table. The argument can be one of the following
193
- # types:
194
- #
195
- # +Array+::
196
- # <tt>[w0, w1, w2, ...]</tt> (specify a width for each column)
197
- # +Hash+::
198
- # <tt>{0 => w0, 1 => w1, ...}</tt> (keys are column names, values are
199
- # widths)
200
- # +Numeric+::
201
- # +72+ (sets width for all columns)
202
- #
203
- def column_widths=(widths)
204
- case widths
205
- when Array
206
- widths.each_with_index { |w, i| column(i).width = w }
207
- when Hash
208
- widths.each { |i, w| column(i).width = w }
209
- when Numeric
210
- cells.width = widths
211
- else
212
- raise ArgumentError, "cannot interpret column widths"
213
- end
214
- end
215
-
216
- # Returns the height of the table in PDF points.
217
- #
218
- def height
219
- cells.height
220
- end
221
-
222
- # If +true+, designates the first row as a header row to be repeated on
223
- # every page. If an integer, designates the number of rows to be treated
224
- # as a header Does not change row numbering -- row numbers always index
225
- # into the data array provided, with no modification.
226
- #
227
- attr_writer :header
228
-
229
- # Accepts an Array of alternating row colors to stripe the table.
230
- #
231
- attr_writer :row_colors
232
-
233
- # Sets styles for all cells.
234
- #
235
- # pdf.table(data, :cell_style => { :borders => [:left, :right] })
236
- #
237
- def cell_style=(style_hash)
238
- cells.style(style_hash)
239
- end
240
-
241
- # Allows generic stylable content. This is an alternate syntax that some
242
- # prefer to the attribute-based syntax. This code using style:
243
- #
244
- # pdf.table(data) do
245
- # style(row(0), :background_color => 'ff00ff')
246
- # style(column(0)) { |c| c.border_width += 1 }
247
- # end
248
- #
249
- # is equivalent to:
250
- #
251
- # pdf.table(data) do
252
- # row(0).style :background_color => 'ff00ff'
253
- # column(0).style { |c| c.border_width += 1 }
254
- # end
255
- #
256
- def style(stylable, style_hash={}, &block)
257
- stylable.style(style_hash, &block)
258
- end
259
-
260
- # Draws the table onto the document at the document's current y-position.
261
- #
262
- def draw
263
- with_position do
264
- # The cell y-positions are based on an infinitely long canvas. The offset
265
- # keeps track of how much we have to add to the original, theoretical
266
- # y-position to get to the actual position on the current page.
267
- offset = @pdf.y
268
-
269
- # Reference bounds are the non-stretchy bounds used to decide when to
270
- # flow to a new column / page.
271
- ref_bounds = @pdf.reference_bounds
272
-
273
- last_y = @pdf.y
274
-
275
- # Determine whether we're at the top of the current bounds (margin box or
276
- # bounding box). If we're at the top, we couldn't gain any more room by
277
- # breaking to the next page -- this means, in particular, that if the
278
- # first row is taller than the margin box, we will only move to the next
279
- # page if we're below the top. Some floating-point tolerance is added to
280
- # the calculation.
281
- #
282
- # Note that we use the actual bounds, not the reference bounds. This is
283
- # because even if we are in a stretchy bounding box, flowing to the next
284
- # page will not buy us any space if we are at the top.
285
- if @pdf.y > @pdf.bounds.height + @pdf.bounds.absolute_bottom - 0.001
286
- # we're at the top of our bounds
287
- started_new_page_at_row = 0
288
- else
289
- started_new_page_at_row = -1
290
-
291
- # If there isn't enough room left on the page to fit the first data row
292
- # (excluding the header), start the table on the next page.
293
- needed_height = row(0).height
294
- if @header
295
- if @header.is_a? Integer
296
- needed_height += row(1..@header).height
297
- else
298
- needed_height += row(1).height
299
- end
300
- end
301
- if needed_height > @pdf.y - ref_bounds.absolute_bottom
302
- @pdf.bounds.move_past_bottom
303
- offset = @pdf.y
304
- started_new_page_at_row = 0
305
- end
306
- end
307
-
308
- # Duplicate each cell of the header row into @header_row so it can be
309
- # modified in before_rendering_page callbacks.
310
- if @header
311
- @header_row = Cells.new
312
- if @header.is_a? Integer
313
- @header.times do |r|
314
- row(r).each { |cell| @header_row[cell.row, cell.column] = cell.dup }
315
- end
316
- else
317
- row(0).each { |cell| @header_row[cell.row, cell.column] = cell.dup }
318
- end
319
- end
320
-
321
- # Track cells to be drawn on this page. They will all be drawn when this
322
- # page is finished.
323
- cells_this_page = []
324
-
325
- @cells.each do |cell|
326
- if cell.height > (cell.y + offset) - ref_bounds.absolute_bottom &&
327
- cell.row > started_new_page_at_row
328
- # Ink all cells on the current page
329
- if defined?(@before_rendering_page) && @before_rendering_page
330
- c = Cells.new(cells_this_page.map { |ci, _| ci })
331
- @before_rendering_page.call(c)
332
- end
333
- if @header_row.nil? || cells_this_page.size > @header_row.size
334
- Cell.draw_cells(cells_this_page)
335
- end
336
- cells_this_page = []
337
-
338
- # start a new page or column
339
- @pdf.bounds.move_past_bottom
340
- x_offset = @pdf.bounds.left_side - @pdf.bounds.absolute_left
341
- if cell.row > 0 && @header
342
- if @header.is_a? Integer
343
- header_height = 0
344
- y_coord = @pdf.cursor
345
- @header.times do |h|
346
- additional_header_height = add_header(cells_this_page, x_offset, y_coord-header_height, cell.row-1, h)
347
- header_height += additional_header_height
348
- end
349
- else
350
- header_height = add_header(cells_this_page, x_offset, @pdf.cursor, cell.row-1)
351
- end
352
- else
353
- header_height = 0
354
- end
355
- offset = @pdf.y - cell.y - header_height
356
- started_new_page_at_row = cell.row
357
- end
358
-
359
- # Don't modify cell.x / cell.y here, as we want to reuse the original
360
- # values when re-inking the table. #draw should be able to be called
361
- # multiple times.
362
- x, y = cell.x, cell.y
363
- y += offset
364
-
365
- # Translate coordinates to the bounds we are in, since drawing is
366
- # relative to the cursor, not ref_bounds.
367
- x += @pdf.bounds.left_side - @pdf.bounds.absolute_left
368
- y -= @pdf.bounds.absolute_bottom
369
-
370
- # Set background color, if any.
371
- if defined?(@row_colors) && @row_colors && (!@header || cell.row > 0)
372
- # Ensure coloring restarts on every page (to make sure the header
373
- # and first row of a page are not colored the same way).
374
- if @header.is_a? Integer
375
- rows = @header
376
- elsif @header
377
- rows = 1
378
- else
379
- rows = 0
380
- end
381
- index = cell.row - [started_new_page_at_row, rows].max
382
-
383
- cell.background_color ||= @row_colors[index % @row_colors.length]
384
- end
385
-
386
- cells_this_page << [cell, [x, y]]
387
- last_y = y
388
- end
389
- # Draw the last page of cells
390
- if defined?(@before_rendering_page) && @before_rendering_page
391
- c = Cells.new(cells_this_page.map { |ci, _| ci })
392
- @before_rendering_page.call(c)
393
- end
394
- Cell.draw_cells(cells_this_page)
395
-
396
- @pdf.move_cursor_to(last_y - @cells.last.height)
397
- end
398
- end
399
-
400
- # Calculate and return the constrained column widths, taking into account
401
- # each cell's min_width, max_width, and any user-specified constraints on
402
- # the table or column size.
403
- #
404
- # Because the natural widths can be silly, this does not always work so well
405
- # at guessing a good size for columns that have vastly different content. If
406
- # you see weird problems like CannotFit errors or shockingly bad column
407
- # sizes, you should specify more column widths manually.
408
- #
409
- def column_widths
410
- @column_widths ||= begin
411
- if width - cells.min_width < -epsilon
412
- raise Errors::CannotFit,
413
- "Table's width was set too small to contain its contents " +
414
- "(min width #{cells.min_width}, requested #{width})"
415
- end
416
-
417
- if width - cells.max_width > epsilon
418
- raise Errors::CannotFit,
419
- "Table's width was set larger than its contents' maximum width " +
420
- "(max width #{cells.max_width}, requested #{width})"
421
- end
422
-
423
- if width - natural_width < -epsilon
424
- # Shrink the table to fit the requested width.
425
- f = (width - cells.min_width).to_f / (natural_width - cells.min_width)
426
-
427
- (0...column_length).map do |c|
428
- min, nat = column(c).min_width, natural_column_widths[c]
429
- (f * (nat - min)) + min
430
- end
431
- elsif width - natural_width > epsilon
432
- # Expand the table to fit the requested width.
433
- f = (width - cells.width).to_f / (cells.max_width - cells.width)
434
-
435
- (0...column_length).map do |c|
436
- nat, max = natural_column_widths[c], column(c).max_width
437
- (f * (max - nat)) + nat
438
- end
439
- else
440
- natural_column_widths
441
- end
442
- end
443
- end
444
-
445
- # Returns an array with the height of each row.
446
- #
447
- def row_heights
448
- @natural_row_heights ||=
449
- begin
450
- heights_by_row = Hash.new(0)
451
- cells.each do |cell|
452
- next if cell.is_a?(Cell::SpanDummy)
453
-
454
- # Split the height of row-spanned cells evenly by rows
455
- height_per_row = cell.height.to_f / cell.rowspan
456
- cell.rowspan.times do |i|
457
- heights_by_row[cell.row + i] =
458
- [heights_by_row[cell.row + i], height_per_row].max
459
- end
460
- end
461
- heights_by_row.sort_by { |row, _| row }.map { |_, h| h }
462
- end
463
- end
464
-
465
- protected
466
-
467
- # Converts the array of cellable objects given into instances of
468
- # Prawn::Table::Cell, and sets up their in-table properties so that they
469
- # know their own position in the table.
470
- #
471
- def make_cells(data)
472
- assert_proper_table_data(data)
473
-
474
- cells = Cells.new
475
-
476
- row_number = 0
477
- data.each do |row_cells|
478
- column_number = 0
479
- row_cells.each do |cell_data|
480
- # If we landed on a spanned cell (from a rowspan above), continue
481
- # until we find an empty spot.
482
- column_number += 1 until cells[row_number, column_number].nil?
483
-
484
- # Build the cell and store it in the Cells collection.
485
- cell = Cell.make(@pdf, cell_data)
486
- cells[row_number, column_number] = cell
487
-
488
- # Add dummy cells for the rest of the cells in the span group. This
489
- # allows Prawn to keep track of the horizontal and vertical space
490
- # occupied in each column and row spanned by this cell, while still
491
- # leaving the master (top left) cell in the group responsible for
492
- # drawing. Dummy cells do not put ink on the page.
493
- cell.rowspan.times do |i|
494
- cell.colspan.times do |j|
495
- next if i == 0 && j == 0
496
-
497
- # It is an error to specify spans that overlap; catch this here
498
- if cells[row_number + i, column_number + j]
499
- raise Prawn::Errors::InvalidTableSpan,
500
- "Spans overlap at row #{row_number + i}, " +
501
- "column #{column_number + j}."
502
- end
503
-
504
- dummy = Cell::SpanDummy.new(@pdf, cell)
505
- cells[row_number + i, column_number + j] = dummy
506
- cell.dummy_cells << dummy
507
- end
508
- end
509
-
510
- column_number += cell.colspan
511
- end
512
-
513
- row_number += 1
514
- end
515
-
516
- # Calculate the number of rows and columns in the table, taking into
517
- # account that some cells may span past the end of the physical cells we
518
- # have.
519
- @row_length = cells.map do |cell|
520
- cell.row + cell.rowspan
521
- end.max
522
-
523
- @column_length = cells.map do |cell|
524
- cell.column + cell.colspan
525
- end.max
526
-
527
- cells
528
- end
529
-
530
- # Add the header row(s) to the given array of cells at the given y-position.
531
- # Number the row with the given +row+ index, so that the header appears (in
532
- # any Cells built for this page) immediately prior to the first data row on
533
- # this page.
534
- #
535
- # Return the height of the header.
536
- #
537
- def add_header(page_of_cells, x_offset, y, row, row_of_header=nil)
538
- rows_to_operate_on = @header_row
539
- rows_to_operate_on = @header_row.rows(row_of_header) if row_of_header
540
- rows_to_operate_on.each do |cell|
541
- cell.row = row
542
- cell.dummy_cells.each {|c| c.row = row + c.row }
543
- page_of_cells << [cell, [cell.x + x_offset, y]]
544
- end
545
- rows_to_operate_on.height
546
- end
547
-
548
- # Raises an error if the data provided cannot be converted into a valid
549
- # table.
550
- #
551
- def assert_proper_table_data(data)
552
- if data.nil? || data.empty?
553
- raise Prawn::Errors::EmptyTable,
554
- "data must be a non-empty, non-nil, two dimensional array " +
555
- "of cell-convertible objects"
556
- end
557
-
558
- unless data.all? { |e| Array === e }
559
- raise Prawn::Errors::InvalidTableData,
560
- "data must be a two dimensional array of cellable objects"
561
- end
562
- end
563
-
564
- # Returns an array of each column's natural (unconstrained) width.
565
- #
566
- def natural_column_widths
567
- @natural_column_widths ||= ColumnWidthCalculator.new(cells).natural_widths
568
- end
569
-
570
- # Returns the "natural" (unconstrained) width of the table. This may be
571
- # extremely silly; for example, the unconstrained width of a paragraph of
572
- # text is the width it would assume if it were not wrapped at all. Could be
573
- # a mile long.
574
- #
575
- def natural_width
576
- @natural_width ||= natural_column_widths.inject(0, &:+)
577
- end
578
-
579
- # Assigns the calculated column widths to each cell. This ensures that each
580
- # cell in a column is the same width. After this method is called,
581
- # subsequent calls to column_widths and width should return the finalized
582
- # values that will be used to ink the table.
583
- #
584
- def set_column_widths
585
- column_widths.each_with_index do |w, col_num|
586
- column(col_num).width = w
587
- end
588
- end
589
-
590
- # Assigns the row heights to each cell. This ensures that every cell in a
591
- # row is the same height.
592
- #
593
- def set_row_heights
594
- row_heights.each_with_index { |h, row_num| row(row_num).height = h }
595
- end
596
-
597
- # Set each cell's position based on the widths and heights of cells
598
- # preceding it.
599
- #
600
- def position_cells
601
- # Calculate x- and y-positions as running sums of widths / heights.
602
- x_positions = column_widths.inject([0]) { |ary, x|
603
- ary << (ary.last + x); ary }[0..-2]
604
- x_positions.each_with_index { |x, i| column(i).x = x }
605
-
606
- # y-positions assume an infinitely long canvas starting at zero -- this
607
- # is corrected for in Table#draw, and page breaks are properly inserted.
608
- y_positions = row_heights.inject([0]) { |ary, y|
609
- ary << (ary.last - y); ary}[0..-2]
610
- y_positions.each_with_index { |y, i| row(i).y = y }
611
- end
612
-
613
- # Sets up a bounding box to position the table according to the specified
614
- # :position option, and yields.
615
- #
616
- def with_position
617
- x = case defined?(@position) && @position || :left
618
- when :left then return yield
619
- when :center then (@pdf.bounds.width - width) / 2.0
620
- when :right then @pdf.bounds.width - width
621
- when Numeric then @position
622
- else raise ArgumentError, "unknown position #{@position.inspect}"
623
- end
624
- dy = @pdf.bounds.absolute_top - @pdf.y
625
- final_y = nil
626
-
627
- @pdf.bounding_box([x, @pdf.bounds.top], :width => width) do
628
- @pdf.move_down dy
629
- yield
630
- final_y = @pdf.y
631
- end
632
-
633
- @pdf.y = final_y
634
- end
635
-
636
- private
637
-
638
- def epsilon
639
- @epsilon
640
- end
641
- end
642
-
643
-
644
- end