diff --git a/src/pretix/base/invoicing/pdf.py b/src/pretix/base/invoicing/pdf.py index 21765a629e..efa3a0280b 100644 --- a/src/pretix/base/invoicing/pdf.py +++ b/src/pretix/base/invoicing/pdf.py @@ -22,9 +22,7 @@ import datetime import logging import math -import re import textwrap -import unicodedata from collections import defaultdict from decimal import Decimal from io import BytesIO @@ -58,8 +56,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, register_ttf_font_if_new, - reshaper, + FontFallbackParagraph, PlainTextParagraph, ThumbnailingImageReader, + normalize_text, register_ttf_font_if_new, reshaper, ) from pretix.presale.style import get_fonts @@ -259,18 +257,8 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer): register_ttf_font_if_new(family + ' B I', finders.find(styles['bolditalic']['truetype'])) def _normalize(self, text): - # reportlab does not support unicode combination characters - # It's important we do this before we use ArabicReshaper - text = unicodedata.normalize("NFKC", text) - - # reportlab does not support RTL, ligature-heavy scripts like Arabic. Therefore, we use ArabicReshaper - # to resolve all ligatures and python-bidi to switch RTL texts. - try: - text = "
".join(get_display(reshaper.reshape(l)) for l in re.split("
", text)) - except: - logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text))) - - return text + # alias kept for plugin compatibility + return normalize_text(text) def _upper(self, val): # We uppercase labels, but not in every language @@ -351,10 +339,15 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer): return 'invoice.pdf', 'application/pdf', buffer.read() def _clean_text(self, text, tags=None): - return self._normalize(bleach.clean( - text, - tags=set(tags) if tags else set() - ).strip().replace('
', '
').replace('\n', '
\n')) + # For backwards compatibility with customer content, we need to support tags like
and in a few text + # fields. Therefore, we can't use PlainTextParagraph for these, but run bleach instead to limit the allowed + # tags. + return self._normalize( + bleach.clean( + text, + tags=set(tags) if tags else set() + ).strip().replace('
', '
').replace('\n', '
\n') + ) class PaidMarker(Flowable): @@ -405,8 +398,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): invoice_to_top = 52 * mm def _draw_invoice_to(self, canvas): - p = FontFallbackParagraph(self._clean_text(self.invoice.address_invoice_to), - style=self.stylesheet['Normal']) + p = PlainTextParagraph(self.invoice.address_invoice_to, style=self.stylesheet['Normal']) p.wrapOn(canvas, self.invoice_to_width, self.invoice_to_height) p_size = p.wrap(self.invoice_to_width, self.invoice_to_height) p.drawOn(canvas, self.invoice_to_left, self.pagesize[1] - p_size[1] - self.invoice_to_top) @@ -417,8 +409,8 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): invoice_from_top = 17 * mm def _draw_invoice_from(self, canvas): - p = FontFallbackParagraph( - self._clean_text(self.invoice.full_invoice_from), + p = PlainTextParagraph( + self.invoice.full_invoice_from, style=self.stylesheet['InvoiceFrom'] ) p.wrapOn(canvas, self.invoice_from_width, self.invoice_from_height) @@ -548,13 +540,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): def _draw_event(self, canvas): def shorten(txt): txt = str(txt) - txt = bleach.clean(txt, tags=set()).strip() - p = FontFallbackParagraph(self._normalize(txt.strip().replace('\n', '
\n')), style=self.stylesheet['Normal']) + p = PlainTextParagraph(txt, style=self.stylesheet['Normal']) p_size = p.wrap(self.event_width, self.event_height) while p_size[1] > 2 * self.stylesheet['Normal'].leading: txt = ' '.join(txt.replace('…', '').split()[:-1]) + '…' - p = FontFallbackParagraph(self._normalize(txt.strip().replace('\n', '
\n')), style=self.stylesheet['Normal']) + p = PlainTextParagraph(txt, style=self.stylesheet['Normal']) p_size = p.wrap(self.event_width, self.event_height) return txt @@ -572,7 +563,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): else: p_str = shorten(self.invoice.event.name) - p = FontFallbackParagraph(self._normalize(p_str.strip().replace('\n', '
\n')), style=self.stylesheet['Normal']) + p = PlainTextParagraph(p_str, style=self.stylesheet['Normal']) p.wrapOn(canvas, self.event_width, self.event_height) p_size = p.wrap(self.event_width, self.event_height) p.drawOn(canvas, self.event_left, self.pagesize[1] - self.event_top - p_size[1]) @@ -645,39 +636,37 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): type_info_text = self.invoice.transmission_type_instance.pdf_info_text() if type_info_text: - story.append(FontFallbackParagraph( + story.append(PlainTextParagraph( type_info_text, self.stylesheet['WarningBlock'] )) if self.invoice.custom_field: - story.append(FontFallbackParagraph( + story.append(PlainTextParagraph( '{}: {}'.format( - self._clean_text(str(self.invoice.event.settings.invoice_address_custom_field)), - self._clean_text(self.invoice.custom_field), + str(self.invoice.event.settings.invoice_address_custom_field), + self.invoice.custom_field, ), self.stylesheet['Normal'] )) if self.invoice.internal_reference: - story.append(FontFallbackParagraph( - self._normalize(pgettext('invoice', 'Customer reference: {reference}').format( - reference=self._clean_text(self.invoice.internal_reference), - )), + story.append(PlainTextParagraph( + pgettext('invoice', 'Customer reference: {reference}').format( + reference=self.invoice.internal_reference, + ), self.stylesheet['Normal'] )) if self.invoice.invoice_to_vat_id: - story.append(FontFallbackParagraph( - self._normalize(pgettext('invoice', 'Customer VAT ID')) + ': ' + - self._clean_text(self.invoice.invoice_to_vat_id), + story.append(PlainTextParagraph( + pgettext('invoice', 'Customer VAT ID') + ': ' + self.invoice.invoice_to_vat_id, self.stylesheet['Normal'] )) if self.invoice.invoice_to_beneficiary: - story.append(FontFallbackParagraph( - self._normalize(pgettext('invoice', 'Beneficiary')) + ':
' + - self._clean_text(self.invoice.invoice_to_beneficiary), + story.append(PlainTextParagraph( + pgettext('invoice', 'Beneficiary') + ':\n' + self.invoice.invoice_to_beneficiary, self.stylesheet['Normal'] )) @@ -707,11 +696,11 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): story = [ NextPageTemplate('FirstPage'), - FontFallbackParagraph( - self._normalize( + PlainTextParagraph( + ( pgettext('invoice', 'Tax Invoice') if str(self.invoice.invoice_from_country) == 'AU' else pgettext('invoice', 'Invoice') - ) if not self.invoice.is_cancellation else self._normalize(pgettext('invoice', 'Cancellation')), + ) if not self.invoice.is_cancellation else pgettext('invoice', 'Cancellation'), self.stylesheet['Heading1'] ), Spacer(1, 5 * mm), @@ -733,17 +722,17 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): ] if has_taxes: tdata = [( - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Description')), self.stylesheet['Bold']), - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Qty')), self.stylesheet['BoldRightNoSplit']), - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax rate')), self.stylesheet['BoldRightNoSplit']), - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Net')), self.stylesheet['BoldRightNoSplit']), - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Gross')), self.stylesheet['BoldRightNoSplit']), + PlainTextParagraph(pgettext('invoice', 'Description'), self.stylesheet['Bold']), + PlainTextParagraph(pgettext('invoice', 'Qty'), self.stylesheet['BoldRightNoSplit']), + PlainTextParagraph(pgettext('invoice', 'Tax rate'), self.stylesheet['BoldRightNoSplit']), + PlainTextParagraph(pgettext('invoice', 'Net'), self.stylesheet['BoldRightNoSplit']), + PlainTextParagraph(pgettext('invoice', 'Gross'), self.stylesheet['BoldRightNoSplit']), )] else: tdata = [( - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Description')), self.stylesheet['Bold']), - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Qty')), self.stylesheet['BoldRightNoSplit']), - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Amount')), self.stylesheet['BoldRightNoSplit']), + PlainTextParagraph(pgettext('invoice', 'Description'), self.stylesheet['Bold']), + PlainTextParagraph(pgettext('invoice', 'Qty'), self.stylesheet['BoldRightNoSplit']), + PlainTextParagraph(pgettext('invoice', 'Amount'), self.stylesheet['BoldRightNoSplit']), )] def _group_key(line): @@ -780,8 +769,8 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): max_height = self.stylesheet['Normal'].leading * 5 p_style = self.stylesheet['Normal'] for __ in range(1000): - p = FontFallbackParagraph( - self._clean_text(curr_description, tags=['br']), + p = PlainTextParagraph( + curr_description, p_style ) h = p.wrap(max_width, doc.height)[1] @@ -862,7 +851,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): # Group together at the end of the invoice request_show_service_date = period_line elif period_line: - description_p_list.append(FontFallbackParagraph( + description_p_list.append(PlainTextParagraph( period_line, self.stylesheet['Fineprint'] )) @@ -874,7 +863,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): net_price=money_filter(net_value, self.invoice.event.currency), gross_price=money_filter(gross_value, self.invoice.event.currency), ) - description_p_list.append(FontFallbackParagraph( + description_p_list.append(PlainTextParagraph( single_price_line, self.stylesheet['Fineprint'] )) @@ -883,11 +872,11 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): description_p_list.pop(0), str(len(lines)), localize(tax_rate) + " %", - FontFallbackParagraph( + PlainTextParagraph( money_filter(net_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '), self.stylesheet['NormalRight'] ), - FontFallbackParagraph( + PlainTextParagraph( money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '), self.stylesheet['NormalRight'] ), @@ -904,14 +893,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): single_price_line = pgettext('invoice', 'Single price: {price}').format( price=money_filter(gross_value, self.invoice.event.currency), ) - description_p_list.append(FontFallbackParagraph( + description_p_list.append(PlainTextParagraph( single_price_line, self.stylesheet['Fineprint'] )) tdata.append(( description_p_list.pop(0), str(len(lines)), - FontFallbackParagraph( + PlainTextParagraph( money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '), self.stylesheet['NormalRight'] ), @@ -944,12 +933,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): if has_taxes: tdata.append([ - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Invoice total')), self.stylesheet['Bold']), '', '', '', + PlainTextParagraph(pgettext('invoice', 'Invoice total'), self.stylesheet['Bold']), '', '', '', money_filter(total, self.invoice.event.currency) ]) else: tdata.append([ - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Invoice total')), self.stylesheet['Bold']), '', + PlainTextParagraph(pgettext('invoice', 'Invoice total'), self.stylesheet['Bold']), '', money_filter(total, self.invoice.event.currency) ]) @@ -958,12 +947,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): pending_sum = self.invoice.order.pending_sum if pending_sum != total: tdata.append( - [FontFallbackParagraph(self._normalize(pgettext('invoice', 'Received payments')), self.stylesheet['Normal'])] + + [PlainTextParagraph(pgettext('invoice', 'Received payments'), self.stylesheet['Normal'])] + (['', '', ''] if has_taxes else ['']) + [money_filter(pending_sum - total, self.invoice.event.currency)] ) tdata.append( - [FontFallbackParagraph(self._normalize(pgettext('invoice', 'Outstanding payments')), self.stylesheet['Bold'])] + + [PlainTextParagraph(pgettext('invoice', 'Outstanding payments'), self.stylesheet['Bold'])] + (['', '', ''] if has_taxes else ['']) + [money_filter(pending_sum, self.invoice.event.currency)] ) @@ -980,12 +969,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): s=Sum('amount') )['s'] or Decimal('0.00') tdata.append( - [FontFallbackParagraph(self._normalize(pgettext('invoice', 'Paid by gift card')), self.stylesheet['Normal'])] + + [PlainTextParagraph(pgettext('invoice', 'Paid by gift card'), self.stylesheet['Normal'])] + (['', '', ''] if has_taxes else ['']) + [money_filter(giftcard_sum, self.invoice.event.currency)] ) tdata.append( - [FontFallbackParagraph(self._normalize(pgettext('invoice', 'Remaining amount')), self.stylesheet['Bold'])] + + [PlainTextParagraph(pgettext('invoice', 'Remaining amount'), self.stylesheet['Bold'])] + (['', '', ''] if has_taxes else ['']) + [money_filter(total - giftcard_sum, self.invoice.event.currency)] ) @@ -1008,14 +997,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): story.append(Spacer(1, 10 * mm)) if request_show_service_date: - story.append(FontFallbackParagraph( - self._normalize(pgettext('invoice', 'Invoice period: {daterange}').format(daterange=request_show_service_date)), + story.append(PlainTextParagraph( + pgettext('invoice', 'Invoice period: {daterange}').format(daterange=request_show_service_date), self.stylesheet['Normal'] )) if self.invoice.payment_provider_text: story.append(FontFallbackParagraph( - self._normalize(self.invoice.payment_provider_text), + self._clean_text(self.invoice.payment_provider_text, tags=['br', 'b']), self.stylesheet['Normal'] )) @@ -1039,10 +1028,10 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): ('FONTNAME', (0, 0), (-1, -1), self.font_regular), ] thead = [ - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax rate')), self.stylesheet['Fineprint']), - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Net value')), self.stylesheet['FineprintRight']), - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Gross value')), self.stylesheet['FineprintRight']), - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax')), self.stylesheet['FineprintRight']), + PlainTextParagraph(pgettext('invoice', 'Tax rate'), self.stylesheet['Fineprint']), + PlainTextParagraph(pgettext('invoice', 'Net value'), self.stylesheet['FineprintRight']), + PlainTextParagraph(pgettext('invoice', 'Gross value'), self.stylesheet['FineprintRight']), + PlainTextParagraph(pgettext('invoice', 'Tax'), self.stylesheet['FineprintRight']), '' ] tdata = [thead] @@ -1053,7 +1042,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): continue tax = taxvalue_map[idx] tdata.append([ - FontFallbackParagraph(self._normalize(localize(rate) + " % " + name), self.stylesheet['Fineprint']), + PlainTextParagraph(localize(rate) + " % " + name, self.stylesheet['Fineprint']), money_filter(gross - tax, self.invoice.event.currency), money_filter(gross, self.invoice.event.currency), money_filter(tax, self.invoice.event.currency), @@ -1072,7 +1061,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): table.setStyle(TableStyle(tstyledata)) story.append(Spacer(5 * mm, 5 * mm)) story.append(KeepTogether([ - FontFallbackParagraph(self._normalize(pgettext('invoice', 'Included taxes')), self.stylesheet['FineprintHeading']), + PlainTextParagraph(pgettext('invoice', 'Included taxes'), self.stylesheet['FineprintHeading']), table ])) @@ -1089,7 +1078,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): net = gross - tax tdata.append([ - FontFallbackParagraph(self._normalize(localize(rate) + " % " + name), self.stylesheet['Fineprint']), + PlainTextParagraph(localize(rate) + " % " + name, self.stylesheet['Fineprint']), fmt(net), fmt(gross), fmt(tax), '' ]) @@ -1098,13 +1087,13 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): story.append(KeepTogether([ Spacer(1, height=2 * mm), - FontFallbackParagraph( - self._normalize(pgettext( + PlainTextParagraph( + pgettext( 'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on ' '{date}, this corresponds to:' ).format(rate=localize(self.invoice.foreign_currency_rate), authority=SOURCE_NAMES.get(self.invoice.foreign_currency_source, "?"), - date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT"))), + date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT")), self.stylesheet['Fineprint'] ), Spacer(1, height=3 * mm), @@ -1113,14 +1102,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): elif self.invoice.foreign_currency_display and self.invoice.foreign_currency_rate: foreign_total = round_decimal(total * self.invoice.foreign_currency_rate) story.append(Spacer(1, 5 * mm)) - story.append(FontFallbackParagraph(self._normalize( + story.append(PlainTextParagraph( pgettext( 'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on ' '{date}, the invoice total corresponds to {total}.' ).format(rate=localize(self.invoice.foreign_currency_rate), date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT"), authority=SOURCE_NAMES.get(self.invoice.foreign_currency_source, "?"), - total=fmt(foreign_total))), + total=fmt(foreign_total)), self.stylesheet['Fineprint'] )) @@ -1162,11 +1151,8 @@ class Modern1Renderer(ClassicInvoiceRenderer): def _draw_invoice_from(self, canvas): if not self.invoice.invoice_from: return - c = [ - self._clean_text(l) - for l in self.invoice.address_invoice_from.strip().split('\n') - ] - p = FontFallbackParagraph(self._normalize(' · '.join(c)), style=self.stylesheet['Sender']) + c = self.invoice.address_invoice_from.strip().split('\n') + p = PlainTextParagraph(' · '.join(c), style=self.stylesheet['Sender']) p.wrapOn(canvas, self.invoice_to_width, 15.7 * mm) p.drawOn(canvas, self.invoice_to_left, self.pagesize[1] - self.invoice_to_top + 2 * mm) super()._draw_invoice_from(canvas) @@ -1225,8 +1211,8 @@ class Modern1Renderer(ClassicInvoiceRenderer): _draw(pgettext('invoice', 'Order code'), self.invoice.order.full_code, value_size, self.left_margin, 45 * mm, **kwargs) ] - p = FontFallbackParagraph( - self._normalize(date_format(self.invoice.date, "DATE_FORMAT")), + p = PlainTextParagraph( + date_format(self.invoice.date, "DATE_FORMAT"), style=ParagraphStyle(name=f'Normal{value_size}', fontName=self.font_regular, fontSize=value_size, leading=value_size * 1.2) ) w = stringWidth(p.text, p.frags[0].fontName, p.frags[0].fontSize) @@ -1283,7 +1269,7 @@ class Modern1SimplifiedRenderer(Modern1Renderer): i = [] if not self.invoice.event.has_subevents and self.invoice.event.settings.show_dates_on_frontpage: - i.append(FontFallbackParagraph( + i.append(PlainTextParagraph( pgettext('invoice', 'Event date: {date_range}').format( date_range=self.invoice.event.get_date_range_display(), ), diff --git a/src/pretix/base/pdf.py b/src/pretix/base/pdf.py index 5d11c35045..e8388276be 100644 --- a/src/pretix/base/pdf.py +++ b/src/pretix/base/pdf.py @@ -1045,7 +1045,7 @@ class Renderer: except: logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text))) - p = Paragraph(text, style=style) + p = Paragraph(text, style=style) # not using AutoEscapeParagraph is safe as we escape above return p, ad, lineheight def _draw_textcontainer(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict): diff --git a/src/pretix/control/sysreport.py b/src/pretix/control/sysreport.py index 6beb0e4fc8..e2a908cb09 100644 --- a/src/pretix/control/sysreport.py +++ b/src/pretix/control/sysreport.py @@ -38,6 +38,7 @@ from pretix import __version__ from pretix.base.models import Order, OrderPayment, Transaction from pretix.base.plugins import get_all_plugins from pretix.base.templatetags.money import money_filter +from pretix.helpers.reportlab import PlainTextParagraph from pretix.plugins.reports.exporters import ReportlabExportMixin from pretix.settings import DATA_DIR @@ -79,23 +80,23 @@ class SysReport(ReportlabExportMixin): style_small.fontSize = 6 story = [ - Paragraph("System report", headlinestyle), + PlainTextParagraph("System report", headlinestyle), Spacer(1, 5 * mm), - Paragraph("Usage", subheadlinestyle), + PlainTextParagraph("Usage", subheadlinestyle), Spacer(1, 5 * mm), self._usage_table(), Spacer(1, 5 * mm), - Paragraph("Installed versions", subheadlinestyle), + PlainTextParagraph("Installed versions", subheadlinestyle), Spacer(1, 5 * mm), self._tech_table(), Spacer(1, 5 * mm), - Paragraph("Plugins", subheadlinestyle), + PlainTextParagraph("Plugins", subheadlinestyle), Spacer(1, 5 * mm), - Paragraph(self._get_plugin_versions(), style_small), + PlainTextParagraph(self._get_plugin_versions(), style_small), Spacer(1, 5 * mm), - Paragraph("Custom templates", subheadlinestyle), + PlainTextParagraph("Custom templates", subheadlinestyle), Spacer(1, 5 * mm), - Paragraph(self._get_custom_templates(), style_small), + PlainTextParagraph(self._get_custom_templates(), style_small), Spacer(1, 5 * mm), ] @@ -121,13 +122,13 @@ class SysReport(ReportlabExportMixin): ("RIGHTPADDING", (-1, 0), (-1, -1), 0), ] tdata = [ - [Paragraph("Site URL:", style), Paragraph(settings.SITE_URL, style)], - [Paragraph("pretix version:", style), Paragraph(__version__, style)], - [Paragraph("Python version:", style), Paragraph(sys.version, style)], - [Paragraph("Platform:", style), Paragraph(platform.platform(), style)], + [PlainTextParagraph("Site URL:", style), Paragraph(settings.SITE_URL, style)], + [PlainTextParagraph("pretix version:", style), Paragraph(__version__, style)], + [PlainTextParagraph("Python version:", style), Paragraph(sys.version, style)], + [PlainTextParagraph("Platform:", style), Paragraph(platform.platform(), style)], [ - Paragraph("Database engine:", style), - Paragraph(settings.DATABASES["default"]["ENGINE"], style), + PlainTextParagraph("Database engine:", style), + PlainTextParagraph(settings.DATABASES["default"]["ENGINE"], style), ], ] table = Table(tdata, colWidths=colwidths, repeatRows=0) @@ -206,7 +207,7 @@ class SysReport(ReportlabExportMixin): year_last = now().year tdata = [ [ - Paragraph(l, style_small_head) + PlainTextParagraph(l, style_small_head) for l in ( "Time frame", "Currency", @@ -257,19 +258,19 @@ class SysReport(ReportlabExportMixin): tdata.append( ( - Paragraph( + PlainTextParagraph( date_format(first_day, "M Y") + " – " + date_format(after_day - timedelta(days=1), "M Y"), style_small, ), - Paragraph(c, style_small), - Paragraph(str(orders_count), style_small) if i == 0 else "", - Paragraph(money_filter(revenue_data.get("s_net") or 0, c), style_small), - Paragraph(str(testmode_count), style_small) if i == 0 else "", - Paragraph(str(unconfirmed_count), style_small) if i == 0 else "", - Paragraph(str(revenue_data.get("c") or 0), style_small), - Paragraph(money_filter(revenue_data.get("s_gross") or 0, c), style_small), + PlainTextParagraph(c, style_small), + PlainTextParagraph(str(orders_count), style_small) if i == 0 else "", + PlainTextParagraph(money_filter(revenue_data.get("s_net") or 0, c), style_small), + PlainTextParagraph(str(testmode_count), style_small) if i == 0 else "", + PlainTextParagraph(str(unconfirmed_count), style_small) if i == 0 else "", + PlainTextParagraph(str(revenue_data.get("c") or 0), style_small), + PlainTextParagraph(money_filter(revenue_data.get("s_gross") or 0, c), style_small), ) ) diff --git a/src/pretix/helpers/reportlab.py b/src/pretix/helpers/reportlab.py index 5a39ac4a5b..618dbb87ef 100644 --- a/src/pretix/helpers/reportlab.py +++ b/src/pretix/helpers/reportlab.py @@ -20,14 +20,19 @@ # . # import logging +import re +import unicodedata from arabic_reshaper import ArabicReshaper +from bidi import get_display from django.conf import settings from django.utils.functional import SimpleLazyObject +from django.utils.html import escape from PIL import Image from reportlab.lib.styles import ParagraphStyle from reportlab.lib.utils import ImageReader from reportlab.pdfbase import pdfmetrics +from reportlab.pdfbase.ttfonts import TTFont from reportlab.platypus import Paragraph from pretix.presale.style import get_fonts @@ -70,6 +75,20 @@ reshaper = SimpleLazyObject(lambda: ArabicReshaper(configuration={ })) +def normalize_text(text: str) -> str: + # reportlab does not support unicode combination characters + # It's important we do this before we use ArabicReshaper + text = unicodedata.normalize("NFKC", text) + + # reportlab does not support RTL, ligature-heavy scripts like Arabic. Therefore, we use ArabicReshaper + # to resolve all ligatures and python-bidi to switch RTL texts. + try: + text = "\n".join(get_display(reshaper.reshape(l)) for l in re.split("\n", text)) + except: + logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text))) + return text + + class FontFallbackParagraph(Paragraph): def __init__(self, text, style=None, *args, **kwargs): if style is None: @@ -87,6 +106,8 @@ class FontFallbackParagraph(Paragraph): if not text: return True font = pdfmetrics.getFont(font_name) + if not isinstance(font, TTFont): + return True return all( ord(c) in font.face.charToGlyph or not c.isprintable() for c in text @@ -102,6 +123,24 @@ class FontFallbackParagraph(Paragraph): return family +class PlainTextParagraph(FontFallbackParagraph): + def __init__(self, text, style=None, linebreaks=True, *args, **kwargs): + if not isinstance(text, str): + if hasattr(text, '__html__'): + raise ValueError("It is contradictory to pass escaped content to PlainTextParagraph") + text = str(text) + + # Normalize unicode and apply reshaping + text = normalize_text(text) + + # Escape any HTML in the text + text = escape(text) + + if linebreaks: + text = text.strip().replace("\n", "
\n") + super().__init__(text, style, *args, **kwargs) + + def register_ttf_font_if_new(name, path): from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont diff --git a/src/pretix/plugins/checkinlists/exporters.py b/src/pretix/plugins/checkinlists/exporters.py index 21739e1048..c12807f6be 100644 --- a/src/pretix/plugins/checkinlists/exporters.py +++ b/src/pretix/plugins/checkinlists/exporters.py @@ -64,7 +64,7 @@ from pretix.base.timeframes import ( from pretix.control.forms.widgets import Select2 from pretix.helpers.filenames import safe_for_filename from pretix.helpers.iter import chunked_iterable -from pretix.helpers.reportlab import FontFallbackParagraph +from pretix.helpers.reportlab import PlainTextParagraph from pretix.helpers.templatetags.jsonfield import JSONExtract from pretix.plugins.reports.exporters import ReportlabExportMixin @@ -344,7 +344,7 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter): ] story = [ - FontFallbackParagraph( + PlainTextParagraph( cl.name, headlinestyle ), @@ -352,7 +352,7 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter): if cl.subevent: story += [ Spacer(1, 3 * mm), - FontFallbackParagraph( + PlainTextParagraph( '{} ({} {})'.format( cl.subevent.name, cl.subevent.get_date_range_display(), @@ -382,10 +382,10 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter): headrowstyle.fontName = 'OpenSansBd' for q in questions: txt = str(q.question) - p = FontFallbackParagraph(txt, headrowstyle) + p = PlainTextParagraph(txt, headrowstyle) while p.wrap(colwidths[len(tdata[0])], 5000)[1] > 30 * mm: txt = txt[:len(txt) - 50] + "..." - p = FontFallbackParagraph(txt, headrowstyle) + p = PlainTextParagraph(txt, headrowstyle) tdata[0].append(p) qs = self._get_queryset(cl, form_data) @@ -432,8 +432,8 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter): CBFlowable(bool(op.last_checked_in)) if not op.blocked else '—', '✘' if op.order.status != Order.STATUS_PAID else '✔', op.order.code, - FontFallbackParagraph(name, self.get_style()), - FontFallbackParagraph(bleach.clean(str(item), tags={'br'}).strip().replace('
', '
'), self.get_style()), + PlainTextParagraph(name, self.get_style()), + PlainTextParagraph(bleach.clean(str(item), tags={'br'}).strip().replace('
', '
'), self.get_style()), ] acache = {} if op.addon_to: @@ -444,10 +444,10 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter): for q in questions: txt = acache.get(q.pk, '') txt = bleach.clean(txt, tags={'br'}).strip().replace('
', '
') - p = FontFallbackParagraph(txt, self.get_style()) + p = PlainTextParagraph(txt, self.get_style()) while p.wrap(colwidths[len(row)], 5000)[1] > 50 * mm: txt = txt[:len(txt) - 50] + "..." - p = FontFallbackParagraph(txt, self.get_style()) + p = PlainTextParagraph(txt, self.get_style()) row.append(p) if op.order.status != Order.STATUS_PAID: tstyledata += [ diff --git a/src/pretix/plugins/reports/accountingreport.py b/src/pretix/plugins/reports/accountingreport.py index 17315eb02b..6c09ad141b 100644 --- a/src/pretix/plugins/reports/accountingreport.py +++ b/src/pretix/plugins/reports/accountingreport.py @@ -36,7 +36,7 @@ from reportlab.lib import colors, pagesizes from reportlab.lib.enums import TA_CENTER, TA_RIGHT from reportlab.lib.units import mm from reportlab.platypus import ( - KeepTogether, PageTemplate, Paragraph, Spacer, Table, TableStyle, + KeepTogether, PageTemplate, Spacer, Table, TableStyle, ) from pretix.base.exporter import BaseExporter @@ -49,7 +49,7 @@ from pretix.base.timeframes import ( resolve_timeframe_to_datetime_start_inclusive_end_exclusive, ) from pretix.control.forms.filter import get_all_payment_providers -from pretix.helpers.reportlab import FontFallbackParagraph +from pretix.helpers.reportlab import PlainTextParagraph from pretix.plugins.reports.exporters import ReportlabExportMixin @@ -311,13 +311,13 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): tdata = [ [ - FontFallbackParagraph(self._transaction_group_header_label(), tstyle_bold), - FontFallbackParagraph(_("Price"), tstyle_bold_right), - FontFallbackParagraph(_("Tax rate"), tstyle_bold_right), - FontFallbackParagraph("#", tstyle_bold_right), - FontFallbackParagraph(_("Net total"), tstyle_bold_right), - FontFallbackParagraph(_("Tax total"), tstyle_bold_right), - FontFallbackParagraph(_("Gross total"), tstyle_bold_right), + PlainTextParagraph(self._transaction_group_header_label(), tstyle_bold), + PlainTextParagraph(_("Price"), tstyle_bold_right), + PlainTextParagraph(_("Tax rate"), tstyle_bold_right), + PlainTextParagraph("#", tstyle_bold_right), + PlainTextParagraph(_("Net total"), tstyle_bold_right), + PlainTextParagraph(_("Tax total"), tstyle_bold_right), + PlainTextParagraph(_("Gross total"), tstyle_bold_right), ] ] @@ -347,12 +347,12 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): if e != last_group: if last_group_head_idx > 0 and e is not None: - tdata[last_group_head_idx][4] = Paragraph(money_filter(sum_price_by_group - sum_tax_by_group, currency), tstyle_bold_right), - tdata[last_group_head_idx][5] = Paragraph(money_filter(sum_tax_by_group, currency), tstyle_bold_right), - tdata[last_group_head_idx][6] = Paragraph(money_filter(sum_price_by_group, currency), tstyle_bold_right), + tdata[last_group_head_idx][4] = PlainTextParagraph(money_filter(sum_price_by_group - sum_tax_by_group, currency), tstyle_bold_right), + tdata[last_group_head_idx][5] = PlainTextParagraph(money_filter(sum_tax_by_group, currency), tstyle_bold_right), + tdata[last_group_head_idx][6] = PlainTextParagraph(money_filter(sum_price_by_group, currency), tstyle_bold_right), tdata.append( [ - FontFallbackParagraph( + PlainTextParagraph( e, tstyle_bold, ), @@ -375,20 +375,20 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): text = self._transaction_row_label(r) tdata.append( [ - FontFallbackParagraph(text, tstyle), - Paragraph( + PlainTextParagraph(text, tstyle), + PlainTextParagraph( money_filter(r["price"], currency) if "price" in r and r["price"] is not None else "", tstyle_right, ), - Paragraph(localize(r["tax_rate"].normalize()) + " %", tstyle_right), - Paragraph(str(r["sum_cont"]), tstyle_right), - Paragraph( + PlainTextParagraph(localize(r["tax_rate"].normalize()) + " %", tstyle_right), + PlainTextParagraph(str(r["sum_cont"]), tstyle_right), + PlainTextParagraph( money_filter(r["sum_price"] - r["sum_tax"], currency), tstyle_right ), - Paragraph(money_filter(r["sum_tax"], currency), tstyle_right), - Paragraph(money_filter(r["sum_price"], currency), tstyle_right), + PlainTextParagraph(money_filter(r["sum_tax"], currency), tstyle_right), + PlainTextParagraph(money_filter(r["sum_price"], currency), tstyle_right), ] ) sum_cnt_by_tax_rate[r["tax_rate"]] += r["sum_cont"] @@ -398,19 +398,19 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): sum_tax_by_group += r["sum_tax"] if last_group_head_idx > 0 and last_group is not None: - tdata[last_group_head_idx][4] = Paragraph(money_filter(sum_price_by_group - sum_tax_by_group, currency), tstyle_bold_right), - tdata[last_group_head_idx][5] = Paragraph(money_filter(sum_tax_by_group, currency), tstyle_bold_right), - tdata[last_group_head_idx][6] = Paragraph(money_filter(sum_price_by_group, currency), tstyle_bold_right), + tdata[last_group_head_idx][4] = PlainTextParagraph(money_filter(sum_price_by_group - sum_tax_by_group, currency), tstyle_bold_right), + tdata[last_group_head_idx][5] = PlainTextParagraph(money_filter(sum_tax_by_group, currency), tstyle_bold_right), + tdata[last_group_head_idx][6] = PlainTextParagraph(money_filter(sum_price_by_group, currency), tstyle_bold_right), if len(sum_tax_by_tax_rate) > 1: for tax_rate in sorted(sum_tax_by_tax_rate.keys(), reverse=True): tdata.append( [ - FontFallbackParagraph(_("Sum"), tstyle), - Paragraph("", tstyle_right), - Paragraph(localize(tax_rate.normalize()) + " %", tstyle_right), - Paragraph("", tstyle_right), - Paragraph( + PlainTextParagraph(_("Sum"), tstyle), + PlainTextParagraph("", tstyle_right), + PlainTextParagraph(localize(tax_rate.normalize()) + " %", tstyle_right), + PlainTextParagraph("", tstyle_right), + PlainTextParagraph( money_filter( sum_price_by_tax_rate[tax_rate] - sum_tax_by_tax_rate[tax_rate], @@ -418,10 +418,10 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): ), tstyle_right, ), - Paragraph( + PlainTextParagraph( money_filter(sum_tax_by_tax_rate[tax_rate], currency), tstyle_right ), - Paragraph( + PlainTextParagraph( money_filter(sum_price_by_tax_rate[tax_rate], currency), tstyle_right, ), @@ -439,11 +439,11 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): tdata.append( [ - FontFallbackParagraph(_("Sum"), tstyle_bold), - Paragraph("", tstyle_right), - Paragraph("", tstyle_right), - Paragraph("", tstyle_bold_right), - Paragraph( + PlainTextParagraph(_("Sum"), tstyle_bold), + PlainTextParagraph("", tstyle_right), + PlainTextParagraph("", tstyle_right), + PlainTextParagraph("", tstyle_bold_right), + PlainTextParagraph( money_filter( sum(sum_price_by_tax_rate.values()) - sum(sum_tax_by_tax_rate.values()), @@ -451,11 +451,11 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): ), tstyle_bold_right, ), - Paragraph( + PlainTextParagraph( money_filter(sum(sum_tax_by_tax_rate.values()), currency), tstyle_bold_right, ), - Paragraph( + PlainTextParagraph( money_filter(sum(sum_price_by_tax_rate.values()), currency), tstyle_bold_right, ), @@ -493,10 +493,10 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): tdata = [ [ - FontFallbackParagraph(_("Payment method"), tstyle_bold), - FontFallbackParagraph(_("Payments"), tstyle_bold_right), - FontFallbackParagraph(_("Refunds"), tstyle_bold_right), - FontFallbackParagraph(_("Total"), tstyle_bold_right), + PlainTextParagraph(_("Payment method"), tstyle_bold), + PlainTextParagraph(_("Payments"), tstyle_bold_right), + PlainTextParagraph(_("Refunds"), tstyle_bold_right), + PlainTextParagraph(_("Total"), tstyle_bold_right), ] ] @@ -537,20 +537,20 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): for p in providers: tdata.append( [ - Paragraph(provider_names.get(p, p), tstyle), - FontFallbackParagraph( + PlainTextParagraph(provider_names.get(p, p), tstyle), + PlainTextParagraph( money_filter(payments_by_provider[p], currency) if p in payments_by_provider else "", tstyle_right, ), - Paragraph( + PlainTextParagraph( money_filter(refunds_by_provider[p], currency) if p in refunds_by_provider else "", tstyle_right, ), - Paragraph( + PlainTextParagraph( money_filter( payments_by_provider.get(p, Decimal("0.00")) - refunds_by_provider.get(p, Decimal("0.00")), @@ -563,20 +563,20 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): tdata.append( [ - FontFallbackParagraph(_("Sum"), tstyle_bold), - Paragraph( + PlainTextParagraph(_("Sum"), tstyle_bold), + PlainTextParagraph( money_filter( sum(payments_by_provider.values(), Decimal("0.00")), currency ), tstyle_bold_right, ), - Paragraph( + PlainTextParagraph( money_filter( sum(refunds_by_provider.values(), Decimal("0.00")), currency ), tstyle_bold_right, ), - Paragraph( + PlainTextParagraph( money_filter( sum(payments_by_provider.values(), Decimal("0.00")) - sum(refunds_by_provider.values(), Decimal("0.00")), @@ -641,7 +641,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): open_before = tx_before - p_before + r_before tdata.append( [ - FontFallbackParagraph( + PlainTextParagraph( _("Pending payments at {datetime}").format( datetime=date_format( (df_start - datetime.timedelta.resolution).astimezone( @@ -653,7 +653,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): tstyle, ), "", - Paragraph(money_filter(open_before, currency), tstyle_right), + PlainTextParagraph(money_filter(open_before, currency), tstyle_right), ] ) else: @@ -670,30 +670,30 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): ] or Decimal("0.00") tdata.append( [ - FontFallbackParagraph(_("Orders"), tstyle), - Paragraph("+", tstyle_center), - Paragraph(money_filter(tx_during, currency), tstyle_right), + PlainTextParagraph(_("Orders"), tstyle), + PlainTextParagraph("+", tstyle_center), + PlainTextParagraph(money_filter(tx_during, currency), tstyle_right), ] ) tdata.append( [ - FontFallbackParagraph(_("Payments"), tstyle), - Paragraph("-", tstyle_center), - Paragraph(money_filter(p_during, currency), tstyle_right), + PlainTextParagraph(_("Payments"), tstyle), + PlainTextParagraph("-", tstyle_center), + PlainTextParagraph(money_filter(p_during, currency), tstyle_right), ] ) tdata.append( [ - FontFallbackParagraph(_("Refunds"), tstyle), - Paragraph("+", tstyle_center), - Paragraph(money_filter(r_during, currency), tstyle_right), + PlainTextParagraph(_("Refunds"), tstyle), + PlainTextParagraph("+", tstyle_center), + PlainTextParagraph(money_filter(r_during, currency), tstyle_right), ] ) open_after = open_before + tx_during - p_during + r_during tdata.append( [ - Paragraph( + PlainTextParagraph( _("Pending payments at {datetime}").format( datetime=date_format( ((df_end or now()) - datetime.timedelta.resolution).astimezone( @@ -704,8 +704,8 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): ), tstyle_bold, ), - Paragraph("=", tstyle_center), - Paragraph(money_filter(open_after, currency), tstyle_bold_right), + PlainTextParagraph("=", tstyle_center), + PlainTextParagraph(money_filter(open_after, currency), tstyle_bold_right), ] ) tstyledata += [ @@ -752,7 +752,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): ) tdata.append( [ - Paragraph( + PlainTextParagraph( _("Total gift card value at {datetime}").format( datetime=date_format( (df_start - datetime.timedelta.resolution).astimezone( @@ -763,7 +763,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): ), tstyle, ), - Paragraph(money_filter(tx_before, currency), tstyle_right), + PlainTextParagraph(money_filter(tx_before, currency), tstyle_right), ] ) else: @@ -774,8 +774,8 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): ] or Decimal("0.00") tdata.append( [ - FontFallbackParagraph(_("Gift card transactions (credit)"), tstyle), - Paragraph(money_filter(tx_during_pos, currency), tstyle_right), + PlainTextParagraph(_("Gift card transactions (credit)"), tstyle), + PlainTextParagraph(money_filter(tx_during_pos, currency), tstyle_right), ] ) @@ -784,15 +784,15 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): ] or Decimal("0.00") tdata.append( [ - FontFallbackParagraph(_("Gift card transactions (debit)"), tstyle), - Paragraph(money_filter(tx_during_neg, currency), tstyle_right), + PlainTextParagraph(_("Gift card transactions (debit)"), tstyle), + PlainTextParagraph(money_filter(tx_during_neg, currency), tstyle_right), ] ) open_after = tx_before + tx_during_pos + tx_during_neg tdata.append( [ - Paragraph( + PlainTextParagraph( _("Total gift card value at {datetime}").format( datetime=date_format( ((df_end or now()) - datetime.timedelta.resolution).astimezone( @@ -803,7 +803,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): ), tstyle_bold, ), - Paragraph(money_filter(open_after, currency), tstyle_bold_right), + PlainTextParagraph(money_filter(open_after, currency), tstyle_bold_right), ] ) tstyledata += [ @@ -854,10 +854,10 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): style_small.leading = 10 story = [ - FontFallbackParagraph(self.verbose_name, style_h1), + PlainTextParagraph(self.verbose_name, style_h1), Spacer(0, 3 * mm), - FontFallbackParagraph( - "
".join(escape(f) for f in self.describe_filters(form_data)), + PlainTextParagraph( + "\n".join(escape(f) for f in self.describe_filters(form_data)), style_small, ), ] @@ -870,7 +870,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): if s: story += [ Spacer(0, 3 * mm), - FontFallbackParagraph(_("Orders") + c_head, style_h2), + PlainTextParagraph(_("Orders") + c_head, style_h2), Spacer(0, 3 * mm), *s ] @@ -881,7 +881,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): if s: story += [ Spacer(0, 8 * mm), - FontFallbackParagraph(_("Payments") + c_head, style_h2), + PlainTextParagraph(_("Payments") + c_head, style_h2), Spacer(0, 3 * mm), *s ] @@ -894,7 +894,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): Spacer(0, 8 * mm), KeepTogether( [ - FontFallbackParagraph(_("Open items") + c_head, style_h2), + PlainTextParagraph(_("Open items") + c_head, style_h2), Spacer(0, 3 * mm), *s ] @@ -912,7 +912,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter): Spacer(0, 8 * mm), KeepTogether( [ - FontFallbackParagraph(_("Gift cards") + c_head, style_h2), + PlainTextParagraph(_("Gift cards") + c_head, style_h2), Spacer(0, 3 * mm), *s, ] diff --git a/src/pretix/plugins/reports/exporters.py b/src/pretix/plugins/reports/exporters.py index 6957ab396f..98241865e8 100644 --- a/src/pretix/plugins/reports/exporters.py +++ b/src/pretix/plugins/reports/exporters.py @@ -70,7 +70,7 @@ from pretix.base.timeframes import ( ) from pretix.control.forms.filter import OverviewFilterForm from pretix.helpers.reportlab import ( - FontFallbackParagraph, register_ttf_font_if_new, + PlainTextParagraph, register_ttf_font_if_new, ) from pretix.presale.style import get_fonts @@ -282,7 +282,7 @@ class OverviewReport(Report): headlinestyle.fontSize = 15 headlinestyle.fontName = 'OpenSansBd' story = [ - FontFallbackParagraph(_('Orders by product') + ' ' + (_('(excl. taxes)') if net else _('(incl. taxes)')), headlinestyle), + PlainTextParagraph(_('Orders by product') + ' ' + (_('(excl. taxes)') if net else _('(incl. taxes)')), headlinestyle), Spacer(1, 5 * mm) ] return story @@ -292,7 +292,7 @@ class OverviewReport(Report): if form_data.get('date_axis') and form_data.get('date_range'): d_start, d_end = resolve_timeframe_to_dates_inclusive(now(), form_data['date_range'], self.timezone) story += [ - FontFallbackParagraph(_('{axis} between {start} and {end}').format( + PlainTextParagraph(_('{axis} between {start} and {end}').format( axis=dict(OverviewFilterForm(event=self.event).fields['date_axis'].choices)[form_data.get('date_axis')], start=date_format(d_start, 'SHORT_DATE_FORMAT') if d_start else '–', end=date_format(d_end, 'SHORT_DATE_FORMAT') if d_end else '–', @@ -305,13 +305,13 @@ class OverviewReport(Report): subevent = self.event.subevents.get(pk=self.form_data.get('subevent')) except SubEvent.DoesNotExist: subevent = self.form_data.get('subevent') - story.append(FontFallbackParagraph(pgettext('subevent', 'Date: {}').format(subevent), self.get_style())) + story.append(PlainTextParagraph(pgettext('subevent', 'Date: {}').format(subevent), self.get_style())) story.append(Spacer(1, 5 * mm)) if form_data.get('subevent_date_range'): d_start, d_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), form_data['subevent_date_range'], self.timezone) story += [ - FontFallbackParagraph(_('{axis} between {start} and {end}').format( + PlainTextParagraph(_('{axis} between {start} and {end}').format( axis=_('Event date'), start=date_format(d_start, 'SHORT_DATE_FORMAT') if d_start else '–', end=date_format(d_end - timedelta(hours=1), 'SHORT_DATE_FORMAT') if d_end else '–', @@ -384,13 +384,13 @@ class OverviewReport(Report): tdata = [ [ _('Product'), - FontFallbackParagraph(_('Canceled'), tstyle_th), + PlainTextParagraph(_('Canceled'), tstyle_th), '', - FontFallbackParagraph(_('Expired'), tstyle_th), + PlainTextParagraph(_('Expired'), tstyle_th), '', - FontFallbackParagraph(_('Approval pending'), tstyle_th), + PlainTextParagraph(_('Approval pending'), tstyle_th), '', - FontFallbackParagraph(_('Purchased'), tstyle_th), + PlainTextParagraph(_('Purchased'), tstyle_th), '', '', '', '', '' ], [ @@ -421,14 +421,14 @@ class OverviewReport(Report): for tup in items_by_category: if tup[0]: tdata.append([ - FontFallbackParagraph(str(tup[0]), tstyle_bold) + PlainTextParagraph(str(tup[0]), tstyle_bold) ]) for l, s in states: tdata[-1].append(str(tup[0].num[l][0])) tdata[-1].append(floatformat(tup[0].num[l][2 if net else 1], places)) for item in tup[1]: tdata.append([ - FontFallbackParagraph(str(item), tstyle) + PlainTextParagraph(str(item), tstyle) ]) for l, s in states: tdata[-1].append(str(item.num[l][0])) @@ -436,7 +436,7 @@ class OverviewReport(Report): if item.has_variations: for var in item.all_variations: tdata.append([ - FontFallbackParagraph(" " + str(var), tstyle) + PlainTextParagraph(" " + str(var), tstyle) ]) for l, s in states: tdata[-1].append(str(var.num[l][0])) @@ -568,7 +568,7 @@ class OrderTaxListReportPDF(Report): tstyledata.append(('SPAN', (5 + 2 * i, 0), (6 + 2 * i, 0))) story = [ - FontFallbackParagraph(_('Orders by tax rate ({currency})').format(currency=self.event.currency), headlinestyle), + PlainTextParagraph(_('Orders by tax rate ({currency})').format(currency=self.event.currency), headlinestyle), Spacer(1, 5 * mm) ] tdata = [