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!

Tony Tambe
  • 573
  • 1
  • 10
  • 30

4 Answers4

2

i added this in css it worked for me

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

Hemanth M C
  • 436
  • 1
  • 4
  • 18
  • 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. – Tony Tambe Dec 12 '14 at 18:54
  • try without @media print with your css like table, tr, td, th, tbody, thead, tfoot { page-break-inside: avoid !important; } – Hemanth M C Dec 15 '14 at 15:47
  • 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! – Tony Tambe Dec 15 '14 at 19:00
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.

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
Shifa Khan
  • 769
  • 6
  • 12
  • 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? – Tony Tambe Jan 07 '15 at 02:36
  • 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](https://github.com/pdfkit/pdfkit#configuration) to your config file. – Shifa Khan Jan 07 '15 at 06:45
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.

cph
  • 458
  • 2
  • 6
  • 24
  • I tried removing the
    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.
    – Tony Tambe Jan 23 '15 at 01:54
  • Also, what version of wkhtmltopdf are you using? – Tony Tambe Jan 23 '15 at 02:29
  • 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. – Tony Tambe Jan 24 '15 at 14:28
  • Try this tutorial. http://mac-how-to.wonderhowto.com/how-to/open-third-party-apps-from-unidentified-developers-mac-os-x-0158095/ – cph Jan 24 '15 at 19:38
  • 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] : 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." – Tony Tambe Jan 24 '15 at 22:43