diff --git a/doc/admin/config.rst b/doc/admin/config.rst index cf0858974..aecfda11c 100644 --- a/doc/admin/config.rst +++ b/doc/admin/config.rst @@ -295,5 +295,13 @@ various places like order codes, secrets in the ticket QR codes, etc. Example:: ; Voucher code needs to be < 255 characters, default is 16 voucher_code=16 +External tools +-------------- + +pretix can make use of some external tools if they are installed. Currently, they are all optional. Example:: + + [tools] + pdftk=/usr/bin/pdftk + .. _Python documentation: https://docs.python.org/3/library/configparser.html?highlight=configparser#supported-ini-file-structure .. _Celery documentation: http://docs.celeryproject.org/en/latest/userguide/configuration.html diff --git a/src/pretix/base/pdf.py b/src/pretix/base/pdf.py index 54a842376..09bbf09d6 100644 --- a/src/pretix/base/pdf.py +++ b/src/pretix/base/pdf.py @@ -1,11 +1,15 @@ import copy import logging +import os import re +import subprocess +import tempfile import uuid from collections import OrderedDict from io import BytesIO import bleach +from django.conf import settings from django.contrib.staticfiles import finders from django.utils.formats import date_format from django.utils.translation import ugettext_lazy as _ @@ -222,10 +226,11 @@ class Renderer: self.layout = layout self.background_file = background_file self.variables = get_variables(event) - if self.background_file: - self.bg_pdf = PdfFileReader(BytesIO(self.background_file.read()), strict=False) - else: - self.bg_pdf = None + if not settings.PDFTK: + if self.background_file: + self.bg_pdf = PdfFileReader(BytesIO(self.background_file.read()), strict=False) + else: + self.bg_pdf = None @classmethod def _register_fonts(cls): @@ -340,21 +345,40 @@ class Renderer: canvas.showPage() def render_background(self, buffer, title=_('Ticket')): - from PyPDF2 import PdfFileWriter, PdfFileReader - buffer.seek(0) - new_pdf = PdfFileReader(buffer) - output = PdfFileWriter() + if settings.PDFTK: + buffer.seek(0) + with tempfile.TemporaryDirectory() as d: + with open(os.path.join(d, 'back.pdf'), 'wb') as f: + f.write(self.background_file.read()) + with open(os.path.join(d, 'front.pdf'), 'wb') as f: + f.write(buffer.read()) + subprocess.run([ + settings.PDFTK, + os.path.join(d, 'front.pdf'), + 'multistamp', + os.path.join(d, 'back.pdf'), + 'output', + os.path.join(d, 'out.pdf'), + 'compress' + ], check=True) + with open(os.path.join(d, 'out.pdf'), 'rb') as f: + return BytesIO(f.read()) + else: + from PyPDF2 import PdfFileWriter, PdfFileReader + buffer.seek(0) + new_pdf = PdfFileReader(buffer) + output = PdfFileWriter() - for page in new_pdf.pages: - bg_page = copy.copy(self.bg_pdf.getPage(0)) - bg_page.mergePage(page) - output.addPage(bg_page) + for page in new_pdf.pages: + bg_page = copy.copy(self.bg_pdf.getPage(0)) + bg_page.mergePage(page) + output.addPage(bg_page) - output.addMetadata({ - '/Title': str(title), - '/Creator': 'pretix', - }) - outbuffer = BytesIO() - output.write(outbuffer) - outbuffer.seek(0) - return outbuffer + output.addMetadata({ + '/Title': str(title), + '/Creator': 'pretix', + }) + outbuffer = BytesIO() + output.write(outbuffer) + outbuffer.seek(0) + return outbuffer diff --git a/src/pretix/settings.py b/src/pretix/settings.py index 5f179e4a7..7092b974e 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -57,6 +57,8 @@ else: debug_fallback = "runserver" in sys.argv DEBUG = config.getboolean('django', 'debug', fallback=debug_fallback) +PDFTK = config.get('tools', 'pdftk', fallback=None) + db_backend = config.get('database', 'backend', fallback='sqlite3') if db_backend == 'postgresql_psycopg2': db_backend = 'postgresql'