3

How to create a multipage PDF in Rails representing one table of data (including column headers on each new page)? I've looked at many examples with wicked-pdf, pdfkit, and prawn but haven't seen anything that specifically addresses the overflow into subsequent pages and the need to repeat the headers with each new page. Thanks for the help.

ob1
  • 1,792
  • 15
  • 20
  • 1
    what about creating a header function (like a partial or something).. then after n records writed place a page break... http://stackoverflow.com/questions/5806943/rails-wickedpdf-page-breaks , i did something like this once but in classic asp.... btw this is a dumb approach and use it as last resource.. – Orlando May 28 '12 at 19:14
  • This may not be a bad approach if the content of each row is predictably one line per record. – ob1 Jun 06 '12 at 17:28
  • yeah it worked right when i did it because i knew the exact size of each row (don't know if this is your case).. so.. after 40 records do a page break, if no more records, put the "footer" of the document.. .. it was simple but im sure there's a more efficient way to do it – Orlando Jun 06 '12 at 17:35

3 Answers3

4

I use Prawn to generate my pdfs, and can get the effect you desire like this:

def render_photo_table(pdf)
  ps = self.photos.map do |photo|
    [photo.filename, photo.image_height, photo.image_width]
  end
  ps.insert(0, ['Filename', 'Height', 'Width'])
  pdf.table(ps) do |table|
    table.header = true
  end
end

This yields a header (set in array position 0) at the top of each page.

railsdog
  • 1,503
  • 10
  • 10
3

Had trouble with this too, but found a fairly easy way(after much headaches) to do this.

Place a floated placemark div above the items so you can get starting coordinates (I call it ID item_top).

Then render the page with top level divs of data, with class 'unbreakable'.

Also include a class to force a page break:

.page-breaker {
  display: block;
  clear: both;
  page-break-after: always;
}

Then after rendering

<script>
// Very top of items
var current_top = $('#item_top').offset().top;
// Check distance from top of page to bottom of item
// If greater than page size put page break and headers before
// Reset top of page to top of item.
$('.unbreakable_section').each(function(index) {
  var item_bottom = $(this).offset().top + $(this).outerHeight(true);
  if ((item_bottom - current_top) > 1250) {
    $(this).before("<div class='page-breaker'></div>");
    $(this).before("Headings Div here");
    current_top = $(this).offset().top - 48; // item - header height
  }
});

</script>

This will measure the vertical space since the last page break and start a new page and place a header above it, if the space exceeds the page height. (1250 is approximate which was close enough for me - I have a footer on the page as well so height may vary depending on your setup).

Tested on a 100 page document works as expected.

riley
  • 2,387
  • 1
  • 25
  • 31
1

We wrote our in-house jquery code for this.

If you have pages like:

<div class="mypage">
  <div class="mypage_footer"></div>
</div>

And outside you have:

<div class="mybox">
  <table><tbody><tr><td>omg</td></tr></tbody></table>
</div>
<div class="mybox">
  <table><tbody><tr><td>lol</td></tr></tbody></table>
</div>

Assuming you have a fixed page height (preferably A4 size), you can use jquery to:

  • Compute remaining space inside mypage by subtracting the $(element).height() of the page's children from the height of mypage
  • While there is remaining space, pick a mybox from outside the page and place it inside the page
  • If there is no more space, create a copy of the first page and repeat.
nurettin
  • 11,090
  • 5
  • 65
  • 85
  • Interesting approach. Did you see any limit to the number of pages you could handle this way? Can you share the actual function? – ob1 Jun 04 '12 at 16:11
  • Yes, in fact after 50 or so pages, the wkhtmltopdf zoom parameter loses its accurracy and millimetric sliding starts to happen. I will ask if I can share the code. – nurettin Jun 05 '12 at 08:49
  • Also, we can't get more than 96dpi with this method. – nurettin Jun 05 '12 at 08:51