diff --git a/src/pretix/base/invoice.py b/src/pretix/base/invoice.py index 113294bb8c..8ba7c096b1 100644 --- a/src/pretix/base/invoice.py +++ b/src/pretix/base/invoice.py @@ -184,10 +184,15 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer): class ThumbnailingImageReader(ImageReader): def resize(self, width, height, dpi): + if width is None: + width = height * self._image.size[0] / self._image.size[1] + if height is None: + height = width * self._image.size[1] / self._image.size[0] self._image.thumbnail( size=(int(width * dpi / 72), int(height * dpi / 72)), resample=BICUBIC ) + return width, height class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): diff --git a/src/pretix/base/pdf.py b/src/pretix/base/pdf.py index 26364d3708..2347583fe8 100644 --- a/src/pretix/base/pdf.py +++ b/src/pretix/base/pdf.py @@ -24,6 +24,7 @@ from reportlab.pdfbase.ttfonts import TTFont from reportlab.pdfgen.canvas import Canvas from reportlab.platypus import Paragraph +from pretix.base.invoice import ThumbnailingImageReader from pretix.base.models import Order, OrderPosition from pretix.base.signals import layout_text_variables from pretix.base.templatetags.money import money_filter @@ -210,6 +211,22 @@ class Renderer: if 'bolditalic' in styles: pdfmetrics.registerFont(TTFont(family + ' B I', finders.find(styles['bolditalic']['truetype']))) + def _draw_poweredby(self, canvas: Canvas, op: OrderPosition, o: dict): + content = o.get('content', 'dark') + img = finders.find('pretixpresale/pdf/powered_by_pretix_{}.png'.format(content)) + + ir = ThumbnailingImageReader(img) + try: + width, height = ir.resize(None, float(o['size']) * mm, 300) + except: + logger.exception("Can not resize image") + pass + canvas.drawImage(ir, + float(o['left']) * mm, float(o['bottom']) * mm, + width=width, height=height, + preserveAspectRatio=True, anchor='n', + mask='auto') + def _draw_barcodearea(self, canvas: Canvas, op: OrderPosition, o: dict): content = o.get('content', 'secret') if content == 'secret': @@ -284,6 +301,8 @@ class Renderer: self._draw_barcodearea(canvas, op, o) elif o['type'] == "textarea": self._draw_textarea(canvas, op, order, o) + elif o['type'] == "poweredby": + self._draw_poweredby(canvas, op, o) canvas.showPage() def render_background(self, buffer, title=_('Ticket')): diff --git a/src/pretix/control/templates/pretixcontrol/pdf/index.html b/src/pretix/control/templates/pretixcontrol/pdf/index.html index 17d7eec75d..893b0d2273 100644 --- a/src/pretix/control/templates/pretixcontrol/pdf/index.html +++ b/src/pretix/control/templates/pretixcontrol/pdf/index.html @@ -2,7 +2,7 @@ {% load i18n %} {% load staticfiles %} {% load compress %} -{% block title %}{% trans "PDF Ticket Editor" %}{% endblock %} +{% block title %}{% trans "PDF Editor" %}{% endblock %} {% block custom_header %} {{ block.super }} {% compress css %} @@ -204,7 +204,7 @@ id="toolbox-position-y"> -
+

+
+
+
+ +
+

