import copy import logging from collections import OrderedDict from io import BytesIO from django import forms from django.contrib.staticfiles import finders from django.core.files import File from django.core.files.storage import default_storage from django.utils.translation import ugettext_lazy as _ from pretix.base.models import Order from pretix.base.ticketoutput import BaseTicketOutput from pretix.control.forms import ExtFileField logger = logging.getLogger('pretix.plugins.ticketoutputpdf') class PdfTicketOutput(BaseTicketOutput): identifier = 'pdf' verbose_name = _('PDF output') download_button_text = _('PDF') def _draw_page(self, p, op, order): from reportlab.graphics.shapes import Drawing from reportlab.lib import units from reportlab.graphics.barcode.qr import QrCodeWidget from reportlab.graphics import renderPDF event_s = self.settings.get('event_s', default=22, as_type=float) if event_s: p.setFont("Helvetica", event_s) event_x = self.settings.get('event_x', default=15, as_type=float) event_y = self.settings.get('event_y', default=235, as_type=float) p.drawString(event_x * units.mm, event_y * units.mm, str(self.event.name)) order_s = self.settings.get('order_s', default=17, as_type=float) if order_s: p.setFont("Helvetica", order_s) order_x = self.settings.get('order_x', default=15, as_type=float) order_y = self.settings.get('order_y', default=220, as_type=float) p.drawString(order_x * units.mm, order_y * units.mm, _('Order code: {code}').format(code=order.code)) name_s = self.settings.get('name_s', default=17, as_type=float) if name_s: p.setFont("Helvetica", name_s) name_x = self.settings.get('name_x', default=15, as_type=float) name_y = self.settings.get('name_y', default=210, as_type=float) item = str(op.item.name) if op.variation: item += " – " + str(op.variation) p.drawString(name_x * units.mm, name_y * units.mm, item) price_s = self.settings.get('price_s', default=17, as_type=float) if price_s: p.setFont("Helvetica", price_s) price_x = self.settings.get('price_x', default=15, as_type=float) price_y = self.settings.get('price_y', default=200, as_type=float) p.drawString(price_x * units.mm, price_y * units.mm, "%s %s" % (str(op.price), self.event.currency)) qr_s = self.settings.get('qr_s', default=80, as_type=float) if qr_s: reqs = qr_s * units.mm qrw = QrCodeWidget(op.secret, barLevel='H') b = qrw.getBounds() w = b[2] - b[0] h = b[3] - b[1] d = Drawing(reqs, reqs, transform=[reqs / w, 0, 0, reqs / h, 0, 0]) d.add(qrw) qr_x = self.settings.get('qr_x', default=10, as_type=float) qr_y = self.settings.get('qr_y', default=120, as_type=float) renderPDF.draw(d, p, qr_x * units.mm, qr_y * units.mm) code_s = self.settings.get('code_s', default=11, as_type=float) if code_s: p.setFont("Helvetica", code_s) code_x = self.settings.get('code_x', default=15, as_type=float) code_y = self.settings.get('code_y', default=120, as_type=float) p.drawString(code_x * units.mm, code_y * units.mm, op.secret) attendee_s = self.settings.get('attendee_s', default=0, as_type=float) if attendee_s and op.attendee_name: p.setFont("Helvetica", attendee_s) attendee_x = self.settings.get('attendee_x', default=15, as_type=float) attendee_y = self.settings.get('attendee_y', default=90, as_type=float) p.drawString(attendee_x * units.mm, attendee_y * units.mm, op.attendee_name) p.showPage() def generate_order(self, order: Order): buffer = BytesIO() p = self._create_canvas(buffer) for op in order.positions.all(): if op.addon_to_id and not self.event.settings.ticket_download_addons: continue self._draw_page(p, op, order) p.save() outbuffer = self._render_with_background(buffer) return 'order%s%s.pdf' % (self.event.slug, order.code), 'application/pdf', outbuffer.read() def generate(self, op): buffer = BytesIO() p = self._create_canvas(buffer) order = op.order self._draw_page(p, op, order) p.save() outbuffer = self._render_with_background(buffer) return 'order%s%s.pdf' % (self.event.slug, order.code), 'application/pdf', outbuffer.read() def _create_canvas(self, buffer): from reportlab.pdfgen import canvas from reportlab.lib import pagesizes pagesize = self.settings.get('pagesize', default='A4') if hasattr(pagesizes, pagesize): pagesize = getattr(pagesizes, pagesize) else: pagesize = pagesizes.A4 orientation = self.settings.get('orientation', default='portrait') if hasattr(pagesizes, orientation): pagesize = getattr(pagesizes, orientation)(pagesize) return canvas.Canvas(buffer, pagesize=pagesize) def _render_with_background(self, buffer): from PyPDF2 import PdfFileWriter, PdfFileReader buffer.seek(0) new_pdf = PdfFileReader(buffer) output = PdfFileWriter() bg_file = self.settings.get('background', as_type=File) if isinstance(bg_file, File): bgf = default_storage.open(bg_file.name, "rb") else: bgf = open(finders.find('pretixpresale/pdf/ticket_default_a4.pdf'), "rb") bg_pdf = PdfFileReader(bgf) for page in new_pdf.pages: bg_page = copy.copy(bg_pdf.getPage(0)) bg_page.mergePage(page) output.addPage(bg_page) outbuffer = BytesIO() output.write(outbuffer) outbuffer.seek(0) return outbuffer @property def settings_form_fields(self) -> dict: return OrderedDict( list(super().settings_form_fields.items()) + [ ('paper_size', forms.ChoiceField( label=_('Paper size'), choices=( ('A4', 'A4'), ('A5', 'A5'), ('B4', 'B4'), ('B5', 'B5'), ('letter', 'Letter'), ('legal', 'Legal'), ), required=False )), ('orientation', forms.ChoiceField( label=_('Paper orientation'), choices=( ('portrait', _('Portrait')), ('landscape', _('Landscape')), ), required=False )), ('background', ExtFileField( label=_('Background PDF'), ext_whitelist=(".pdf", ), required=False )), ('qr_x', forms.FloatField(label=_('QR-Code x position (mm)'), required=False)), ('qr_y', forms.FloatField(label=_('QR-Code y position (mm)'), required=False)), ('qr_s', forms.FloatField(label=_('QR-Code size (mm)'), required=False)), ('code_x', forms.FloatField(label=_('Ticket code x position (mm)'), required=False)), ('code_y', forms.FloatField(label=_('Ticket code y position (mm)'), required=False)), ('code_s', forms.FloatField(label=_('Ticket code size (mm)'), required=False, help_text=_('Visible by default, set this to 0 to hide the element.'))), ('order_x', forms.FloatField(label=_('Order x position (mm)'), required=False)), ('order_y', forms.FloatField(label=_('Order y position (mm)'), required=False)), ('order_s', forms.FloatField(label=_('Order size (mm)'), required=False, help_text=_('Visible by default, set this to 0 to hide the element.'))), ('name_x', forms.FloatField(label=_('Product name x position (mm)'), required=False)), ('name_y', forms.FloatField(label=_('Product name y position (mm)'), required=False)), ('name_s', forms.FloatField(label=_('Product name size (mm)'), required=False, help_text=_('Visible by default, set this to 0 to hide the element.'))), ('price_x', forms.FloatField(label=_('Price x position (mm)'), required=False)), ('price_y', forms.FloatField(label=_('Price y position (mm)'), required=False)), ('price_s', forms.FloatField(label=_('Price size (mm)'), required=False, help_text=_('Visible by default, set this to 0 to hide the element.'))), ('event_x', forms.FloatField(label=_('Event name x position (mm)'), required=False)), ('event_y', forms.FloatField(label=_('Event name y position (mm)'), required=False)), ('event_s', forms.FloatField(label=_('Event name size (mm)'), required=False, help_text=_('Visible by default, set this to 0 to hide the element.'))), ('attendee_x', forms.FloatField(label=_('Attendee name x position (mm)'), required=False)), ('attendee_y', forms.FloatField(label=_('Attendee name y position (mm)'), required=False)), ('attendee_s', forms.FloatField(label=_('Attendee name size (mm)'), required=False, help_text=_('Invisible by default, set this to a number greater than 0 ' 'to show.'))) ] )