7

I've been searching the web for examples of people using the pdfkit (python wrapper) in implementing headers and footers and could not find any examples.
Would anyone be able to show some examples of how to implement the options in wkhtmltopdf using the pdfkit python wrapper?

user3368835
  • 357
  • 2
  • 7
  • 15

5 Answers5

15

I'm using it only with headers but I think that it will work the same with footers.

You need to have separate html file for the header.

header.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
</head>
<body>

    Code of your header goes here.

</body>
</html>

Then you can use it like that in Python

import pdfkit

pdfkit.from_file('path/to/your/file.html', 'out.pdf', {
    '--header-html': 'path/to/header.html'
})

The tricky part if you use some backend like Django and want to use templates is that you can't pass the header html as rendered string. You need to have a file.

This is what I do to render PDFs with Django.

import os
import tempfile
import pdfkit

from django.template.loader import render_to_string


def render_pdf(template, context, output, header_template=None):
    """
    Simple function for easy printing of pdfs from django templates

    Header template can also be set
    """
    html = render_to_string(template, context)
    options = {
        '--load-error-handling': 'skip',
    }
    try:
        if header_template:
            with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as header_html:
                options['header-html'] = header_html.name
                header_html.write(render_to_string(header_template, context).encode('utf-8'))

        return pdfkit.from_string(html, output, options=options)
    finally:
        # Ensure temporary file is deleted after finishing work
        if header_template:
            os.remove(options['header-html'])

In my example I create temporary file where I put rendered content. Important part is that temporary file need to end with .html and to be deleted manually.

VStoykov
  • 1,705
  • 12
  • 15
4

Improving @V Stoykov answer as it helped me using Flask, the render function with custom header in Flask will be as follows:

import os
import tempfile

import pdfkit
from flask import render_template, make_response


@app.route('/generate_pdf')
def render_pdf_custom_header(foo, bar):
    main_content = render_template('main_pdf.html', foo=foo)
    options = {
        '--encoding': "utf-8"
    }

    add_pdf_header(options, bar)
    add_pdf_footer(options)

    try:
        pdf = pdfkit.from_string(main_content, False, options=options)
    finally:
        os.remove(options['--header-html'])
        os.remove(options['--footer-html'])

    response = build_response(pdf)
    return response

def add_pdf_header(options, bar):
    with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as header:
        options['--header-html'] = header.name
        header.write(
            render_template('header.html', bar=bar).encode('utf-8')
        )
    return

def add_pdf_footer(options):
    # same behaviour as add_pdf_header but without passing any variable
    return

def build_response(pdf):
    response = make_response(pdf)
    response.headers['Content-Type'] = 'application/pdf'
    filename = 'pdf-from-html.pdf'
    response.headers['Content-Disposition'] = ('attachment; filename=' + filename)
    return response

Notice that I used the '--header-html' and '--footer-html' notation as it matches the wkhtmltopdf options format.

Martin Alonso
  • 726
  • 2
  • 9
  • 16
4
options = {
'page-size': 'Letter',
'margin-top': '0.9in',
'margin-right': '0.9in',
'margin-bottom': '0.9in',
'margin-left': '0.9in',
'encoding': "UTF-8",
'header-center': 'YOUR HEADER',
'custom-header' : [
    ('Accept-Encoding', 'gzip')
],
'no-outline':None
}

you can add the header, which you need in the value for header-center

Venkatachalam
  • 16,288
  • 9
  • 49
  • 77
Ngoc-Vuong Ho
  • 111
  • 1
  • 2
0

This question and its answers are quite old and not working for me.

wkhtmltopdf version: $ wkhtmltopdf --version

wkhtmltopdf 0.12.6 (with patched qt)

python 3.8

For wkhtmltopdf, header-html and footer-html can only be URI, e.g. html url or file path, CANNOT be string. so the idea is to keep each html file on cloud or create a temp file in local as footer and header for reference.

Request:

  1. content as url or html
  2. header as html url or html string
  3. footer as html url or html string

Example:

import logging
import os
import tempfile
import pdfkit
from flask import Flask, Response, make_response
from flask_restx import Resource, Api, fields

Request = api.model('Request', {
    'url': fields.String(
        required=False,
        description='url',
        example='https://www.w3schools.com/html/html5_svg.asp',
    ),
    'html': fields.String(
        required=False,
        description='content html string',
        example=example_content_html
    ),
    'header_html': fields.String(
        required=False,
        description='pdf header html string',
        example=example_header_html
    ),
    'footer_html': fields.String(
        required=False,
        description='pdf footer html string',
        example=example_footer_html
    ),
})
@api.route("/convert_html_to_pdf", endpoint = 'html2pdf')
@api.representation('application/octet-stream')
class PdfConverter(Resource):
    @api.doc(body=Request)
    def post(self):
        logging.info(request.json)
        url = request.json.get('url')
        html = request.json.get('html')
        header_html = request.json.get('header_html')
        footer_html = request.json.get('footer_html')
        header_uri = 'https://xxxxx/header.html' # default header
        footer_uri = 'https://xxxxx/footer.html' # default footer

        if header_html:
            fph = tempfile.NamedTemporaryFile(suffix='.html')
            fph.write(header_html.encode('utf-8'))
            fph.flush()
            header_uri = fph.name

        if footer_html:
            fpf = tempfile.NamedTemporaryFile(suffix='.html')
            fpf.write(footer_html.encode('utf-8'))
            fpf.flush()
            footer_uri = fpf.name

        options = {
          'page-size': 'A4',
          'margin-top': '32mm',
          'header-spacing': 6,
          'footer-spacing': 6,
          'header-html': header_uri,
          'footer-html': footer_uri,
          'margin-right': '0',
          'margin-bottom': '16mm',
          'margin-left': '0',
          'encoding': "UTF-8",
          'cookie': [
            ('cookie-empty-value', '""'),
            ('cookie-name1', 'cookie-value1'),
            ('cookie-name2', 'cookie-value2'),
          ],
          'no-outline': None
        }
        logging.info(options)

        if url:
            # api.payload['requestedBlobUrl'] = url
            # return api.payload
            pdf = pdfkit.from_url(url, options=options)
        else:
            pdf = pdfkit.from_string(html, options=options)

        if header_html:
            fph.close()
        if footer_html:
            fpf.close() # close will delete the temp file

        response = make_response(pdf)
        response.headers['Content-Type'] = 'application/pdf'
        response.headers['Content-Disposition'] = 'attachment;filename=report.pdf'

        return response
Alba Hoo
  • 154
  • 8
0

Python pdfkit wrapper only supports html files as header and footer. To be able to support string, all you need to do is generate a temporary file and delete it on close. Here is my code sample.

Using tempfile with delete=True and suffix='.html' arguments will generate a deletable file on temp.close()

import tempfile

temp = tempfile.NamedTemporaryFile(delete=True,suffix='.html')
with open(temp.name, 'w') as f:
  f.write("""
          <!DOCTYPE html>
          <html>
          <head>
              <meta charset="UTF-8">
          </head>
          <body>

              Code of your header goes here.

          </body>
          </html>
          """)
options = {
    'page-size': 'A4',
    'margin-top': '1in',
    'margin-bottom': '0.75in',
    'margin-right': '0.75in',
    'margin-left': '0.75in',
    'encoding': "UTF-8",
    'header-html': temp.name,
    'footer-center': "Page [page] of [topage]",
    'footer-font-size': "9",
     'custom-header': [
        ('Accept-Encoding', 'gzip')
      ],
     'enable-local-file-access': False,
     'no-outline': None
}
pdf = pdfkit.from_string(html_string, options=options)
temp.close()