WKHTMLTOPDF with pdfkit on Rails ignoring table page breaks
Asked Answered
A

4

1

I know there are a lot of issues with wkhtmltopdf and page breaks that date back years, but I haven't worked out a solution yet. I'm using the PDFKit gem to generate my html pages into pdfs, but I don't want the pages to break in the middle of a table row.

I'm using wkhtmltopdf-binary (0.9.9.3), which looks to be the most updated version

My CSS:

@media print {

  #scores table tr td, #scores table tr th {
    page-break-inside: avoid !important;
  }

  table, tr, td, th, tbody, thead, tfoot {
    page-break-inside: avoid !important;
  } 
}

My table:

<div class="score_table">
    <table id="scores" class="print-friendly">
      <tbody>
            <% @chapters.each do |chapter| %>
                <tr>
                    <th colspan="3" ><%= chapter.name %></th>
                </tr>   
                <% chapter.rules.each do |rule| %>
                    <tr>
                        <th colspan="2" >Rule: <%= rule.name %></th>
                        <th></th>
                    </tr>     
    <!-- Triggers -->               
                    <% rule.triggers.each do |trigger| %>
                        <tr>
                            <td>T</td>
                            <td><%= markdown(trigger.body) %></td>
                            <td><%= markdown(trigger.explanation) %></td>
                        </tr>
                        <% if trigger.image? || trigger.image2? %>
                            <tr>    
                                <td></td>
                                <% if trigger.image? %>
                                    <td><%= image_tag trigger.image.url(:thumb) %></td>
                                <% else %>
                                    <td></td>
                                <% end %>   
                                <% if trigger.image2? %>    
                                <td><%= image_tag trigger.image2.url(:thumb) %></td>
                            <% else %>
                                <td></td>   
                            <% end %>   
                            </tr>   
                        <% end %>   
                    <% end %>   
    <!-- Questions -->  
                    <% rule.questions.each do |question| %>
                        <tr>
                            <td>Q</td>
                            <td><%= markdown(question.body) %></td>
                            <td><%= markdown(question.answer) %></td>
                        </tr>
                        <% if question.image? || question.image2? %>
                            <tr>    
                                <td></td>
                                <% if question.image? %>
                                    <td><%= image_tag question.image.url(:thumb) %></td>
                                <% else %>
                                    <td></td>
                                <% end %>
                                <% if question.image2? %>       
                                <td><%= image_tag question.image2.url(:thumb) %></td>
                            <% else %>
                                <td></td>
                            <% end %>       
                            </tr>   
                        <% end %>   
                    <% end %>   
    <!-- Hints -->  
                    <% rule.hints.each do |hint| %>
                        <tr>
                            <td>H</td>
                            <td><%= markdown(hint.body) %></td>
                            <td><%= markdown(hint.explanation) %></td>
                        </tr>
                        <% if hint.image? || hint.image2? %>
                            <tr>    
                                <td></td>
                                <% if hint.image? %>
                                    <td><%= image_tag hint.image.url(:thumb) %></td>
                                <% else %>  
                                    <td></td>
                                <% end %>   
                                <% if hint.image2? %>
                                <td><%= image_tag hint.image2.url(:thumb) %></td>
                            <% else %>  
                                <td></td>
                            <% end %>   
                            </tr>   
                        <% end %>   
                    <% end %>   
                <% end %>
            <% end %>
    </tbody>
    </table>
</div>

Is there a work around, or is there something that I'm doing wrong? This is the result:

enter image description here

I could post the PDFKit code as well, but it sounds like a wkhtmltopdf issue

***Update - My CSS @print isn't affecting the page when .pdf is added to the url. I have my stylesheet link with media: "all"

<%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>

Here's my initializer pdfkit.rb:

ActionController::Base.asset_host = Proc.new { |source, request|  

if request.env["REQUEST_PATH"].include? ".pdf"
    "file://#{Rails.root.join('public')}"
  else
    "#{request.protocol}#{request.host_with_port}"
  end
}

If I can fix the CSS, then I probably will solve the page break issue!

Americium answered 28/11, 2014 at 17:25 Comment(0)
C
2

i added this in css it worked for me

@media print {
#scores {
      page-break-before: always;
    }
}

Counterpoint answered 12/12, 2014 at 14:33 Comment(3)
I tried that with no luck, BUT I've discovered that my @media print isn't actually affecting the CSS when I print it to .pdf. I'll update the question above.Americium
try without @media print with your css like table, tr, td, th, tbody, thead, tfoot { page-break-inside: avoid !important; }Counterpoint
I tried that, but it has no effect on the .pdf version. The .pdf version isn't receiving any of my CSS, which is why I think it's an issue with the initializer or something in the setup. It's driving me crazy!Americium
D
1

I am using version wkhtmltopdf 0.12.0

For me, page breaks ONLY work with --print-media-type. Without it, page break protection for images works, but not page-break-after or before.

I had to make a special css file for print media to get it work.

Setting the paper size to 'A3' or using the 'overflow: visible' didn't make any difference.

I ended up with the following:

