diff --git a/doc/development/api/invoice.rst b/doc/development/api/invoice.rst
index 69dcc222d..7b62a3727 100644
--- a/doc/development/api/invoice.rst
+++ b/doc/development/api/invoice.rst
@@ -84,6 +84,8 @@ convenient to you:
.. automethod:: _register_fonts
+ .. automethod:: _register_event_fonts
+
.. automethod:: _on_first_page
.. automethod:: _on_other_page
diff --git a/src/pretix/base/invoice.py b/src/pretix/base/invoice.py
index 81ffc5555..0b391cfd0 100644
--- a/src/pretix/base/invoice.py
+++ b/src/pretix/base/invoice.py
@@ -182,7 +182,7 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
pdfmetrics.registerFontFamily('OpenSans', normal='OpenSans', bold='OpenSansBd',
italic='OpenSansIt', boldItalic='OpenSansBI')
- for family, styles in get_fonts().items():
+ for family, styles in get_fonts(event=self.event, pdf_support_required=True).items():
if family == self.event.settings.invoice_renderer_font:
pdfmetrics.registerFont(TTFont(family, finders.find(styles['regular']['truetype'])))
self.font_regular = family
diff --git a/src/pretix/base/middleware.py b/src/pretix/base/middleware.py
index 6b96d68cb..dbca8e7ba 100644
--- a/src/pretix/base/middleware.py
+++ b/src/pretix/base/middleware.py
@@ -20,7 +20,7 @@
# .
#
from collections import OrderedDict
-from urllib.parse import urlsplit
+from urllib.parse import urlparse, urlsplit
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from django.conf import settings
@@ -40,6 +40,7 @@ from pretix.base.settings import global_settings_object
from pretix.multidomain.urlreverse import (
get_event_domain, get_organizer_domain,
)
+from pretix.presale.style import get_fonts
_supported = None
@@ -240,6 +241,14 @@ class SecurityMiddleware(MiddlewareMixin):
)
def process_response(self, request, resp):
+ def nested_dict_values(d):
+ for v in d.values():
+ if isinstance(v, dict):
+ yield from nested_dict_values(v)
+ else:
+ if isinstance(v, str):
+ yield v
+
url = resolve(request.path_info)
if settings.DEBUG and resp.status_code >= 400:
@@ -259,6 +268,14 @@ class SecurityMiddleware(MiddlewareMixin):
if gs.settings.leaflet_tiles:
img_src.append(gs.settings.leaflet_tiles[:gs.settings.leaflet_tiles.index("/", 10)].replace("{s}", "*"))
+ font_src = set()
+ if hasattr(request, 'event'):
+ for font in get_fonts(request.event, pdf_support_required=False).values():
+ for path in list(nested_dict_values(font)):
+ font_location = urlparse(path)
+ if font_location.scheme and font_location.netloc:
+ font_src.add('{}://{}'.format(font_location.scheme, font_location.netloc))
+
h = {
'default-src': ["{static}"],
'script-src': ['{static}'],
@@ -267,7 +284,7 @@ class SecurityMiddleware(MiddlewareMixin):
'style-src': ["{static}", "{media}"],
'connect-src': ["{dynamic}", "{media}"],
'img-src': ["{static}", "{media}", "data:"] + img_src,
- 'font-src': ["{static}"],
+ 'font-src': ["{static}"] + list(font_src),
'media-src': ["{static}", "data:"],
# form-action is not only used to match on form actions, but also on URLs
# form-actions redirect to. In the context of e.g. payment providers or
diff --git a/src/pretix/base/pdf.py b/src/pretix/base/pdf.py
index 7224066a1..cb2d94616 100644
--- a/src/pretix/base/pdf.py
+++ b/src/pretix/base/pdf.py
@@ -78,7 +78,7 @@ from reportlab.pdfgen.canvas import Canvas
from reportlab.platypus import Paragraph
from pretix.base.i18n import language
-from pretix.base.models import Order, OrderPosition, Question
+from pretix.base.models import Event, Order, OrderPosition, Question
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.base.signals import layout_image_variables, layout_text_variables
from pretix.base.templatetags.money import money_filter
@@ -738,9 +738,10 @@ class Renderer:
else:
self.bg_bytes = None
self.bg_pdf = None
+ self.event_fonts = list(get_fonts(event, pdf_support_required=True).keys()) + ['Open Sans']
@classmethod
- def _register_fonts(cls):
+ def _register_fonts(cls, event: Event = None):
if hasattr(cls, '_fonts_registered'):
return
pdfmetrics.registerFont(TTFont('Open Sans', finders.find('fonts/OpenSans-Regular.ttf')))
@@ -748,7 +749,7 @@ class Renderer:
pdfmetrics.registerFont(TTFont('Open Sans B', finders.find('fonts/OpenSans-Bold.ttf')))
pdfmetrics.registerFont(TTFont('Open Sans B I', finders.find('fonts/OpenSans-BoldItalic.ttf')))
- for family, styles in get_fonts().items():
+ for family, styles in get_fonts(event, 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'])))
@@ -939,6 +940,12 @@ class Renderer:
if o['italic']:
font += ' I'
+ # Since pdfmetrics.registerFont is global, we want to make sure that no one tries to sneak in a font, they
+ # should not have access to.
+ if font not in self.event_fonts:
+ logger.warning(f'Unauthorized use of font "{font}"')
+ font = 'Open Sans'
+
try:
ad = getAscentDescent(font, float(o['fontsize']))
except KeyError: # font not known, fall back
diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py
index 77fe13100..6eab68fd8 100644
--- a/src/pretix/base/settings.py
+++ b/src/pretix/base/settings.py
@@ -89,7 +89,7 @@ def primary_font_kwargs():
choices = [('Open Sans', 'Open Sans')]
choices += sorted([
- (a, {"title": a, "data": v}) for a, v in get_fonts().items() if not v.get('pdf_only', False)
+ (a, {"title": a, "data": v}) for a, v in get_fonts(pdf_support_required=False).items()
], key=lambda a: a[0])
return {
'choices': choices,
diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py
index 49f6fcdc9..d530e8cc4 100644
--- a/src/pretix/control/forms/event.py
+++ b/src/pretix/control/forms/event.py
@@ -79,6 +79,7 @@ from pretix.helpers.countries import CachedCountries
from pretix.multidomain.models import KnownDomain
from pretix.multidomain.urlreverse import build_absolute_uri
from pretix.plugins.banktransfer.payment import BankTransfer
+from pretix.presale.style import get_fonts
class EventWizardFoundationForm(forms.Form):
@@ -652,6 +653,9 @@ class EventSettingsForm(EventSettingsValidationMixin, FormPlaceholderMixin, Sett
del self.fields['event_list_available_only']
del self.fields['event_list_filters']
del self.fields['event_calendar_future_only']
+ self.fields['primary_font'].choices += [
+ (a, {"title": a, "data": v}) for a, v in get_fonts(self.event, pdf_support_required=False).items()
+ ]
# create "virtual" fields for better UX when editing _asked and _required fields
self.virtual_keys = []
@@ -932,6 +936,9 @@ class InvoiceSettingsForm(EventSettingsValidationMixin, SettingsForm):
)
)
self.fields['invoice_generate'].choices = generate_choices
+ self.fields['invoice_renderer_font'].choices += [
+ (a, a) for a in get_fonts(event, pdf_support_required=True).keys()
+ ]
def contains_web_channel_validate(val):
diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html
index 1b2998c8c..3c74d9d2b 100644
--- a/src/pretix/control/templates/pretixcontrol/event/settings.html
+++ b/src/pretix/control/templates/pretixcontrol/event/settings.html
@@ -7,7 +7,7 @@
{% block title %}{% trans "General settings" %}{% endblock %}
{% block custom_header %}
{{ block.super }}
-
+
{% endblock %}
{% block inside %}