mirror of
https://github.com/pretix/pretix.git
synced 2025-12-13 12:42:26 +00:00
Badges: Create templates for common paper sizes (#3660)
* Badges: Create templates for common paper sizes * Add more sizes * format lazy
This commit is contained in:
@@ -25,9 +25,18 @@ from django.forms.models import ModelChoiceIterator
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from pretix.plugins.badges.models import BadgeItem, BadgeLayout
|
from pretix.plugins.badges.models import BadgeItem, BadgeLayout
|
||||||
|
from pretix.plugins.badges.templates import TEMPLATES
|
||||||
|
|
||||||
|
|
||||||
class BadgeLayoutForm(forms.ModelForm):
|
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:
|
class Meta:
|
||||||
model = BadgeLayout
|
model = BadgeLayout
|
||||||
fields = ('name',)
|
fields = ('name',)
|
||||||
|
|||||||
237
src/pretix/plugins/badges/templates.py
Normal file
237
src/pretix/plugins/badges/templates.py
Normal file
@@ -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 <https://pretix.eu/about/en/license>.
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
# <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -18,21 +18,12 @@
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% bootstrap_form_errors form %}
|
{% bootstrap_form_errors form %}
|
||||||
{% bootstrap_field form.name layout="control" %}
|
{% bootstrap_field form.name layout="control" %}
|
||||||
<div class="form-group">
|
{% if form.template %}
|
||||||
<label class="col-md-3 control-label">
|
{% bootstrap_field form.template layout="control" %}
|
||||||
{% trans "Badge design" %}
|
{% endif %}
|
||||||
</label>
|
|
||||||
<div class="col-md-9">
|
|
||||||
<p>
|
|
||||||
{% blocktrans trimmed %}
|
|
||||||
You can modify the design after you saved this page.
|
|
||||||
{% endblocktrans %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group submit-group">
|
<div class="form-group submit-group">
|
||||||
<button type="submit" class="btn btn-primary btn-save">
|
<button type="submit" class="btn btn-primary btn-save">
|
||||||
{% trans "Save" %}
|
{% trans "Save & continue" %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -23,9 +23,11 @@ import json
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
from _decimal import Decimal
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.staticfiles import finders
|
from django.contrib.staticfiles import finders
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
|
from django.core.files.base import ContentFile
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.http import Http404
|
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.utils.translation import gettext_lazy as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.generic import CreateView, DetailView, ListView
|
from django.views.generic import CreateView, DetailView, ListView
|
||||||
|
from pypdf import PdfWriter
|
||||||
from reportlab.lib import pagesizes
|
from reportlab.lib import pagesizes
|
||||||
from reportlab.pdfgen import canvas
|
from reportlab.pdfgen import canvas
|
||||||
|
|
||||||
@@ -51,6 +54,7 @@ from pretix.plugins.badges.tasks import badges_create_pdf
|
|||||||
|
|
||||||
from ...helpers.compat import CompatDeleteView
|
from ...helpers.compat import CompatDeleteView
|
||||||
from .models import BadgeLayout
|
from .models import BadgeLayout
|
||||||
|
from .templates import TEMPLATES
|
||||||
|
|
||||||
|
|
||||||
class LayoutListView(EventPermissionRequiredMixin, ListView):
|
class LayoutListView(EventPermissionRequiredMixin, ListView):
|
||||||
@@ -71,14 +75,32 @@ class LayoutCreate(EventPermissionRequiredMixin, CreateView):
|
|||||||
context_object_name = 'layout'
|
context_object_name = 'layout'
|
||||||
success_url = '/ignored'
|
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
|
@transaction.atomic
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.event = self.request.event
|
form.instance.event = self.request.event
|
||||||
if not self.request.event.badge_layouts.filter(default=True).exists():
|
if not self.request.event.badge_layouts.filter(default=True).exists():
|
||||||
form.instance.default = True
|
form.instance.default = True
|
||||||
messages.success(self.request, _('The new badge layout has been created.'))
|
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)
|
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.background.save('background.pdf', form.instance.background)
|
||||||
form.instance.log_action('pretix.plugins.badges.layout.added', user=self.request.user,
|
form.instance.log_action('pretix.plugins.badges.layout.added', user=self.request.user,
|
||||||
data=dict(form.cleaned_data))
|
data=dict(form.cleaned_data))
|
||||||
|
|||||||
Reference in New Issue
Block a user