Skip to content

Support for Table of Contents #136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions wkhtmltopdf/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ def test_wkhtmltopdf(self):
finally:
temp_file.close()

def test_table_of_contents(self):
title = 'Chapter 1'
template = loader.get_template('sample.html')
temp_file = render_to_temporary_file(template, context={'title': title})
try:
pdf_output = wkhtmltopdf(pages=[temp_file.name])
self.assertTrue(pdf_output.find("Pages 2") > -1)
finally:
temp_file.close()

def test_wkhtmltopdf_with_unicode_content(self):
"""A wkhtmltopdf call should render unicode content properly"""
title = u'♥'
Expand Down
48 changes: 42 additions & 6 deletions wkhtmltopdf/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from copy import copy
from itertools import chain
import logging
import os
import re
import sys
Expand All @@ -24,6 +25,8 @@

from .subprocess import check_output

logger = logging.getLogger(__name__)

NO_ARGUMENT_OPTIONS = ['--collate', '--no-collate', '-H', '--extended-help', '-g',
'--grayscale', '-h', '--help', '--htmldoc', '--license', '-l',
'--lowquality', '--manpage', '--no-pdf-compression', '-q',
Expand Down Expand Up @@ -106,6 +109,7 @@ def wkhtmltopdf(pages, output=None, **kwargs):
if output is None:
# Standard output.
output = '-'
has_cover = kwargs.pop('has_cover', False)

# Default options:
options = getattr(settings, 'WKHTMLTOPDF_CMD_OPTIONS', None)
Expand All @@ -115,6 +119,18 @@ def wkhtmltopdf(pages, output=None, **kwargs):
options = copy(options)
options.update(kwargs)

page_list = list(pages)

# handle Table of Contents
use_toc = options.pop('toc', False)
toc_xsl = options.pop('toc_xsl', '')
if use_toc:
insert_at = 1 if has_cover else 0
if toc_xsl:
page_list.insert(insert_at, 'toc --xsl-style-sheet %s' % toc_xsl)
else:
page_list.insert(insert_at, 'toc')

# Force --encoding utf8 unless the user has explicitly overridden this.
options.setdefault('encoding', 'utf8')

Expand All @@ -127,9 +143,12 @@ def wkhtmltopdf(pages, output=None, **kwargs):

ck_args = list(chain(shlex.split(cmd),
_options_to_args(**options),
list(pages),
page_list,
[output]))
ck_kwargs = {'env': env}
ck_kwargs = {
'env': env,
'shell': True
}
# Handling of fileno() attr. based on https://github.com/GrahamDumpleton/mod_wsgi/issues/85
try:
i = sys.stderr.fileno()
Expand All @@ -138,21 +157,28 @@ def wkhtmltopdf(pages, output=None, **kwargs):
# can't call fileno() on mod_wsgi stderr object
pass

ck_args = ' '.join(ck_args)
return check_output(ck_args, **ck_kwargs)

def convert_to_pdf(filename, header_filename=None, footer_filename=None, cmd_options=None):
def convert_to_pdf(filename, header_filename=None, footer_filename=None, cmd_options=None, cover_filename=None):
# Clobber header_html and footer_html only if filenames are
# provided. These keys may be in self.cmd_options as hardcoded
# static files.
# The argument `filename` may be a string or a list. However, wkhtmltopdf
# will coerce it into a list if a string is passed.
cmd_options = cmd_options if cmd_options else {}

if cover_filename:
pages = [cover_filename, filename]
cmd_options['has_cover'] = True
else:
pages = [filename]

if header_filename is not None:
cmd_options['header_html'] = header_filename
if footer_filename is not None:
cmd_options['footer_html'] = footer_filename
return wkhtmltopdf(pages=filename, **cmd_options)
return wkhtmltopdf(pages=pages, **cmd_options)

class RenderedFile(object):
"""
Expand All @@ -179,7 +205,8 @@ def __del__(self):
if self.temporary_file is not None:
self.temporary_file.close()

def render_pdf_from_template(input_template, header_template, footer_template, context, request=None, cmd_options=None):
def render_pdf_from_template(input_template, header_template, footer_template, context, request=None, cmd_options=None,
cover_template=None):
# For basic usage. Performs all the actions necessary to create a single
# page PDF from a single template and context.
cmd_options = cmd_options if cmd_options else {}
Expand Down Expand Up @@ -211,10 +238,19 @@ def render_pdf_from_template(input_template, header_template, footer_template, c
)
footer_filename = footer_file.filename

cover = None
if cover_template:
cover = RenderedFile(
template=cover_template,
context=context,
request=request
)

return convert_to_pdf(filename=input_file.filename,
header_filename=header_filename,
footer_filename=footer_filename,
cmd_options=cmd_options)
cmd_options=cmd_options,
cover_filename=cover.filename if cover else None)

def content_disposition_filename(filename):
"""
Expand Down
7 changes: 6 additions & 1 deletion wkhtmltopdf/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def __init__(self, request, template, context=None,
filename=None, show_content_in_browser=None,
header_template=None, footer_template=None,
cmd_options=None, *args, **kwargs):
cover_template = kwargs.pop('cover_template', None)

super(PDFTemplateResponse, self).__init__(request=request,
template=template,
Expand All @@ -54,6 +55,7 @@ def __init__(self, request, template, context=None,

self.header_template = header_template
self.footer_template = footer_template
self.cover_template = cover_template

if cmd_options is None:
cmd_options = {}
Expand All @@ -75,7 +77,8 @@ def rendered_content(self):
self.resolve_template(self.footer_template),
context=self.resolve_context(self.context_data),
request=self._request,
cmd_options=cmd_options
cmd_options=cmd_options,
cover_template=self.resolve_template(self.cover_template)
)

class PDFTemplateView(TemplateView):
Expand All @@ -91,6 +94,7 @@ class PDFTemplateView(TemplateView):
template_name = None
header_template = None
footer_template = None
cover_template = None

# TemplateResponse classes for PDF and HTML
response_class = PDFTemplateResponse
Expand Down Expand Up @@ -147,6 +151,7 @@ def render_to_response(self, context, **response_kwargs):
header_template=self.header_template,
footer_template=self.footer_template,
cmd_options=cmd_options,
cover_template=self.cover_template,
**response_kwargs
)
else:
Expand Down