in config/initializers:

PDFKit.configure do |config|
  config.wkhtmltopdf = '/usr/bin/wkhtmltopdf'
  config.default_options = {
    :encoding=>"UTF-8",
    :page_size => 'A3',
    :zoom => 0.9,
    :javascript_delay => 2000,
    :print_media_type => true,
    :footer_right => 'report, page [page] of [toPage]',
    :no_background => true,
  }
end

I am not 100% sure if the no_background is needed.

Then, in the controller:

def report_as_pdf
    require 'nokogiri'
    root_url = Rails.env.development? ? 'http://localhost:3000' : 'https://my.example.com'
    html = render_to_string(layout: true, template: "reports/report_as_pdf").gsub('/assets', root_url + '/assets') # since wkhtmltopdf can not find the css files, we add the root_url to all assets
    html_doc = Nokogiri::HTML(html)
    html_doc.css('div.contain-to-grid').remove
    html_doc.css('nav#rapportage_bar').remove
    # Had to remove certain elements on the page. Not relevant for this particular problem, but it may help.
    html = html_doc.to_html
    filename = 'report_' + Time.now.to_s(:db)[0..9]
    if Rails.env.development?
      File.write("/tmp/#{filename}.html", html)
      render :text => "wrote /tmp/#{filename}.html"
      fork do
        system("wkhtmltopdf --footer-right 'report, page [page] of [toPage]' --page-size 'A3' --zoom 0.9 --javascript-delay 2000 --no-background --print-media-type  file:////tmp/#{filename}.html  /tmp/#{filename}.pdf")
      end
      # forking was the only way I could get it to work in development mode, using thin as webserver instead of webrick (but it might work with webrick, I didn't try).
    else
      kit = PDFKit.new(html)
      send_data(kit.to_pdf, :filename => filename + '.pdf', :type => 'application/pdf')
      # all is simpeler in production mode.
    end
  end

Note, that in the default layout (normally app/views/layouts/application.html.erb) I added one line to introduce a special css file for print:

stylesheet_link_tag    "application_print", media: "print"

This file actually imports some tables from ZURB Foundation, which is cool.

@import "foundation/components/tables";

h1 {
  font-variant: small-caps;
  font-weight: bolder;
  font-size: 320%;
  border-bottom: 12px solid black;
  margin-bottom: 20px;
  margin-top: 80px;
  }

h2 { (etc etc etc)

To make page breaks in a view, I just insert this in the right places:

I hope this will be useful to someone someday.

Dysphasia answered 21/2, 2015 at 18:39 Comment(0)
C
0

Can you upload the controller code where you've called PDFkit.new ?

Your stylesheet might not be included. Try adding the stylesheet in your controller code like this:

def export
    kit = PDFKit.new(render_to_string(layout: false, handlers: [:haml], formats: :html, template: 'score_tables/export'))
    kit.stylesheets << "#{Rails.root}/vendor/assets/stylesheets/bootstrap.css"
    kit.stylesheets << "#{Rails.root}/app/assets/stylesheets/score_tables.css"
    kit
end
Cocaine answered 6/1, 2015 at 9:37 Comment(2)
I don't currently have anything in my controller. I use the middelware that allows to print any page to .pdf. I really only need to print the "guide" page, so would I put this in the pages controller and call it in the view somehow on the guide page?Americium
I'm not sure of the middleware. You can try adding 'print_media_type' option in application.rb config.middleware.use PDFKit::Middleware, :print_media_type => true If this doesn't work out, you can drop the middleware, and add this to your config file.Cocaine
L
0

Here is how I fixed my issue. - Updated PDFKIT to as of ( 01/22/2014 ) - I had to move my table out of the <div> as the nested table messes everything up.

Example:

No div tag wrapping the table.

From

<div class="score_table">
<table id="scores" class="print-friendly">
...rest of the HTML

To

<table id="scores" class="print-friendly">
...rest of the HTML

Hope this helps.

Logotype answered 22/1, 2015 at 7:59 Comment(5)
I tried removing the <div> as you suggested, but that didn't help. The strange thing is that I can do page-break-before: always; on the headers and that works, but page-break-inside: avoid doesn't work. Also, I can change the CSS with @media print to change the font-size and font-weight, but it won't print in colors. The backgrounds are all just white.Americium
Also, what version of wkhtmltopdf are you using?Americium
That could be my problem. Sorry to be needy, but can you advise how you installed that version? I downloaded the .pkg file the other day and tried to open it, but Yosemite blocks it since it's not from the app store. Once I do open it and run it, is that it? Thanks a million.Americium
Try this tutorial. mac-how-to.wonderhowto.com/how-to/…Logotype
Thanks. I ran the installer and removed teh wkhtmltopdf-binary gem. Now when I do wkhtmltopdf -v I see 0.12.2.1, so it appears to be updated. When I run the app I get the following error: "Jan 24 17:25:42 Anthonys-MBP-2.home wkhtmltopdf[883] <Error>: CGContextSetFillColorSpace: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update."Americium

© 2022 - 2024 — McMap. All rights reserved.