# File lib/pdf/simpletable.rb, line 239
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:                 hOID = __open_new_object__(pdf) if @shade_headings
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.close_object
570:                   pdf.fill_color! @shade_heading_color
571:                   pdf.rectangle(x0 - @gap / 2, y, x1 - x0, _height).fill
572:                   pdf.reopen_object(hOID)
573:                   pdf.close_object
574:                   pdf.restore_state
575:                 end
576: 
577:                 dm = pdf.absolute_left_margin - base_left_margin
578:                 base_pos.each { |k, v| pos[k] = v + dm }
579:                 x0 = base_x0 + dm
580:                 x1 = base_x1 + dm
581:                 middle = (x0 + x1) / 2.0
582:               else
583:                 y1 = y0
584:               end
585: 
586:               first_line = true
587:               y -= height
588:               low_y = y if low_y.nil? or y < low_y 
589:             end
590: 
591:             newRow = false
592: 
593:               # Write the actual data. If these cells need to be split over
594:               # a page, then newPage will be set, and the remaining text
595:               # will be placed in leftOvers
596:             newPage = false
597:             leftOvers = PDF::Writer::OHash.new
598: 
599:             @cols.each do |name, column|
600:               pdf.pointer = y + height
601:               colNewPage = false
602: 
603:               unless row[name].nil?
604:                 lines = row[name].to_s.split(/\n/)
605:                 if column and column.link_name
606:                   lines.map! do |kk|
607:                     link = row[column.link_name]
608:                     if link
609:                       "<c:alink uri='#{link}'>#{kk}</c:alink>"
610:                     else
611:                       kk
612:                     end
613:                   end
614:                 end
615:               else
616:                 lines = []
617:               end
618: 
619:               pdf.y -= @row_gap
620: 
621:               lines.each do |line|
622:                 pdf.send(:preprocess_text, line)
623:                 start = true
624: 
625:                 loop do
626:                   break if (line.nil? or line.empty?) and not start
627:                   start = false
628: 
629:                   _y = pdf.y - height if not colNewPage
630: 
631:                     # a new page is required
632:                   newPage = colNewPage = true if _y < pdf.absolute_bottom_margin
633: 
634:                   if colNewPage
635:                     if leftOvers[name].nil?
636:                       leftOvers[name] = [line]
637:                     else
638:                       leftOvers[name] << "\n#{line}"
639:                     end
640:                     line = nil
641:                   else
642:                     if column and column.justification
643:                       just = column.justification
644:                     end
645:                     just ||= :left
646: 
647:                     pdf.y = _y
648:                     line = pdf.add_text_wrap(pos[name], pdf.y,
649:                                              max_width[name], line,
650:                                              @font_size, just)
651:                   end
652:                 end
653:               end
654: 
655:               dy = y + height - pdf.y + @row_gap
656:               mx = dy - height * (newPage ? 1 : 0) if (dy - height * (newPage ? 1 : 0)) > mx
657:             end
658: 
659:               # Set row to leftOvers so that they will be processed onto the
660:               # new page
661:             row = leftOvers
662: 
663:             # Now add the shading underneath
664:             unless :none == @shade_rows
665:               pdf.close_object
666: 
667:               if (cnt % 2).zero?
668:                 pdf.fill_color!(@shade_color)
669:                 pdf.rectangle(x0 - @gap / 2.0, y + descender + height - mx, x1 - x0, mx).fill
670:               elsif (cnt % 2).nonzero? and :striped == @shade_rows
671:                 pdf.fill_color!(@shade_color2)
672:                 pdf.rectangle(x0 - @gap / 2.0, y + descender + height - mx, x1 - x0, mx).fill
673:               end
674:               pdf.reopen_object(tOID)
675:             end
676: 
677:             if :inner == @show_lines or :all == @show_lines
678:               # draw a line on the top of the block
679:               pdf.save_state
680:               pdf.stroke_color! @line_color
681:               if first_line
682:                 pdf.stroke_style @outer_line_style
683:                 first_line = false
684:               else
685:                 pdf.stroke_style @inner_line_style
686:               end
687:               pdf.line(x0 - @gap / 2.0, y + descender + height, x1 - @gap / 2.0, y + descender + height).stroke
688:               pdf.restore_state
689:             end
690:           end
691: 
692:           y = y - mx + height
693:           pdf.y = y
694:           low_y = y if low_y.nil? or y < low_y 
695: 
696:             # checking row split over pages
697:           unless @split_rows
698:             if (((pdf.pageset.size != pageStart) or (pdf.columns? and columnStart != pdf.column_number)) and not second_turn)
699:               # then we need to go back and try that again!
700:               newPage = second_turn = true
701:               tg.rewind_transaction(:row)
702:               row = row_orig
703:               low_y = y = y_orig
704:               y0 = y0_orig
705:               y1 = y1_orig
706:               ok = false
707: 
708:               dm = pdf.absolute_left_margin - base_left_margin
709:               base_pos.each { |k, v| pos[k] = v + dm }
710:               x0 = base_x0 + dm
711:               x1 = base_x1 + dm
712:             else
713:               tg.commit_transaction(:row)
714:               ok = true
715:             end
716:           else
717:             ok = true # don't go 'round the loop if splitting rows is allowed
718:           end
719:         end
720: 
721:         if abortTable
722:             # abort_transaction if not ok only the outer transaction should
723:             # be operational.
724:           tg.rewind_transaction(:table)
725:           pdf.start_new_page
726:             # fix a bug where a moved table will take up the whole page.
727:           low_y = nil
728:           pdf.save_state
729:           break
730:         end
731:       end
732:     end
733: 
734:     if low_y <= y
735:       y2 = low_y + descender
736:     else
737:       y2 = y + descender
738:     end
739: 
740:     unless :none == @show_lines
741:       y0 = y1 unless @show_headings
742: 
743:       __table_draw_lines__(pdf, pos, @gap, x0, x1, y0, y1, y2, @line_color,
744:         @inner_line_style, @outer_line_style, @show_lines)
745:     end
746: 
747:     # close the object for drawing the text on top
748:     unless :none == @shade_rows
749:       pdf.close_object
750:       pdf.restore_state
751:     end
752: 
753:     pdf.y = low_y
754: 
755:       # Table has been put on the page, the rows guarded as required; commit.
756:     tg.commit_transaction(:table)
757: 
758:     y
759:   rescue Exception => ex
760:     begin
761:       tg.abort_transaction(:table) if tg.transaction_open?
762:     rescue
763:       nil
764:     end
765:     raise ex
766:   end