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?

- 357
- 2
- 7
- 15
-
if you think that the answer is working for you then you can mark it as accepted answer. – VStoykov Jun 01 '17 at 10:40
5 Answers
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.

- 1,705
- 12
- 15
-
1
-
-
-
You need to do `header_html.flush()` after `header_html.write(...)`, otherwise you are simply using a blank temp file for header and footer, so they looks like they are ignored. – Alba Hoo Mar 22 '22 at 05:15
-
-
@AgentNirmites after `header_html.write(render_to_string(header_template, context).encode('utf-8'))`, you can try `header_html.flush()` to make this header_html file persistent with its content, I have also posted a proper answer with some working example in my case. – Alba Hoo Mar 24 '22 at 14:43
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.

- 726
- 2
- 9
- 16
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

- 16,288
- 9
- 49
- 77

- 111
- 1
- 2
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:
- content as
url
orhtml
- header as
html url
orhtml string
- footer as
html url
orhtml 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

- 154
- 8
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()