diff --git a/src/pretix/plugins/badges/forms.py b/src/pretix/plugins/badges/forms.py index 970a96f4c9..c7d50dac71 100644 --- a/src/pretix/plugins/badges/forms.py +++ b/src/pretix/plugins/badges/forms.py @@ -25,9 +25,18 @@ from django.forms.models import ModelChoiceIterator from django.utils.translation import gettext_lazy as _ from pretix.plugins.badges.models import BadgeItem, BadgeLayout +from pretix.plugins.badges.templates import TEMPLATES class BadgeLayoutForm(forms.ModelForm): + template = forms.ChoiceField( + label=_('Template'), + help_text=_('You can modify the layout or change to a different page size in the next step.'), + choices=((k, v['label']) for k, v in TEMPLATES.items()), + widget=forms.RadioSelect, + initial='a6l', + ) + class Meta: model = BadgeLayout fields = ('name',) diff --git a/src/pretix/plugins/badges/templates.py b/src/pretix/plugins/badges/templates.py new file mode 100644 index 0000000000..de48e5d319 --- /dev/null +++ b/src/pretix/plugins/badges/templates.py @@ -0,0 +1,237 @@ +# +# This file is part of pretix (Community Edition). +# +# Copyright (C) 2014-2020 Raphael Michel and contributors +# Copyright (C) 2020-2021 rami.io GmbH and contributors +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General +# Public License as published by the Free Software Foundation in version 3 of the License. +# +# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are +# applicable granting you additional permissions and placing additional restrictions on your usage of this software. +# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive +# this file, see . +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along with this program. If not, see +# . +# +from django.utils.text import format_lazy +from django.utils.translation import gettext_lazy as _ +from reportlab.lib import pagesizes +from reportlab.lib.units import mm + + +def _simple_template(w, h): + name_size = max(min(20, w / 20), 12) # Heuristic for font size + company_size = name_size - 2 + return [ + { + "type": "textarea", + "page": 1, + "locale": "", + "left": "5.00", + "bottom": "%.2f" % (((h - company_size * 1.5 - name_size) / 2 + company_size * 1.5) / mm), + "fontsize": name_size, + "lineheight": "1", + "color": [0, 0, 0, 1], + "fontfamily": "Open Sans", + "bold": True, + "italic": False, + "width": "%.2f" % (w / mm - 10), + "downward": False, + "content": "attendee_name", + "text": "John Doe", + "text_i18n": {}, + "rotation": 0, + "align": "center", + }, + { + "type": "textarea", + "page": 1, + "locale": "", + "left": "5.00", + "bottom": "%.2f" % ((((h - company_size * 1.5 - name_size) / 2) + company_size) / mm), + "fontsize": company_size, + "lineheight": "1", + "color": [0, 0, 0, 1], + "fontfamily": "Open Sans", + "bold": False, + "italic": False, + "width": "%.2f" % (w / mm - 10), + "downward": True, + "content": "attendee_company", + "text": "Sample company", + "text_i18n": {}, + "rotation": 0, + "align": "center", + }, + ] + + +TEMPLATES = { + "a6l": { + "label": _("A6 landscape"), + "pagesize": pagesizes.landscape(pagesizes.A6), + "layout": _simple_template(*pagesizes.landscape(pagesizes.A6)), + }, + "a6p": { + "label": _("A6 portrait"), + "pagesize": pagesizes.portrait(pagesizes.A6), + "layout": _simple_template(*pagesizes.portrait(pagesizes.A6)), + }, + "a7l": { + "label": _("A7 landscape"), + "pagesize": pagesizes.landscape(pagesizes.A7), + "layout": _simple_template(*pagesizes.landscape(pagesizes.A7)), + }, + "a7p": { + "label": _("A7 portrait"), + "pagesize": pagesizes.portrait(pagesizes.A7), + "layout": _simple_template(*pagesizes.portrait(pagesizes.A7)), + }, + "82x203butterfly": { + "label": format_lazy(_("{width} x {height} mm butterfly badge"), width=82, height=203), + "pagesize": (82 * mm, 203 * mm), + "layout": [ + { + "type": "textarea", + "page": 1, + "locale": "", + "left": "5.00", + "bottom": "152.55", + "fontsize": "20.0", + "lineheight": "1", + "color": [0, 0, 0, 1], + "fontfamily": "Open Sans", + "bold": True, + "italic": False, + "width": "72.00", + "downward": False, + "content": "attendee_name", + "text": "John Doe", + "text_i18n": {}, + "rotation": 0, + "align": "center", + }, + { + "type": "textarea", + "page": 1, + "locale": "", + "left": "5.00", + "bottom": "144.55", + "fontsize": "18.0", + "lineheight": "1", + "color": [0, 0, 0, 1], + "fontfamily": "Open Sans", + "bold": False, + "italic": False, + "width": "72.00", + "downward": False, + "content": "attendee_company", + "text": "Sample company", + "text_i18n": {}, + "rotation": 0, + "align": "center", + }, + { + "type": "textarea", + "page": 1, + "locale": "", + "left": "77.10", + "bottom": "34.68", + "fontsize": "20.0", + "lineheight": "1", + "color": [0, 0, 0, 1], + "fontfamily": "Open Sans", + "bold": True, + "italic": False, + "width": "72.00", + "downward": False, + "content": "attendee_name", + "text": "John Doe", + "text_i18n": {}, + "rotation": 180, + "align": "center", + }, + { + "type": "textarea", + "page": 1, + "locale": "", + "left": "77.06", + "bottom": "44.28", + "fontsize": "18.0", + "lineheight": "1", + "color": [0, 0, 0, 1], + "fontfamily": "Open Sans", + "bold": False, + "italic": False, + "width": "72.00", + "downward": False, + "content": "attendee_company", + "text": "Sample company", + "text_i18n": {}, + "rotation": 180, + "align": "center", + }, + ], + }, + "100x50": { + "label": format_lazy(_("{width} x {height} mm label"), width=100, height=50), + "pagesize": (100 * mm, 50 * mm), + "layout": _simple_template(100 * mm, 50 * mm), + }, + "83x50": { + "label": format_lazy(_("{width} x {height} mm label"), width=83, height=50), + "pagesize": (83 * mm, 50 * mm), + "layout": _simple_template(83 * mm, 50 * mm), + }, + "80x50": { + "label": format_lazy(_("{width} x {height} mm label"), width=80, height=50), + "pagesize": (80 * mm, 50 * mm), + "layout": _simple_template(80 * mm, 50 * mm), + }, + "75x52": { + "label": format_lazy(_("{width} x {height} mm label"), width=75, height=52), + "pagesize": (75 * mm, 52 * mm), + "layout": _simple_template(75 * mm, 52 * mm), + }, + "70x36": { + "label": format_lazy(_("{width} x {height} mm label"), width=70, height=36), + "pagesize": (70 * mm, 36 * mm), + "layout": _simple_template(70 * mm, 36 * mm), + }, + "63x29": { + "label": format_lazy(_("{width} x {height} mm label"), width=63, height=29), + "pagesize": (63.5 * mm, 29.6 * mm), + "layout": _simple_template(63.5 * mm, 29.6 * mm), + }, + "60x90": { + "label": format_lazy(_("{width} x {height} mm label"), width=60, height=90), + "pagesize": (60 * mm, 90 * mm), + "layout": _simple_template(60 * mm, 90 * mm), + }, + "54x90": { + "label": format_lazy(_("{width} x {height} mm label"), width=54, height=90), + "pagesize": (54 * mm, 90 * mm), + "layout": _simple_template(54 * mm, 90 * mm), + }, + "50x80": { + "label": format_lazy(_("{width} x {height} mm label"), width=50, height=80), + "pagesize": (50 * mm, 80 * mm), + "layout": _simple_template(50 * mm, 80 * mm), + }, + "40x75": { + "label": format_lazy(_("{width} x {height} mm label"), width=40, height=75), + "pagesize": (40 * mm, 75 * mm), + "layout": _simple_template(40 * mm, 75 * mm), + }, + "40x40": { + "label": format_lazy(_("{width} x {height} mm label"), width=40, height=40), + "pagesize": (40 * mm, 40 * mm), + "layout": _simple_template(40 * mm, 40 * mm), + }, +} diff --git a/src/pretix/plugins/badges/templates/pretixplugins/badges/edit.html b/src/pretix/plugins/badges/templates/pretixplugins/badges/edit.html index 125a36e21a..ee39c0a32f 100644 --- a/src/pretix/plugins/badges/templates/pretixplugins/badges/edit.html +++ b/src/pretix/plugins/badges/templates/pretixplugins/badges/edit.html @@ -18,21 +18,12 @@ {% csrf_token %} {% bootstrap_form_errors form %} {% bootstrap_field form.name layout="control" %} -
- -
-

