From bb5a9bdbf10a59568cecc07e3a625f3359fd2fb7 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 5 Jan 2026 12:15:35 +0100 Subject: [PATCH] PDF rendering: Do not create TTFont if already cached (#5748) This provides a massive speedup for invoice rendering --- src/pretix/base/invoicing/pdf.py | 20 ++++++++++---------- src/pretix/base/pdf.py | 22 +++++++++++----------- src/pretix/helpers/reportlab.py | 8 ++++++++ src/pretix/plugins/reports/exporters.py | 21 ++++++++++----------- 4 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/pretix/base/invoicing/pdf.py b/src/pretix/base/invoicing/pdf.py index af9d3f0fd5..2eed2f8e33 100644 --- a/src/pretix/base/invoicing/pdf.py +++ b/src/pretix/base/invoicing/pdf.py @@ -46,7 +46,6 @@ from reportlab.lib.styles import ParagraphStyle, StyleSheet1 from reportlab.lib.units import mm from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.pdfmetrics import stringWidth -from reportlab.pdfbase.ttfonts import TTFont from reportlab.pdfgen.canvas import Canvas from reportlab.platypus import ( BaseDocTemplate, Flowable, Frame, KeepTogether, NextPageTemplate, @@ -59,7 +58,8 @@ from pretix.base.services.currencies import SOURCE_NAMES from pretix.base.signals import register_invoice_renderers from pretix.base.templatetags.money import money_filter from pretix.helpers.reportlab import ( - FontFallbackParagraph, ThumbnailingImageReader, reshaper, + FontFallbackParagraph, ThumbnailingImageReader, register_ttf_font_if_new, + reshaper, ) from pretix.presale.style import get_fonts @@ -234,25 +234,25 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer): """ Register fonts with reportlab. By default, this registers the OpenSans font family """ - pdfmetrics.registerFont(TTFont('OpenSans', finders.find('fonts/OpenSans-Regular.ttf'))) - pdfmetrics.registerFont(TTFont('OpenSansIt', finders.find('fonts/OpenSans-Italic.ttf'))) - pdfmetrics.registerFont(TTFont('OpenSansBd', finders.find('fonts/OpenSans-Bold.ttf'))) - pdfmetrics.registerFont(TTFont('OpenSansBI', finders.find('fonts/OpenSans-BoldItalic.ttf'))) + register_ttf_font_if_new('OpenSans', finders.find('fonts/OpenSans-Regular.ttf')) + register_ttf_font_if_new('OpenSansIt', finders.find('fonts/OpenSans-Italic.ttf')) + register_ttf_font_if_new('OpenSansBd', finders.find('fonts/OpenSans-Bold.ttf')) + register_ttf_font_if_new('OpenSansBI', finders.find('fonts/OpenSans-BoldItalic.ttf')) pdfmetrics.registerFontFamily('OpenSans', normal='OpenSans', bold='OpenSansBd', italic='OpenSansIt', boldItalic='OpenSansBI') for family, styles in get_fonts(event=self.event, pdf_support_required=True).items(): - pdfmetrics.registerFont(TTFont(family, finders.find(styles['regular']['truetype']))) + register_ttf_font_if_new(family, finders.find(styles['regular']['truetype'])) if family == self.event.settings.invoice_renderer_font: self.font_regular = family if 'bold' in styles: self.font_bold = family + ' B' if 'italic' in styles: - pdfmetrics.registerFont(TTFont(family + ' I', finders.find(styles['italic']['truetype']))) + register_ttf_font_if_new(family + ' I', finders.find(styles['italic']['truetype'])) if 'bold' in styles: - pdfmetrics.registerFont(TTFont(family + ' B', finders.find(styles['bold']['truetype']))) + register_ttf_font_if_new(family + ' B', finders.find(styles['bold']['truetype'])) if 'bolditalic' in styles: - pdfmetrics.registerFont(TTFont(family + ' B I', finders.find(styles['bolditalic']['truetype']))) + register_ttf_font_if_new(family + ' B I', finders.find(styles['bolditalic']['truetype'])) def _normalize(self, text): # reportlab does not support unicode combination characters diff --git a/src/pretix/base/pdf.py b/src/pretix/base/pdf.py index e298288644..5d11c35045 100644 --- a/src/pretix/base/pdf.py +++ b/src/pretix/base/pdf.py @@ -71,9 +71,7 @@ from reportlab.lib.colors import Color from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT from reportlab.lib.styles import ParagraphStyle from reportlab.lib.units import mm -from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.pdfmetrics import getAscentDescent -from reportlab.pdfbase.ttfonts import TTFont from reportlab.pdfgen.canvas import Canvas from reportlab.platypus import Paragraph @@ -84,7 +82,9 @@ from pretix.base.signals import layout_image_variables, layout_text_variables from pretix.base.templatetags.money import money_filter from pretix.base.templatetags.phone_format import phone_format from pretix.helpers.daterange import datetimerange -from pretix.helpers.reportlab import ThumbnailingImageReader, reshaper +from pretix.helpers.reportlab import ( + ThumbnailingImageReader, register_ttf_font_if_new, reshaper, +) from pretix.presale.style import get_fonts logger = logging.getLogger(__name__) @@ -794,19 +794,19 @@ class Renderer: def _register_fonts(cls, event: Event = None): if hasattr(cls, '_fonts_registered'): return - pdfmetrics.registerFont(TTFont('Open Sans', finders.find('fonts/OpenSans-Regular.ttf'))) - pdfmetrics.registerFont(TTFont('Open Sans I', finders.find('fonts/OpenSans-Italic.ttf'))) - pdfmetrics.registerFont(TTFont('Open Sans B', finders.find('fonts/OpenSans-Bold.ttf'))) - pdfmetrics.registerFont(TTFont('Open Sans B I', finders.find('fonts/OpenSans-BoldItalic.ttf'))) + register_ttf_font_if_new('Open Sans', finders.find('fonts/OpenSans-Regular.ttf')) + register_ttf_font_if_new('Open Sans I', finders.find('fonts/OpenSans-Italic.ttf')) + register_ttf_font_if_new('Open Sans B', finders.find('fonts/OpenSans-Bold.ttf')) + register_ttf_font_if_new('Open Sans B I', finders.find('fonts/OpenSans-BoldItalic.ttf')) for family, styles in get_fonts(event, pdf_support_required=True).items(): - pdfmetrics.registerFont(TTFont(family, finders.find(styles['regular']['truetype']))) + register_ttf_font_if_new(family, finders.find(styles['regular']['truetype'])) if 'italic' in styles: - pdfmetrics.registerFont(TTFont(family + ' I', finders.find(styles['italic']['truetype']))) + register_ttf_font_if_new(family + ' I', finders.find(styles['italic']['truetype'])) if 'bold' in styles: - pdfmetrics.registerFont(TTFont(family + ' B', finders.find(styles['bold']['truetype']))) + register_ttf_font_if_new(family + ' B', finders.find(styles['bold']['truetype'])) if 'bolditalic' in styles: - pdfmetrics.registerFont(TTFont(family + ' B I', finders.find(styles['bolditalic']['truetype']))) + register_ttf_font_if_new(family + ' B I', finders.find(styles['bolditalic']['truetype'])) cls._fonts_registered = True diff --git a/src/pretix/helpers/reportlab.py b/src/pretix/helpers/reportlab.py index 448ac5c847..5a39ac4a5b 100644 --- a/src/pretix/helpers/reportlab.py +++ b/src/pretix/helpers/reportlab.py @@ -100,3 +100,11 @@ class FontFallbackParagraph(Paragraph): if (original_font.endswith("Bd") or original_font.endswith(" B")) and "bold" in styles: return family + " B" return family + + +def register_ttf_font_if_new(name, path): + from reportlab.pdfbase import pdfmetrics + from reportlab.pdfbase.ttfonts import TTFont + + if name not in pdfmetrics.getRegisteredFontNames(): + pdfmetrics.registerFont(TTFont(name, path)) diff --git a/src/pretix/plugins/reports/exporters.py b/src/pretix/plugins/reports/exporters.py index 3a0cd9a154..56df5682d2 100644 --- a/src/pretix/plugins/reports/exporters.py +++ b/src/pretix/plugins/reports/exporters.py @@ -69,7 +69,9 @@ from pretix.base.timeframes import ( resolve_timeframe_to_datetime_start_inclusive_end_exclusive, ) from pretix.control.forms.filter import OverviewFilterForm -from pretix.helpers.reportlab import FontFallbackParagraph +from pretix.helpers.reportlab import ( + FontFallbackParagraph, register_ttf_font_if_new, +) from pretix.presale.style import get_fonts @@ -130,21 +132,18 @@ class ReportlabExportMixin: @staticmethod def register_fonts(): - from reportlab.pdfbase import pdfmetrics - from reportlab.pdfbase.ttfonts import TTFont - - pdfmetrics.registerFont(TTFont('OpenSans', finders.find('fonts/OpenSans-Regular.ttf'))) - pdfmetrics.registerFont(TTFont('OpenSansIt', finders.find('fonts/OpenSans-Italic.ttf'))) - pdfmetrics.registerFont(TTFont('OpenSansBd', finders.find('fonts/OpenSans-Bold.ttf'))) + register_ttf_font_if_new('OpenSans', finders.find('fonts/OpenSans-Regular.ttf')) + register_ttf_font_if_new('OpenSansIt', finders.find('fonts/OpenSans-Italic.ttf')) + register_ttf_font_if_new('OpenSansBd', finders.find('fonts/OpenSans-Bold.ttf')) for family, styles in get_fonts(None, pdf_support_required=True).items(): - pdfmetrics.registerFont(TTFont(family, finders.find(styles['regular']['truetype']))) + register_ttf_font_if_new(family, finders.find(styles['regular']['truetype'])) if 'italic' in styles: - pdfmetrics.registerFont(TTFont(family + ' I', finders.find(styles['italic']['truetype']))) + register_ttf_font_if_new(family + ' I', finders.find(styles['italic']['truetype'])) if 'bold' in styles: - pdfmetrics.registerFont(TTFont(family + ' B', finders.find(styles['bold']['truetype']))) + register_ttf_font_if_new(family + ' B', finders.find(styles['bold']['truetype'])) if 'bolditalic' in styles: - pdfmetrics.registerFont(TTFont(family + ' B I', finders.find(styles['bolditalic']['truetype']))) + register_ttf_font_if_new(family + ' B I', finders.find(styles['bolditalic']['truetype'])) def get_doc_template(self): from reportlab.platypus import BaseDocTemplate