improve bg performance by using pdftk

This commit is contained in:
Richard Schreiber
2022-09-30 09:28:40 +02:00
parent 7205d0689e
commit 8a079a2356
+47 -62
View File
@@ -43,13 +43,12 @@ import dateutil.parser
from django import forms from django import forms
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.models import Exists, OuterRef, Q from django.db.models import Exists, OuterRef, Q
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.utils.timezone import make_aware from django.utils.timezone import make_aware
from django.utils.translation import gettext as _, gettext_lazy from django.utils.translation import gettext as _, gettext_lazy
from PyPDF2 import Transformation
from PyPDF2.generic import RectangleObject
from reportlab.lib import pagesizes from reportlab.lib import pagesizes
from reportlab.lib.units import mm from reportlab.lib.units import mm
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas
@@ -167,7 +166,8 @@ OPTIONS = OrderedDict([
def render_pdf(event, positions, opt): def render_pdf(event, positions, opt):
from PyPDF2 import PdfReader, PdfWriter from PyPDF2 import PdfMerger, PdfReader, PdfWriter, Transformation
from PyPDF2.generic import RectangleObject
Renderer._register_fonts() Renderer._register_fonts()
renderermap = { renderermap = {
@@ -178,71 +178,56 @@ def render_pdf(event, positions, opt):
default_renderer = _renderer(event, event.badge_layouts.get(default=True)) default_renderer = _renderer(event, event.badge_layouts.get(default=True))
except BadgeLayout.DoesNotExist: except BadgeLayout.DoesNotExist:
default_renderer = None default_renderer = None
output_pdf_writer = PdfWriter()
any = False op_renderers = [(op, renderermap.get(op.item_id, default_renderer)) for op in positions if renderermap.get(op.item_id, default_renderer)]
npp = opt['cols'] * opt['rows'] if not len(op_renderers):
raise OrderError(_("None of the selected products is configured to print badges."))
def render_page(positions): # render each badge on its own page first
buffer = BytesIO() merger = PdfMerger()
p = canvas.Canvas(buffer, pagesize=pagesizes.A4) merger.add_metadata({
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({
'/Title': 'Badges', '/Title': 'Badges',
'/Creator': 'pretix', '/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) 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):
if (i % badges_per_page == 0):
nup_page = nup_pdf.add_blank_page(
width=opt['pagesize'][0],
height=opt['pagesize'][1],
)
tx = opt['margins'][3] + (i % opt['cols']) * opt['offsets'][0]
ty = opt['margins'][2] + (opt['rows'] - 1 - i // opt['cols']) * opt['offsets'][1]
nup_page.mergeTranslatedPage(page, tx, ty)
outbuffer = BytesIO()
nup_pdf.write(outbuffer)
outbuffer.seek(0)
return outbuffer return outbuffer