Dokumentation

Daten und Assets

Reaktive Daten

Der ganze Sinn, in Vue zu schreiben: Ihre Daten steuern das Dokument. v-for, v-if, computed und Slots funktionieren alle - der Renderer läuft durch denselben Baum, den Vue baut, also wird alles, was einen Baum erzeugt, zu einem PDF.

<script setup lang="ts">
import { computed } from "vue";
import { Document, Page, Row, Text, Divider } from "@jasy/vue";

const props = defineProps<{ lines: { id: number; name: string; price: number }[] }>();
const total = computed(() => props.lines.reduce((sum, l) => sum + l.price, 0));
</script>

<template>
  <Document :size="11">
    <Page :size="'A4'" :margin="48" :gap="8">
      <Row v-for="line in lines" :key="line.id" :justify="'between'">
        <Text>{{ line.name }}</Text>
        <Text>{{ line.price }} EUR</Text>
      </Row>

      <Divider />
      <Row :justify="'between'">
        <Text bold>Total</Text>
        <Text bold>{{ total }} EUR</Text>
      </Row>
    </Page>
  </Document>
</template>

Ein PDF wird einmal gerendert und nicht wie eine Seite am Leben gehalten: Laden Sie, was das Dokument braucht, vor renderToPdf und übergeben Sie es als Props. Eine Änderung in onMounted (oder ein asynchroner Fetch dort) kommt zu spät - das Dokument ist dann schon gelesen.

Eigene Fonts

Die Standard-PDF-Fonts (Helvetica, Times, Courier) brauchen nichts. Für einen eigenen registrieren Sie den Font unter einem Namen an <Document :fonts> und verwenden diesen Namen dann als font. Im Browser holen Sie zuerst die Bytes und übergeben sie:

<!-- Report.vue -->
<script setup lang="ts">
import { Document, Page, Text } from "@jasy/vue";

defineProps<{ font: Uint8Array }>();
</script>

<template>
  <Document :fonts="{ Inter: font }" font="Inter">
    <Page size="A4" :margin="48">
      <Text :size="24" bold>Set in Inter</Text>
    </Page>
  </Document>
</template>
const font = new Uint8Array(await (await fetch("/fonts/Inter.woff2")).arrayBuffer());
const bytes = await renderToPdf(Report, { font });

Das Format spielt keine Rolle: .ttf, .woff und .woff2 sind hier dasselbe - jasy entpackt den Wrapper selbst. Volles Unicode kommt mit, und der Font wird auf dem Weg ins PDF gesubsettet, die Datei bleibt also klein. Für mehrere Schriftstärken registrieren Sie eine Datei pro Stil als { Inter: { normal, bold, italic } }.

Auf dem Server (definePdfHandler in Nuxt oder jeder Node-Prozess) funktioniert auch ein einfacher Pfad - dort ist :fonts="{ Inter: '/path/to/Inter.woff2' }" schon alles. Im Browser gibt es kein Dateisystem, auf das man zeigen könnte, also sind es Bytes.

Bilder

<Image :src> nimmt die Bild-Bytes als Uint8Array, eine URL oder - auf einem Server - einen Pfad. JPEG und PNG funktionieren beide, und die Transparenz eines PNG bleibt erhalten: Es liegt über dem, was dahinter ist, nicht in einem weißen Kasten. Bytes kommen genauso wie ein Font, als Prop:

<template>
  <Image :src="logo" :width="120" :height="40" fit="contain" />
</template>

Eine URL wird geladen, während das Dokument vorbereitet wird, noch vor dem Layout, ihre Größe ist also bekannt, wenn die Seite angeordnet wird.

Render-Optionen

renderToPdf (und renderToPdfString) nehmen die Render-Optionen der Engine als drittes Argument:

const bytes = await renderToPdf(Invoice, props, {
  onOverflow: "warn",
  accessible: true,
  encrypt: { userPassword: "secret" },
  onDiagnostic: (d) => console.log(d.code, d.message),
});
  • onOverflow - was passiert, wenn ein einzelnes unteilbares Element höher als die Seite ist: "error" (Standard) wirft, "warn" platziert es abgeschnitten und protokolliert, "ignore" platziert es abgeschnitten.
  • accessible - ein getaggtes PDF mit Structure Tree für Screenreader, siehe Barrierefreiheit.
  • encrypt - Passwortschutz mit AES-256, siehe Verschlüsselung.
  • onDiagnostic - alles, was die Engine über das Dokument zu sagen hat, ein Aufruf pro Befund, siehe Ausgabeoptionen.

Ein Descriptor für einen Server

toDocumentDescriptor(Component, props) hält vor dem PDF an und gibt den Descriptor der Engine zurück: einen einfachen Baum { type, props, children }, den buildDocument zum Dokument macht. Um ihn als JSON zu senden, kodieren Sie Font- und Bild-Bytes selbst - JSON kennt kein Uint8Array - und dekodieren sie auf der anderen Seite. Funktionen überleben JSON gar nicht, ein Dokument mit <Canvas>, <Flow> oder <PageBuilder> geht also über renderToPdf.