Flask-WeasyPrint

Make PDF with WeasyPrint in your Flask app.

Installation

Once you have WeasyPrint installed and working, just install the extension with pip:

$ pip install Flask-WeasyPrint

Introduction

Let’s assume you have a Flask application serving an HTML document at http://example.net/hello/ with a print-ready CSS stylesheet. WeasyPrint can render this document to PDF:

from weasyprint import HTML
pdf = HTML('http://example.net/hello/').write_pdf()

WeasyPrint will fetch the stylesheet, the images as well as the document itself over HTTP, just like a web browser would. Of course, going through the network is a bit silly if WeasyPrint is running on the same server as the application. Flask-WeasyPrint can help:

from my_flask_application import app
from flask_weasyprint import HTML
with app.test_request_context(base_url='http://example.net/'):
    # /hello/ is resolved relative to the context’s URL.
    pdf = HTML('/hello/').write_pdf()

Just import HTML() or CSS() from flask_weasyprint rather than weasyprint, and use them from within a Flask request context. For URLs below the application’s root URL, Flask-WeasyPrint will short-circuit the network and make the request at the WSGI level, without leaving the Python process.

Note that from a Flask view function you already are in a request context and thus do not need test_request_context().

An example app

Here is a simple hello world application that uses Flask-WeasyPrint:

from flask import Flask, render_template, url_for
from flask_weasyprint import HTML, render_pdf

app = Flask(__name__)

@app.route('/hello/', defaults={'name': 'World'})
@app.route('/hello/<name>/')
def hello_html(name):
    return render_template('hello.html', name=name)

@app.route('/hello_<name>.pdf')
def hello_pdf(name):
    # Make a PDF from another view
    return render_pdf(url_for('hello_html', name=name))

# Alternatively, if the PDF does not have a matching HTML page:

@app.route('/hello_<name>.pdf')
def hello_pdf(name):
    # Make a PDF straight from HTML in a string.
    html = render_template('hello.html', name=name)
    return render_pdf(HTML(string=html))

templates/hello.html:

<!doctype html>
<title>Hello</title>
<link rel=stylesheet href="{{ url_for('static', filename='style.css') }}" />

<p>Hello, {{ name }}!</p>
<nav><a href="{{ url_for('hello_pdf', name=name) }}">Get as PDF</a></nav>

static/style.css:

body { font: 2em Fontin, serif }
nav { font-size: .7em }

@page { size: A5; margin: 1cm }
@media print {
    nav { display: none }
}

render_pdf() helps making a Response with the correct MIME type. You can give it an URL or an HTML object.

In the HTML you can use url_for() or relative URLs. Flask-WeasyPrint will do the right thing to fetch resources and make hyperlinks absolute in the PDF output.

In CSS, @page and @media print can be used to have print-specific styles. Here the “Get as PDF” link is not displayed in the PDF itself, although it still exists in the HTML.

flask_weasyprint.test_app.run()[source]

A more involved application, with a dynamic SVG graph.

Run it with python -m flask_weasyprint.test_app or have a look at the source code.

API

flask_weasyprint.make_flask_url_dispatcher()[source]

Return an URL dispatcher based on the current request context.

You generally don’t need to call this directly.

The context is used when the dispatcher is first created but not afterwards. It is not required after this function has returned.

Dispatch to the context’s app URLs below the context’s root URL. If the app has a SERVER_NAME config, also accept URLs that have that domain name or a subdomain thereof.

flask_weasyprint.make_url_fetcher(dispatcher=None, next_fetcher=weasyprint.default_url_fetcher)[source]

Return an function suitable as a url_fetcher in WeasyPrint. You generally don’t need to call this directly.

If dispatcher is not provided, make_flask_url_dispatcher() is called to get one. This requires a request context.

Otherwise, it must be a callable that take an URL and return either None or a (wsgi_callable, base_url, path) tuple. For None next_fetcher is used. (By default, fetch normally over the network.) For a tuple the request is made at the WSGI level. wsgi_callable must be a Flask application or another WSGI callable. base_url is the root URL for the application while path is the path within the application. Typically base_url + path is equal or equivalent to the passed URL.

flask_weasyprint.HTML(guess=None, **kwargs)[source]

Like weasyprint.HTML() but:

  • make_url_fetcher() is used to create an url_fetcher
  • If guess is not a file object, it is an URL relative to the current request context. This means that you can just pass a result from flask.url_for().
  • If string is passed, base_url defaults to the current request’s URL.

This requires a Flask request context.

flask_weasyprint.CSS(guess=None, **kwargs)[source]

Like weasyprint.CSS() but:

  • make_url_fetcher() is used to create an url_fetcher
  • If guess is not a file object, it is an URL relative to the current request context. This means that you can just pass a result from flask.url_for().
  • If string is passed, base_url defaults to the current request’s URL.

This requires a Flask request context.

flask_weasyprint.render_pdf(html, stylesheets=None, download_filename=None)[source]

Render a PDF to a response with the correct Content-Type header.

Parameters:
  • html – Either a weasyprint.HTML object or an URL to be passed to flask_weasyprint.HTML(). The latter case requires a request context.
  • stylesheets – A list of user stylesheets, passed to write_pdf()
  • download_filename – If provided, the Content-Disposition header is set so that most web browser will show the “Save as…” dialog with the value as the default filename.
Returns:

a flask.Response object.

Changelog

Version 0.5

Released on 2015-03-27.

Don’t crash on URLs with no hostname, including ‘data:’ URLs.

Version 0.4

Released on 2013-06-13.

Add Python 3.3 support. This requires Flask >= 0.10 and Werkzeug >= 0.9.

Version 0.3

Released on 2013-02-27.

Fix Unicode %-encoded URLs.

Version 0.2

Released on 2012-07-23.

Add URL dispatchers and make Flask-WeasyPrint do the right thing with apps that use subdomains (when the SERVER_NAME config is set).

Version 0.1

Released on 2012-07-19.

First public release.