@@ -327,6 +336,12 @@ {% trans "QR code for Lead Scanning" %} +
@@ -349,6 +364,8 @@ + + {% for family, styles in fonts.items %} {% for style, formats in styles.items %} diff --git a/src/pretix/plugins/ticketoutputpdf/migrations/0003_auto_20180710_1321.py b/src/pretix/plugins/ticketoutputpdf/migrations/0003_auto_20180710_1321.py new file mode 100644 index 0000000000..8580f8dedf --- /dev/null +++ b/src/pretix/plugins/ticketoutputpdf/migrations/0003_auto_20180710_1321.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import json + +from django.db import migrations +from django.db.models import Q + + +def add_pretix_logo(app, schema_editor): + TicketLayout = app.get_model('ticketoutputpdf', 'TicketLayout') + for tl in TicketLayout.objects.filter(Q(background__isnull=True) | Q(background="")): + l = json.loads(tl.layout) + l.append({"type": "poweredby", "left": "88.72", "bottom": "10.00", "size": "20.00", "content": "dark"}) + tl.layout = json.dumps(l) + tl.save(update_fields=['layout']) + + +class Migration(migrations.Migration): + dependencies = [ + ('ticketoutputpdf', '0002_auto_20180605_2022'), + ] + + operations = [ + migrations.RunPython(add_pretix_logo, migrations.RunPython.noop) + ] diff --git a/src/pretix/plugins/ticketoutputpdf/models.py b/src/pretix/plugins/ticketoutputpdf/models.py index bbb191aa17..4310d699e2 100644 --- a/src/pretix/plugins/ticketoutputpdf/models.py +++ b/src/pretix/plugins/ticketoutputpdf/models.py @@ -53,7 +53,8 @@ class TicketLayout(LoggedModel): '"type": "textarea"}, {"italic": false, "bottom": "194.50", "align": "left", "fontfamily": "Open ' 'Sans", "width": "90.00", "left": "102.50", "text": "tdmruoekvkpbv1o2mv8xccvqcikvr58u", "content": ' '"secret", "fontsize": "13.0", "bold": false, "color": [0, 0, 0, 1], "type": "textarea"}, ' - '{"left": "130.40", "bottom": "204.50", "type": "barcodearea", "size": "64.00"}]' + '{"left": "130.40", "bottom": "204.50", "type": "barcodearea", "size": "64.00"},{"type":"poweredby",' + '"left":"88.72","bottom":"10.00","size":"20.00","content":"dark"}]' ) background = models.FileField(null=True, blank=True, upload_to=bg_name, max_length=255) diff --git a/src/pretix/plugins/ticketoutputpdf/ticketoutput.py b/src/pretix/plugins/ticketoutputpdf/ticketoutput.py index e9d0f951ca..86e427a159 100644 --- a/src/pretix/plugins/ticketoutputpdf/ticketoutput.py +++ b/src/pretix/plugins/ticketoutputpdf/ticketoutput.py @@ -157,7 +157,8 @@ class PdfTicketOutput(BaseTicketOutput): {"type": "textarea", "left": "102.50", "bottom": "194.50", "fontsize": "13.0", "color": [0, 0, 0, 1], "fontfamily": "Open Sans", "bold": False, "italic": False, "width": "90.00", "content": "secret", "text": "tdmruoekvkpbv1o2mv8xccvqcikvr58u", "align": "left"}, - {"type": "barcodearea", "left": "130.40", "bottom": "204.50", "size": "64.00"} + {"type": "barcodearea", "left": "130.40", "bottom": "204.50", "size": "64.00"}, + {"type": "poweredby", "left": "88.72", "bottom": "10.00", "size": "20.00"}, ] def _migrate_from_old_settings(self): diff --git a/src/pretix/static/pretixcontrol/js/ui/editor.js b/src/pretix/static/pretixcontrol/js/ui/editor.js index 17ba02bf0d..2511ed1317 100644 --- a/src/pretix/static/pretixcontrol/js/ui/editor.js +++ b/src/pretix/static/pretixcontrol/js/ui/editor.js @@ -1,4 +1,25 @@ /*globals $, gettext, fabric, PDFJS*/ +fabric.Poweredby = fabric.util.createClass(fabric.Image, { + type: 'poweredby', + + initialize: function (options) { + options || (options = {}); + + this.callSuper('initialize', $("#poweredby-" + options.content).get(0), options); + this.set('label', options.label || ''); + }, + + toObject: function () { + return fabric.util.object.extend(this.callSuper('toObject'), {}); + }, + + _render: function (ctx) { + this.callSuper('_render', ctx); + }, +}); +fabric.Poweredby.fromObject = function (object, callback, forceAsync) { + return fabric.Object._fromObject('Poweredby', object, callback, forceAsync); +}; fabric.Barcodearea = fabric.util.createClass(fabric.Rect, { type: 'barcodearea', @@ -119,6 +140,14 @@ var editor = { size: editor._px2mm(o.height * o.scaleY).toFixed(2), content: o.content, }); + } else if (o.type === "poweredby") { + d.push({ + type: "poweredby", + 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, + }); } } return d; @@ -129,6 +158,10 @@ var editor = { o = editor._add_qrcode(); o.content = d.content; o.scaleToHeight(editor._mm2px(d.size)); + } else if (d.type === "poweredby") { + o = editor._add_poweredby(d.content); + o.content = d.content; + o.scaleToHeight(editor._mm2px(d.size)); } else if (d.type === "textarea" || o.type === "text") { o = editor._add_text(); o.setColor('rgb(' + d.color[0] + ',' + d.color[1] + ',' + d.color[2] + ')'); @@ -289,6 +322,9 @@ var editor = { if (o.type === "barcodearea") { $("#toolbox-squaresize").val(editor._px2mm(o.height * o.scaleY).toFixed(2)); + } else if (o.type === "poweredby") { + $("#toolbox-squaresize").val(editor._px2mm(o.height * o.scaleY).toFixed(2)); + $("#toolbox-poweredby-style").val(o.content); } else if (o.type === "text" || o.type === "textarea") { var col = (new fabric.Color(o.getFill()))._source; $("#toolbox-col").val("#" + ((1 << 24) + (col[0] << 16) + (col[1] << 8) + col[2]).toString(16).slice(1)); @@ -334,6 +370,23 @@ var editor = { o.setScaleX(1); o.setScaleY(1); o.set('top', new_top) + } else if (o.type === "poweredby") { + var new_h = Math.max(1, editor._mm2px($("#toolbox-squaresize").val())); + new_top += o.height * o.scaleY - new_h; + o.setWidth(new_h / o.height * o.width); + o.setHeight(new_h); + o.setScaleX(1); + o.setScaleY(1); + o.set('top', new_top) + if ($("#toolbox-poweredby-style").val() !== o.content) { + var data = editor.dump([o]); + data[0].content = $("#toolbox-poweredby-style").val(); + var newo = editor._add_from_data(data[0]); + o.remove(); + editor.fabric.discardActiveGroup(); + editor.fabric.discardActiveObject(); + editor.fabric.setActiveObject(newo); + } } else if (o.type === "textarea" || o.type === "text") { o.setColor($("#toolbox-col").val()); o.setFontSize(editor._pt2px($("#toolbox-fontsize").val())); @@ -371,6 +424,8 @@ var editor = { $("#toolbox-heading").text(gettext("Text object")); } else if (o.type === "barcodearea") { $("#toolbox-heading").text(gettext("Barcode area")); + } else if (o.type === "poweredby") { + $("#toolbox-heading").text(gettext("Powered by pretix")); } else { $("#toolbox-heading").text(gettext("Object")); } @@ -415,6 +470,22 @@ var editor = { return text; }, + _add_poweredby: function (content) { + var rect = new fabric.Poweredby({ + left: 100, + top: 100, + width: 205, + height: 126, + lockRotation: true, + lockUniScaling: true, + content: content + }); + rect.setControlsVisibility({'mtr': false}); + editor.fabric.add(rect); + editor._create_savepoint(); + return rect; + }, + _add_qrcode: function () { var rect = new fabric.Barcodearea({ left: 100, @@ -654,6 +725,7 @@ var editor = { editor._load_pdf(); $("#editor-add-qrcode, #editor-add-qrcode-lead").click(editor._add_qrcode); $("#editor-add-text").click(editor._add_text); + $("#editor-add-poweredby").click(function() {editor._add_poweredby("dark")}); editor.$cva.get(0).tabIndex = 1000; editor.$cva.on("keydown", editor._on_keydown); $("#editor-save").on("click", editor._save); diff --git a/src/pretix/static/pretixcontrol/scss/pdfeditor.css b/src/pretix/static/pretixcontrol/scss/pdfeditor.css index 5c40fef5e6..0d499a2e1f 100644 --- a/src/pretix/static/pretixcontrol/scss/pdfeditor.css +++ b/src/pretix/static/pretixcontrol/scss/pdfeditor.css @@ -13,10 +13,19 @@ body { #toolbox .control-group { margin-bottom: 5px; } -#toolbox .position, #toolbox .squaresize, #toolbox[data-type] .pdf-info, #toolbox .text, #toolbox .object-buttons { +#toolbox .position, +#toolbox .squaresize, +#toolbox .poweredby, +#toolbox[data-type] .pdf-info, +#toolbox .text, +#toolbox .object-buttons { display: none; } -#toolbox[data-type] .position, #toolbox[data-type=barcodearea] .squaresize, #toolbox[data-type=text] .text, #toolbox[data-type=textarea] .text, +#toolbox[data-type] .position, +#toolbox[data-type=barcodearea] .squaresize, +#toolbox[data-type=poweredby] .poweredby, +#toolbox[data-type=text] .text, +#toolbox[data-type=textarea] .text, #toolbox[data-type] .object-buttons { display: block; } diff --git a/src/pretix/static/pretixpresale/pdf/powered_by_pretix_dark.png b/src/pretix/static/pretixpresale/pdf/powered_by_pretix_dark.png new file mode 100644 index 0000000000..e244e4de1a Binary files /dev/null and b/src/pretix/static/pretixpresale/pdf/powered_by_pretix_dark.png differ diff --git a/src/pretix/static/pretixpresale/pdf/powered_by_pretix_dark.svg b/src/pretix/static/pretixpresale/pdf/powered_by_pretix_dark.svg new file mode 100644 index 0000000000..4a7da8e011 --- /dev/null +++ b/src/pretix/static/pretixpresale/pdf/powered_by_pretix_dark.svg @@ -0,0 +1,89 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + ticketing powered by + + + diff --git a/src/pretix/static/pretixpresale/pdf/powered_by_pretix_white.png b/src/pretix/static/pretixpresale/pdf/powered_by_pretix_white.png new file mode 100644 index 0000000000..d05c47565d Binary files /dev/null and b/src/pretix/static/pretixpresale/pdf/powered_by_pretix_white.png differ diff --git a/src/pretix/static/pretixpresale/pdf/powered_by_pretix_white.svg b/src/pretix/static/pretixpresale/pdf/powered_by_pretix_white.svg new file mode 100644 index 0000000000..76066bdc61 --- /dev/null +++ b/src/pretix/static/pretixpresale/pdf/powered_by_pretix_white.svg @@ -0,0 +1,92 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + ticketing powered by + + + diff --git a/src/pretix/static/pretixpresale/pdf/ticket_default_a4.pdf b/src/pretix/static/pretixpresale/pdf/ticket_default_a4.pdf index 2ac04cf1d6..820cab02df 100644 Binary files a/src/pretix/static/pretixpresale/pdf/ticket_default_a4.pdf and b/src/pretix/static/pretixpresale/pdf/ticket_default_a4.pdf differ diff --git a/src/pretix/static/pretixpresale/pdf/ticket_default_a4.svg b/src/pretix/static/pretixpresale/pdf/ticket_default_a4.svg index d9a9d1359f..1a68a2b03f 100644 --- a/src/pretix/static/pretixpresale/pdf/ticket_default_a4.svg +++ b/src/pretix/static/pretixpresale/pdf/ticket_default_a4.svg @@ -14,7 +14,7 @@ viewBox="0 0 744.09448819 1052.3622047" id="svg2" version="1.1" - inkscape:version="0.92.1 r" + inkscape:version="0.92.2 2405546, 2018-03-11" sodipodi:docname="ticket_default_a4.svg"> @@ -26,15 +26,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.98994949" - inkscape:cx="212.60764" - inkscape:cy="434.76784" + inkscape:cx="378.27266" + inkscape:cy="353.95564" inkscape:document-units="px" inkscape:current-layer="layer1-3" showgrid="false" inkscape:window-width="1916" - inkscape:window-height="1041" + inkscape:window-height="987" inkscape:window-x="1920" - inkscape:window-y="18" + inkscape:window-y="72" inkscape:window-maximized="0" /> @@ -44,7 +44,7 @@ image/svg+xml - + @@ -56,18 +56,6 @@ transform="translate(-235.06454,-531.49224)" id="layer1-3" inkscape:label="Ebene 1"> - - - - ticketing powered by