prawn 1.1.0 → 1.2.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 (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