ChromicPDF.print_to_pdf
print_to_pdf
, go back to ChromicPDF module for more information.
Specs
print_to_pdf(input :: source() | source_and_options(), opts :: [pdf_option()]) :: return()
Prints a PDF.
This call blocks until the PDF has been created.
Output options
Print and return Base64-encoded PDF
{:ok, blob} = ChromicPDF.print_to_pdf({:url, "file:///example.html"})
# Can be displayed in iframes
"data:application/pdf;base64,\#{blob}"
Print to file
:ok = ChromicPDF.print_to_pdf({:url, "file:///example.html"}, output: "output.pdf")
Print to temporary file
{:ok, :some_result} =
ChromicPDF.print_to_pdf({:url, "file:///example.html"}, output: fn path ->
send_download(path)
:some_result
end)
The temporary file passed to the callback will be deleted when the callback returns.
Input options
ChromicPDF offers two primary methods of supplying Chrome with the HTML source to print. You can choose between passing in an URL for Chrome to load and injecting the HTML markup directly into the DOM through the remote debugging API.
Print from URL
Passing in a URL is the simplest way of printing a PDF. A target in Chrome is told to navigate to the given URL. When navigation is finished, the PDF is printed.
ChromicPDF.print_to_pdf({:url, "file:///example.html"})
ChromicPDF.print_to_pdf({:url, "http:///example.net"})
ChromicPDF.print_to_pdf({:url, "https:///example.net"})
Cookies
If your URL requires authentication, you can pass in a session cookie. The cookie is automatically cleared after the PDF has been printed.
cookie = %{
name: "foo",
value: "bar",
domain: "localhost"
}
ChromicPDF.print_to_pdf({:url, "http:///example.net"}, set_cookie: cookie)
See Network.setCookie
for options. name
and value
keys are required.
Print from in-memory HTML
Alternatively, print_to_pdf/2
allows to pass an in-memory HTML blob to Chrome in a
{:html, blob()}
tuple. The HTML is sent to the target using the Page.setDocumentContent
function. Oftentimes this method is preferable over printing a URL if you intend to render
PDFs from templates rendered within the application that also hosts ChromicPDF, without the
need to route the content through an actual HTTP endpoint. Also, this way of passing the
HTML source has slightly better performance than printing a URL.
ChromicPDF.print_to_pdf(
{:html, "<h1>Hello World!</h1>"}
)
In-memory content can be iodata
In-memory HTML for both the main input parameter as well as the header and footer options
can be passed as iodata
. Such lists
are converted to String before submission to the session process by passing them through
:erlang.iolist_to_binary/1
.
ChromicPDF.print_to_pdf(
{:html, ["<style>p { color: green; }</style>", "<p>green paragraph</p>"]}
)
Caveats
Please mind the following caveats.
References to external files in HTML source
Please note that since the document content is replaced without navigating to a URL, Chrome has no way of telling which host to prepend to relative URLs contained in the source. This means, if your HTML contains markup like
<!-- BAD: relative link to stylesheet in <head> element -->
<head>
<link rel="stylesheet" href="selfhtml.css">
</head>
<!-- BAD: relative link to image -->
<img src="some_logo.png">
... you will need to replace these lines with either absolute URLs or inline data.
Of course, absolute URLs can use the file://
scheme to point to files on the local
filesystem, assuming Chrome has access to them. For the purpose of displaying small
inline images (e.g. logos), data URLs
are a good way of embedding them without the need for an absolute URL.
<!-- GOOD: inline styles -->
<style>
/* ... */
</style>
<!-- GOOD: data URLs -->
<img src="data:image/png;base64,R0lGODdhEA...">
<!-- GOOD: absolute URLs -->
<img src="http://localhost/path/to/image.png">
<img src="file:///path/to/image.png">
Content from Phoenix templates
If your content is generated by a Phoenix template (and hence comes in the form of
{:safe, iodata()}
), you will need to pass it to Phoenix.HTML.safe_to_string/1
first.
content = SomeView.render("body.html") |> Phoenix.HTML.safe_to_string()
ChromicPDF.print_to_pdf({:html, content})
PDF printing options
ChromicPDF.print_to_pdf(
{:url, "file:///example.html"},
print_to_pdf: %{
# Margins are in given inches
marginTop: 0.393701,
marginLeft: 0.787402,
marginRight: 0.787402,
marginBottom: 1.1811,
# Print header and footer (on each page).
# This will print the default templates if none are given.
displayHeaderFooter: true,
# Even on empty string.
# To disable header or footer, pass an empty element.
headerTemplate: "<span></span>",
# Example footer template.
# They are completely unstyled by default and have a font-size of zero,
# so don't despair if they don't show up at first.
# There's a lot of documentation online about how to style them properly,
# this is just a basic example. Also, take a look at the documentation for the
# ChromicPDF.Template module.
# The <span> classes shown below are interpolated by Chrome.
footerTemplate: """
<style>
p {
color: #333;
font-size: 10pt;
text-align: right;
margin: 0 0.787402in;
width: 100%;
z-index: 1000;
}
</style>
<p>
Page <span class="pageNumber"></span> of <span class="totalPages"></span>
</p>
"""
}
)
Please note the camel-case. For a full list of options to the printToPDF
function,
please see the Chrome documentation at:
https://chromedevtools.github.io/devtools-protocol/tot/Page#method-printToPDF
Page size and margins
Chrome will use the provided pagerWidth
and paperHeight
dimensions as the PDF paper
format. Please be aware that the @page
section in the body CSS is not correctly
interpreted, see ChromicPDF.Template
for a discussion.
Header and footer
Chrome's support for native header and footer sections is a little bit finicky. Still, to the best of my knowledge, Chrome is currently the only well-functioning solution for HTML-to-PDF conversion if you need headers or footers that are repeated on multiple pages even in the presence of body elements stretching across a page break.
In order to make header and footer visible in the first place, you will need to be aware of a couple of caveats:
HTML for header and footer is interpreted in a new page context which means no body styles will be applied. In fact, even default browser styles are not present, so all content will have a default
font-size
of zero, and so on.You need to make space for the header and footer templates first, by adding page margins. Margins can either be given using the
marginTop
andmarginBottom
options or with CSS styles. If you use the options, the height of header and footer elements will inherit these values. If you use CSS styles, make sure to set the height of the elements in CSS as well.Header and footer have a default padding to the page ends of 0.4 centimeters. To remove this, add the following to header/footer template styles (source).
#header, #footer { padding: 0 !important; }
Header and footer have a default
zoom
level of 1/0.75 so everything appears to be smaller than in the body when the same styles are applied.If header or footer are not displayed even though they should, make sure your HTML is valid. Tuning the margins for an hour looking for mistakes there, only to discover that you are missing a closing
</style>
tag, can be quite painful.Javascript is not interpreted.
Background colors are not applied unless you include
-webkit-print-color-adjust: exact
in your stylesheet.
See print_header_footer_template.html
from the Chromium sources to see how these values are interpreted.
Dynamic Content
Evaluate script before printing
In case your print source is generated by client-side scripts, for instance to render graphics or load additional resources, you can trigger these by evaluating a JavaScript expression before the PDF is printed.
evaluate = %{
expression: """
document.querySelector('body').innerHTML = 'hello world';
"""
}
ChromicPDF.print_to_pdf({:url, "http://example.net"}, evaluate: evaluate)
If your script returns a Promise, Chrome will wait for it to be resolved.
Wait for attribute on element
Some JavaScript libraries signal their successful initialization to the user by setting an
attribute on a DOM element. The wait_for
option allows you to wait for this attribute to
be set before printing. It evaluates a script that repeatedly queries the element given by
the query selector and tests whether it has the given attribute.
wait_for = %{
selector: "#my-element",
attribute: "ready-to-print"
}
ChromicPDF.print_to_pdf({:url, "http:///example.net"}, wait_for: wait_for)