Dokumentation
PDFs erstellen
PDFs in Vue
PDFs in Nuxt
PDFs in React
PDFs in Angular
Service & Vorschau
Einrichtung
provideJasy() ist optional. Ohne funktioniert inject(JasyPdf) mit den Standardschriften und den
Standardwerten der Engine. Es gehört in die app.config.ts: jasy ist ein Service für die ganze App,
provideJasy() in den Providern einer Lazy Route ändert ihn also nicht.
// app.config.ts
import { provideJasy } from "@jasy/angular";
export const appConfig: ApplicationConfig = {
providers: [
provideJasy({
// Every document can use these by name: Text("...", { font: "Inter" }).
fonts: {
Inter: { normal: "/fonts/Inter-Regular.woff2", bold: "/fonts/Inter-Bold.woff2" },
},
// Applied to every PDF; the options of a single call win.
defaults: { accessible: true },
// Load the font data once the app is idle, so the first PDF does not wait for it.
preload: true,
}),
],
};
| Option | Was es tut |
|---|---|
fonts | Schriften nach Namen: eine URL oder Bytes, oder eine Familie { normal, bold?, italic?, boldItalic? } |
defaults | Render-Optionen für jedes PDF, etwa accessible oder encrypt |
preload | die Schriftdaten laden, sobald die App gestartet und der Browser untätig ist, statt mit dem ersten PDF |
Eine Schrift-URL wird geladen, wenn ein PDF sie zum ersten Mal braucht, und bleibt für die Laufzeit der App.
.ttf, .woff und .woff2 funktionieren alle. Schlägt ein Request fehl, versucht es das nächste PDF neu.
Eine URL, hier und bei einem Formular, geht über den HttpClient von Angular,
Ihre Interceptors greifen also: ein Auth-Header, eine Basis-URL. Ab Angular 21 gibt es ihn immer; unter 20
ohne provideHttpClient() nutzt jasy fetch, und ein 401 oder 403 sagt, dass das der Grund ist.
Ein Dokument
PdfDocumentFn<T> ist der Typ eines Dokuments: (data: T) => Document(...), synchron oder asynchron.
PdfDocumentFn ohne Typargument ist ein Dokument, das keine Daten nimmt.
Ein Dokument läuft in einem Injection Context, wie funktionale Guards und Resolver. inject(LOCALE_ID)
gibt Ihnen die Sprache der App, und inject() erreicht Ihre Services. In einer pdfResource läuft es im
Injector seiner Komponente, sieht also auch, was die Komponente und ihre Route bereitstellen. Auch ein
getaggtes PDF übernimmt seine Sprache aus LOCALE_ID.
inject(JasyPdf)
| Methode | Was sie tut |
|---|---|
render(document, data, options?) | Die PDF-Bytes, ein Uint8Array. Im Browser und auf dem Server. |
blob(document, data, options?) | Das PDF als Blob, bereit für einen Upload. |
download(document, data, filename?, options?) | Speichert das PDF im Browser. |
open(document, data, options?) | Öffnet das PDF in einem neuen Tab, aus einem Click-Handler. |
download(bytes, filename?) · open(bytes) | Dasselbe für ein fertiges PDF, etwa ein ausgefülltes Formular. |
preload() | Lädt die Schriftdaten jetzt, etwa beim Hover, damit das nächste PDF nicht wartet. |
options sind die Render-Optionen der Engine: { encrypt: { userPassword } } für
AES-256, { accessible: true } für ein getaggtes PDF,
onDiagnostic für das, was die Engine zu sagen hat. Sie werden über die defaults von provideJasy
gelegt.
Ein Dokument ohne Daten braucht keine: render(doc), download(doc, undefined, "a.pdf"). Über den Service
aufgerufen, läuft ein Dokument im Root-Injector der App.
Zwei Fehler, die Sie am Namen abfangen können:
JasyBrowserOnlyError-download()undopen()auf dem Server, wo es keinen Tab zum Speichern gibt. Nutzen Sie dortrender().JasyPopupBlockedError- der Browser hat den neuen Tab blockiert, weilopen()nicht direkt aus einem Klick kam. Ihre App bleibt, wo sie ist;download()funktioniert immer.
pdfResource()
pdfResource(document, data?, { options?, injector? })
Ein PDF, das Ihren Signals folgt. Es ist eine ResourceRef von Angular - value(), isLoading(),
error(), reload() - plus latest(), das letzte PDF, das fertig gerendert wurde.
datawird gelesen wieresource({ params }): Ändert sich ein Signal darin, rendert das PDF neu, und ein Ergebnis, das ein neueres ersetzt hat, wird verworfen. Geben Sieundefinedzurück, um zu warten, etwa bis ein Input einen Wert hat. Ein Dokument ohne Daten nimmt keine:pdfResource(doc).optionskann eine Funktion sein und wird wiedataverfolgt. Ein Signal darin rendert das PDF neu.injectorist für einen Aufruf außerhalb eines Injection Context.
Ein gerendertes PDF hält seine bytes, seine blob:-URL als url und safeUrl, das direkt in
<iframe [src]> passt. Die alte URL wird freigegeben, wenn eine neue sie ersetzt, und die letzte, wenn die
Komponente zerstört wird.
Binden Sie eine Vorschau an latest(), nicht an value(). Angular leert value() bei jeder Änderung
der Daten, ein <iframe> hinter hasValue() würde also bei jedem Tastendruck entfernt und neu angelegt und
verlöre Scroll-Position und Zoom. latest() behält das letzte PDF, während das nächste rendert, und wenn
eines fehlschlägt:
@if (preview.latest(); as pdf) {
<iframe [src]="pdf.safeUrl" title="Preview"></iframe>
} @if (preview.isLoading()) {
<p>Rendering ...</p>
} @if (preview.error(); as error) {
<p>{{ error.message }}</p>
}
pdfResource() baut auf resource() von Angular auf, das Angular in 20 und 21 als experimentell markiert
und ab 22 als stabil. Der Service hängt nicht davon ab.
Auf dem Server
render() und blob() funktionieren in Node wie im Browser, ein Server kann die Bytes also erzeugen und
mit dem Content-Type application/pdf senden. Eine pdfResource bleibt auf dem Server untätig, denn eine
Vorschau hat dort niemanden, der sie ansieht; im Browser rendert sie nach der Hydration.
In einer SSR-App erlaubt ng add zusätzlich die CommonJS-Module von jimp, das der Server für Bilder nutzt.
Bundle-Größe
@jasy/angular legt die Engine nicht in Ihr Start-Bundle; Ihre Dokumente bringen sie mit, wo immer sie
importiert werden. Die Schriftdaten (158 KB minifiziert, 12 KB mit gzip) laden mit dem ersten PDF, oder
früher mit preload.
Legen Sie das PDF-Feature also hinter @defer oder eine Lazy Route. Eine neue Angular-20-App mit
provideJasy() startet dann mit 231 KB. Direkt in der Root-Komponente gerendert, startet sie mit 522 KB,
knapp über der Standardwarnung von Angular bei 500 KB.
@Component({
selector: "app-billing",
imports: [InvoicePreview],
template: `
@defer (on interaction) {
<app-invoice-preview [invoice]="invoice()" />
} @placeholder {
<button>Show the invoice</button>
}
`,
})
export class Billing {
readonly invoice = input.required<Invoice>();
}
Tests
JasyPdf und JasyForms sind Injection Tokens wie alle anderen:
TestBed.configureTestingModule({
providers: [{ provide: JasyPdf, useValue: { download: vi.fn() } }],
});