class PDF::SimpleTable
This class will create tables with a relatively simple API and internal implementation.
Constants
- VERSION
- WIDTH_FACTOR
Attributes
Makes the heading text bold if true
. Defaults to false
.
The space, in PDF
user units, on the left and right sides of each cell. Default 5 units.
An array that defines the order of the columns in the table. The values in this array are the column names in data
. The columns will be presented in the order defined here.
An array that defines columns and column options for the table. The entries should be PDF::SimpleTable::Column
objects.
An array of Hash entries. Each row is a Hash where the keys are the names of the columns as specified in column_order
and the values are the values of the cell.
The font size of the data cells, in points. Defaults to 10 points.
The number of PDF
user units to leave open at the top of a page after a page break. This is typically used for a repeating page header, etc. Defaults to zero units.
The text colour of the heading. Defaults to Color::RGB::Black.
The font size of the heading cells, in points. Defaults to 12 points.
Defines the inner line style. The default style is a solid line with a thickness of 1 unit.
The colour of the table lines. Defaults to Color::RGB::Black.
Specifies the maximum width of the table. The table will not grow larger than this width under any circumstances.
Defaults to zero, which indicates that there is no maximum width (aside from the margin size).
The minimum space between the bottom of each row and the bottom margin. If the amount of space is less than this, a new page will be started. Default is 100 PDF
user units.
Defines the outer line style. The default style is a solid line with a thickness of 1 unit.
The x
position of the table. This will be one of:
:left
-
Aligned with the left margin.
:right
-
Aligned with the right margin.
:center
-
Centered between the margins. Default.
- offset
-
The absolute position of the table, relative from the left margin.
The number of rows to hold with the heading on the page. If there are less than this number of rows on the page, then move the whole lot onto the next page. Default is one row.
The space, in PDF
user units, added to the top and bottom of each row between the text and the lines of the cell. Default 2 units.
The main row shading colour. Defaults to Color::RGB::Grey80. Used with shade_rows
of :shaded
and :striped
.
The alternate row shading colour, used with shade_rows
of :striped
. Defaults to Color::RGB::Grey70.
Defines the colour of the background shading for the heading if shade_headings
is true
. Default is Color::RGB::Grey90.
Places a background colour in the heading if true
.
Controls row shading.
:none
-
No row shading; all rows are the standard background colour.
:shaded
-
Alternate lines will be shaded; half of the rows will be the standard background colour; the rest of the rows will be shaded with
shade_color
. Default :striped
-
Alternate lines will be shaded; half of the rows will be shaded with
shade_color
; the rest of the rows will be shaded withshade_color2
.
Displays the headings for the table if true
. The default is true
.
Whether to display the lines on the table or not. Valid values are:
:none
-
Displays no lines.
:outer
-
Displays outer lines only. Default
:inner
-
Displays inner lines only.
:all
-
Displays all lines, inner and outer.
Allows a table’s rows to be split across page boundaries if true
. This defaults to false
.
The text colour of the body cells. Defaults to Color::RGB::Black.
The title to be put on the top of the table.
The text colour of the title. Defaults to Color::RGB::Black.
The font size of the title, in points. Defaults to 12 points.
The gap, in PDF
units, between the title and the table. Defaults to 5 units.
Specifies the width of the table. If the table is smaller than the provided width, columns are proportionally stretched to fit the width of the table. If the table is wider than the provided width, columns are proportionally shrunk to fit the width of the table. Content may need to wrap in this case.
Defaults to zero, which indicates that the size whould be determined automatically based on the content and the margins.
Public Class Methods
# File lib/pdf/simpletable.rb 70 def initialize 71 @column_order = [] 72 @data = [] 73 @columns = {} 74 75 @show_lines = :outer 76 @show_headings = true 77 @shade_rows = :shaded 78 @shade_color = Color::RGB::Grey80 79 @shade_color2 = Color::RGB::Grey70 80 @shade_headings = false 81 @shade_heading_color = Color::RGB::Grey90 82 @font_size = 10 83 @heading_font_size = 12 84 @title_font_size = 12 85 @title_gap = 5 86 @title_color = Color::RGB::Black 87 @heading_color = Color::RGB::Black 88 @text_color = Color::RGB::Black 89 @line_color = Color::RGB::Black 90 @position = :center 91 @orientation = :center 92 @bold_headings = false 93 94 @cols = PDF::Writer::OHash.new 95 @width = 0 96 @maximum_width = 0 97 98 @gap = 5 99 @row_gap = 2 100 @column_gap = 5 101 @header_gap = 0 102 103 @minimum_space = 0 104 @protect_rows = 1 105 @split_rows = false 106 107 @inner_line_style = PDF::Writer::StrokeStyle.new(1) 108 @outer_line_style = PDF::Writer::StrokeStyle.new(1) 109 110 yield self if block_given? 111 end
Public Instance Methods
Render the table on the PDF::Writer
document provided.
# File lib/pdf/simpletable.rb 239 def render_on(pdf) 240 if @column_order.empty? 241 raise TypeError, PDF::Writer::Lang[:simpletable_columns_undefined] 242 end 243 if @data.empty? 244 raise TypeError, PDF::Writer::Lang[:simpletable_data_empty] 245 end 246 247 low_y = descender = y0 = y1 = y = nil 248 249 @cols = PDF::Writer::OHash.new 250 @column_order.each do |name| 251 col = @columns[name] 252 if col 253 @cols[name] = col 254 else 255 @cols[name] = PDF::SimpleTable::Column.new(name) 256 end 257 end 258 259 @gap = 2 * @column_gap 260 261 max_width = __find_table_max_width__(pdf) 262 pos, t, x, adjustment_width, set_width = __find_table_positions__(pdf, max_width) 263 264 # if max_width is specified, and the table is too wide, and the width 265 # has not been set, then set the width. 266 if @width.zero? and @maximum_width.nonzero? and ((t - x) > @maximum_width) 267 @width = @maximum_width 268 end 269 270 if @width and (adjustment_width > 0) and (set_width < @width) 271 # First find the current widths of the columns involved in this 272 # mystery 273 cols0 = PDF::Writer::OHash.new 274 cols1 = PDF::Writer::OHash.new 275 276 xq = presentWidth = 0 277 last = nil 278 279 pos.each do |name, colpos| 280 if @cols[last].nil? or 281 @cols[last].width.nil? or 282 @cols[last].width <= 0 283 unless last.nil? or last.empty? 284 cols0[last] = colpos - xq - @gap 285 presentWidth += (colpos - xq - @gap) 286 end 287 else 288 cols1[last] = colpos - xq 289 end 290 last = name 291 xq = colpos 292 end 293 294 # cols0 contains the widths of all the columns which are not set 295 needed_width = @width - set_width 296 297 # If needed width is negative then add it equally to each column, 298 # else get more tricky. 299 if presentWidth < needed_width 300 diff = (needed_width - presentWidth) / cols0.size.to_f 301 cols0.each_key { |name| cols0[name] += diff } 302 else 303 cnt = 0 304 loop do 305 break if (presentWidth <= needed_width) or (cnt >= 100) 306 cnt += 1 # insurance policy 307 # Find the widest columns and the next to widest width 308 aWidest = [] 309 nWidest = widest = 0 310 cols0.each do |name, w| 311 if w > widest 312 aWidest = [ name ] 313 nWidest = widest 314 widest = w 315 elsif w == widest 316 aWidest << name 317 end 318 end 319 320 # Then figure out what the width of the widest columns would 321 # have to be to take up all the slack. 322 newWidestWidth = widest - (presentWidth - needed_width) / aWidest.size.to_f 323 if newWidestWidth > nWidest 324 aWidest.each { |name| cols0[name] = newWidestWidth } 325 presentWidth = needed_width 326 else 327 # There is no space, reduce the size of the widest ones down 328 # to the next size down, and we will go round again 329 aWidest.each { |name| cols0[name] = nWidest } 330 presentWidth -= (widest - nWidest) * aWidest.size 331 end 332 end 333 end 334 335 # cols0 now contains the new widths of the constrained columns. now 336 # need to update the pos and max_width arrays 337 xq = 0 338 pos.each do |name, colpos| 339 pos[name] = xq 340 341 if @cols[name].nil? or 342 @cols[name].width.nil? or 343 @cols[name].width <= 0 344 if not cols0[name].nil? 345 xq += cols0[name] + @gap 346 max_width[name] = cols0[name] 347 end 348 else 349 xq += cols1[name] unless cols1[name].nil? 350 end 351 end 352 353 t = x + @width 354 pos[:__last_column__] = t 355 end 356 357 # now adjust the table to the correct location across the page 358 case @position 359 when :left 360 xref = pdf.absolute_left_margin 361 when :right 362 xref = pdf.absolute_right_margin 363 when :center 364 xref = pdf.margin_x_middle 365 else 366 xref = @position 367 end 368 369 case @orientation 370 when :left 371 dx = xref - t 372 when :right 373 dx = xref 374 when :center 375 dx = xref - (t / 2.0) 376 else 377 dx = xref + @orientation 378 end 379 380 pos.each { |k, v| pos[k] = v + dx } 381 382 base_x0 = x0 = x + dx 383 base_x1 = x1 = t + dx 384 385 base_left_margin = pdf.absolute_left_margin 386 base_pos = pos.dup 387 388 # Ok, just about ready to make me a table. 389 pdf.fill_color @text_color 390 pdf.stroke_color @shade_color 391 392 middle = (x0 + x1) / 2.0 393 394 # Start a transaction. This transaction will be used to regress the 395 # table if there are not enough rows protected. 396 tg = Transaction::Simple::Group.new(pdf, self) 397 tg.start_transaction(:table) 398 moved_once = false if @protect_rows.nonzero? 399 400 abortTable = true 401 loop do # while abortTable 402 break unless abortTable 403 abortTable = false 404 405 dm = pdf.absolute_left_margin - base_left_margin 406 base_pos.each { |k, v| pos[k] = v + dm } 407 x0 = base_x0 + dm 408 x1 = base_x1 + dm 409 middle = (x0 + x1) / 2.0 410 411 # If the title is set, then render it. 412 unless @title.nil? or @title.empty? 413 w = pdf.text_width(@title, @title_font_size) 414 _y = pdf.y - pdf.font_height(@title_font_size) 415 if _y < pdf.absolute_bottom_margin 416 pdf.start_new_page 417 418 # margins may have changed on the new page 419 dm = pdf.absolute_left_margin - base_left_margin 420 base_pos.each { |k, v| pos[k] = v + dm } 421 x0 = base_x0 + dm 422 x1 = base_x1 + dm 423 middle = (x0 + x1) / 2.0 424 end 425 426 pdf.y -= pdf.font_height(@title_font_size) 427 pdf.fill_color @title_color 428 pdf.add_text(middle - w / 2.0, pdf.y, title, @title_font_size) 429 pdf.y -= @title_gap 430 end 431 432 # Margins may have changed on the new_page. 433 dm = pdf.absolute_left_margin - base_left_margin 434 base_pos.each { |k, v| pos[k] = v + dm } 435 x0 = base_x0 + dm 436 x1 = base_x1 + dm 437 middle = (x0 + x1) / 2.0 438 439 y = pdf.y # simplifies the code a bit 440 low_y = y if low_y.nil? or y < low_y 441 442 # Make the table 443 height = pdf.font_height @font_size 444 descender = pdf.font_descender @font_size 445 446 y0 = y + descender 447 dy = 0 448 449 if @show_headings 450 # This function will move the start of the table to a new page if 451 # it does not fit on this one. 452 hOID = __open_new_object__(pdf) if @shade_headings 453 pdf.fill_color @heading_color 454 _height, y = __table_column_headings__(pdf, pos, max_width, height, 455 descender, @row_gap, @heading_font_size, y) 456 pdf.fill_color @text_color 457 y0 = y + _height 458 y1 = y 459 460 if @shade_headings 461 pdf.close_object 462 pdf.fill_color! @shade_heading_color 463 pdf.rectangle(x0 - @gap / 2.0, y, x1 - x0, _height).fill 464 pdf.reopen_object(hOID) 465 pdf.close_object 466 pdf.restore_state 467 end 468 469 # Margins may have changed on the new_page 470 dm = pdf.absolute_left_margin - base_left_margin 471 base_pos.each { |k, v| pos[k] = v + dm } 472 x0 = base_x0 + dm 473 x1 = base_x1 + dm 474 middle = (x0 + x1) / 2.0 475 else 476 y1 = y0 477 end 478 479 first_line = true 480 481 # open an object here so that the text can be put in over the 482 # shading 483 tOID = __open_new_object__(pdf) unless :none == @shade_rows 484 485 cnt = 0 486 cnt = 1 unless @shade_headings 487 newPage = false 488 @data.each do |row| 489 cnt += 1 490 # Start a transaction that will be used for this row to prevent it 491 # from being split. 492 unless @split_rows 493 pageStart = pdf.pageset.size 494 495 columnStart = pdf.column_number if pdf.columns? 496 497 tg.start_transaction(:row) 498 row_orig = row 499 y_orig = y 500 y0_orig = y0 501 y1_orig = y1 502 end # unless @split_rows 503 504 ok = false 505 second_turn = false 506 loop do # while !abortTable and !ok 507 break if abortTable or ok 508 509 mx = 0 510 newRow = true 511 512 loop do # while !abortTable and (newPage or newRow) 513 break if abortTable or not (newPage or newRow) 514 515 y -= height 516 low_y = y if low_y.nil? or y < low_y 517 518 if newPage or y < (pdf.absolute_bottom_margin + @minimum_space) 519 # check that enough rows are with the heading 520 moved_once = abortTable = true if @protect_rows.nonzero? and not moved_once and cnt <= @protect_rows 521 522 y2 = y - mx + (2 * height) + descender - (newRow ? 1 : 0) * height 523 524 unless :none == @show_lines 525 y0 = y1 unless @show_headings 526 527 __table_draw_lines__(pdf, pos, @gap, x0, x1, y0, y1, y2, 528 @line_color, @inner_line_style, @outer_line_style, 529 @show_lines) 530 end 531 532 unless :none == @shade_rows 533 pdf.close_object 534 pdf.restore_state 535 end 536 537 pdf.start_new_page 538 pdf.save_state 539 540 # and the margins may have changed, this is due to the 541 # possibility of the columns being turned on as the columns are 542 # managed by manipulating the margins 543 dm = pdf.absolute_left_margin - base_left_margin 544 base_pos.each { |k, v| pos[k] = v + dm } 545 x0 = base_x0 + dm 546 x1 = base_x1 + dm 547 548 tOID = __open_new_object__(pdf) unless :none == @shade_rows 549 550 pdf.fill_color! @text_color 551 552 y = pdf.absolute_top_margin - @header_gap 553 low_y = y 554 y0 = y + descender 555 mx = 0 556 557 if @show_headings 558 old_y = y 559 560 pdf.fill_color @heading_color 561 _height, y = __table_column_headings__(pdf, pos, max_width, 562 height, descender, @row_gap, @heading_font_size, y) 563 pdf.fill_color @text_color 564 565 y0 = y + _height 566 y1 = y 567 568 if @shade_headings 569 pdf.fill_color! @shade_heading_color 570 pdf.rectangle(x0 - @gap / 2, y, x1 - x0, _height).fill 571 pdf.fill_color @heading_color 572 __table_column_headings__(pdf, pos, max_width, height, 573 descender, @row_gap, 574 @heading_font_size, old_y) 575 pdf.fill_color @text_color 576 end 577 578 dm = pdf.absolute_left_margin - base_left_margin 579 base_pos.each { |k, v| pos[k] = v + dm } 580 x0 = base_x0 + dm 581 x1 = base_x1 + dm 582 middle = (x0 + x1) / 2.0 583 else 584 y1 = y0 585 end 586 587 first_line = true 588 y -= height 589 low_y = y if low_y.nil? or y < low_y 590 end 591 592 newRow = false 593 594 # Write the actual data. If these cells need to be split over 595 # a page, then newPage will be set, and the remaining text 596 # will be placed in leftOvers 597 newPage = false 598 leftOvers = PDF::Writer::OHash.new 599 600 @cols.each do |name, column| 601 pdf.pointer = y + height 602 colNewPage = false 603 604 unless row[name].nil? 605 lines = row[name].to_s.split(/\n/) 606 if column and column.link_name 607 lines.map! do |kk| 608 link = row[column.link_name] 609 if link 610 "<c:alink uri='#{link}'>#{kk}</c:alink>" 611 else 612 kk 613 end 614 end 615 end 616 else 617 lines = [] 618 end 619 620 pdf.y -= @row_gap 621 622 lines.each do |line| 623 pdf.send(:preprocess_text, line) 624 start = true 625 626 loop do 627 break if (line.nil? or line.empty?) and not start 628 start = false 629 630 _y = pdf.y - height if not colNewPage 631 632 # a new page is required 633 newPage = colNewPage = true if _y < pdf.absolute_bottom_margin 634 635 if colNewPage 636 if leftOvers[name].nil? 637 leftOvers[name] = [line] 638 else 639 leftOvers[name] << "\n#{line}" 640 end 641 line = nil 642 else 643 if column and column.justification 644 just = column.justification 645 end 646 just ||= :left 647 648 pdf.y = _y 649 line = pdf.add_text_wrap(pos[name], pdf.y, 650 max_width[name], line, 651 @font_size, just) 652 end 653 end 654 end 655 656 dy = y + height - pdf.y + @row_gap 657 mx = dy - height * (newPage ? 1 : 0) if (dy - height * (newPage ? 1 : 0)) > mx 658 end 659 660 # Set row to leftOvers so that they will be processed onto the 661 # new page 662 row = leftOvers 663 664 # Now add the shading underneath 665 unless :none == @shade_rows 666 pdf.close_object 667 668 if (cnt % 2).zero? 669 pdf.fill_color!(@shade_color) 670 pdf.rectangle(x0 - @gap / 2.0, y + descender + height - mx, x1 - x0, mx).fill 671 elsif (cnt % 2).nonzero? and :striped == @shade_rows 672 pdf.fill_color!(@shade_color2) 673 pdf.rectangle(x0 - @gap / 2.0, y + descender + height - mx, x1 - x0, mx).fill 674 end 675 pdf.reopen_object(tOID) 676 end 677 678 if :inner == @show_lines or :all == @show_lines 679 # draw a line on the top of the block 680 pdf.save_state 681 pdf.stroke_color! @line_color 682 if first_line 683 pdf.stroke_style @outer_line_style 684 first_line = false 685 else 686 pdf.stroke_style @inner_line_style 687 end 688 pdf.line(x0 - @gap / 2.0, y + descender + height, x1 - @gap / 2.0, y + descender + height).stroke 689 pdf.restore_state 690 end 691 end 692 693 y = y - mx + height 694 pdf.y = y 695 low_y = y if low_y.nil? or y < low_y 696 697 # checking row split over pages 698 unless @split_rows 699 if (((pdf.pageset.size != pageStart) or (pdf.columns? and columnStart != pdf.column_number)) and not second_turn) 700 # then we need to go back and try that again! 701 newPage = second_turn = true 702 tg.rewind_transaction(:row) 703 row = row_orig 704 low_y = y = y_orig 705 y0 = y0_orig 706 y1 = y1_orig 707 ok = false 708 709 dm = pdf.absolute_left_margin - base_left_margin 710 base_pos.each { |k, v| pos[k] = v + dm } 711 x0 = base_x0 + dm 712 x1 = base_x1 + dm 713 else 714 tg.commit_transaction(:row) 715 ok = true 716 end 717 else 718 ok = true # don't go 'round the loop if splitting rows is allowed 719 end 720 end 721 722 if abortTable 723 # abort_transaction if not ok only the outer transaction should 724 # be operational. 725 tg.rewind_transaction(:table) 726 pdf.start_new_page 727 # fix a bug where a moved table will take up the whole page. 728 low_y = nil 729 pdf.save_state 730 break 731 end 732 end 733 end 734 735 if low_y <= y 736 y2 = low_y + descender 737 else 738 y2 = y + descender 739 end 740 741 unless :none == @show_lines 742 y0 = y1 unless @show_headings 743 744 __table_draw_lines__(pdf, pos, @gap, x0, x1, y0, y1, y2, @line_color, 745 @inner_line_style, @outer_line_style, @show_lines) 746 end 747 748 # close the object for drawing the text on top 749 unless :none == @shade_rows 750 pdf.close_object 751 pdf.restore_state 752 end 753 754 pdf.y = low_y 755 756 # Table has been put on the page, the rows guarded as required; commit. 757 tg.commit_transaction(:table) 758 759 y 760 rescue Exception => ex 761 begin 762 tg.abort_transaction(:table) if tg.transaction_open? 763 rescue 764 nil 765 end 766 raise ex 767 end
Private Instance Methods
Find the maximum widths of the text within each column. Default to zero.
# File lib/pdf/simpletable.rb 773 def __find_table_max_width__(pdf) 774 max_width = PDF::Writer::OHash.new(-1) 775 776 # Find the maximum cell widths based on the data and the headings. 777 # Passing through the data multiple times is unavoidable as we must 778 # do some analysis first. 779 @data.each do |row| 780 @cols.each do |name, column| 781 w = pdf.text_width(row[name].to_s, @font_size) 782 w *= PDF::SimpleTable::WIDTH_FACTOR 783 784 max_width[name] = w if w > max_width[name] 785 end 786 end 787 788 @cols.each do |name, column| 789 title = column.heading.title if column.heading 790 title ||= column.name 791 w = pdf.text_width(title, @heading_font_size) 792 w *= PDF::SimpleTable::WIDTH_FACTOR 793 max_width[name] = w if w > max_width[name] 794 end 795 max_width 796 end
Calculate the start positions of each of the columns. This is based on max_width, but may be modified with column options.
# File lib/pdf/simpletable.rb 801 def __find_table_positions__(pdf, max_width) 802 pos = PDF::Writer::OHash.new 803 x = t = adjustment_width = set_width = 0 804 805 max_width.each do |name, w| 806 pos[name] = t 807 # If the column width has been specified then set that here, also 808 # total the width avaliable for adjustment. 809 if not @cols[name].nil? and 810 not @cols[name].width.nil? and 811 @cols[name].width > 0 812 t += @cols[name].width 813 max_width[name] = @cols[name].width - @gap 814 set_width += @cols[name].width 815 else 816 t += w + @gap 817 adjustment_width += w 818 set_width += @gap 819 end 820 end 821 pos[:__last_column__] = t 822 823 [pos, t, x, adjustment_width, set_width] 824 end
# File lib/pdf/simpletable.rb 939 def __open_new_object__(pdf) 940 pdf.save_state 941 tOID = pdf.open_object 942 pdf.close_object 943 pdf.add_object(tOID) 944 pdf.reopen_object(tOID) 945 tOID 946 end
Uses ezText to add the text, and returns the height taken by the largest heading. This page will move the headings to a new page if they will not fit completely on this one transaction support will be used to implement this.
# File lib/pdf/simpletable.rb 831 def __table_column_headings__(pdf, pos, max_width, height, descender, gap, size, y) 832 mx = second_go = 0 833 start_page = pdf.pageset.size 834 835 # y is the position at which the top of the table should start, so the 836 # base of the first text, is y-height-gap-descender, but ezText starts 837 # by dropping height. 838 839 # The return from this function is the total cell height, including 840 # gaps, and y is adjusted to be the postion of the bottom line. 841 tg = Transaction::Simple::Group.new(pdf, self) 842 tg.start_transaction(:column_headings) 843 844 ok = false 845 y -= gap 846 loop do 847 break if ok 848 @cols.each do |name, column| 849 pdf.pointer = y 850 851 if column.heading 852 justification = column.heading.justification 853 bold = column.heading.bold 854 title = column.heading.title 855 end 856 857 justification ||= :left 858 bold ||= @bold_headings 859 title ||= column.name 860 861 title = "<b>#{title}</b>" if bold 862 863 pdf.text(title, :font_size => size, :absolute_left => pos[name], 864 :absolute_right => (max_width[name] + pos[name]), 865 :justification => justification) 866 dy = y - pdf.y 867 mx = dy if dy > mx 868 end 869 870 y -= (mx + gap) - descender # y = y - mx - gap + descender 871 872 # If this has been moved to a new page, then abort the transaction; 873 # move to a new page, and put it there. Do not check on the second 874 # time around to avoid an infinite loop. 875 if (pdf.pageset.size != start_page and not second_go) 876 tg.rewind_transaction(:column_headings) 877 878 pdf.start_new_page 879 save_state 880 y = @y - gap - descender 881 ok = false 882 second_go = true 883 mx = 0 884 else 885 tg.commit_transaction(:column_headings) 886 ok = true 887 end 888 end 889 890 return [mx + gap * 2 - descender, y] 891 rescue Exception => ex 892 begin 893 tg.abort_transaction(:column_headings) if tg.transaction_open?(:column_headings) 894 rescue 895 nil 896 end 897 raise ex 898 end
# File lib/pdf/simpletable.rb 901 def __table_draw_lines__(pdf, pos, gap, x0, x1, y0, y1, y2, col, inner, outer, opt = :outer) 902 x0 = 1000 903 x1 = 0 904 905 pdf.stroke_color(col) 906 907 cnt = 0 908 n = pos.size 909 910 pos.each do |name, x| 911 cnt += 1 912 913 if (cnt == 1 or cnt == n) 914 pdf.stroke_style outer 915 else 916 pdf.stroke_style inner 917 end 918 919 pdf.line(x - gap / 2.0, y0, x - gap / 2.0, y2).stroke 920 x1 = x if x > x1 921 x0 = x if x < x0 922 end 923 924 pdf.stroke_style outer 925 926 pdf.line(x0 - (gap / 2.0) - (outer.width / 2.0), y0, 927 x1 - (gap / 2.0) + (outer.width / 2.0), y0).stroke 928 929 # Only do the second line if it is different than the first AND each 930 # row does not have a line on it. 931 if y0 != y1 and @show_lines == :outer 932 pdf.line(x0 - gap / 2.0, y1, x1 - gap / 2.0, y1).stroke 933 end 934 pdf.line(x0 - (gap / 2.0) - (outer.width / 2.0), y2, 935 x1 - (gap / 2.0) + (outer.width / 2.0), y2).stroke 936 end