From ab0f76c7bbcdabf4b9f47db193a30b28a7d35427 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Thu, 17 Mar 2022 16:43:18 +0100 Subject: [PATCH] Revert "PDF editor improvements (#2529)" This reverts commit e4c1f30b9d014c6f194c652283b8d02cdb86999a. --- src/pretix/base/pdf.py | 88 ++------ .../templates/pretixcontrol/pdf/index.html | 33 +-- src/pretix/control/views/pdf.py | 21 +- src/pretix/plugins/badges/exporters.py | 2 +- .../static/pretixcontrol/js/ui/editor.js | 195 ++++-------------- .../static/pretixcontrol/scss/pdfeditor.css | 8 - 6 files changed, 74 insertions(+), 273 deletions(-) diff --git a/src/pretix/base/pdf.py b/src/pretix/base/pdf.py index ca96f03d95..889a7c4e34 100644 --- a/src/pretix/base/pdf.py +++ b/src/pretix/base/pdf.py @@ -37,7 +37,6 @@ import hashlib import itertools import logging import os -import re import subprocess import tempfile import uuid @@ -55,7 +54,6 @@ 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 @@ -204,11 +202,6 @@ 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"), @@ -623,14 +616,12 @@ class Renderer: preserveAspectRatio=True, anchor='n', mask='auto') - def _draw_barcodearea(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict): + def _draw_barcodearea(self, canvas: Canvas, op: OrderPosition, 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 - else: - content = self._get_text_content(op, order, o) + elif content == 'pseudonymization_id': + content = op.pseudonymization_id level = 'H' if len(content) > 32: @@ -657,45 +648,20 @@ 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' 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) - + if o['content'] == 'other': + return o['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): @@ -788,30 +754,20 @@ class Renderer: p.drawOn(canvas, 0, -h - ad[1]) canvas.restoreState() - 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 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 render_background(self, buffer, title=_('Ticket')): if settings.PDFTK: @@ -824,7 +780,7 @@ class Renderer: subprocess.run([ settings.PDFTK, os.path.join(d, 'front.pdf'), - 'multibackground', + 'background', os.path.join(d, 'back.pdf'), 'output', os.path.join(d, 'out.pdf'), @@ -838,8 +794,8 @@ class Renderer: new_pdf = PdfFileReader(buffer) output = PdfFileWriter() - for i, page in enumerate(new_pdf.pages): - bg_page = copy.copy(self.bg_pdf.getPage(i)) + for page in new_pdf.pages: + bg_page = copy.copy(self.bg_pdf.getPage(0)) 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 ee35023d16..c258bab304 100644 --- a/src/pretix/control/templates/pretixcontrol/pdf/index.html +++ b/src/pretix/control/templates/pretixcontrol/pdf/index.html @@ -23,7 +23,7 @@
-
+
@@ -48,8 +48,6 @@ {% trans "Editor" %}
-
{% trans "Upload custom background" %} - +
@@ -206,14 +204,6 @@ {% endblocktrans %}

