Let a theme bring its own files: fonts, vector graphics and images inlined when drawing, and shown in the preview #343

Closed
opened 2026-10-01 07:02:08 +00:00 by tiagoagueda · 0 comments
Owner

A theme cannot bring a file of its own. The templates plugin is asked that "every template must hold its assets, so fonts, logos, icons, or other graphical elements can be printed", vector graphics preferred (postulo/postulo-templates#8).

What exists today

  • No static files for a plugin: "A logo inside a plugin wheel is invisible to the static machinery, always" (src/postulo/plugins/logos.py).
  • Nothing is fetched while drawing. WeasyPrint gets URLFetcher(allowed_protocols={"data"}, allow_redirects=False) and no base_url; Chromium runs page.route("**/*", lambda route: route.abort()) (src/postulo/documents/pdf.py). That is deliberate (#163), and tests/security/test_pdf_fetching.py holds it.
  • The rule for a theme is in the wiki's CVs: "its CSS inline, and anything else as a data: address". Nothing helps a theme do that: no tag or loader turns a file in its package into one.
  • Fonts are the server's. The image installs DejaVu and Noto; "the theme names fonts, the fonts are whatever the server happens to have". check_fonts probes system fonts only (#74).
  • The preview's policy (src/postulo/config/settings/base.py): img-src 'self' data:, font-src 'self', style-src 'self' with a nonce. A data: image shows; a data: font does not; a style attribute or a <style> without the nonce, inside an inline SVG included, is refused.

Proposal

  1. A tag on the plugin surface that takes the name of a file inside the theme's own package and writes it into the document: an SVG as markup, an image as a data: address, a font as an @font-face rule with a data: source. The path is held to the package's own directory, and the types and sizes are limited.
  2. Nothing is fetched, still. The fetchers stay as they are; the tag inlines before the renderer starts.
  3. A preview that shows what the PDF shows: the document preview's policy allows the fonts the tag wrote, and an inlined SVG's styles carry the nonce.
  4. Fonts a theme brings are checked like the server's: a theme says which scripts its fonts draw, and a document in a script they lack falls back to the server's fonts, as #74 argued ("a declared package is not a drawn glyph").
  5. A test draws a theme with a font, an SVG and an image in both renderers and in the preview. The wiki's Writing a plugin and CVs describe the tag.

Decisions

  • Inline, or a scheme the fetcher resolves. Recommended: inline, which leaves the fetchers closed. Alternative: a theme: address the fetcher maps to package files, which keeps each document smaller and opens the fetcher a little.
  • data: fonts in the preview. Recommended: allowed on the document preview only, not on Postulo's pages.
  • Size. Assets are inlined into every drawn document, and a filed document keeps its HTML. Recommended: a ceiling per asset and per document, stated in the wiki.

Needed by

postulo/postulo-templates#8.

Done when

  • A plugin theme draws its own font, an SVG logo and an image in the PDF from both renderers and in the preview, with no request made.
  • A path outside the theme's package is refused.
  • tests/security/test_pdf_fetching.py still passes unchanged.
  • New names on postulo.plugins.api get their line in the wiki's Writing a plugin (tests/test_wiki_surface.py) and a test in tests/test_plugin_surface.py; new pages join the page-coverage walk (tests/test_page_coverage.py) and the isolation sweep (tests/security/test_isolation_sweep.py); new strings go in the working catalogues (English, French, and European and Brazilian Portuguese as drafts).

From the list of template improvements of 2026-09-30.

A theme cannot bring a file of its own. The templates plugin is asked that "every template must hold its assets, so fonts, logos, icons, or other graphical elements can be printed", vector graphics preferred (postulo/postulo-templates#8). ## What exists today - **No static files for a plugin**: "A logo inside a plugin wheel is invisible to the static machinery, always" (`src/postulo/plugins/logos.py`). - **Nothing is fetched while drawing.** WeasyPrint gets `URLFetcher(allowed_protocols={"data"}, allow_redirects=False)` and no `base_url`; Chromium runs `page.route("**/*", lambda route: route.abort())` (`src/postulo/documents/pdf.py`). That is deliberate (#163), and `tests/security/test_pdf_fetching.py` holds it. - **The rule for a theme** is in the wiki's *CVs*: "its CSS inline, and anything else as a `data:` address". Nothing helps a theme do that: no tag or loader turns a file in its package into one. - **Fonts are the server's.** The image installs DejaVu and Noto; "the theme names fonts, the fonts are whatever the server happens to have". `check_fonts` probes system fonts only (#74). - **The preview's policy** (`src/postulo/config/settings/base.py`): `img-src 'self' data:`, `font-src 'self'`, `style-src 'self'` with a nonce. A `data:` image shows; a `data:` font does not; a `style` attribute or a `<style>` without the nonce, inside an inline SVG included, is refused. ## Proposal 1. **A tag on the plugin surface** that takes the name of a file inside the theme's own package and writes it into the document: an SVG as markup, an image as a `data:` address, a font as an `@font-face` rule with a `data:` source. The path is held to the package's own directory, and the types and sizes are limited. 2. **Nothing is fetched, still.** The fetchers stay as they are; the tag inlines before the renderer starts. 3. **A preview that shows what the PDF shows**: the document preview's policy allows the fonts the tag wrote, and an inlined SVG's styles carry the nonce. 4. **Fonts a theme brings are checked like the server's**: a theme says which scripts its fonts draw, and a document in a script they lack falls back to the server's fonts, as #74 argued ("a declared package is not a drawn glyph"). 5. **A test** draws a theme with a font, an SVG and an image in both renderers and in the preview. The wiki's *Writing a plugin* and *CVs* describe the tag. ## Decisions - **Inline, or a scheme the fetcher resolves.** Recommended: inline, which leaves the fetchers closed. Alternative: a `theme:` address the fetcher maps to package files, which keeps each document smaller and opens the fetcher a little. - **`data:` fonts in the preview.** Recommended: allowed on the document preview only, not on Postulo's pages. - **Size.** Assets are inlined into every drawn document, and a filed document keeps its HTML. Recommended: a ceiling per asset and per document, stated in the wiki. ## Needed by postulo/postulo-templates#8. ## Done when - A plugin theme draws its own font, an SVG logo and an image in the PDF from both renderers and in the preview, with no request made. - A path outside the theme's package is refused. - `tests/security/test_pdf_fetching.py` still passes unchanged. - New names on `postulo.plugins.api` get their line in the wiki's *Writing a plugin* (`tests/test_wiki_surface.py`) and a test in `tests/test_plugin_surface.py`; new pages join the page-coverage walk (`tests/test_page_coverage.py`) and the isolation sweep (`tests/security/test_isolation_sweep.py`); new strings go in the working catalogues (English, French, and European and Brazilian Portuguese as drafts). From the list of template improvements of 2026-09-30.
tiagoagueda added this to the 0.5.0 milestone 2026-10-25 17:04:56 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#343
No description provided.