elixir-pdf-generator

by gutschilla

Create PDFs with wkhtmltopdf or puppeteer/chromium from Elixir.

226 Stars 52 Forks Last release: 10 months ago (v0.6.0) MIT License 137 Commits 19 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

elixir-pdf-generator

A wrapper for both wkhtmltopdf and chrome-headless plus PDFTK (adds in encryption) for use in Elixir projects.

Latest release v0.6.2 on 2020-03-05

CircleCI

  • 0.6.2
    • BUGFIX: missing
      priv
      directory in hex release prevented
      make chrome
      to work for project-local chrome-headless-redereder-pdf binary. Reported by Manuel Rubio
  • 0.6.1
    • documentation about keeping
      xvfb
      buffer, thanks for your feedback, kiere
  • 0.6.0
    • introducting
      make
      as build tool (optional) for chromium binaries (puppeteer)
    • BUGFIX: documentation: option
      pagesize
      requires string argument (for example
      "letter"
      or
      "A4"
      )
    • updated some npm dependencies for chromium

For a proper changelog, see CHANGES

Usage

Hint: In IEX,

h PdfGenerator.generate
is your friend.

Add this to your dependencies in your mix.exs:

    def application do
        [applications: [
            :logger,
            :pdf_generator # =0.6.0" }, # 

If you want to use a locally-installed chromium in RELEASES (think

mix
release
), alter your mixfile to let
make
take care of compilation and dependency-fetching:
defp deps do
  [
    { :pdf_generator, ">=0.6.2", compile: "make chrome" }
    # if you run into issues try
    # {:pdf_generator, "~> 0.6.2", github: "gutschilla/elixir-pdf-generator", compile: "make chrome"}
  ]
end

This will embed a 300 MB (yes, that large) Chromium binary into your priv folder which will survive packaging as Erlang release. This can be handy as this will run on slim Alpine docker images with just NodeJS installed.

The recommended way still is to install Chromium/Puppeteer globally and set the

prefer_system_executable: true
option when generating PDFs.

In development: While this usually works, it unfortunately leads to pdf_generator to be compiled all the time again and again due to my bad Makefile skills. Help is very much appreciated.

Try it out

Pass some HTML to PdfGenerator.generate:

$ iex -S mix

html = "

Hi there!

"

be aware, this may take a while...

{:ok, filename} = PdfGenerator.generate(html, page_size: "A5") {:ok, pdf_content} = File.read(filename)

or, if you prefer methods that raise on error:

filename = PdfGenerator.generate!(html, generator: :chrome)

Or, pass some URL

PdfGenerator.generate {:url, "http://google.com"}, page_size: "A5"

Or use the bang-methods:

filename   = PdfGenerator.generate! "..."
pdf_binary = PdfGenerator.generate_binary! "..."

Chrome

Or, use chrome-headless.

Unless your mixfile sais

{:pdf_generator, ">=6.0.0", compile: "make chrome"}
Chrome won't be installed into your application. Please set the
prefer_system_executable: true
option in this case.
html_works_too = "

Minimalism!" {:ok, filename} = PdfGenerator.generate html_works_too, generator: :chrome, prefer_system_executable: true

Docker

If using chrome in a superuser/root environment (read: docker), make sure to pass an option to chrome to disable sandboxing. And be aware of the implications.

html_works_too = "

I need Docker, baby docker is what I need!" {:ok, filename} = PdfGenerator.generate html_works_too, generator: :chrome, no_sandbox: true, page_size: "letter"

System prerequisites

It's either

  • wkhtmltopdf or

  • nodejs (for Chrome-headless/Puppeteer)

chrome-headless

This will allow you to make more use of Javascript and advanced CSS as it's just your Chrome/Chromium browser rendering your web page as HTML and printing it as PDF. Rendering tend to be a bit faster than with wkhtmltopdf. The price tag is that PDFs printed with chrome/chromium are usually considerably bigger than those generated with wkhtmltopdf.

global install (great for Docker images)

Run

npm -g install chrome-headless-render-pdf puppeteer
.

This requires nodejs, of course. This will install a recent chromium and chromedriver to run Chrome in headless mode and use this browser and its API to print PDFs globally on your machine.

If you prefer a project-local install, use the

compile: "make chrome"
option in your mixfile's dependency-line.

On some machines, this doesn't install Chromium and fails. Here's how to get this running on Ubuntu 18:

DEBIAN_FRONTEND=noninteractive PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=TRUE \
  apt-get install -y chromium-chromedriver \
  && npm -g install chrome-headless-render-pdf puppeteer

local install

Run

make priv/node_modules
. This requires both
nodejs
(insallation see above) and
make
.

Or, run

cd priv && npm install

wkhtmltopdf

  • Alpine (tested on 3.11):

    apk add wkhtmltodf
    - gone are the days of manually fumbling around with wkhtmltopdf and its musl preference over glibc.
  • Ubuntu 19.10:

    apt-get install wkhtmltopdf
    and you'll have 0.12.5 on $PATH
  • Ubuntu 18.04: Download wkhtmltopdf and place it in your $PATH. Current binaries can be found here: http://wkhtmltopdf.org/downloads.html

For the impatient (Ubuntu 18.04 Bionic Beaver):

  apt-get -y install xfonts-base xfonts-75dpi \
    && wget https://downloads.wkhtmltopdf.org/0.12/0.12.5/wkhtmltox_0.12.5-1.bionic_amd64.deb \
    && dpkg -i wkhtmltox_0.12.5-1.bionic_amd64.deb

For other distributions, refer to http://wkhtmltopdf.org/downloads.html – For example, replace

bionic
with
xenial
if you're on Ubuntu 16.04.

optional dependencies

  1. optional: Install
    xvfb
    (shouldn't be required with the binary mentioned above):

To use other wkhtmltopdf executables comiled with an unpatched Qt on systems without an X window server installed, please install

xvfb-run
from your repository (on Debian/Ubuntu:
sudo apt-get install xvfb
).

I am glad to have received feedback that people are actually using this feature.

  1. optional: Install
    pdftk
    via your package manager or homebrew. The project page also contains a Windows installer. On Debian/Ubuntu just type:
    apt-get -y install pdftk

Options and Configuration

This module will automatically try to finde both

wkhtmltopdf
and
pdftk
in your path. But you may override or explicitly set their paths in your
config/config.exs
.
config :pdf_generator,
    wkhtml_path:    "/usr/bin/wkhtmltopdf",   # 

or, if you prefer chrome-headless

config :pdf_generator,
    use_chrome: true,                           # 

More options

  • filename
    - filename for the output pdf file (without .pdf extension, defaults to a random string)
  • page_size
    :
    • defaults to
      "A4"
      , see
      wkhtmltopdf
      for more options
    • "letter"
      (for US letter) be translated to 8x11.5 inches (currently, only in chrome).
  • open_password
    : requires
    pdftk
    , set password to encrypt PDFs with
  • edit_password
    : requires
    pdftk
    , set password for edit permissions on PDF
  • shell_params
    : pass custom parameters to
    wkhtmltopdf
    . CAUTION: BEWARE OF SHELL INJECTIONS!
  • command_prefix
    : prefix
    wkhtmltopdf
    with some command or a command with options (e.g.
    xvfb-run -a
    ,
    sudo
    ..)
  • delete_temporary
    : immediately remove temp files after generation

Contribution; how to run tests

You're more than welcome ot submit patches. Please run

mix test
to ensure at bit of stability. Tests require a full-fledged environment, with all of
wkhtmltopdf
,
xvfb
and
chrome-headless-render-pdf
available path. Also make to to have run
npm install
in the app's base directory (will install chrome-headless-render-pdf non-globally in there). With all these installed,
mix test
should run smoothly.

Hint: Getting

:enoent
errors ususally means that chrome or xvfb couldn't be run. Yes, this should output a nicer error.

Heroku Setup

If you want to use this project on heroku, you can use buildpacks instead of binaries to load

pdftk
and
wkhtmltopdf
:
https://github.com/fxtentacle/heroku-pdftk-buildpack
https://github.com/dscout/wkhtmltopdf-buildpack
https://github.com/HashNuke/heroku-buildpack-elixir
https://github.com/gjaldon/phoenix-static-buildpack

note: The list also includes Elixir and Phoenix buildpacks to show you that they must be placed after

pdftk
and
wkhtmltopdf
. It won't work if you load the Elixir and Phoenix buildpacks first.

Running non-patched wkhtmltopdf headless

This section only applies to

wkhtmltopdf
users using wkhtmltopdf w/o the qt patch. If you are using the latest 0.12 binaries from https://downloads.wkhtmltopdf.org (recommended) you can safely skip this section.

If you want to run

wkhtmltopdf
with an unpatched verison of webkit that requires an X Window server, but your server (or Mac) does not have one installed, you may find the
command_prefix
handy:
PdfGenerator.generate "

This can also be configured globally in your

config/config.exs
:
config :pdf_generator,
    command_prefix: "/usr/bin/xvfb-run"

If you will be generating multiple PDFs simultaneously, or in rapid succession, you will need to configure

xvfb-run
to search for a free X server number, or set the server number explicitly. You can use the
command_prefix
to pass options to the
xvfb-run
command.
config :pdf_generator,
    command_prefix: ["xvfb-run", "-a"]

Documentation

For more info, read the docs on hex or issue

h PdfGenerator.generate
in your iex shell.

Known issues

Unfortunately, with Elixir 1.7+

System.cmd
seems to pass parameters differently to the environment than it did before, now requiring shell options like
--foo=bar
to be split up as
["--foo", "bar"]
. This behaviour seemingly went away with OTP 22 in May 2019 and Elixir 1.8.2. So if you run into issues, try upgrading to the latest Erlang/OTP and Elixir first, and do not hesitate file a report.

Contributing

Contributions (Issues, PRs…) are more than welcome. Please ave a quick read at the Contribution tips, though. It's basically about scope and kindness.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.