- {% blocktrans trimmed %} - You can modify the design after you saved this page. - {% endblocktrans %} -

-
-
+ {% if form.template %} + {% bootstrap_field form.template layout="control" %} + {% endif %}
diff --git a/src/pretix/plugins/badges/views.py b/src/pretix/plugins/badges/views.py index ccc42b1f0e..bee4be88f2 100644 --- a/src/pretix/plugins/badges/views.py +++ b/src/pretix/plugins/badges/views.py @@ -23,9 +23,11 @@ import json from datetime import timedelta from io import BytesIO +from _decimal import Decimal from django.contrib import messages from django.contrib.staticfiles import finders from django.core.files import File +from django.core.files.base import ContentFile from django.core.files.storage import default_storage from django.db import transaction from django.http import Http404 @@ -37,6 +39,7 @@ from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from django.views import View from django.views.generic import CreateView, DetailView, ListView +from pypdf import PdfWriter from reportlab.lib import pagesizes from reportlab.pdfgen import canvas @@ -51,6 +54,7 @@ from pretix.plugins.badges.tasks import badges_create_pdf from ...helpers.compat import CompatDeleteView from .models import BadgeLayout +from .templates import TEMPLATES class LayoutListView(EventPermissionRequiredMixin, ListView): @@ -71,14 +75,32 @@ class LayoutCreate(EventPermissionRequiredMixin, CreateView): context_object_name = 'layout' success_url = '/ignored' + def get_form(self, form_class=None): + form = super().get_form(form_class) + if self.copy_from: + del form.fields['template'] + return form + @transaction.atomic def form_valid(self, form): form.instance.event = self.request.event if not self.request.event.badge_layouts.filter(default=True).exists(): form.instance.default = True messages.success(self.request, _('The new badge layout has been created.')) + if not self.copy_from: + form.instance.layout = json.dumps(TEMPLATES[form.cleaned_data["template"]]["layout"]) super().form_valid(form) - if form.instance.background and form.instance.background.name: + if not self.copy_from: + p = PdfWriter() + p.add_blank_page( + width=Decimal('%.5f' % TEMPLATES[form.cleaned_data["template"]]["pagesize"][0]), + height=Decimal('%.5f' % TEMPLATES[form.cleaned_data["template"]]["pagesize"][1]), + ) + buffer = BytesIO() + p.write(buffer) + buffer.seek(0) + form.instance.background.save('background.pdf', ContentFile(buffer.read())) + elif form.instance.background and form.instance.background.name: form.instance.background.save('background.pdf', form.instance.background) form.instance.log_action('pretix.plugins.badges.layout.added', user=self.request.user, data=dict(form.cleaned_data))