X

Markdown is allowed. Examples:

You type:

Here's a regular old comment!

Returns:

Here's a regular old comment!


You type:

Here's a sentence with `a little bit of code` in it.

Returns:

Here's a sentence with a little bit of code in it.


You type:

here is some text
This line must be blank! Press Enter!
``` JavaScript
thing = function() {
  return "stuff"
}
```

Returns:

here is some text

thing = function() {
  return "stuff"
}

In the above example, I've specified a language. If you don't, the default is Ruby (as it's clearly the best).

wicked_pdf on wkhtmltopdf: Bootstrap 4 woes resolved (Rails)

A simple solution to the Bootstrap 4 problem using wicked_pdf, wkhtmltopdf_binary, and Bootstrap 4

07/19/2018

By Aaron Rohrbacher

Share:

Yes, I wrote another Ruby on Rails invoicing application. It's mine, I use it, and I like it!

The Problem

I hit a snag while building PDFs of my invoices using the infamous wicked_pdf gem on wkhtmltopdf-binary 0.12.3.1. Bootstrap 4 responsiveness rules were getting all up in my kool-aid. In my case, I wanted my col-md-6 divs to remain in "wide mode." While I could generate the PDF invoice, the responsiveness in Bootstrap 4 would cause the invoice to appear in a mobile view, regardless of the (wonderful) viewpoint setting included with wicked_pdf.

The Solution

After sifting through (way) too many Google/Stack Overflow searches, I noticed that few were applicable to Bootstrap 4. I finally came across an article in Bootstrap's own documentation - for v4-alpha - that included some limited info about overriding responsiveness features. These suggestions still work as of Bootstrap 4.1.1 - hopefully my next blog post won't be about me doing this all over again after another Bootstrap upgrade (but it probably will)!

I had to maintain the float CSS property in my PDF.

I'm using SASS with Rails. If you're not, I'm sure you'll conjure a similar solution from this.

/* app/assets/stylesheets/pdf.scss */
@import 'application';
.overrideResponsive {
  @include float-left;
}

.hideOnPdf {
  display:none;
}

Here, I'm importing the application stylesheet (including Bootstrap), and creating a rule .overrideResponsive that requires the float property with a value of left (float:left) to remain intact at all times. This mix-in is provided by Bootstrap (for now). Next (less pertinent to this subject, but fun nonetheless), I've created a rule .hideOnPdf that will hide anything I apply the class to on the PDF, but not on the view itself (so I can have buttons and links and such on the view, but they won't show up on the generated PDF).

<!-- app/views/layouts/pdf.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title>PDF</title>
  <%= wicked_pdf_stylesheet_link_tag "pdf" %>
</head>
<body>
  <div class = "container">
    <%= yield %>
  </div>
</body>
</html>

Here, I'm including the wicked_pdf_stylesheet_link_tag helper method to reference my pdf.scss stylesheet. note: I'm only including my pdf stylesheet, as the .overrideResponsive rule (and .hideOnPdf rule) should apply here, but not on the invoice view itself.

<h2>Invoice</h2>
<div class= "row">
  <div class= "col-md-6">
    <p>Invoice Number: <%= @invoice.invoice_number %></p>
    <p>Date: <%= @invoice.sent_date.strftime("%B %d, %Y") %>
    <p><strong>Due Date: <%= @invoice.due_date.strftime("%B %d, %Y") %></strong></p>
  </div>
  <div class= "col-md-6"></div>
</div>
<div class= "row">
  <div class= "col-md-6 overrideResponsive">
    <div class= "card">
      <div class= "card-header">
        From:
      </div>
      <div class= "card-body">
        <h5><%= @invoice.user.first_name + " " + @invoice.user.last_name %></h5>
        <p>
          <%= @invoice.user.address_1 %><br>
          <% if !@invoice.user.address_2.blank? %>
            <%= @invoice.user.address_2 %><br>
          <% end %>
          <% if !@invoice.user.city.blank? %>
            <%= "#{@invoice.user.city}, #{@invoice.user.state} #{@invoice.user.zip}" %><br>
          <% end %>
          <%= number_to_phone(@invoice.user.phone_1, area_code: true) %>
        </p>
      </div>
    </div>
  </div>
  <div class= "col-md-6 overrideResponsive">
    <div class= "card">
      <div class= "card-header">
        Bill To:
      </div>
      <div class= "card-body">
        <h5><%= @invoice.company.name %></h5>
        <p>
          <%= @invoice.company.address_1 %><br>
          <% if !@invoice.company.address_2.blank? %>
            <%= @invoice.company.address_2 %><br>
          <% end %>
          <% if !@invoice.company.city.blank? %>
            <%= "#{@invoice.company.city}, #{@invoice.company.state} #{@invoice.company.zip}" %><br>
          <% end %>
          <%= number_to_phone(@invoice.company.phone_1, area_code: true) %>
        </p>
      </div>
    </div>
  </div>
</div>
<span class = "gapper"></span>
<div class= "row">
  <span class = "hideOnPdf">
    <%= link_to 'Add Line Item', new_invoice_line_item_path(@invoice), class: 'btn btn-primary' %>
    <%= link_to 'Track Time', new_invoice_time_record_path(@invoice), class: 'btn btn-primary' %>
    <%= link_to 'Edit Invoice Details', edit_invoice_path(@invoice), class: 'btn btn-primary' %>
    <%= link_to 'Generate PDF', invoice_path(@invoice, format: :pdf), class: 'btn btn-primary' %>
    <%= link_to 'Delete Invoice', invoice_path(@invoice), class: 'btn btn-danger', method: :delete, data: { confirm: "Permanently deleting. This can't be undone. Are you sure?" } %>
  </span>
...

I debated posting this much of the whole shebang (no pun intended), but hopefully this example shows that each element with a class including my new rule overrideResponsive will float:left when being viewed as a PDF. Additionally, the <span> element includes a class with my new rule hideOnPdf, which will hide my big ugly buttons for adding stuff to the invoice when generating the PDF, while showing them on the view.

class InvoicesController < ApplicationController
  def show
    @invoice = Invoice.find(params[:id])
    respond_to do |format|
       format.html
       format.pdf do
         render pdf: "Invoice",
         template: "invoices/show.html.erb",
         layout: 'pdf.html',
         viewport_size: '1920x1080'
       end
     end
   end
...

Here, I'm asking Rails to render the usual view if the format is html, which is the default behavior. If the URL for the invoice is appended with .pdf, wicked_pdf does its magic with wkhtmltopdf_binary. I'm setting the viewport at 1920x1080, which worked. In the newest version of the wkhtmltopdf-binary gem (0.12.4), the viewport settings with wicked_pdf doesn't seem to do stuff, though the responsiveness issue remained solved... I'll look forward to the blog post on my next gem update session!

Notes:

Yes, I tried col-xs-6.
Yes, I tried simply setting the viewport. Like all of the viewports.

Before:

Invoice before>

After:

Invoice before>

Feel free to poke around in my invoicing app. Tax/expense features coming soon!
https://github.com/AaronRohrbacher/TimeToInvoice

Here's the bootstrap documentation on "Responsive Helpers," referred to earlier:
https://v4-alpha.getbootstrap.com/utilities/responsive-helpers/

Thank you for reading! Your comments, negative or positive, are welcomed!


Comments:

Chiel said:


Serious, no comments yet here??

This solution saved my day!
After searching the entire internet en trying thousands of solutions I finally found it. THANKS :-)


Aaron Rohrbacher said:


Chiel, thank you for your comment. I'm glad this helped at least one other person!


Oscar said:


Great article, but already not working for Bootstrap 4.3.1. How did you figure out the problem was the float-left?


Oscar said:


Great article, actually working with latest version of binary gem and bootstrap. Thanks!


Aaron said:


Hi Oscar, thanks for your comments! Apologies for taking so long... I may have ignored this app for a little too long. I'm glad to hear that this fix is working with the latest Bootstrap and WKHTML gems - I've been putting off the update, but do still frequently use my precious invoicing app! Frankly, the idea of overriding float: left came from the mixin article provided by Bootstrap, as mentioned at the very bottom of this post: https://v4-alpha.getbootstrap.com/utilities/responsive-helpers/. It makes sense to me that maintaining the float property would kill the media rules that cause col divs to drop. I do still look forward to the results upon gem updates- will keep you posted! But it sounds like it went well, so thank you for the easing of my mind!


Add a comment:

Markdown is allowed. Click here for instructions