diff --git a/src/pretix/base/pdf.py b/src/pretix/base/pdf.py
index 889a7c4e34..ca96f03d95 100644
--- a/src/pretix/base/pdf.py
+++ b/src/pretix/base/pdf.py
@@ -37,6 +37,7 @@ import hashlib
import itertools
import logging
import os
+import re
import subprocess
import tempfile
import uuid
@@ -54,6 +55,7 @@ from django.utils.functional import SimpleLazyObject
from django.utils.html import conditional_escape
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
+from i18nfield.strings import LazyI18nString
from PyPDF2 import PdfFileReader
from pytz import timezone
from reportlab.graphics import renderPDF
@@ -202,6 +204,11 @@ DEFAULT_VARIABLES = OrderedDict((
"editor_sample": 'foo@bar.com',
"evaluate": lambda op, order, ev: op.attendee_email or (op.addon_to.attendee_email if op.addon_to else '')
}),
+ ("pseudonymization_id", {
+ "label": _("Pseudonymization ID (lead scanning)"),
+ "editor_sample": "GG89JUJDTA",
+ "evaluate": lambda orderposition, order, event: orderposition.pseudonymization_id,
+ }),
("event_name", {
"label": _("Event name"),
"editor_sample": _("Sample event name"),
@@ -616,12 +623,14 @@ class Renderer:
preserveAspectRatio=True, anchor='n',
mask='auto')
- def _draw_barcodearea(self, canvas: Canvas, op: OrderPosition, o: dict):
+ def _draw_barcodearea(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):
content = o.get('content', 'secret')
if content == 'secret':
+ # do not use get_text_content because it uses a shortened version of secret
+ # and does not deal with our default value here properly
content = op.secret
- elif content == 'pseudonymization_id':
- content = op.pseudonymization_id
+ else:
+ content = self._get_text_content(op, order, o)
level = 'H'
if len(content) > 32:
@@ -648,20 +657,45 @@ class Renderer:
return self._get_text_content(op, order, o, True)
ev = self._get_ev(op, order)
+
if not o['content']:
return '(error)'
- if o['content'] == 'other':
- return o['text']
+
+ if o['content'] == 'other' or o['content'] == 'other_i18n':
+ if o['content'] == 'other_i18n':
+ text = str(LazyI18nString(o['text_i18n']))
+ else:
+ text = o['text']
+
+ def replace(x):
+ if x.group(1) not in self.variables:
+ return x.group(0)
+ if x.group(1) == 'secret':
+ # Do not use shortened version
+ return op.secret
+ try:
+ return self.variables[x.group(1)]['evaluate'](op, order, ev)
+ except:
+ logger.exception('Failed to process variable.')
+ return '(error)'
+
+ # We do not use str.format like in emails so we (a) can evaluate lazily and (b) can re-implement this
+ # 1:1 on other platforms that render PDFs through our API (libpretixprint)
+ return re.sub(r'\{([a-zA-Z0-9_]+)\}', replace, text)
+
elif o['content'].startswith('itemmeta:'):
return op.item.meta_data.get(o['content'][9:]) or ''
+
elif o['content'].startswith('meta:'):
return ev.meta_data.get(o['content'][5:]) or ''
+
elif o['content'] in self.variables:
try:
return self.variables[o['content']]['evaluate'](op, order, ev)
except:
logger.exception('Failed to process variable.')
return '(error)'
+
return ''
def _draw_imagearea(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):
@@ -754,20 +788,30 @@ class Renderer:
p.drawOn(canvas, 0, -h - ad[1])
canvas.restoreState()
- def draw_page(self, canvas: Canvas, order: Order, op: OrderPosition, show_page=True):
- for o in self.layout:
- if o['type'] == "barcodearea":
- self._draw_barcodearea(canvas, op, o)
- elif o['type'] == "imagearea":
- self._draw_imagearea(canvas, op, order, o)
- elif o['type'] == "textarea":
- self._draw_textarea(canvas, op, order, o)
- elif o['type'] == "poweredby":
- self._draw_poweredby(canvas, op, o)
- if self.bg_pdf:
- canvas.setPageSize((self.bg_pdf.getPage(0).mediaBox[2], self.bg_pdf.getPage(0).mediaBox[3]))
- if show_page:
- canvas.showPage()
+ def draw_page(self, canvas: Canvas, order: Order, op: OrderPosition, show_page=True, only_page=None):
+ page_count = self.bg_pdf.getNumPages()
+
+ if not only_page and not show_page:
+ raise ValueError("only_page=None and show_page=False cannot be combined")
+
+ for page in range(page_count):
+ if only_page and only_page != page + 1:
+ continue
+ for o in self.layout:
+ if o.get('page', 1) != page + 1:
+ continue
+ if o['type'] == "barcodearea":
+ self._draw_barcodearea(canvas, op, order, o)
+ elif o['type'] == "imagearea":
+ self._draw_imagearea(canvas, op, order, o)
+ elif o['type'] == "textarea":
+ self._draw_textarea(canvas, op, order, o)
+ elif o['type'] == "poweredby":
+ self._draw_poweredby(canvas, op, o)
+ if self.bg_pdf:
+ canvas.setPageSize((self.bg_pdf.getPage(page).mediaBox[2], self.bg_pdf.getPage(page).mediaBox[3]))
+ if show_page:
+ canvas.showPage()
def render_background(self, buffer, title=_('Ticket')):
if settings.PDFTK:
@@ -780,7 +824,7 @@ class Renderer:
subprocess.run([
settings.PDFTK,
os.path.join(d, 'front.pdf'),
- 'background',
+ 'multibackground',
os.path.join(d, 'back.pdf'),
'output',
os.path.join(d, 'out.pdf'),
@@ -794,8 +838,8 @@ class Renderer:
new_pdf = PdfFileReader(buffer)
output = PdfFileWriter()
- for page in new_pdf.pages:
- bg_page = copy.copy(self.bg_pdf.getPage(0))
+ for i, page in enumerate(new_pdf.pages):
+ bg_page = copy.copy(self.bg_pdf.getPage(i))
bg_page.mergePage(page)
output.addPage(bg_page)
diff --git a/src/pretix/control/templates/pretixcontrol/pdf/index.html b/src/pretix/control/templates/pretixcontrol/pdf/index.html
index c258bab304..ee35023d16 100644
--- a/src/pretix/control/templates/pretixcontrol/pdf/index.html
+++ b/src/pretix/control/templates/pretixcontrol/pdf/index.html
@@ -23,7 +23,7 @@
-
+
@@ -48,6 +48,8 @@
{% trans "Editor" %}
-
+
-
+
+
+ {% for l in request.event.settings.locales %}
+
+ {% endfor %}
+
@@ -401,13 +417,20 @@
{% trans "QR code for Lead Scanning" %}
+
-