rails_pdf icon indicating copy to clipboard operation
rails_pdf copied to clipboard

A reliable way to generate PDF of any complexity in Ruby on Rails apps

Rails PDF

RailsJazz https://www.patreon.com/igorkasyanchuk Listed on OpenSource-Heroes.com

Create PDF documents in Rails your app from HTML, CSS and JS.

Features:

  • Basically, you can create any HTML/CSS/JS/Images page and save into PDF
  • Generate PDF on the fly or save to disk
  • With header, footer, page numbers, layout support
  • Has few starter templates to help with most popular reports. Just create some and re-edit it
  • Support for Charts libraries
  • ERB/SCSS support
  • Custome & Google fonts
  • Separates PDF templates from app views
  • Doesn't insert any middleware into your app
  • Pub format is similar to slim
  • Pass locals to the view
  • Works with ActiveStorage

It's uses ReLaXedJS tool, which is wrapper arround chromium headless.

The idea of this gem is to separate logic of PDF creation from regular Rails views/controllers. Make it independent and easy to maintain.

Template starters (build-it with generator)

If you want to contribute and add more templates - it' very easy to do. See #Templates section of this doc.

Template: simple_invoice Template: basic_invoice Template: chart1
Template: products

Usage

You can use predefined starter templates (and you are welcome to contribute and create additional templates):

Use template starters:

  • rails g rails_pdf new invoice_report (create blank template for PDF)
  • rails g rails_pdf basic_invoice report
  • rails g rails_pdf chart1 report
  • rails g rails_pdf simple_invoice report

After you've generated PDF template, you can edit it in app/pdf/<folder>/<file> file.

You can use JS/CSS files from app/pdf/shared (which includes bootstrap 4, foundation 6, Found Awesome 5, Charts.js).

This is how you can generate and send PDF files on the fly:

  def report
    RailsPDF.template("report2/invoice.pug.erb").render do |data|
      send_data(data, type: 'application/pdf', disposition: 'inline', filename: 'report.pdf')
    end
  end

  # or return file as attachment

  def invoice
    RailsPDF.template("report2/invoice.pug.erb").render do |data|
      send_data(data, type: 'application/pdf', disposition: 'attachment', filename: 'report.pdf')
    end
  end

  # sample with locals
  # works similar how regular partials works

  def report
    @invoice = Invoice.find(params[:id])
    RailsPDF.template("report2/invoice.pug.erb").locals(invoice: @invoice).render do |data|
      send_data(data, type: 'application/pdf', disposition: 'inline', filename: 'report.pdf')
    end
  end

If you need to create PDF file and save to file on drive:

RailsPDF.template("report/chart.pug.erb").render_to_file('path/docs/report.pdf') # File

# or for html template

RailsPDF.template("sales/invoice.html.erb").render_to_file('path/docs/report.pdf') # File

Same but save PDF into Temfile:

RailsPDF.template("report/chart.pug.erb").render_to_tempfile('report.pdf') # Tempfile

With ERB files you can use App code (like models, etc). For example you can iterate over @users and output in PDF.

JS/CSS/Images

Basically you need to put an absolute path to asset or remote URL (for example on CDN. but local files works faster).

style
  include:scss <%= Rails.root %>/app/pdf/report/stylesheets/invoice.scss

img(src="<%= Rails.root %>/app/pdf/shared/images/rails_pdf.png")

script(src='<%= Rails.root %>/app/pdf/shared/javascripts/Chart.bundle.min.js')

ActiveStorage integration

You need to specify path to file on disk (or URL to the image if stored in the cloud).

# model
class Project < ApplicationRecord
  has_one_attached :logo
end

# simple controller
def download_project
  RailsPDF.template("report3/invoice.pug.erb").locals(project: Project.first).render do |data|
    send_data(data, type: 'application/pdf', disposition: 'inline', filename: 'report.pdf')
  end
end
body
  header.clearfix
    #logo
      img(src="<%= ActiveStorage::Blob.service.send(:path_for, project.logo.key) %>")
    h1 <%= project.title %>

Installation

Installation of gem is very simple, it's just requires one additional step to install RelaxedJS tool which is using Chrome headless.

Requirements

  • RelaxedJS 0.2.0+ (check with relaxed --version)
  • Chrome headless (bundled with relaxedjs)
  • Rails 4.2+ app

Install RelaxedJS

$ git clone https://github.com/RelaxedJS/ReLaXed.git .
$ npm install
$ sudo npm link --unsafe-perm=true

Verify it's installed with: relaxed --version.

Gemfile

gem 'rails_pdf'

And then execute:

$ bundle

Templates

Tips

  • if you want to add a page-break in document: div(style="page-break-before:always")
  • if you are using bootstrap and you want to use columns - include bootstrap.print.css and use styles from it.
  • if you are using Charts.js and you want to clear and readable text put in options: devicePixelRatio: 3,
  • you can define size of page using in SCSS:
  // A4
  $page-width: 8.27in;
  $page-height: 11.69in;
  • if you want to add header/footer (sample: lib/generators/rails_pdf/templates/simple_invoice/invoice.pug.erb)
  h1 My document
  p some paragraph

  template#page-header
    p I appear at the top of the page

  template#page-footer
    p I appear at the bottom of the page
  • if you see an error, or something is not generated check TMP folder (e.g. /tmp) tmp/*.html file (see most recent files).
  • if you have problems with Charts.js you can add setTimeout(...) and execute chart creation in 200-300ms.

Development

  • open test/dummy
  • bundle exec rake db:migrate
  • bundle exec rails s -b 0.0.0.0
  • open localhost:3000/report.pdf
  • modify templates in app/pdf

Adding a new template

  • add new template in lib/generators/rails_pdf/templates and add folder with template (html,css,js)
  • you can use CSS/JS from templates/shared folder
  • edit lib/generators/rails_pdf/rails_pdf_generator.rb add new type of report
  • create screenshot of template and put in docs folder
  • update docs
  • create PR

TODO

  • more starter templates
    • add starter template with page numbers
  • add different charts
  • better way to include JS/CSS/images
  • maybe we don't need to include all views
  • support non-rails apps
  • specs
  • travis CI
  • codeclimate
  • check support for older Rails (should work but check is needed)
  • check embedding in emails
  • maybe add ability to save webpage by url or HTML snippet to PDF, e.g. RailsPDF.url("http://google.com").render_to_file("google.com.pdf")
  • better gem logo :)

Production

Before deploy app to production please don't forget to install Relaxed.JS on it.

Contributing

You are welcome to contribute.

Contributors

  • https://github.com/gambala

License

The gem is available as open source under the terms of the MIT License.