Custom PDF Fonts with Wicked_PDF and Heroku

05/03/2015

One great feature of Branch Fitness is the ability to generate a custom PDF of any workout. Each PDF print out offers you a clear outline of your workout along with an easy way to track it. Supersets or Circuits? No problem - these are clearly marked on the PDF along with the target set counts, reps, weight, and distance (as applicable). Any specific notes from the workout are also displayed, outlining precise instructions specific to the workout.

Here are a few examples of custom workout plans and their PDFs found on Branch Fitness. To open the PDF, simply click the tab marked ‘PDF’.
High Volume Legs Pyramid Workout
5K Prep Strength Training Workout

Armed with the custom PDF for your workout, you can easily follow, perform, and track your workout, and you are well on your way to reaching your defined fitness goals. Sign Up for Branch Fitness and you can create your own workouts, drag and drop your exercises, and print unique PDFs.


The Challenge and End Result

The tutorial below gives background and insight into the technical details behind the development and deployment of PDFs. One of the biggest challenges comes in the deployment and hosting of PDFs and including any custom fonts in the final output. This tutorial uses the following resources:

Framework: Ruby on Rails
Gems: wicked_pdf & wkhtmltopdf
Hosting: Heroku
Custom font: Source Sans Pro - font squirrel

In addition to the examples found on Branch Fitness linked above, you can also see a very basic example of a PDF, hosted on Heroku, with a custom font - here.


The Step by Step

Please note that this tutorial requires prior knowledge of rails, ruby, and basic experience with Heroku. You can see the github repository and code here.

The first steps for this tutorial are all the setup of a basic rails app and the main views we will use for the example.

1- Start by running setup commands, migrating the new database (note that the database is never used in this tutorial), and initializing a git repository.

rails new heroku_pdfs --database=postgresql
rake db:create db:migrate

At this point localhost:3000 should be ready to go.

2- One of the best resources I personally used for learning rails was the tutorial from Michael Hartl. We will use his example of a ‘static_pages’ controller for this example, so we will next run the generator:

rails g controller static_pages home

And in the routes file

#routes.rb
root 'static_pages#home'

3- Next we will use the rails generator to create a new controller and associated view directory for the actual pdf.

rails g controller pdf_pages show

4 - In the newly generated ‘show’ page we will add some html tags and text which will form the basis for our pdf as well.

<%# views/pdf_pages/show.html.erb %>
<div class="custom_font">This is the custom font</div>

5 - At this point we are all setup and ready to install the two gems we will be using:
wicked_pdf & wkhtmltopdf

<%# Gemfile %>
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
bundle install

The documentation for the setup of the wicked_pdf gem recommends running the generator ‘rails generate wicked_pdf’. This adds a file which looks like this:

# /config/initializers.wicked_pdf.rb 
WickedPdf.config = {
  #:wkhtmltopdf => '/usr/local/bin/wkhtmltopdf',
  #:layout => "pdf.html",
  #:exe_path => '/usr/local/bin/wkhtmltopdf' 
}

From the documentation, make sure that “wkhtmltopdf is on your webservers path.” On my machine, specifying the path was unnecessary (hence the above is all commented out) and gave a “bad path error.”

6 - Next we will modify the html view to also render as a pdf. For this, we will modify the pdf_pages_controller for the ‘show’ view to take advantage of wicked_pdf. Our controller method for show now looks like this:

#pdf_pages_controller.rb
class PdfPagesController < ApplicationController
  def show
    respond_to do |format|
      format.html
      format.pdf do
        render  :pdf => 'This is the PDF Name',
                :template => 'pdf_pages/show.html.erb'
      end
    end
  end
end

Note that the format.pdf calls the ‘show’ page as the template. For all usage options, refer to the documentation provided by wicked_pdf.

7 - In the home page, we add another link to ensure that the ‘pdf’ version gets called when clicking.

<%# /views/static_pages/home.html.erb %>
<%= link_to "PDF - new tab", pdf_pages_show_path(format: 'pdf'), target: "_blank" %>

At this point the pdf should be rendering when clicking the new link.

8 - Now it is time to add in the custom font. For the purposes of this tutorial, we will use the Source Sans Prol font. Font squirrel is a great resource for finding new fonts. Downloading the font gives a folder with about a dozen ‘.otf’ file extensions. First add the fonts directory to the asset pipeline so that the /assets directory now has /fonts, /images, /javascripts, and /stylesheets. Next, copy each of the specific fonts you wish to reference into this directory.

9 - Create a new file (with a css.scss.erb extension) to reference the fonts that we just copied into the font directory. Add in references to each otf font file you imported. Use the @font-face declaration to reference the specific .otf font files.

<%# /fonts/custom_fonts.css.scss.erb %>
@font-face {
    font-family: "SourceSansPro-Regular";
    src: url('<%= asset_path("SourceSansPro-Regular.otf") %>');
}
@font-face {
    font-family: "SourceSansPro-Bold";
    src: url('<%= asset_path("SourceSansPro-Bold.otf") %>');
}
@font-face {
    font-family: "SourceSansPro-Light";
    src: url('<%= asset_path("SourceSansPro-Light.otf") %>');
}
@font-face {
    font-family: "SourceSansPro-ExtraLight";
    src: url('<%= asset_path("SourceSansPro-ExtraLight.otf") %>');
}
@font-face {
    font-family: "SourceSansPro-Semibold";
    src: url('<%= asset_path("SourceSansPro-Semibold.otf") %>');
}

At this point, the html version is now correctly showing the fonts, but rendering the view as the PDF still does not include the font. Onto the next step.

10 - To make use of the custom font, we need to reference it using the wicked pdf stylesheet reference tag and also ‘base 64 encode the font.’ First, create a new scss file called ‘pdf.css.scss.’

11 - To base 64 encode a font, you can use this site. Select the ‘.otf’ file extension of your desired font when choosing the ‘encode binary file’ button. The base-64 encoded output will be a random string that is several hundred lines of alpha-numeric characters. Next copy this output into the new pdf.css.css file. Your file will look something like this:

<%# /stylesheets/pdf.css.scss %>
@font-face {
  font-family: 'Source Sans Pro Light';
  src: url(data:font/truetype;charset=utf-8;base64,T1RUTw-----THIS IS HUNDREDS OF LINES LONG -------sGAnBSvO7nBqXQ==)
}

.custom_font {
  font-size: 24px;
  font-family: 'Source Sans Pro ExtraLight';
}

12 - Now we need to reference this new file using the wicked_pdf stylesheet tags. We can include the syntax directly in the html view for the pdf we wish to generate.

<%# views/pdf_pages/show.html.erb %>
<meta charset='utf-8' />
<%= wicked_pdf_stylesheet_link_tag "pdf" %>

13 - Lastly, we must include the pdf.css.scss file in the application. This file will not be included in the application.css file so it must be precompiled separately. This is to ensure it works correctly when hosted on Heroku. Add this line to include pdf.css.scss in the application.

#application.rb
config.assets.precompile += ['pdf.css']

14 - At this point, the font should now render correctly in both html and in the rendered pdf. And now it is time to push to Heroku. Make sure the rails_12factor gem and ruby version are specified in your gemfile before deployment to Heroku and you are all set.

#gemfile
ruby '2.0.0'
gem 'rails_12factor', group: :production

15 - The specifics to get pdfs deployed to Heroku with custom fonts may differ depending upon your application's configuration. Please see the the links and resources below for debugging or anything I may have missed here.


Acknowledgements and Thank You's

This tutorial was inspired by the following posts and authors and would not have been possible without their original contributions: