Weasyprint and CSS: header, footer, pagebreak and positioning

6.5k Views Asked by At

I am building a invoice report template in HTML and using Weasyprint to generate it as a PDF(and as a docx eventually)

The issue I'm having is in the inability to not only page-break, but to also generate a running header and footer properly without the body contents overlapping and turning my data into Zalgo texts.

My report template has this simple format:

+==========================+
+ Header                   + 
+==========================+
+ Body                     +
+==========================+
+ Footer                   +
+==========================+

Both the header and footer will more or less be prevalent over the pages. The header includes a page counter while the footer will display a value within a textbox only on the last page.

Both my header and footer are referenced to separate HTML templates for versatility, using the include keyword to include them. As this is a template for an invoice, the header is more similar to a letter head.

The main content will be in the body. If the content is too much, it will break and continue on to the next page.

For all 3 parts, I am using tables for formatting purpose, mainly to keep my data aligned.

Here is a sample of my main HTML body:

<!DOCTYPE html>
<html>
<head>
    <style type="text/css" media="all">
        @page {
                size: A4 portrait; /* can use also 'landscape' for orientation */
                margin: 1cm;

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

                @bottom-left{
                    content: element(footer);
                }

        }

        header, footer, .body_content{
           font-size: 12px;
           /* color: #000; */
           font-family: Arial;
           width: 100%;
           position: relative;
        }


        header {
            /*position: fixed;*/
            /*position: running(header); */
            /*display: block; */
        }

        footer {
            position: fixed;
            /*position: running(footer);*/
            /*position: absolute;*/
            bottom: 0;
            /*display: block;*/
        }

        .body_content {
            position: relative;
            page-break-inside: auto;
            height: 320pt;
            /*overflow: hidden;*/
        }
    </style>

</head>

<body>
    <header>
    {% include 'sampleTemplate_header.html' %}
    </header>
    <div >
        <table class="body_content">
            <tbody >
                <tr style="padding-top:5px;" >
                    <td style="width:60%;" >
                    </td>
                    <td style="width:10%;" >
                    </td>
                    <td style="width:15%;" >
                    </td>
                    <td style="width:15%;" >
                    </td>
                </tr>
                <tr>
                </tr>
                <tr >
                    <td style="width:60%;" >
                    </td>
                    <td style="width:10%;" >
                    </td>
                    <td style="width:15%;" align="right" >
                    </td>
                    <td style="width:15%;" align="center" >
                    </td>
                </tr>


                    <tr >
                        <td style="width:60%;" id="testCell" >
                        </td>
                        <td style="width:10%;" >
                        </td>
                        <td style="width:15%;" >
                        </td>
                        <td style="width:15%;" >
                        </td>
                    </tr>

            </tbody>
        </table>

    </div>  

    <footer>
    {% include 'sampleTemplate_footer.html' %}
    </footer>
</body>

</html>

The CSS portion has a lot of commented code due to my experimenting on the layout, but as much as I change, I can't seem to get the layout I need.

One of my most prevalent issue has been the overlapping text of the body content with the header or the footer. The later even happens, despite a forced page-break-after.

1

There are 1 best solutions below

0
On

I got it working with the running elements (I was reading about it here: https://www.w3.org/TR/css-gcpm-3/#running-elements).

If you put the footer before the main content, then it will show on every page. Running elements apparently moves an element from the main flow into the margin, so I guess if it isn't in the page yet it can't move it into the margin.

To get the header/footer to stop overlapping with the contents, I had to play around with the margin value for the @page. Since running elements moves the element into the margin, making the margin bigger gives it more space. In the example below, if you decrease the value for top (or bottom) margin, the header (or footer) will overlap.

Sometimes I have to set the header/footer height value to get it to position in the margin properly, but I didn't have to for this example.

<!DOCTYPE html>
<html>
<head>
    <style type="text/css" media="all">
        @page {
                size: A4 portrait; /* can use also 'landscape' for orientation */
                margin: 100px 1cm 150px 1cm;

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

                @bottom-left{
                    content: element(footer);
                }

        }


        header {
            position: running(header); 
            /*height: 100px;*/
        }

        footer {
            position: running(footer);
            /*height: 150px;*/
        }
    </style>

</head>

<body>
    <header>
    multiline<br>header<br>lots<br>of<br>lines<br>here<br>
    </header>

    <footer>
    multiline<br>footer<br>lots<br>of<br>lines<br>here
    </footer>
    <div >
        stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff

    </div>  
</body>

</html>