Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate PDF with WeasyPrint having common header/footer and pagination

I am using WeasyPrint to generate PDF in Django. I can generate pdf from a static html file like below -

from django.template import Context, Template
import weasyprint

with open('static_file.html', 'r') as myfile:
    html_str = myfile.read()

template = Template(html_message)
    context = Context({'some_key': 'some_value'})
    rendered_str = template.render(context)

weasyprint.HTML(string=rendered_str).write_pdf('generated.pdf')

But I want to generate a PDF in which, I can include a common header/footer in each page and add pagination.

Also it will be very helpful if anyone can tell how to include a custom font to generate the PDF. I have installed the font in the OS (Ubuntu 14.04) but it is not working.

I have searched a lot on the web about these. But could not find a proper solution.

like image 642
Tanvir Hossain Bhuiyan Avatar asked Oct 09 '16 09:10

Tanvir Hossain Bhuiyan


3 Answers

Since Weasyprint supports CSS Paged Media Module Level 3, simple headers and footers (e.g. pagination, like you mentioned) can be accomplished using CSS:

@page {
    @top-right{
        content: "Page " counter(page) " of " counter(pages);
    }
}

Make sure you include your stylesheets when rendering:

HTML(string=rendered_html,
     base_url=settings.SITE_URL).write_pdf(stylesheets=[CSS(settings.STATIC_ROOT + '/css/pdf_render.css')])

However, getting more complex headers/footers to render can be more.. complex. Some people have suggested the method of including a div element in the header that renders only for print (but I must admit I have only been able to get simple elements to render properly with this method):

@page {
    @top-left {
        content: element(pageHeader);
    }
}
@media print {
    #divHeader{
        position: running(pageHeader);
    }
}

There is also another method using fixed positions, as demonstrated in this gist: https://gist.github.com/pikhovkin/5642563

like image 73
Keith Avatar answered Nov 18 '22 02:11

Keith


Currently running elements are not supported by WeasyPrint. Nevertheless I found a way to achieve the same result using named strings:

 @page {
   @top-center {
     content:  string(title);
   }
 }

 header {
   width: 0;
   height: 0;
   visibility: hidden;
   string-set: title content();
}

Now you can add your content to an invisible HTML header element.

<header>Content of the header goes here</header>
like image 38
adonig Avatar answered Nov 18 '22 03:11

adonig


I could get this to work simply by using position: fixed for the header and footer.

First, as an element in fixed position does not occupy space on the page, you have to take into account for it by giving appropriate margins on the page, e.g.:

@page {
    margin: 5cm 0 3cm 0;
    size: A4;
}

then simply position the header and footer with respect to this margin:

header, footer {
    position: fixed;
    left: 0;
    right: 0;
}
header {
    /* subtract @page margin */
    top: 5cm;
    height: 5cm
}
footer {
    /* subtract @page margin */
    bottom: 3cm;
    height: 3cm;
}

With this you can just put arbitrary HTML in <header> and <footer> elements and they will be repeated on every page.

Page counters do not seem to work there though, so you will need to implement them based @page rules as described in the other answers.

like image 1
Didier L Avatar answered Nov 18 '22 01:11

Didier L