Images & fonts
Local asset and font roots, the validation boundary, and the opt-in remote allowlist with its security posture.
Images and fonts are resolved and embedded during the Nuxt build from explicit local roots. Remote fetching is off by default and, when enabled, is gated by an operator-owned allowlist. This is a fail-closed resource boundary, not a general filesystem or network sandbox.
Local images
Put PNG or JPEG files in pdfs/assets and reference the path relative to that
directory:
<PdfImage
src="brand/logo.png"
:style="{ height: 40, objectFit: 'contain', width: 120 }"
/>Local fonts
Put TTF or OTF files in pdfs/fonts and register each face in nuxt.config.ts:
export default defineNuxtConfig({
modules: ["@lupinum/nuxt-pdf"],
pdf: {
fonts: [{
family: "Invoice Sans",
src: "InvoiceSans-Regular.ttf",
fontWeight: 400,
fontStyle: "normal",
}],
},
});Then use the family in a style object. Font inheritance flows through the tree, so setting it on the page covers the document:
<PdfPage :style="{ fontFamily: 'Invoice Sans' }">
<PdfText>Invoice</PdfText>
</PdfPage>Register one entry per weight/style you use; fontWeight accepts a number or a
named weight ("bold" or "medium"). See the
module options reference for the full font
declaration.
Completed render diagnostics expose registeredFontFaces (family, weight, and
style only), and the development preview lists the same safe facts. This proves
which faces were configured; it deliberately does not claim that a missing
glyph fell back or that every character exists in a face.
Typography support boundary
Every claim assumes the application embeds a font containing the required glyphs. The calibration fixture checks shaping, extraction, geometry, and a reviewed raster, not only whether rendering avoided an exception.
| Text behavior | Status | Evidence and boundary |
|---|---|---|
| Latin Extended, punctuation, currency | Supported | Render, extraction, wrapping, and raster evidence with Roboto |
| Greek and Cyrillic | Supported | Representative sentences render and extract exactly with Roboto |
| Custom hyphenation | Supported | Paired layout/raster conformance; application supplies the callback |
| CJK | Experimental | Representative Chinese/Japanese text renders and extracts with a supplied Noto subset; broad font coverage and line-breaking policy remain application evidence |
| Combining marks | Experimental | Marks render correctly, but PDF extraction can detach or omit mark association (Ångström extracts as A ngstrom) |
| Arabic and mixed bidirectional text | Experimental | Representative Arabic shapes visually and reports an RTL run; extracted mixed-order text follows bidi ordering rather than the authored string |
| Variable fonts | Experimental | A Source Code variable TTF renders/extracts at its default instance; axis selection is not a public API |
| Emoji | Unsupported | Monochrome face emoji do not serialize faithfully through the pinned engine; use an admitted PNG or SVG asset instead |
| Font-family fallback chains | Unsupported | fontFamily names one registered family; Nuxt PDF does not invent missing-glyph or fallback detection |
Use static faces for production documents unless your own semantic and raster suite proves an experimental script/font combination.
The validation boundary
Every configured resource is checked at build time. Resources are:
- structurally checked for PNG/JPEG chunks, dimensions, and decoded pixels; TTF/OTF signature, matching extension, SFNT directory bounds, and required outline tables;
- size-checked with limits of 10 MB for images and 5 MB for fonts;
- realpath-contained, so resolved paths must stay inside the declared root; and
- embedded into the server build, so the bytes travel in the build and there is no runtime filesystem read.
Absolute paths, .. traversal, symlink escapes, missing files, ambiguous
sources, and unsupported URL schemes are all rejected. There is no runtime
filesystem fallback.
Opt-in remote images
Remote images are unsupported until you configure an allowlist. Remote
fonts are always unsupported; keep fonts in pdfs/fonts so builds are
reproducible. With pdf.remote absent the module performs zero network I/O and
every URL source fails closed.
When an operator sets pdf.remote.allow, the module fetches allowlisted images
and converts them to validated bytes before
layout:
export default defineNuxtConfig({
modules: ["@lupinum/nuxt-pdf"],
pdf: {
remote: {
allow: [
"https://cdn.example.com/brand/",
"https://images.example.com/logos/",
],
// Optional per-hop timeout (default shown):
// timeoutMs: 10_000,
},
},
});The security posture
The allowlist is the operator's trust decision, and the fetch path is deliberately narrow:
- Exact HTTPS prefixes only. Each entry must be an
https://host/path/prefix with a trailing slash. Prefix entries reject queries too; requested image URLs may carry a query, which is never shown in errors.http://, wildcards, embedded credentials, fragments, and non-matching hosts, ports, or paths are blocked. - Redirects re-checked per hop. Redirects are followed manually (bounded to three) and the allowlist is re-checked on every hop, so an allowlisted host cannot redirect out of the allowlist.
- One render budget.
pdf.limitsowns per-image and aggregate source bytes, decoded pixels, request count, concurrency, output bytes, and the whole-render deadline. A fatal resource error aborts sibling requests. - Signature is authoritative. A deceptive
Content-Typecannot make non-image bytes validate; PNG/JPEG structure and dimensions are inspected before engine admission. - No credentials, no headers. Fetches are
GETonly, send no request headers, and carry no cookies or credentials, under a per-hop timeout.
Remote images resolve at render time (deduplicated per render, no cross-render cache). Local fonts are validated and embedded at build time.