Compare commits

...

6 Commits

Author SHA1 Message Date
Richard Schreiber
3a1eca7d9c add comment regarding rotation direction 2022-10-04 13:27:37 +02:00
Richard Schreiber
37cfc8eb62 fix handling of rotated background 2022-10-04 13:13:42 +02:00
Richard Schreiber
571c68904d fix code style and imports 2022-09-30 09:46:29 +02:00
Richard Schreiber
e4d2264d5e fix page translate 2022-09-30 09:28:40 +02:00
Richard Schreiber
0ba93c06a4 fix code style issues 2022-09-30 09:28:40 +02:00
Richard Schreiber
8a079a2356 improve bg performance by using pdftk 2022-09-30 09:28:40 +02:00
2 changed files with 85 additions and 64 deletions

View File

@@ -891,7 +891,11 @@ class Renderer:
elif o['type'] == "poweredby": elif o['type'] == "poweredby":
self._draw_poweredby(canvas, op, o) self._draw_poweredby(canvas, op, o)
if self.bg_pdf: 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: if show_page:
canvas.showPage() canvas.showPage()
@@ -915,13 +919,37 @@ class Renderer:
with open(os.path.join(d, 'out.pdf'), 'rb') as f: with open(os.path.join(d, 'out.pdf'), 'rb') as f:
return BytesIO(f.read()) return BytesIO(f.read())
else: else:
from PyPDF2 import PdfReader, PdfWriter from PyPDF2 import PdfReader, PdfWriter, Transformation
from PyPDF2.generic import RectangleObject
buffer.seek(0) buffer.seek(0)
new_pdf = PdfReader(buffer) new_pdf = PdfReader(buffer)
output = PdfWriter() output = PdfWriter()
for i, page in enumerate(new_pdf.pages): for i, page in enumerate(new_pdf.pages):
bg_page = copy.copy(self.bg_pdf.pages[i]) 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) bg_page.merge_page(page)
output.add_page(bg_page) output.add_page(bg_page)

View File

@@ -32,7 +32,6 @@
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # 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. # License for the specific language governing permissions and limitations under the License.
import copy
import json import json
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime, time, timedelta from datetime import datetime, time, timedelta
@@ -43,12 +42,13 @@ 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 import PdfMerger, PdfReader, PdfWriter, Transformation
from PyPDF2.generic import RectangleObject 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
@@ -167,7 +167,6 @@ OPTIONS = OrderedDict([
def render_pdf(event, positions, opt): def render_pdf(event, positions, opt):
from PyPDF2 import PdfReader, PdfWriter
Renderer._register_fonts() Renderer._register_fonts()
renderermap = { renderermap = {
@@ -178,71 +177,65 @@ 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):
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 return outbuffer