Advanced usage

Usage in Python scripts

For basic PDF rendering, you’ll need to use the xhtml2pdf.pisa.CreatePDF() function. Here’s an example script that will generate a test.pdf file with the text “To PDF or not to PDF” in the top left of the page:

# import python module
from xhtml2pdf import pisa

# enable logging
pisa.showLogging()

# Define your page data
source_html = "<html><body><p>To PDF or not to PDF</p></body></html>"

# open output file for writing (truncated binary)
with open("test.pdf", "w+b") as result_file:
    # convert HTML to PDF
    pisa_status = pisa.CreatePDF(
        source_html,       # the HTML to convert
        dest=result_file,  # file handle to receive result
    )

    if pisa_status.err:
        print("An error occurred!")

You can generate files in-memory by writing to a io.StringIO instance.

Usage in Django apps

To allow URL references to be resolved using Django’s STATIC_URL and MEDIA_URL settings, xhtml2pdf allows users to specify a link_callback parameter to point to a function that converts relative URLs to absolute system paths.

import os
from django.conf import settings
from django.contrib.staticfiles import finders
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa


def link_callback(uri, rel):
    """
    Convert HTML URIs to absolute system paths so xhtml2pdf can access those
    resources
    """
    result = finders.find(uri)

    if result:
        if not isinstance(result, (list, tuple)):
            result = [result]
        result = list(os.path.realpath(path) for path in result)
        path = result[0]
    else:
        static_url = settings.STATIC_URL    # Usually /static/
        static_root = settings.STATIC_ROOT  # Usually /home/user/project_static/
        media_url = settings.MEDIA_URL      # Usually /media/
        media_root = settings.MEDIA_ROOT    # Usually /home/user/project_static/media/

        if uri.startswith(media_url):
            path = os.path.join(media_root, uri.replace(media_url, ""))
        elif uri.startswith(static_url):
            path = os.path.join(static_root, uri.replace(static_url, ""))
        else:
            return uri

    # make sure that file exists
    if not os.path.isfile(path):
        raise RuntimeError(
            f'media URI must start with {static_url} or {media_url}'
        )
    return path

Then, in your Django view:

def render_pdf_view(request):
    template_path = 'user_printer.html'
    context = {'myvar': 'this is your template context'}

    # Create a Django response object, and set content type to PDF
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="report.pdf"'

    # find the template and render it.
    template = get_template(template_path)
    html = template.render(context)

    # create a pdf
    pisa_status = pisa.CreatePDF(
       html,
       dest=response,
       link_callback=link_callback,  # defined above
    )

    # if error then show some funny view
    if pisa_status.err:
       return HttpResponse('We had some errors <pre>' + html + '</pre>')

    return response

You can see it in action in demo/djangoproject folder.

Warning

Since 0.2.19 this example needs one more argument. STATIC_ROOT and MEDIA_ROOT are outside the directory of the document being rendered, so the resource policy refuses what the callback resolves and every image is dropped. Name those directories:

from pathlib import Path

from xhtml2pdf.config.resources import ResourceAccessPolicy

POLICY = ResourceAccessPolicy(
    base_dir=Path(settings.STATIC_ROOT),
    extra_roots=(Path(settings.MEDIA_ROOT),),
)

pisa_status = pisa.CreatePDF(
    html,
    dest=response,
    link_callback=link_callback,
    resource_policy=POLICY,
)

A view that renders a template containing anything a user wrote is the case the policy exists for; see Security before widening it.

Usage as a command line tool

xhtml2pdf also provides a convenient tool that you can use to convert HTML files to PDF documents using the command line. In an environment where the package is installed, run:

xhtml2pdf test.html

This basic command will convert the content of test.html to PDF and save it to test.pdf.

The -s/--start-viewer option can be used to start the default PDF viewer after the conversion:

xhtml2pdf -s test.html

Demonstration

Relative references resolved through a link_callback, as this page describes – these very pages build it with one.

advanced-usage.html
<html>
<head>
    <title>Resolving relative references</title>
    <style>
        @page { size: a4 portrait; margin: 2cm; }
        body { font-family: Helvetica; }
        img { width: 120px; }
        .note { background-color: #eeeeec; padding: 0.6em; }
    </style>
</head>
<body>
    <h1>Relative references</h1>

    <p class="note">
        Neither of the images below is addressed by a path this converter could
        open on its own. Each is written the way it would be in a template
        &mdash; <code>img/denker.png</code> &mdash; and turned into a file on
        disk by the <code>link_callback</code> this page describes. The
        documentation build passes one in.
    </p>

    <p><img src="img/denker.png" alt="denker"> <img src="img/beach.jpg" alt="beach"></p>

    <p>
        Without the callback the converter has nothing to fetch and both images
        would be missing from this page.
    </p>
</body>
</html>