mirror of
https://github.com/pretix/pretix.git
synced 2025-12-09 00:42:28 +00:00
Compare commits
2 Commits
answer-exp
...
ttf-pragra
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c896adf48 | ||
|
|
36e4bfb7f2 |
@@ -48,7 +48,7 @@ from reportlab.pdfbase.ttfonts import TTFont
|
|||||||
from reportlab.pdfgen.canvas import Canvas
|
from reportlab.pdfgen.canvas import Canvas
|
||||||
from reportlab.platypus import (
|
from reportlab.platypus import (
|
||||||
BaseDocTemplate, Flowable, Frame, KeepTogether, NextPageTemplate,
|
BaseDocTemplate, Flowable, Frame, KeepTogether, NextPageTemplate,
|
||||||
PageTemplate, Paragraph, Spacer, Table, TableStyle,
|
PageTemplate, Spacer, Table, TableStyle,
|
||||||
)
|
)
|
||||||
|
|
||||||
from pretix.base.decimal import round_decimal
|
from pretix.base.decimal import round_decimal
|
||||||
@@ -56,7 +56,9 @@ from pretix.base.models import Event, Invoice, Order, OrderPayment
|
|||||||
from pretix.base.services.currencies import SOURCE_NAMES
|
from pretix.base.services.currencies import SOURCE_NAMES
|
||||||
from pretix.base.signals import register_invoice_renderers
|
from pretix.base.signals import register_invoice_renderers
|
||||||
from pretix.base.templatetags.money import money_filter
|
from pretix.base.templatetags.money import money_filter
|
||||||
from pretix.helpers.reportlab import ThumbnailingImageReader, reshaper
|
from pretix.helpers.reportlab import (
|
||||||
|
FontFallbackParagraph, ThumbnailingImageReader, reshaper,
|
||||||
|
)
|
||||||
from pretix.presale.style import get_fonts
|
from pretix.presale.style import get_fonts
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -235,16 +237,17 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
|
|||||||
italic='OpenSansIt', boldItalic='OpenSansBI')
|
italic='OpenSansIt', boldItalic='OpenSansBI')
|
||||||
|
|
||||||
for family, styles in get_fonts(event=self.event, pdf_support_required=True).items():
|
for family, styles in get_fonts(event=self.event, pdf_support_required=True).items():
|
||||||
|
pdfmetrics.registerFont(TTFont(family, finders.find(styles['regular']['truetype'])))
|
||||||
if family == self.event.settings.invoice_renderer_font:
|
if family == self.event.settings.invoice_renderer_font:
|
||||||
pdfmetrics.registerFont(TTFont(family, finders.find(styles['regular']['truetype'])))
|
|
||||||
self.font_regular = family
|
self.font_regular = family
|
||||||
if 'italic' in styles:
|
|
||||||
pdfmetrics.registerFont(TTFont(family + ' I', finders.find(styles['italic']['truetype'])))
|
|
||||||
if 'bold' in styles:
|
if 'bold' in styles:
|
||||||
pdfmetrics.registerFont(TTFont(family + ' B', finders.find(styles['bold']['truetype'])))
|
|
||||||
self.font_bold = family + ' B'
|
self.font_bold = family + ' B'
|
||||||
if 'bolditalic' in styles:
|
if 'italic' in styles:
|
||||||
pdfmetrics.registerFont(TTFont(family + ' B I', finders.find(styles['bolditalic']['truetype'])))
|
pdfmetrics.registerFont(TTFont(family + ' I', finders.find(styles['italic']['truetype'])))
|
||||||
|
if 'bold' in styles:
|
||||||
|
pdfmetrics.registerFont(TTFont(family + ' B', finders.find(styles['bold']['truetype'])))
|
||||||
|
if 'bolditalic' in styles:
|
||||||
|
pdfmetrics.registerFont(TTFont(family + ' B I', finders.find(styles['bolditalic']['truetype'])))
|
||||||
|
|
||||||
def _normalize(self, text):
|
def _normalize(self, text):
|
||||||
# reportlab does not support unicode combination characters
|
# reportlab does not support unicode combination characters
|
||||||
@@ -393,8 +396,8 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
invoice_to_top = 52 * mm
|
invoice_to_top = 52 * mm
|
||||||
|
|
||||||
def _draw_invoice_to(self, canvas):
|
def _draw_invoice_to(self, canvas):
|
||||||
p = Paragraph(self._clean_text(self.invoice.address_invoice_to),
|
p = FontFallbackParagraph(self._clean_text(self.invoice.address_invoice_to),
|
||||||
style=self.stylesheet['Normal'])
|
style=self.stylesheet['Normal'])
|
||||||
p.wrapOn(canvas, self.invoice_to_width, self.invoice_to_height)
|
p.wrapOn(canvas, self.invoice_to_width, self.invoice_to_height)
|
||||||
p_size = p.wrap(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)
|
p.drawOn(canvas, self.invoice_to_left, self.pagesize[1] - p_size[1] - self.invoice_to_top)
|
||||||
@@ -405,7 +408,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
invoice_from_top = 17 * mm
|
invoice_from_top = 17 * mm
|
||||||
|
|
||||||
def _draw_invoice_from(self, canvas):
|
def _draw_invoice_from(self, canvas):
|
||||||
p = Paragraph(
|
p = FontFallbackParagraph(
|
||||||
self._clean_text(self.invoice.full_invoice_from),
|
self._clean_text(self.invoice.full_invoice_from),
|
||||||
style=self.stylesheet['InvoiceFrom']
|
style=self.stylesheet['InvoiceFrom']
|
||||||
)
|
)
|
||||||
@@ -523,12 +526,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
def shorten(txt):
|
def shorten(txt):
|
||||||
txt = str(txt)
|
txt = str(txt)
|
||||||
txt = bleach.clean(txt, tags=set()).strip()
|
txt = bleach.clean(txt, tags=set()).strip()
|
||||||
p = Paragraph(self._normalize(txt.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
p = FontFallbackParagraph(self._normalize(txt.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
||||||
p_size = p.wrap(self.event_width, self.event_height)
|
p_size = p.wrap(self.event_width, self.event_height)
|
||||||
|
|
||||||
while p_size[1] > 2 * self.stylesheet['Normal'].leading:
|
while p_size[1] > 2 * self.stylesheet['Normal'].leading:
|
||||||
txt = ' '.join(txt.replace('…', '').split()[:-1]) + '…'
|
txt = ' '.join(txt.replace('…', '').split()[:-1]) + '…'
|
||||||
p = Paragraph(self._normalize(txt.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
p = FontFallbackParagraph(self._normalize(txt.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
||||||
p_size = p.wrap(self.event_width, self.event_height)
|
p_size = p.wrap(self.event_width, self.event_height)
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
@@ -554,7 +557,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
else:
|
else:
|
||||||
p_str = shorten(self.invoice.event.name)
|
p_str = shorten(self.invoice.event.name)
|
||||||
|
|
||||||
p = Paragraph(self._normalize(p_str.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
p = FontFallbackParagraph(self._normalize(p_str.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
||||||
p.wrapOn(canvas, self.event_width, self.event_height)
|
p.wrapOn(canvas, self.event_width, self.event_height)
|
||||||
p_size = p.wrap(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])
|
p.drawOn(canvas, self.event_left, self.pagesize[1] - self.event_top - p_size[1])
|
||||||
@@ -608,7 +611,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
def _get_intro(self):
|
def _get_intro(self):
|
||||||
story = []
|
story = []
|
||||||
if self.invoice.custom_field:
|
if self.invoice.custom_field:
|
||||||
story.append(Paragraph(
|
story.append(FontFallbackParagraph(
|
||||||
'{}: {}'.format(
|
'{}: {}'.format(
|
||||||
self._clean_text(str(self.invoice.event.settings.invoice_address_custom_field)),
|
self._clean_text(str(self.invoice.event.settings.invoice_address_custom_field)),
|
||||||
self._clean_text(self.invoice.custom_field),
|
self._clean_text(self.invoice.custom_field),
|
||||||
@@ -617,7 +620,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
))
|
))
|
||||||
|
|
||||||
if self.invoice.internal_reference:
|
if self.invoice.internal_reference:
|
||||||
story.append(Paragraph(
|
story.append(FontFallbackParagraph(
|
||||||
self._normalize(pgettext('invoice', 'Customer reference: {reference}').format(
|
self._normalize(pgettext('invoice', 'Customer reference: {reference}').format(
|
||||||
reference=self._clean_text(self.invoice.internal_reference),
|
reference=self._clean_text(self.invoice.internal_reference),
|
||||||
)),
|
)),
|
||||||
@@ -625,14 +628,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
))
|
))
|
||||||
|
|
||||||
if self.invoice.invoice_to_vat_id:
|
if self.invoice.invoice_to_vat_id:
|
||||||
story.append(Paragraph(
|
story.append(FontFallbackParagraph(
|
||||||
self._normalize(pgettext('invoice', 'Customer VAT ID')) + ': ' +
|
self._normalize(pgettext('invoice', 'Customer VAT ID')) + ': ' +
|
||||||
self._clean_text(self.invoice.invoice_to_vat_id),
|
self._clean_text(self.invoice.invoice_to_vat_id),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
))
|
))
|
||||||
|
|
||||||
if self.invoice.invoice_to_beneficiary:
|
if self.invoice.invoice_to_beneficiary:
|
||||||
story.append(Paragraph(
|
story.append(FontFallbackParagraph(
|
||||||
self._normalize(pgettext('invoice', 'Beneficiary')) + ':<br />' +
|
self._normalize(pgettext('invoice', 'Beneficiary')) + ':<br />' +
|
||||||
self._clean_text(self.invoice.invoice_to_beneficiary),
|
self._clean_text(self.invoice.invoice_to_beneficiary),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
@@ -644,7 +647,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
if story:
|
if story:
|
||||||
story.append(Spacer(1, 5 * mm))
|
story.append(Spacer(1, 5 * mm))
|
||||||
|
|
||||||
story.append(Paragraph(
|
story.append(FontFallbackParagraph(
|
||||||
self._clean_text(self.invoice.introductory_text, tags=['br']),
|
self._clean_text(self.invoice.introductory_text, tags=['br']),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
))
|
))
|
||||||
@@ -657,7 +660,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
|
|
||||||
story = [
|
story = [
|
||||||
NextPageTemplate('FirstPage'),
|
NextPageTemplate('FirstPage'),
|
||||||
Paragraph(
|
FontFallbackParagraph(
|
||||||
self._normalize(
|
self._normalize(
|
||||||
pgettext('invoice', 'Tax Invoice') if str(self.invoice.invoice_from_country) == 'AU'
|
pgettext('invoice', 'Tax Invoice') if str(self.invoice.invoice_from_country) == 'AU'
|
||||||
else pgettext('invoice', 'Invoice')
|
else pgettext('invoice', 'Invoice')
|
||||||
@@ -683,17 +686,17 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
]
|
]
|
||||||
if has_taxes:
|
if has_taxes:
|
||||||
tdata = [(
|
tdata = [(
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Description')), self.stylesheet['Bold']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Description')), self.stylesheet['Bold']),
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Qty')), self.stylesheet['BoldRightNoSplit']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Qty')), self.stylesheet['BoldRightNoSplit']),
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Tax rate')), self.stylesheet['BoldRightNoSplit']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax rate')), self.stylesheet['BoldRightNoSplit']),
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Net')), self.stylesheet['BoldRightNoSplit']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Net')), self.stylesheet['BoldRightNoSplit']),
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Gross')), self.stylesheet['BoldRightNoSplit']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Gross')), self.stylesheet['BoldRightNoSplit']),
|
||||||
)]
|
)]
|
||||||
else:
|
else:
|
||||||
tdata = [(
|
tdata = [(
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Description')), self.stylesheet['Bold']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Description')), self.stylesheet['Bold']),
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Qty')), self.stylesheet['BoldRightNoSplit']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Qty')), self.stylesheet['BoldRightNoSplit']),
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Amount')), self.stylesheet['BoldRightNoSplit']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Amount')), self.stylesheet['BoldRightNoSplit']),
|
||||||
)]
|
)]
|
||||||
|
|
||||||
def _group_key(line):
|
def _group_key(line):
|
||||||
@@ -715,14 +718,20 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
)
|
)
|
||||||
description = description + "\n" + single_price_line
|
description = description + "\n" + single_price_line
|
||||||
tdata.append((
|
tdata.append((
|
||||||
Paragraph(
|
FontFallbackParagraph(
|
||||||
self._clean_text(description, tags=['br']),
|
self._clean_text(description, tags=['br']),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
),
|
),
|
||||||
str(len(lines)),
|
str(len(lines)),
|
||||||
localize(tax_rate) + " %",
|
localize(tax_rate) + " %",
|
||||||
Paragraph(money_filter(net_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '), self.stylesheet['NormalRight']),
|
FontFallbackParagraph(
|
||||||
Paragraph(money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '), self.stylesheet['NormalRight']),
|
money_filter(net_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
||||||
|
self.stylesheet['NormalRight']
|
||||||
|
),
|
||||||
|
FontFallbackParagraph(
|
||||||
|
money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
||||||
|
self.stylesheet['NormalRight']
|
||||||
|
),
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
if len(lines) > 1:
|
if len(lines) > 1:
|
||||||
@@ -731,12 +740,15 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
)
|
)
|
||||||
description = description + "\n" + single_price_line
|
description = description + "\n" + single_price_line
|
||||||
tdata.append((
|
tdata.append((
|
||||||
Paragraph(
|
FontFallbackParagraph(
|
||||||
self._clean_text(description, tags=['br']),
|
self._clean_text(description, tags=['br']),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
),
|
),
|
||||||
str(len(lines)),
|
str(len(lines)),
|
||||||
Paragraph(money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '), self.stylesheet['NormalRight']),
|
FontFallbackParagraph(
|
||||||
|
money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
||||||
|
self.stylesheet['NormalRight']
|
||||||
|
),
|
||||||
))
|
))
|
||||||
taxvalue_map[tax_rate, tax_name] += (gross_value - net_value) * len(lines)
|
taxvalue_map[tax_rate, tax_name] += (gross_value - net_value) * len(lines)
|
||||||
grossvalue_map[tax_rate, tax_name] += gross_value * len(lines)
|
grossvalue_map[tax_rate, tax_name] += gross_value * len(lines)
|
||||||
@@ -744,13 +756,13 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
|
|
||||||
if has_taxes:
|
if has_taxes:
|
||||||
tdata.append([
|
tdata.append([
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Invoice total')), self.stylesheet['Bold']), '', '', '',
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Invoice total')), self.stylesheet['Bold']), '', '', '',
|
||||||
money_filter(total, self.invoice.event.currency)
|
money_filter(total, self.invoice.event.currency)
|
||||||
])
|
])
|
||||||
colwidths = [a * doc.width for a in (.50, .05, .15, .15, .15)]
|
colwidths = [a * doc.width for a in (.50, .05, .15, .15, .15)]
|
||||||
else:
|
else:
|
||||||
tdata.append([
|
tdata.append([
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Invoice total')), self.stylesheet['Bold']), '',
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Invoice total')), self.stylesheet['Bold']), '',
|
||||||
money_filter(total, self.invoice.event.currency)
|
money_filter(total, self.invoice.event.currency)
|
||||||
])
|
])
|
||||||
colwidths = [a * doc.width for a in (.65, .20, .15)]
|
colwidths = [a * doc.width for a in (.65, .20, .15)]
|
||||||
@@ -760,12 +772,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
pending_sum = self.invoice.order.pending_sum
|
pending_sum = self.invoice.order.pending_sum
|
||||||
if pending_sum != total:
|
if pending_sum != total:
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[Paragraph(self._normalize(pgettext('invoice', 'Received payments')), self.stylesheet['Normal'])] +
|
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Received payments')), self.stylesheet['Normal'])] +
|
||||||
(['', '', ''] if has_taxes else ['']) +
|
(['', '', ''] if has_taxes else ['']) +
|
||||||
[money_filter(pending_sum - total, self.invoice.event.currency)]
|
[money_filter(pending_sum - total, self.invoice.event.currency)]
|
||||||
)
|
)
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[Paragraph(self._normalize(pgettext('invoice', 'Outstanding payments')), self.stylesheet['Bold'])] +
|
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Outstanding payments')), self.stylesheet['Bold'])] +
|
||||||
(['', '', ''] if has_taxes else ['']) +
|
(['', '', ''] if has_taxes else ['']) +
|
||||||
[money_filter(pending_sum, self.invoice.event.currency)]
|
[money_filter(pending_sum, self.invoice.event.currency)]
|
||||||
)
|
)
|
||||||
@@ -782,12 +794,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
s=Sum('amount')
|
s=Sum('amount')
|
||||||
)['s'] or Decimal('0.00')
|
)['s'] or Decimal('0.00')
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[Paragraph(self._normalize(pgettext('invoice', 'Paid by gift card')), self.stylesheet['Normal'])] +
|
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Paid by gift card')), self.stylesheet['Normal'])] +
|
||||||
(['', '', ''] if has_taxes else ['']) +
|
(['', '', ''] if has_taxes else ['']) +
|
||||||
[money_filter(giftcard_sum, self.invoice.event.currency)]
|
[money_filter(giftcard_sum, self.invoice.event.currency)]
|
||||||
)
|
)
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[Paragraph(self._normalize(pgettext('invoice', 'Remaining amount')), self.stylesheet['Bold'])] +
|
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Remaining amount')), self.stylesheet['Bold'])] +
|
||||||
(['', '', ''] if has_taxes else ['']) +
|
(['', '', ''] if has_taxes else ['']) +
|
||||||
[money_filter(total - giftcard_sum, self.invoice.event.currency)]
|
[money_filter(total - giftcard_sum, self.invoice.event.currency)]
|
||||||
)
|
)
|
||||||
@@ -810,7 +822,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
story.append(Spacer(1, 10 * mm))
|
story.append(Spacer(1, 10 * mm))
|
||||||
|
|
||||||
if self.invoice.payment_provider_text:
|
if self.invoice.payment_provider_text:
|
||||||
story.append(Paragraph(
|
story.append(FontFallbackParagraph(
|
||||||
self._normalize(self.invoice.payment_provider_text),
|
self._normalize(self.invoice.payment_provider_text),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
))
|
))
|
||||||
@@ -819,7 +831,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
story.append(Spacer(1, 3 * mm))
|
story.append(Spacer(1, 3 * mm))
|
||||||
|
|
||||||
if self.invoice.additional_text:
|
if self.invoice.additional_text:
|
||||||
story.append(Paragraph(
|
story.append(FontFallbackParagraph(
|
||||||
self._clean_text(self.invoice.additional_text, tags=['br']),
|
self._clean_text(self.invoice.additional_text, tags=['br']),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
))
|
))
|
||||||
@@ -835,10 +847,10 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
('FONTNAME', (0, 0), (-1, -1), self.font_regular),
|
('FONTNAME', (0, 0), (-1, -1), self.font_regular),
|
||||||
]
|
]
|
||||||
thead = [
|
thead = [
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Tax rate')), self.stylesheet['Fineprint']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax rate')), self.stylesheet['Fineprint']),
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Net value')), self.stylesheet['FineprintRight']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Net value')), self.stylesheet['FineprintRight']),
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Gross value')), self.stylesheet['FineprintRight']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Gross value')), self.stylesheet['FineprintRight']),
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Tax')), self.stylesheet['FineprintRight']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax')), self.stylesheet['FineprintRight']),
|
||||||
''
|
''
|
||||||
]
|
]
|
||||||
tdata = [thead]
|
tdata = [thead]
|
||||||
@@ -849,7 +861,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
continue
|
continue
|
||||||
tax = taxvalue_map[idx]
|
tax = taxvalue_map[idx]
|
||||||
tdata.append([
|
tdata.append([
|
||||||
Paragraph(self._normalize(localize(rate) + " % " + name), self.stylesheet['Fineprint']),
|
FontFallbackParagraph(self._normalize(localize(rate) + " % " + name), self.stylesheet['Fineprint']),
|
||||||
money_filter(gross - tax, self.invoice.event.currency),
|
money_filter(gross - tax, self.invoice.event.currency),
|
||||||
money_filter(gross, self.invoice.event.currency),
|
money_filter(gross, self.invoice.event.currency),
|
||||||
money_filter(tax, self.invoice.event.currency),
|
money_filter(tax, self.invoice.event.currency),
|
||||||
@@ -868,7 +880,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
table.setStyle(TableStyle(tstyledata))
|
table.setStyle(TableStyle(tstyledata))
|
||||||
story.append(Spacer(5 * mm, 5 * mm))
|
story.append(Spacer(5 * mm, 5 * mm))
|
||||||
story.append(KeepTogether([
|
story.append(KeepTogether([
|
||||||
Paragraph(self._normalize(pgettext('invoice', 'Included taxes')), self.stylesheet['FineprintHeading']),
|
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Included taxes')), self.stylesheet['FineprintHeading']),
|
||||||
table
|
table
|
||||||
]))
|
]))
|
||||||
|
|
||||||
@@ -885,7 +897,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
net = gross - tax
|
net = gross - tax
|
||||||
|
|
||||||
tdata.append([
|
tdata.append([
|
||||||
Paragraph(self._normalize(localize(rate) + " % " + name), self.stylesheet['Fineprint']),
|
FontFallbackParagraph(self._normalize(localize(rate) + " % " + name), self.stylesheet['Fineprint']),
|
||||||
fmt(net), fmt(gross), fmt(tax), ''
|
fmt(net), fmt(gross), fmt(tax), ''
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -894,7 +906,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
|
|
||||||
story.append(KeepTogether([
|
story.append(KeepTogether([
|
||||||
Spacer(1, height=2 * mm),
|
Spacer(1, height=2 * mm),
|
||||||
Paragraph(
|
FontFallbackParagraph(
|
||||||
self._normalize(pgettext(
|
self._normalize(pgettext(
|
||||||
'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on '
|
'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on '
|
||||||
'{date}, this corresponds to:'
|
'{date}, this corresponds to:'
|
||||||
@@ -909,7 +921,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
elif self.invoice.foreign_currency_display and self.invoice.foreign_currency_rate:
|
elif self.invoice.foreign_currency_display and self.invoice.foreign_currency_rate:
|
||||||
foreign_total = round_decimal(total * self.invoice.foreign_currency_rate)
|
foreign_total = round_decimal(total * self.invoice.foreign_currency_rate)
|
||||||
story.append(Spacer(1, 5 * mm))
|
story.append(Spacer(1, 5 * mm))
|
||||||
story.append(Paragraph(self._normalize(
|
story.append(FontFallbackParagraph(self._normalize(
|
||||||
pgettext(
|
pgettext(
|
||||||
'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on '
|
'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on '
|
||||||
'{date}, the invoice total corresponds to {total}.'
|
'{date}, the invoice total corresponds to {total}.'
|
||||||
@@ -962,7 +974,7 @@ class Modern1Renderer(ClassicInvoiceRenderer):
|
|||||||
self._clean_text(l)
|
self._clean_text(l)
|
||||||
for l in self.invoice.address_invoice_from.strip().split('\n')
|
for l in self.invoice.address_invoice_from.strip().split('\n')
|
||||||
]
|
]
|
||||||
p = Paragraph(self._normalize(' · '.join(c)), style=self.stylesheet['Sender'])
|
p = FontFallbackParagraph(self._normalize(' · '.join(c)), style=self.stylesheet['Sender'])
|
||||||
p.wrapOn(canvas, self.invoice_to_width, 15.7 * mm)
|
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)
|
p.drawOn(canvas, self.invoice_to_left, self.pagesize[1] - self.invoice_to_top + 2 * mm)
|
||||||
super()._draw_invoice_from(canvas)
|
super()._draw_invoice_from(canvas)
|
||||||
@@ -1021,7 +1033,7 @@ class Modern1Renderer(ClassicInvoiceRenderer):
|
|||||||
_draw(pgettext('invoice', 'Order code'), self.invoice.order.full_code, value_size, self.left_margin, 45 * mm, **kwargs)
|
_draw(pgettext('invoice', 'Order code'), self.invoice.order.full_code, value_size, self.left_margin, 45 * mm, **kwargs)
|
||||||
]
|
]
|
||||||
|
|
||||||
p = Paragraph(
|
p = FontFallbackParagraph(
|
||||||
self._normalize(date_format(self.invoice.date, "DATE_FORMAT")),
|
self._normalize(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)
|
style=ParagraphStyle(name=f'Normal{value_size}', fontName=self.font_regular, fontSize=value_size, leading=value_size * 1.2)
|
||||||
)
|
)
|
||||||
@@ -1079,7 +1091,7 @@ class Modern1SimplifiedRenderer(Modern1Renderer):
|
|||||||
i = []
|
i = []
|
||||||
|
|
||||||
if not self.invoice.event.has_subevents and self.invoice.event.settings.show_dates_on_frontpage:
|
if not self.invoice.event.has_subevents and self.invoice.event.settings.show_dates_on_frontpage:
|
||||||
i.append(Paragraph(
|
i.append(FontFallbackParagraph(
|
||||||
pgettext('invoice', 'Event date: {date_range}').format(
|
pgettext('invoice', 'Event date: {date_range}').format(
|
||||||
date_range=self.invoice.event.get_date_range_display(),
|
date_range=self.invoice.event.get_date_range_display(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -19,11 +19,20 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# 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/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
import logging
|
||||||
|
|
||||||
from arabic_reshaper import ArabicReshaper
|
from arabic_reshaper import ArabicReshaper
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.functional import SimpleLazyObject
|
from django.utils.functional import SimpleLazyObject
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from reportlab.lib.styles import ParagraphStyle
|
||||||
from reportlab.lib.utils import ImageReader
|
from reportlab.lib.utils import ImageReader
|
||||||
|
from reportlab.pdfbase import pdfmetrics
|
||||||
|
from reportlab.platypus import Paragraph
|
||||||
|
|
||||||
|
from pretix.presale.style import get_fonts
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ThumbnailingImageReader(ImageReader):
|
class ThumbnailingImageReader(ImageReader):
|
||||||
@@ -59,3 +68,35 @@ reshaper = SimpleLazyObject(lambda: ArabicReshaper(configuration={
|
|||||||
'delete_harakat': True,
|
'delete_harakat': True,
|
||||||
'support_ligatures': False,
|
'support_ligatures': False,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
class FontFallbackParagraph(Paragraph):
|
||||||
|
def __init__(self, text, style=None, *args, **kwargs):
|
||||||
|
if style is None:
|
||||||
|
style = ParagraphStyle(name='paragraphImplicitDefaultStyle')
|
||||||
|
|
||||||
|
if not self._font_supports_text(text, style.fontName):
|
||||||
|
newFont = self._find_font(text, style.fontName)
|
||||||
|
if newFont:
|
||||||
|
logger.debug(f"replacing {style.fontName} with {newFont} for {text!r}")
|
||||||
|
style = style.clone(name=style.name + '_' + newFont, fontName=newFont)
|
||||||
|
|
||||||
|
super().__init__(text, style, *args, **kwargs)
|
||||||
|
|
||||||
|
def _font_supports_text(self, text, font_name):
|
||||||
|
if not text:
|
||||||
|
return True
|
||||||
|
font = pdfmetrics.getFont(font_name)
|
||||||
|
return all(
|
||||||
|
ord(c) in font.face.charToGlyph or not c.isprintable()
|
||||||
|
for c in text
|
||||||
|
)
|
||||||
|
|
||||||
|
def _find_font(self, text, original_font):
|
||||||
|
for family, styles in get_fonts(pdf_support_required=True).items():
|
||||||
|
if self._font_supports_text(text, family):
|
||||||
|
if (original_font.endswith("It") or original_font.endswith(" I")) and "italic" in styles:
|
||||||
|
return family + " I"
|
||||||
|
if (original_font.endswith("Bd") or original_font.endswith(" B")) and "bold" in styles:
|
||||||
|
return family + " B"
|
||||||
|
return family
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ from django.utils.translation import (
|
|||||||
gettext as _, gettext_lazy, pgettext, pgettext_lazy,
|
gettext as _, gettext_lazy, pgettext, pgettext_lazy,
|
||||||
)
|
)
|
||||||
from reportlab.lib.units import mm
|
from reportlab.lib.units import mm
|
||||||
from reportlab.platypus import Flowable, Paragraph, Spacer, Table, TableStyle
|
from reportlab.platypus import Flowable, Spacer, Table, TableStyle
|
||||||
|
|
||||||
from pretix.base.exporter import BaseExporter, ListExporter
|
from pretix.base.exporter import BaseExporter, ListExporter
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
@@ -64,6 +64,7 @@ from pretix.base.timeframes import (
|
|||||||
from pretix.control.forms.widgets import Select2
|
from pretix.control.forms.widgets import Select2
|
||||||
from pretix.helpers.filenames import safe_for_filename
|
from pretix.helpers.filenames import safe_for_filename
|
||||||
from pretix.helpers.iter import chunked_iterable
|
from pretix.helpers.iter import chunked_iterable
|
||||||
|
from pretix.helpers.reportlab import FontFallbackParagraph
|
||||||
from pretix.helpers.templatetags.jsonfield import JSONExtract
|
from pretix.helpers.templatetags.jsonfield import JSONExtract
|
||||||
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
||||||
|
|
||||||
@@ -343,7 +344,7 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
|
|||||||
]
|
]
|
||||||
|
|
||||||
story = [
|
story = [
|
||||||
Paragraph(
|
FontFallbackParagraph(
|
||||||
cl.name,
|
cl.name,
|
||||||
headlinestyle
|
headlinestyle
|
||||||
),
|
),
|
||||||
@@ -351,7 +352,7 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
|
|||||||
if cl.subevent:
|
if cl.subevent:
|
||||||
story += [
|
story += [
|
||||||
Spacer(1, 3 * mm),
|
Spacer(1, 3 * mm),
|
||||||
Paragraph(
|
FontFallbackParagraph(
|
||||||
'{} ({} {})'.format(
|
'{} ({} {})'.format(
|
||||||
cl.subevent.name,
|
cl.subevent.name,
|
||||||
cl.subevent.get_date_range_display(),
|
cl.subevent.get_date_range_display(),
|
||||||
@@ -381,10 +382,10 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
|
|||||||
headrowstyle.fontName = 'OpenSansBd'
|
headrowstyle.fontName = 'OpenSansBd'
|
||||||
for q in questions:
|
for q in questions:
|
||||||
txt = str(q.question)
|
txt = str(q.question)
|
||||||
p = Paragraph(txt, headrowstyle)
|
p = FontFallbackParagraph(txt, headrowstyle)
|
||||||
while p.wrap(colwidths[len(tdata[0])], 5000)[1] > 30 * mm:
|
while p.wrap(colwidths[len(tdata[0])], 5000)[1] > 30 * mm:
|
||||||
txt = txt[:len(txt) - 50] + "..."
|
txt = txt[:len(txt) - 50] + "..."
|
||||||
p = Paragraph(txt, headrowstyle)
|
p = FontFallbackParagraph(txt, headrowstyle)
|
||||||
tdata[0].append(p)
|
tdata[0].append(p)
|
||||||
|
|
||||||
qs = self._get_queryset(cl, form_data)
|
qs = self._get_queryset(cl, form_data)
|
||||||
@@ -431,8 +432,8 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
|
|||||||
CBFlowable(bool(op.last_checked_in)) if not op.blocked else '—',
|
CBFlowable(bool(op.last_checked_in)) if not op.blocked else '—',
|
||||||
'✘' if op.order.status != Order.STATUS_PAID else '✔',
|
'✘' if op.order.status != Order.STATUS_PAID else '✔',
|
||||||
op.order.code,
|
op.order.code,
|
||||||
Paragraph(name, self.get_style()),
|
FontFallbackParagraph(name, self.get_style()),
|
||||||
Paragraph(bleach.clean(str(item), tags={'br'}).strip().replace('<br>', '<br/>'), self.get_style()),
|
FontFallbackParagraph(bleach.clean(str(item), tags={'br'}).strip().replace('<br>', '<br/>'), self.get_style()),
|
||||||
]
|
]
|
||||||
acache = {}
|
acache = {}
|
||||||
if op.addon_to:
|
if op.addon_to:
|
||||||
@@ -443,10 +444,10 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
|
|||||||
for q in questions:
|
for q in questions:
|
||||||
txt = acache.get(q.pk, '')
|
txt = acache.get(q.pk, '')
|
||||||
txt = bleach.clean(txt, tags={'br'}).strip().replace('<br>', '<br/>')
|
txt = bleach.clean(txt, tags={'br'}).strip().replace('<br>', '<br/>')
|
||||||
p = Paragraph(txt, self.get_style())
|
p = FontFallbackParagraph(txt, self.get_style())
|
||||||
while p.wrap(colwidths[len(row)], 5000)[1] > 50 * mm:
|
while p.wrap(colwidths[len(row)], 5000)[1] > 50 * mm:
|
||||||
txt = txt[:len(txt) - 50] + "..."
|
txt = txt[:len(txt) - 50] + "..."
|
||||||
p = Paragraph(txt, self.get_style())
|
p = FontFallbackParagraph(txt, self.get_style())
|
||||||
row.append(p)
|
row.append(p)
|
||||||
if op.order.status != Order.STATUS_PAID:
|
if op.order.status != Order.STATUS_PAID:
|
||||||
tstyledata += [
|
tstyledata += [
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ from pretix.base.timeframes import (
|
|||||||
resolve_timeframe_to_datetime_start_inclusive_end_exclusive,
|
resolve_timeframe_to_datetime_start_inclusive_end_exclusive,
|
||||||
)
|
)
|
||||||
from pretix.control.forms.filter import get_all_payment_providers
|
from pretix.control.forms.filter import get_all_payment_providers
|
||||||
|
from pretix.helpers.reportlab import FontFallbackParagraph
|
||||||
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
||||||
|
|
||||||
|
|
||||||
@@ -310,13 +311,13 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
|
|
||||||
tdata = [
|
tdata = [
|
||||||
[
|
[
|
||||||
Paragraph(self._transaction_group_header_label(), tstyle_bold),
|
FontFallbackParagraph(self._transaction_group_header_label(), tstyle_bold),
|
||||||
Paragraph(_("Price"), tstyle_bold_right),
|
FontFallbackParagraph(_("Price"), tstyle_bold_right),
|
||||||
Paragraph(_("Tax rate"), tstyle_bold_right),
|
FontFallbackParagraph(_("Tax rate"), tstyle_bold_right),
|
||||||
Paragraph("#", tstyle_bold_right),
|
FontFallbackParagraph("#", tstyle_bold_right),
|
||||||
Paragraph(_("Net total"), tstyle_bold_right),
|
FontFallbackParagraph(_("Net total"), tstyle_bold_right),
|
||||||
Paragraph(_("Tax total"), tstyle_bold_right),
|
FontFallbackParagraph(_("Tax total"), tstyle_bold_right),
|
||||||
Paragraph(_("Gross total"), tstyle_bold_right),
|
FontFallbackParagraph(_("Gross total"), tstyle_bold_right),
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -351,7 +352,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
tdata[last_group_head_idx][6] = Paragraph(money_filter(sum_price_by_group, currency), tstyle_bold_right),
|
tdata[last_group_head_idx][6] = Paragraph(money_filter(sum_price_by_group, currency), tstyle_bold_right),
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(
|
FontFallbackParagraph(
|
||||||
e,
|
e,
|
||||||
tstyle_bold,
|
tstyle_bold,
|
||||||
),
|
),
|
||||||
@@ -374,7 +375,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
text = self._transaction_row_label(r)
|
text = self._transaction_row_label(r)
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(text, tstyle),
|
FontFallbackParagraph(text, tstyle),
|
||||||
Paragraph(
|
Paragraph(
|
||||||
money_filter(r["price"], currency)
|
money_filter(r["price"], currency)
|
||||||
if "price" in r and r["price"] is not None
|
if "price" in r and r["price"] is not None
|
||||||
@@ -405,7 +406,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
for tax_rate in sorted(sum_tax_by_tax_rate.keys(), reverse=True):
|
for tax_rate in sorted(sum_tax_by_tax_rate.keys(), reverse=True):
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(_("Sum"), tstyle),
|
FontFallbackParagraph(_("Sum"), tstyle),
|
||||||
Paragraph("", tstyle_right),
|
Paragraph("", tstyle_right),
|
||||||
Paragraph(localize(tax_rate.normalize()) + " %", tstyle_right),
|
Paragraph(localize(tax_rate.normalize()) + " %", tstyle_right),
|
||||||
Paragraph("", tstyle_right),
|
Paragraph("", tstyle_right),
|
||||||
@@ -438,7 +439,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
|
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(_("Sum"), tstyle_bold),
|
FontFallbackParagraph(_("Sum"), tstyle_bold),
|
||||||
Paragraph("", tstyle_right),
|
Paragraph("", tstyle_right),
|
||||||
Paragraph("", tstyle_right),
|
Paragraph("", tstyle_right),
|
||||||
Paragraph("", tstyle_bold_right),
|
Paragraph("", tstyle_bold_right),
|
||||||
@@ -492,10 +493,10 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
|
|
||||||
tdata = [
|
tdata = [
|
||||||
[
|
[
|
||||||
Paragraph(_("Payment method"), tstyle_bold),
|
FontFallbackParagraph(_("Payment method"), tstyle_bold),
|
||||||
Paragraph(_("Payments"), tstyle_bold_right),
|
FontFallbackParagraph(_("Payments"), tstyle_bold_right),
|
||||||
Paragraph(_("Refunds"), tstyle_bold_right),
|
FontFallbackParagraph(_("Refunds"), tstyle_bold_right),
|
||||||
Paragraph(_("Total"), tstyle_bold_right),
|
FontFallbackParagraph(_("Total"), tstyle_bold_right),
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -537,7 +538,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(provider_names.get(p, p), tstyle),
|
Paragraph(provider_names.get(p, p), tstyle),
|
||||||
Paragraph(
|
FontFallbackParagraph(
|
||||||
money_filter(payments_by_provider[p], currency)
|
money_filter(payments_by_provider[p], currency)
|
||||||
if p in payments_by_provider
|
if p in payments_by_provider
|
||||||
else "",
|
else "",
|
||||||
@@ -562,7 +563,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
|
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(_("Sum"), tstyle_bold),
|
FontFallbackParagraph(_("Sum"), tstyle_bold),
|
||||||
Paragraph(
|
Paragraph(
|
||||||
money_filter(
|
money_filter(
|
||||||
sum(payments_by_provider.values(), Decimal("0.00")), currency
|
sum(payments_by_provider.values(), Decimal("0.00")), currency
|
||||||
@@ -640,7 +641,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
open_before = tx_before - p_before + r_before
|
open_before = tx_before - p_before + r_before
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(
|
FontFallbackParagraph(
|
||||||
_("Pending payments at {datetime}").format(
|
_("Pending payments at {datetime}").format(
|
||||||
datetime=date_format(
|
datetime=date_format(
|
||||||
df_start - datetime.timedelta.resolution,
|
df_start - datetime.timedelta.resolution,
|
||||||
@@ -667,21 +668,21 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
] or Decimal("0.00")
|
] or Decimal("0.00")
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(_("Orders"), tstyle),
|
FontFallbackParagraph(_("Orders"), tstyle),
|
||||||
Paragraph("+", tstyle_center),
|
Paragraph("+", tstyle_center),
|
||||||
Paragraph(money_filter(tx_during, currency), tstyle_right),
|
Paragraph(money_filter(tx_during, currency), tstyle_right),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(_("Payments"), tstyle),
|
FontFallbackParagraph(_("Payments"), tstyle),
|
||||||
Paragraph("-", tstyle_center),
|
Paragraph("-", tstyle_center),
|
||||||
Paragraph(money_filter(p_during, currency), tstyle_right),
|
Paragraph(money_filter(p_during, currency), tstyle_right),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(_("Refunds"), tstyle),
|
FontFallbackParagraph(_("Refunds"), tstyle),
|
||||||
Paragraph("+", tstyle_center),
|
Paragraph("+", tstyle_center),
|
||||||
Paragraph(money_filter(r_during, currency), tstyle_right),
|
Paragraph(money_filter(r_during, currency), tstyle_right),
|
||||||
]
|
]
|
||||||
@@ -767,7 +768,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
] or Decimal("0.00")
|
] or Decimal("0.00")
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(_("Gift card transactions (credit)"), tstyle),
|
FontFallbackParagraph(_("Gift card transactions (credit)"), tstyle),
|
||||||
Paragraph(money_filter(tx_during_pos, currency), tstyle_right),
|
Paragraph(money_filter(tx_during_pos, currency), tstyle_right),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -777,7 +778,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
] or Decimal("0.00")
|
] or Decimal("0.00")
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[
|
[
|
||||||
Paragraph(_("Gift card transactions (debit)"), tstyle),
|
FontFallbackParagraph(_("Gift card transactions (debit)"), tstyle),
|
||||||
Paragraph(money_filter(tx_during_neg, currency), tstyle_right),
|
Paragraph(money_filter(tx_during_neg, currency), tstyle_right),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -845,9 +846,9 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
style_small.leading = 10
|
style_small.leading = 10
|
||||||
|
|
||||||
story = [
|
story = [
|
||||||
Paragraph(self.verbose_name, style_h1),
|
FontFallbackParagraph(self.verbose_name, style_h1),
|
||||||
Spacer(0, 3 * mm),
|
Spacer(0, 3 * mm),
|
||||||
Paragraph(
|
FontFallbackParagraph(
|
||||||
"<br />".join(escape(f) for f in self.describe_filters(form_data)),
|
"<br />".join(escape(f) for f in self.describe_filters(form_data)),
|
||||||
style_small,
|
style_small,
|
||||||
),
|
),
|
||||||
@@ -859,7 +860,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
c_head = f" [{c}]" if len(currencies) > 1 else ""
|
c_head = f" [{c}]" if len(currencies) > 1 else ""
|
||||||
story += [
|
story += [
|
||||||
Spacer(0, 3 * mm),
|
Spacer(0, 3 * mm),
|
||||||
Paragraph(_("Orders") + c_head, style_h2),
|
FontFallbackParagraph(_("Orders") + c_head, style_h2),
|
||||||
Spacer(0, 3 * mm),
|
Spacer(0, 3 * mm),
|
||||||
*self._table_transactions(form_data, c),
|
*self._table_transactions(form_data, c),
|
||||||
]
|
]
|
||||||
@@ -868,7 +869,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
c_head = f" [{c}]" if len(currencies) > 1 else ""
|
c_head = f" [{c}]" if len(currencies) > 1 else ""
|
||||||
story += [
|
story += [
|
||||||
Spacer(0, 8 * mm),
|
Spacer(0, 8 * mm),
|
||||||
Paragraph(_("Payments") + c_head, style_h2),
|
FontFallbackParagraph(_("Payments") + c_head, style_h2),
|
||||||
Spacer(0, 3 * mm),
|
Spacer(0, 3 * mm),
|
||||||
*self._table_payments(form_data, c),
|
*self._table_payments(form_data, c),
|
||||||
]
|
]
|
||||||
@@ -879,7 +880,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
Spacer(0, 8 * mm),
|
Spacer(0, 8 * mm),
|
||||||
KeepTogether(
|
KeepTogether(
|
||||||
[
|
[
|
||||||
Paragraph(_("Open items") + c_head, style_h2),
|
FontFallbackParagraph(_("Open items") + c_head, style_h2),
|
||||||
Spacer(0, 3 * mm),
|
Spacer(0, 3 * mm),
|
||||||
*self._table_open_items(form_data, c),
|
*self._table_open_items(form_data, c),
|
||||||
]
|
]
|
||||||
@@ -895,7 +896,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
|||||||
Spacer(0, 8 * mm),
|
Spacer(0, 8 * mm),
|
||||||
KeepTogether(
|
KeepTogether(
|
||||||
[
|
[
|
||||||
Paragraph(_("Gift cards") + c_head, style_h2),
|
FontFallbackParagraph(_("Gift cards") + c_head, style_h2),
|
||||||
Spacer(0, 3 * mm),
|
Spacer(0, 3 * mm),
|
||||||
*self._table_gift_cards(form_data, c),
|
*self._table_gift_cards(form_data, c),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ from reportlab.lib import colors
|
|||||||
from reportlab.lib.enums import TA_CENTER
|
from reportlab.lib.enums import TA_CENTER
|
||||||
from reportlab.lib.units import mm
|
from reportlab.lib.units import mm
|
||||||
from reportlab.pdfgen.canvas import Canvas
|
from reportlab.pdfgen.canvas import Canvas
|
||||||
from reportlab.platypus import PageBreak, Paragraph, Spacer, Table, TableStyle
|
from reportlab.platypus import PageBreak, Spacer, Table, TableStyle
|
||||||
|
|
||||||
from pretix.base.decimal import round_decimal
|
from pretix.base.decimal import round_decimal
|
||||||
from pretix.base.exporter import BaseExporter, MultiSheetListExporter
|
from pretix.base.exporter import BaseExporter, MultiSheetListExporter
|
||||||
@@ -69,6 +69,8 @@ from pretix.base.timeframes import (
|
|||||||
resolve_timeframe_to_datetime_start_inclusive_end_exclusive,
|
resolve_timeframe_to_datetime_start_inclusive_end_exclusive,
|
||||||
)
|
)
|
||||||
from pretix.control.forms.filter import OverviewFilterForm
|
from pretix.control.forms.filter import OverviewFilterForm
|
||||||
|
from pretix.helpers.reportlab import FontFallbackParagraph
|
||||||
|
from pretix.presale.style import get_fonts
|
||||||
|
|
||||||
|
|
||||||
class NumberedCanvas(Canvas):
|
class NumberedCanvas(Canvas):
|
||||||
@@ -135,6 +137,15 @@ class ReportlabExportMixin:
|
|||||||
pdfmetrics.registerFont(TTFont('OpenSansIt', finders.find('fonts/OpenSans-Italic.ttf')))
|
pdfmetrics.registerFont(TTFont('OpenSansIt', finders.find('fonts/OpenSans-Italic.ttf')))
|
||||||
pdfmetrics.registerFont(TTFont('OpenSansBd', finders.find('fonts/OpenSans-Bold.ttf')))
|
pdfmetrics.registerFont(TTFont('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'])))
|
||||||
|
if 'italic' in styles:
|
||||||
|
pdfmetrics.registerFont(TTFont(family + ' I', finders.find(styles['italic']['truetype'])))
|
||||||
|
if 'bold' in styles:
|
||||||
|
pdfmetrics.registerFont(TTFont(family + ' B', finders.find(styles['bold']['truetype'])))
|
||||||
|
if 'bolditalic' in styles:
|
||||||
|
pdfmetrics.registerFont(TTFont(family + ' B I', finders.find(styles['bolditalic']['truetype'])))
|
||||||
|
|
||||||
def get_doc_template(self):
|
def get_doc_template(self):
|
||||||
from reportlab.platypus import BaseDocTemplate
|
from reportlab.platypus import BaseDocTemplate
|
||||||
|
|
||||||
@@ -272,7 +283,7 @@ class OverviewReport(Report):
|
|||||||
headlinestyle.fontSize = 15
|
headlinestyle.fontSize = 15
|
||||||
headlinestyle.fontName = 'OpenSansBd'
|
headlinestyle.fontName = 'OpenSansBd'
|
||||||
story = [
|
story = [
|
||||||
Paragraph(_('Orders by product') + ' ' + (_('(excl. taxes)') if net else _('(incl. taxes)')), headlinestyle),
|
FontFallbackParagraph(_('Orders by product') + ' ' + (_('(excl. taxes)') if net else _('(incl. taxes)')), headlinestyle),
|
||||||
Spacer(1, 5 * mm)
|
Spacer(1, 5 * mm)
|
||||||
]
|
]
|
||||||
return story
|
return story
|
||||||
@@ -282,7 +293,7 @@ class OverviewReport(Report):
|
|||||||
if form_data.get('date_axis') and form_data.get('date_range'):
|
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)
|
d_start, d_end = resolve_timeframe_to_dates_inclusive(now(), form_data['date_range'], self.timezone)
|
||||||
story += [
|
story += [
|
||||||
Paragraph(_('{axis} between {start} and {end}').format(
|
FontFallbackParagraph(_('{axis} between {start} and {end}').format(
|
||||||
axis=dict(OverviewFilterForm(event=self.event).fields['date_axis'].choices)[form_data.get('date_axis')],
|
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 '–',
|
start=date_format(d_start, 'SHORT_DATE_FORMAT') if d_start else '–',
|
||||||
end=date_format(d_end, 'SHORT_DATE_FORMAT') if d_end else '–',
|
end=date_format(d_end, 'SHORT_DATE_FORMAT') if d_end else '–',
|
||||||
@@ -295,13 +306,13 @@ class OverviewReport(Report):
|
|||||||
subevent = self.event.subevents.get(pk=self.form_data.get('subevent'))
|
subevent = self.event.subevents.get(pk=self.form_data.get('subevent'))
|
||||||
except SubEvent.DoesNotExist:
|
except SubEvent.DoesNotExist:
|
||||||
subevent = self.form_data.get('subevent')
|
subevent = self.form_data.get('subevent')
|
||||||
story.append(Paragraph(pgettext('subevent', 'Date: {}').format(subevent), self.get_style()))
|
story.append(FontFallbackParagraph(pgettext('subevent', 'Date: {}').format(subevent), self.get_style()))
|
||||||
story.append(Spacer(1, 5 * mm))
|
story.append(Spacer(1, 5 * mm))
|
||||||
|
|
||||||
if form_data.get('subevent_date_range'):
|
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)
|
d_start, d_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), form_data['subevent_date_range'], self.timezone)
|
||||||
story += [
|
story += [
|
||||||
Paragraph(_('{axis} between {start} and {end}').format(
|
FontFallbackParagraph(_('{axis} between {start} and {end}').format(
|
||||||
axis=_('Event date'),
|
axis=_('Event date'),
|
||||||
start=date_format(d_start, 'SHORT_DATE_FORMAT') if d_start else '–',
|
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 '–',
|
end=date_format(d_end - timedelta(hours=1), 'SHORT_DATE_FORMAT') if d_end else '–',
|
||||||
@@ -373,13 +384,13 @@ class OverviewReport(Report):
|
|||||||
tdata = [
|
tdata = [
|
||||||
[
|
[
|
||||||
_('Product'),
|
_('Product'),
|
||||||
Paragraph(_('Canceled'), tstyle_th),
|
FontFallbackParagraph(_('Canceled'), tstyle_th),
|
||||||
'',
|
'',
|
||||||
Paragraph(_('Expired'), tstyle_th),
|
FontFallbackParagraph(_('Expired'), tstyle_th),
|
||||||
'',
|
'',
|
||||||
Paragraph(_('Approval pending'), tstyle_th),
|
FontFallbackParagraph(_('Approval pending'), tstyle_th),
|
||||||
'',
|
'',
|
||||||
Paragraph(_('Purchased'), tstyle_th),
|
FontFallbackParagraph(_('Purchased'), tstyle_th),
|
||||||
'', '', '', '', ''
|
'', '', '', '', ''
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -410,14 +421,14 @@ class OverviewReport(Report):
|
|||||||
for tup in items_by_category:
|
for tup in items_by_category:
|
||||||
if tup[0]:
|
if tup[0]:
|
||||||
tdata.append([
|
tdata.append([
|
||||||
Paragraph(str(tup[0]), tstyle_bold)
|
FontFallbackParagraph(str(tup[0]), tstyle_bold)
|
||||||
])
|
])
|
||||||
for l, s in states:
|
for l, s in states:
|
||||||
tdata[-1].append(str(tup[0].num[l][0]))
|
tdata[-1].append(str(tup[0].num[l][0]))
|
||||||
tdata[-1].append(floatformat(tup[0].num[l][2 if net else 1], places))
|
tdata[-1].append(floatformat(tup[0].num[l][2 if net else 1], places))
|
||||||
for item in tup[1]:
|
for item in tup[1]:
|
||||||
tdata.append([
|
tdata.append([
|
||||||
Paragraph(str(item), tstyle)
|
FontFallbackParagraph(str(item), tstyle)
|
||||||
])
|
])
|
||||||
for l, s in states:
|
for l, s in states:
|
||||||
tdata[-1].append(str(item.num[l][0]))
|
tdata[-1].append(str(item.num[l][0]))
|
||||||
@@ -425,7 +436,7 @@ class OverviewReport(Report):
|
|||||||
if item.has_variations:
|
if item.has_variations:
|
||||||
for var in item.all_variations:
|
for var in item.all_variations:
|
||||||
tdata.append([
|
tdata.append([
|
||||||
Paragraph(" " + str(var), tstyle)
|
FontFallbackParagraph(" " + str(var), tstyle)
|
||||||
])
|
])
|
||||||
for l, s in states:
|
for l, s in states:
|
||||||
tdata[-1].append(str(var.num[l][0]))
|
tdata[-1].append(str(var.num[l][0]))
|
||||||
@@ -512,7 +523,7 @@ class OrderTaxListReportPDF(Report):
|
|||||||
|
|
||||||
def get_story(self, doc, form_data):
|
def get_story(self, doc, form_data):
|
||||||
from reportlab.lib.units import mm
|
from reportlab.lib.units import mm
|
||||||
from reportlab.platypus import Paragraph, Spacer, Table, TableStyle
|
from reportlab.platypus import Spacer, Table, TableStyle
|
||||||
|
|
||||||
headlinestyle = self.get_style()
|
headlinestyle = self.get_style()
|
||||||
headlinestyle.fontSize = 15
|
headlinestyle.fontSize = 15
|
||||||
@@ -553,7 +564,7 @@ class OrderTaxListReportPDF(Report):
|
|||||||
tstyledata.append(('SPAN', (5 + 2 * i, 0), (6 + 2 * i, 0)))
|
tstyledata.append(('SPAN', (5 + 2 * i, 0), (6 + 2 * i, 0)))
|
||||||
|
|
||||||
story = [
|
story = [
|
||||||
Paragraph(_('Orders by tax rate ({currency})').format(currency=self.event.currency), headlinestyle),
|
FontFallbackParagraph(_('Orders by tax rate ({currency})').format(currency=self.event.currency), headlinestyle),
|
||||||
Spacer(1, 5 * mm)
|
Spacer(1, 5 * mm)
|
||||||
]
|
]
|
||||||
tdata = [
|
tdata = [
|
||||||
|
|||||||
Reference in New Issue
Block a user