-
@@ -367,9 +357,9 @@
-
+
-
+
-
- {% for l in request.event.settings.locales %} - - {% endfor %} -
@@ -417,20 +401,13 @@ {% trans "QR code for Lead Scanning" %} - - diff --git a/src/pretix/control/views/pdf.py b/src/pretix/control/views/pdf.py index 1837b3da59..cf67bc5a2c 100644 --- a/src/pretix/control/views/pdf.py +++ b/src/pretix/control/views/pdf.py @@ -23,7 +23,6 @@ import json import logging import mimetypes from datetime import timedelta -from decimal import Decimal from io import BytesIO from django.conf import settings @@ -39,8 +38,7 @@ from django.utils.crypto import get_random_string from django.utils.timezone import now from django.utils.translation import gettext as _ from django.views.generic import TemplateView -from PyPDF2 import PdfFileReader, PdfFileWriter -from PyPDF2.utils import PdfReadError +from PyPDF2 import PdfFileWriter from reportlab.lib.units import mm from pretix.base.i18n import language @@ -84,15 +82,15 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView): return None, f def _get_preview_position(self): - item = self.request.event.items.create(name=_("Sample product"), default_price=Decimal('42.23'), + item = self.request.event.items.create(name=_("Sample product"), default_price=42.23, description=_("Sample product description")) - item2 = self.request.event.items.create(name=_("Sample workshop"), default_price=Decimal('23.40')) + item2 = self.request.event.items.create(name=_("Sample workshop"), default_price=23.40) from pretix.base.models import Order order = self.request.event.orders.create(status=Order.STATUS_PENDING, datetime=now(), email='sample@pretix.eu', locale=self.request.event.settings.locale, - expires=now(), code="PREVIEW1234", total=Decimal('119.00')) + expires=now(), code="PREVIEW1234", total=119) scheme = PERSON_NAME_SCHEMES[self.request.event.settings.name_scheme] sample = {k: str(v) for k, v in scheme['sample'].items()} @@ -193,17 +191,6 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView): c.file = fileobj c.save() c.refresh_from_db() - - try: - bg_bytes = c.file.read() - PdfFileReader(BytesIO(bg_bytes), strict=False) - except PdfReadError as e: - return JsonResponse({ - "status": "error", - "error": _('Unfortunately, we were unable to process this PDF file ({reason}).').format( - reason=str(e) - ) - }) return JsonResponse({ "status": "ok", "id": c.id, diff --git a/src/pretix/plugins/badges/exporters.py b/src/pretix/plugins/badges/exporters.py index 29149f2ed2..6effab993d 100644 --- a/src/pretix/plugins/badges/exporters.py +++ b/src/pretix/plugins/badges/exporters.py @@ -181,7 +181,7 @@ def render_pdf(event, positions, opt): offsety = opt['margins'][2] + (opt['rows'] - 1 - i // opt['cols']) * opt['offsets'][1] p.translate(offsetx, offsety) with language(op.order.locale, op.order.event.settings.region): - r.draw_page(p, op.order, op, show_page=False, only_page=1) + r.draw_page(p, op.order, op, show_page=False) p.translate(-offsetx, -offsety) if opt['pagesize']: diff --git a/src/pretix/static/pretixcontrol/js/ui/editor.js b/src/pretix/static/pretixcontrol/js/ui/editor.js index 8fe3672d3f..372b09211e 100644 --- a/src/pretix/static/pretixcontrol/js/ui/editor.js +++ b/src/pretix/static/pretixcontrol/js/ui/editor.js @@ -66,11 +66,9 @@ fabric.Barcodearea = fabric.util.createClass(fabric.Rect, { ctx.font = '16px Helvetica'; ctx.fillStyle = '#fff'; if (this.content === "pseudonymization_id") { - ctx.fillText(this.content, -this.width / 2, -this.height / 2 + 20); - } else if (!this.content || this.content === "secret") { - ctx.fillText(gettext('Check-in QR'), -this.width / 2, -this.height / 2 + 20); + ctx.fillText(gettext('Lead Scan QR'), -this.width / 2, -this.height / 2 + 20); } else { - ctx.fillText(this.content, -this.width / 2, -this.height / 2 + 20); + ctx.fillText(gettext('Check-in QR'), -this.width / 2, -this.height / 2 + 20); } }, }); @@ -104,15 +102,11 @@ var editor = { objects: [], history: [], clipboard: [], - pdf: null, pdf_page: null, - pdf_page_number: 1, - pdf_page_count: 1, pdf_scale: 1, pdf_viewport: null, _history_pos: 0, _history_modification_in_progress: false, - _other_page_objects: [], dirty: false, pdf_url: null, uploaded_file_id: null, @@ -136,7 +130,7 @@ var editor = { }, dump: function (objs) { - var d = !objs ? JSON.parse(JSON.stringify(editor._other_page_objects)) : []; + var d = []; objs = objs || editor.fabric.getObjects(); for (var i in objs) { @@ -155,7 +149,6 @@ var editor = { } d.push({ type: "textarea", - page: editor.pdf_page_number, locale: $("#pdf-info-locale").val(), left: editor._px2mm(left).toFixed(2), bottom: editor._px2mm(bottom).toFixed(2), @@ -169,14 +162,12 @@ var editor = { downward: o.downward || false, content: o.content, text: o.text, - text_i18n: o.text_i18n || {}, rotation: o.angle, align: o.textAlign, }); } else if (o.type === "imagearea") { d.push({ type: "imagearea", - page: editor.pdf_page_number, left: editor._px2mm(left).toFixed(2), bottom: editor._px2mm(editor.pdf_viewport.height - o.height * o.scaleY - top).toFixed(2), height: editor._px2mm(o.height * o.scaleY).toFixed(2), @@ -186,18 +177,15 @@ var editor = { } else if (o.type === "barcodearea") { d.push({ type: "barcodearea", - page: editor.pdf_page_number, left: editor._px2mm(left).toFixed(2), bottom: editor._px2mm(editor.pdf_viewport.height - o.height * o.scaleY - top).toFixed(2), size: editor._px2mm(o.height * o.scaleY).toFixed(2), content: o.content, - text: o.text, nowhitespace: o.nowhitespace || false, }); } else if (o.type === "poweredby") { d.push({ type: "poweredby", - page: editor.pdf_page_number, left: editor._px2mm(left).toFixed(2), bottom: editor._px2mm(editor.pdf_viewport.height - o.height * o.scaleY - top).toFixed(2), size: editor._px2mm(o.height * o.scaleY).toFixed(2), @@ -209,11 +197,6 @@ var editor = { }, _add_from_data: function (d) { - var targetPage = d.page || 1; - if (targetPage !== editor.pdf_page_number) { - editor._other_page_objects.push(d); - return - } if (d.type === "barcodearea") { o = editor._add_qrcode(); o.content = d.content; @@ -247,9 +230,6 @@ var editor = { } if (d.content === "other") { o.setText(d.text); - } else if (d.content === "other_i18n") { - o.text_i18n = d.text_i18n - o.setText(d.text_i18n[Object.keys(d.text_i18n)[0]]); } else { o.setText(editor._get_text_sample(d.content)); } @@ -271,7 +251,6 @@ var editor = { load: function(data) { editor.fabric.clear(); - editor._other_page_objects = []; for (var i in data) { var d = data[i], o; editor._add_from_data(d); @@ -289,71 +268,6 @@ var editor = { return $('#toolbox-content option[value='+key+']').attr('data-sample') || ''; }, - _load_page: function (page_number, dump) { - var previous_dump = editor._fabric_loaded ? editor.dump() : []; - - // Fetch the required page - editor.pdf.getPage(page_number).then(function (page) { - console.log('Page loaded'); - var canvas = document.getElementById('pdf-canvas'); - - var scale = editor.$cva.width() / page.getViewport(1.0).width; - var viewport = page.getViewport(scale); - - // Prepare canvas using PDF page dimensions - var context = canvas.getContext('2d'); - context.clearRect(0, 0, canvas.width, canvas.height); - canvas.height = viewport.height; - canvas.width = viewport.width; - - editor.pdf_page = page; - editor.pdf_scale = scale; - editor.pdf_viewport = viewport; - - // Render PDF page into canvas context - var renderContext = { - canvasContext: context, - viewport: viewport - }; - var renderTask = page.render(renderContext); - renderTask.then(function () { - editor.pdf_page_number = page_number - editor._init_page_nav(); - - console.log('Page rendered'); - if (dump || !editor._fabric_loaded) { - editor._init_fabric(dump); - } else { - editor.load(previous_dump); - } - }); - }); - }, - - _init_page_nav: function () { - if (editor.pdf_page_count === 1) { - $("#page_nav").hide(); - } else { - $("#page_nav").html(""); - for (i = 1; i <= editor.pdf_page_count; i++) { - var $li = $("
  • ").addClass("nav-item"); - var $a = $("").text(i).attr("href", "#").attr("data-page", i).appendTo($li); - if (i === editor.pdf_page_number) { - $li.addClass("active") - } - $("#page_nav").append($li) - $a.on("click", function (event) { - console.log("switch to page", $(this).attr("data-page")); - editor.fabric.deactivateAll(); - editor._load_page(parseInt($(this).attr("data-page"))); - event.preventDefault(); - return true; - }) - } - $("#page_nav").show(); - } - }, - _load_pdf: function (dump) { // TODO: Loading indicators var url = editor.pdf_url; @@ -365,13 +279,36 @@ var editor = { loadingTask.promise.then(function (pdf) { console.log('PDF loaded'); - editor.pdf = pdf; - editor.pdf_page_count = pdf.numPages; - if (editor.pdf_page_count > 10) { - alert('Please do not upload files with more than 10 pages for performance reasons.') - } - editor._init_page_nav(); - editor._load_page(1, dump); + // Fetch the first page + var pageNumber = 1; + pdf.getPage(pageNumber).then(function (page) { + console.log('Page loaded'); + var canvas = document.getElementById('pdf-canvas'); + + var scale = editor.$cva.width() / page.getViewport(1.0).width; + var viewport = page.getViewport(scale); + + // Prepare canvas using PDF page dimensions + var context = canvas.getContext('2d'); + context.clearRect(0, 0, canvas.width, canvas.height); + canvas.height = viewport.height; + canvas.width = viewport.width; + + editor.pdf_page = page; + editor.pdf_scale = scale; + editor.pdf_viewport = viewport; + + // Render PDF page into canvas context + var renderContext = { + canvasContext: context, + viewport: viewport + }; + var renderTask = page.render(renderContext); + renderTask.then(function () { + console.log('Page rendered'); + editor._init_fabric(dump); + }); + }); }, function (reason) { var msg = gettext('The PDF background file could not be loaded for the following reason:'); editor._error(msg + ' ' + reason); @@ -395,7 +332,6 @@ var editor = { editor._update_toolbox(); $("#toolbox-content-other").hide(); - $("#toolbox-content-other-i18n").hide(); $(".add-buttons button").prop('disabled', false); if (dump) { @@ -486,23 +422,14 @@ var editor = { $("#toolbox").find("button[data-action=right]").toggleClass('active', o.textAlign === 'right'); $("#toolbox-textwidth").val(editor._px2mm(o.width).toFixed(2)); $("#toolbox-textrotation").val((o.angle || 0.0).toFixed(1)); - } - - if (o.type === "textarea" || o.type === "barcodearea") { - if (!o.content && o.type == "barcodearea") { - o.content = "secret"; - } - $("#toolbox-content").val(o.content); - $("#toolbox-content-other").toggle($("#toolbox-content").val() === "other"); - $("#toolbox-content-other-i18n").toggle($("#toolbox-content").val() === "other_i18n"); - if (o.content === "other") { - $("#toolbox-content-other").val(o.text); - } else if (o.content === "other_i18n") { - $("#toolbox-content-other-i18n textarea").each(function () { - $(this).val(o.text_i18n[$(this).attr("lang")] || ''); - }); - } else { - $("#toolbox-content-other").val(""); + if (o.type === "textarea") { + $("#toolbox-content").val(o.content); + $("#toolbox-content-other").toggle($("#toolbox-content").val() === "other"); + if (o.content === "other") { + $("#toolbox-content-other").val(o.text); + } else { + $("#toolbox-content-other").val(""); + } } } }, @@ -534,20 +461,6 @@ var editor = { o.setScaleY(1); o.set('top', new_top) o.nowhitespace = $("#toolbox-qrwhitespace").prop("checked") || false; - - $("#toolbox-content-other").toggle($("#toolbox-content").val() === "other"); - $("#toolbox-content-other-i18n").toggle($("#toolbox-content").val() === "other_i18n"); - o.content = $("#toolbox-content").val(); - if ($("#toolbox-content").val() === "other") { - o.text = $("#toolbox-content-other").val(); - } else if ($("#toolbox-content").val() === "other_i18n") { - o.text_i18n = {} - $("#toolbox-content-other-i18n textarea").each(function () { - o.text_i18n[$(this).attr("lang")] = $(this).val(); - }); - } else { - o.text = editor._get_text_sample($("#toolbox-content").val()); - } } else if (o.type === "imagearea") { var new_w = editor._mm2px($("#toolbox-width").val()); var new_h = editor._mm2px($("#toolbox-height").val()); @@ -590,16 +503,9 @@ var editor = { o.downward = $("#toolbox").find("button[data-action=downward]").is('.active'); o.rotate(parseFloat($("#toolbox-textrotation").val())); $("#toolbox-content-other").toggle($("#toolbox-content").val() === "other"); - $("#toolbox-content-other-i18n").toggle($("#toolbox-content").val() === "other_i18n"); o.content = $("#toolbox-content").val(); if ($("#toolbox-content").val() === "other") { o.setText($("#toolbox-content-other").val()); - } else if ($("#toolbox-content").val() === "other_i18n") { - o.text_i18n = {} - $("#toolbox-content-other-i18n textarea").each(function () { - o.text_i18n[$(this).attr("lang")] = $(this).val(); - }); - o.setText($("#toolbox-content-other-i18n textarea").first().val()); } else { o.setText(editor._get_text_sample($("#toolbox-content").val())); } @@ -712,7 +618,6 @@ var editor = { lockUniScaling: true, fill: '#666', content: $(this).attr("data-content"), - text: '', nowhitespace: true, }); rect.setControlsVisibility({'mtr': false}); @@ -759,7 +664,6 @@ var editor = { editor._history_modification_in_progress = true; var objs = []; for (var i in editor.clipboard) { - editor.clipboard[i].page = editor.pdf_page_number; objs.push(editor._add_from_data(editor.clipboard[i])); } editor.fabric.discardActiveObject(); @@ -825,11 +729,6 @@ var editor = { case 46: /* Delete */ editor._delete(); break; - case 65: /* A */ - if (e.ctrlKey) { - editor._selectAll(); - } - break; case 89: /* Y */ if (e.ctrlKey) { editor._redo(); @@ -876,15 +775,6 @@ var editor = { editor.dirty = true; }, - _selectAll: function () { - var group = new fabric.Group(editor.fabric.getObjects(), { - originX: 'center', - originY: 'center', - }); - group.setCoords(); - editor.fabric.setActiveGroup(group); - }, - _undo: function undo() { if (editor._history_pos < editor.history.length - 1) { editor._history_modification_in_progress = true; @@ -937,7 +827,6 @@ var editor = { d = editor.dump(); editor.fabric.dispose(); editor._load_pdf(d); - $(".background-download-button").attr("href", url); }, _source_show: function () { @@ -984,7 +873,7 @@ var editor = { editor.$fcv = $("#fabric-canvas"); editor.$cva = $("#editor-canvas-area"); editor._load_pdf(); - $("#editor-add-qrcode, #editor-add-qrcode-lead, #editor-add-qrcode-other").click(editor._add_qrcode); + $("#editor-add-qrcode, #editor-add-qrcode-lead").click(editor._add_qrcode); $("#editor-add-image").click(editor._add_imagearea); $("#editor-add-text").click(editor._add_text); $("#editor-add-poweredby").click(function() {editor._add_poweredby("dark")}); diff --git a/src/pretix/static/pretixcontrol/scss/pdfeditor.css b/src/pretix/static/pretixcontrol/scss/pdfeditor.css index c4a84e5e65..fd92a1055a 100644 --- a/src/pretix/static/pretixcontrol/scss/pdfeditor.css +++ b/src/pretix/static/pretixcontrol/scss/pdfeditor.css @@ -25,7 +25,6 @@ body { } #toolbox[data-type] .position, #toolbox[data-type=barcodearea] .squaresize, -#toolbox[data-type=barcodearea] .textcontent, #toolbox[data-type=imagearea] .rectsize, #toolbox[data-type=imagearea] .imagecontent, #toolbox[data-type=poweredby] .poweredby, @@ -70,10 +69,3 @@ body { margin-top: 10px; margin-bottom: 0; } -.panel-pdf-editor .panel-body { - padding: 0; -} -.panel-pdf-editor .panel-body .nav-pills { - padding: 15px; - background: #eee; -}