forked from CGM_Public/pretix_original
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a1eca7d9c | ||
|
|
37cfc8eb62 | ||
|
|
571c68904d | ||
|
|
e4d2264d5e | ||
|
|
0ba93c06a4 | ||
|
|
8a079a2356 | ||
|
|
7205d0689e | ||
|
|
cde46012cb | ||
|
|
e4a0122938 | ||
|
|
77c08cb710 | ||
|
|
af49a02047 |
@@ -19,4 +19,4 @@
|
||||
# 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/>.
|
||||
#
|
||||
__version__ = "4.13.0"
|
||||
__version__ = "4.14.0.dev0"
|
||||
|
||||
+30
-2
@@ -891,7 +891,11 @@ class Renderer:
|
||||
elif o['type'] == "poweredby":
|
||||
self._draw_poweredby(canvas, op, o)
|
||||
if self.bg_pdf:
|
||||
canvas.setPageSize((self.bg_pdf.pages[0].mediabox[2], self.bg_pdf.pages[0].mediabox[3]))
|
||||
page_size = (self.bg_pdf.pages[0].mediabox[2], self.bg_pdf.pages[0].mediabox[3])
|
||||
if self.bg_pdf.pages[0].get('/Rotate') in (90, 270):
|
||||
# swap dimensions due to pdf being rotated
|
||||
page_size = page_size[::-1]
|
||||
canvas.setPageSize(page_size)
|
||||
if show_page:
|
||||
canvas.showPage()
|
||||
|
||||
@@ -915,13 +919,37 @@ class Renderer:
|
||||
with open(os.path.join(d, 'out.pdf'), 'rb') as f:
|
||||
return BytesIO(f.read())
|
||||
else:
|
||||
from PyPDF2 import PdfReader, PdfWriter
|
||||
from PyPDF2 import PdfReader, PdfWriter, Transformation
|
||||
from PyPDF2.generic import RectangleObject
|
||||
buffer.seek(0)
|
||||
new_pdf = PdfReader(buffer)
|
||||
output = PdfWriter()
|
||||
|
||||
for i, page in enumerate(new_pdf.pages):
|
||||
bg_page = copy.copy(self.bg_pdf.pages[i])
|
||||
bg_rotation = bg_page.get('/Rotate')
|
||||
if bg_rotation:
|
||||
# /Rotate is clockwise, transformation.rotate is counter-clockwise
|
||||
t = Transformation().rotate(bg_rotation)
|
||||
w = float(page.mediabox.getWidth())
|
||||
h = float(page.mediabox.getHeight())
|
||||
if bg_rotation in (90, 270):
|
||||
# offset due to rotation base
|
||||
if bg_rotation == 90:
|
||||
t = t.translate(h, 0)
|
||||
else:
|
||||
t = t.translate(0, w)
|
||||
# rotate mediabox as well
|
||||
page.mediabox = RectangleObject((
|
||||
page.mediabox.left.as_numeric(),
|
||||
page.mediabox.bottom.as_numeric(),
|
||||
page.mediabox.top.as_numeric(),
|
||||
page.mediabox.right.as_numeric(),
|
||||
))
|
||||
page.trimbox = page.mediabox
|
||||
elif bg_rotation == 180:
|
||||
t = t.translate(w, h)
|
||||
page.add_transformation(t)
|
||||
bg_page.merge_page(page)
|
||||
output.add_page(bg_page)
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
import copy
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime, time, timedelta
|
||||
@@ -43,12 +42,13 @@ import dateutil.parser
|
||||
from django import forms
|
||||
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.models import Exists, OuterRef, Q
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.utils.timezone import make_aware
|
||||
from django.utils.translation import gettext as _, gettext_lazy
|
||||
from PyPDF2 import Transformation
|
||||
from PyPDF2 import PdfMerger, PdfReader, PdfWriter, Transformation
|
||||
from PyPDF2.generic import RectangleObject
|
||||
from reportlab.lib import pagesizes
|
||||
from reportlab.lib.units import mm
|
||||
@@ -96,7 +96,7 @@ OPTIONS = OrderedDict([
|
||||
'cols': 2,
|
||||
'rows': 2,
|
||||
'margins': [0 * mm, 0 * mm, 0 * mm, 0 * mm],
|
||||
'offsets': [pagesizes.portrait(pagesizes.A4)[0] / 2, pagesizes.portrait(pagesizes.A4)[0] / 2],
|
||||
'offsets': [pagesizes.portrait(pagesizes.A4)[0] / 2, pagesizes.portrait(pagesizes.A4)[1] / 2],
|
||||
'pagesize': pagesizes.portrait(pagesizes.A4),
|
||||
}),
|
||||
('a4_a7l', {
|
||||
@@ -112,7 +112,7 @@ OPTIONS = OrderedDict([
|
||||
'cols': 4,
|
||||
'rows': 2,
|
||||
'margins': [0 * mm, 0 * mm, 0 * mm, 0 * mm],
|
||||
'offsets': [pagesizes.landscape(pagesizes.A4)[0] / 4, pagesizes.landscape(pagesizes.A4)[0] / 2],
|
||||
'offsets': [pagesizes.landscape(pagesizes.A4)[0] / 4, pagesizes.landscape(pagesizes.A4)[1] / 2],
|
||||
'pagesize': pagesizes.landscape(pagesizes.A4),
|
||||
}),
|
||||
('durable_54x90', {
|
||||
@@ -167,7 +167,6 @@ OPTIONS = OrderedDict([
|
||||
|
||||
|
||||
def render_pdf(event, positions, opt):
|
||||
from PyPDF2 import PdfReader, PdfWriter
|
||||
Renderer._register_fonts()
|
||||
|
||||
renderermap = {
|
||||
@@ -178,71 +177,65 @@ def render_pdf(event, positions, opt):
|
||||
default_renderer = _renderer(event, event.badge_layouts.get(default=True))
|
||||
except BadgeLayout.DoesNotExist:
|
||||
default_renderer = None
|
||||
output_pdf_writer = PdfWriter()
|
||||
|
||||
any = False
|
||||
npp = opt['cols'] * opt['rows']
|
||||
op_renderers = [(op, renderermap.get(op.item_id, default_renderer)) for op in positions if renderermap.get(op.item_id, default_renderer)]
|
||||
if not len(op_renderers):
|
||||
raise OrderError(_("None of the selected products is configured to print badges."))
|
||||
|
||||
def render_page(positions):
|
||||
buffer = BytesIO()
|
||||
p = canvas.Canvas(buffer, pagesize=pagesizes.A4)
|
||||
for i, (op, r) in enumerate(positions):
|
||||
offsetx = opt['margins'][3] + (i % opt['cols']) * opt['offsets'][0]
|
||||
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)
|
||||
p.translate(-offsetx, -offsety)
|
||||
|
||||
if opt['pagesize']:
|
||||
p.setPageSize(opt['pagesize'])
|
||||
p.showPage()
|
||||
p.save()
|
||||
buffer.seek(0)
|
||||
canvas_pdf_reader = PdfReader(buffer)
|
||||
empty_pdf_page = output_pdf_writer.add_blank_page(
|
||||
width=opt['pagesize'][0] if opt['pagesize'] else positions[0][1].bg_pdf.pages[0].mediabox[2],
|
||||
height=opt['pagesize'][1] if opt['pagesize'] else positions[0][1].bg_pdf.pages[0].mediabox[3],
|
||||
)
|
||||
for i, (op, r) in enumerate(positions):
|
||||
bg_page = copy.copy(r.bg_pdf.pages[0])
|
||||
offsetx = opt['margins'][3] + (i % opt['cols']) * opt['offsets'][0]
|
||||
offsety = opt['margins'][2] + (opt['rows'] - 1 - i // opt['cols']) * opt['offsets'][1]
|
||||
bg_page.add_transformation(Transformation().translate(offsetx, offsety))
|
||||
mb = bg_page.mediabox
|
||||
bg_page.mediabox = RectangleObject((
|
||||
mb.left.as_numeric() + offsetx,
|
||||
mb.bottom.as_numeric() + offsety,
|
||||
mb.right.as_numeric() + offsetx,
|
||||
mb.top.as_numeric() + offsety
|
||||
))
|
||||
bg_page.trimbox = bg_page.mediabox
|
||||
empty_pdf_page.merge_page(bg_page)
|
||||
empty_pdf_page.merge_page(canvas_pdf_reader.pages[0])
|
||||
|
||||
pagebuffer = []
|
||||
outbuffer = BytesIO()
|
||||
for op in positions:
|
||||
r = renderermap.get(op.item_id, default_renderer)
|
||||
if not r:
|
||||
continue
|
||||
any = True
|
||||
pagebuffer.append((op, r))
|
||||
if len(pagebuffer) == npp:
|
||||
render_page(pagebuffer)
|
||||
pagebuffer.clear()
|
||||
|
||||
if pagebuffer:
|
||||
render_page(pagebuffer)
|
||||
|
||||
output_pdf_writer.add_metadata({
|
||||
# render each badge on its own page first
|
||||
merger = PdfMerger()
|
||||
merger.add_metadata({
|
||||
'/Title': 'Badges',
|
||||
'/Creator': 'pretix',
|
||||
})
|
||||
output_pdf_writer.write(outbuffer)
|
||||
for op, renderer in op_renderers:
|
||||
buffer = BytesIO()
|
||||
page = canvas.Canvas(buffer, pagesize=pagesizes.A4)
|
||||
with language(op.order.locale, op.order.event.settings.region):
|
||||
renderer.draw_page(page, op.order, op)
|
||||
|
||||
if opt['pagesize']:
|
||||
page.setPageSize(opt['pagesize'])
|
||||
page.save()
|
||||
buffer = renderer.render_background(buffer, _('Badge'))
|
||||
merger.append(ContentFile(buffer.read()))
|
||||
|
||||
outbuffer = BytesIO()
|
||||
merger.write(outbuffer)
|
||||
outbuffer.seek(0)
|
||||
if not any:
|
||||
raise OrderError(_("None of the selected products is configured to print badges."))
|
||||
|
||||
badges_per_page = opt['cols'] * opt['rows']
|
||||
if badges_per_page == 1:
|
||||
# no need to place multiple badges on one page
|
||||
return outbuffer
|
||||
|
||||
# place n-up badges/pages per page
|
||||
badges_pdf = PdfReader(outbuffer)
|
||||
nup_pdf = PdfWriter()
|
||||
nup_page = None
|
||||
for i, page in enumerate(badges_pdf.pages):
|
||||
di = i % badges_per_page
|
||||
if di == 0:
|
||||
nup_page = nup_pdf.add_blank_page(
|
||||
width=opt['pagesize'][0],
|
||||
height=opt['pagesize'][1],
|
||||
)
|
||||
tx = opt['margins'][3] + (di % opt['cols']) * opt['offsets'][0]
|
||||
ty = opt['margins'][2] + (opt['rows'] - 1 - (di // opt['cols'])) * opt['offsets'][1]
|
||||
page.add_transformation(Transformation().translate(tx, ty))
|
||||
page.mediabox = RectangleObject((
|
||||
page.mediabox.left.as_numeric() + tx,
|
||||
page.mediabox.bottom.as_numeric() + ty,
|
||||
page.mediabox.right.as_numeric() + tx,
|
||||
page.mediabox.top.as_numeric() + ty
|
||||
))
|
||||
page.trimbox = page.mediabox
|
||||
nup_page.merge_page(page)
|
||||
|
||||
outbuffer = BytesIO()
|
||||
nup_pdf.write(outbuffer)
|
||||
outbuffer.seek(0)
|
||||
|
||||
return outbuffer
|
||||
|
||||
|
||||
|
||||
@@ -66,25 +66,25 @@
|
||||
{% endif %}
|
||||
{% if eu_barcodes %}
|
||||
<div id="banktransfer_qrcodes_girocode" class="tab-pane fade {% if not swiss_qrbill %}active in{% endif %}" role="tabpanel" aria-labelledby="banktransfer_qrcodes_girocode_tab">
|
||||
<p class="small">
|
||||
{% trans "Scan the qr-code with your banking app" %}
|
||||
</p>
|
||||
<p>
|
||||
<script type="text/plain" data-size="150" data-replace-with-qr data-desc="{% trans 'Scan this image with your banking app’s QR-Reader to start the payment process.' %}">BCD
|
||||
002
|
||||
2
|
||||
SCT
|
||||
{{ settings.bank_details_sepa_bic }}
|
||||
{{ settings.bank_details_sepa_name|unidecode }}
|
||||
{{ settings.bank_details_sepa_iban }}
|
||||
{{ event.currency }}{{ amount|dotdecimal }}
|
||||
<p class="small">
|
||||
{% trans "Scan the qr-code with your banking app" %}
|
||||
</p>
|
||||
<p>
|
||||
<script type="text/plain" data-size="150" data-replace-with-qr data-desc="{% trans 'Scan this image with your banking app’s QR-Reader to start the payment process.' %}">BCD
|
||||
002
|
||||
2
|
||||
SCT
|
||||
{{ settings.bank_details_sepa_bic }}
|
||||
{{ settings.bank_details_sepa_name|unidecode }}
|
||||
{{ settings.bank_details_sepa_iban }}
|
||||
{{ event.currency }}{{ amount|dotdecimal }}
|
||||
|
||||
|
||||
{{ code }}
|
||||
{{ code }}
|
||||
|
||||
</script>
|
||||
</p>
|
||||
</div>
|
||||
</script>
|
||||
</p>
|
||||
</div>
|
||||
<div id="banktransfer_qrcodes_bezahlcode" class="tab-pane fade" role="tabpanel" aria-labelledby="banktransfer_qrcodes_bezahlcode_tab">
|
||||
<p class="small">
|
||||
{% trans "Scan the qr-code with your banking app" %}
|
||||
|
||||
@@ -208,6 +208,12 @@ svg.svg-icon {
|
||||
left: 0.5em;
|
||||
}
|
||||
|
||||
.badge-primary { background: $brand-primary; }
|
||||
.badge-success { background: $brand-success; }
|
||||
.badge-danger { background: $brand-danger; }
|
||||
.badge-warning { background: $brand-warning; }
|
||||
.badge-info { background: $brand-info; }
|
||||
|
||||
@include table-row-variant('success', lighten($brand-success, 40%));
|
||||
@include table-row-variant('info', lighten($brand-info, 30%));
|
||||
@include table-row-variant('warning', lighten($brand-warning, 40%));
|
||||
|
||||
Reference in New Issue
Block a user