Tips and Tricks for Using Weasyprint to Generate PDFs

Published on August 26, 2023Tips and Tricks for Using Weasyprint to Generate PDFs image

Weasyprint, if you’ve not known before is a wonderful tool to generate PDFs from HTML and CSS. It’s a Python library and it’s very easy to use.

In this post, I’m writing about the things I learned when I was using Weasyprint on a project. Hope that this will help someone!

Setting page size

Using the @page property of CSS one can set the page size. The default page size is A4. But if you want to change it, you can do it like this:

@page {
    size: A3 landscape;
}

See the MDN docs for @page rules’s size property for more information about the values that can be used.

Setting page margins

Similar to setting page size, you can set the page margins using the @page property.

@page {
    margin: 1cm;
}

Page breaks

You can set page breaks using the page-break-before and page-break-after properties. I usually define a class called .page-break and use it like this:

<style>
    .page-break {
        page-break-after: always;
    }
</style>
<div class="page-break"></div>

You can use the page-break-before and page-break-after properties directly on the element you want to break the page before or after.

If you’ve some content that you want to keep together, you can use the page-break-inside property.

.page-break-inside {
    page-break-inside: avoid;
}

See the MDN docs for page-break-inside for more information.

Displaying page numbers

I was generating a report and I wanted to display the page numbers on the bottom of the page. I used the content property of CSS to do this.

@page {
    @bottom-center {
        content: counter(page);
    }
}

The counter function is used to display the page number. The page is a counter that is incremented for each page. You can also use the counter function to display the total number of pages.

@page {
    @bottom-center {
        content: counter(page) " of " counter(pages);
    }
}

See the MDN docs for counter function for more information.

Displaying headers and footers

While you can use the @page property to display headers/footer which contains text displaying something like a logo (an SVG Image) was tricky. I was asked to display the logo of my university at the top of each page. Here’s some code that I used to do this.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        header {
            position: running(header);
            /* height of the header */
            height: 4cm;
        }

        @page {
            size: A4;
            /* Add margin for the header */
            margin-top: 4cm;

            @top-center {
                content: element(header);
            }
        }

        .page-break {
            page-break-after: always;
        }
    </style>
</head>
<body>
    <header>
        <!-- set the height to match the header height -->
        <img src="header.svg" style="height: 4cm" />
    </header>
    <h1>Page 1</h1>
    <div class="page-break"></div>
    <h1>Page 2</h1>
</body>
</html>

You could add anything you would like to display in the header inside the header element. Similarly, you can change the @top-center to @bottom-center to display the footer.

Displaying long tables

If you’ve a long table that spans multiple pages, you can use the table-header and table-footer properties to display the table header and footer on each page.

thead {
    display: table-header-group;
}
tfoot {
    display: table-footer-group;
}

Disabling word wrapping

If you’ve a long word that you don’t want to wrap, you can use the white-space property to do this.

.long-word {
    white-space: nowrap;
}

For me, I had a table with a column that contained a long word. I used the white-space property to disable word wrapping for that column, YMMV.

That’s all

That’s all for now. I’ll update this post if I learn something new. If you’ve any questions, feel free to ask me on twitter or open a new discussion.