Zum Inhalt

pdf und delivery — PDF-Erzeugung & Datei-Auslieferung

KCForms kann das aktuelle Formular als PDF erzeugen und die Datei über eine kurzlebige Download-Adresse an den Browser ausliefern — ohne dass sie vorher irgendwo gespeichert werden muss. Die zwei Objekte dafür:

  • pdf — erzeugt das PDF des aktuellen Formulars.
  • delivery — nimmt beliebige Bytes entgegen und liefert eine Download-Adresse, die nach einer Weile verfällt.

Der übliche Weg

// onSubmit
var url = pdf.render().deliver("Bestaetigung.pdf");
// url == "/api/deliveries/<token>/Bestaetigung.pdf"

view.add(new view.Message(
  'html:Ihre Bestätigung steht bereit: '
  + '<a href="' + url + '" target="_blank">PDF anzeigen</a>',
  "Bestätigung",
  { modal: true, closable: true, okButton: true }
));

Drei Schritte:

  1. pdf.render() rendert das aktuelle Formular nach PDF.
  2. .deliver(dateiname) legt die Bytes zum Abruf bereit und liefert die Adresse.
  3. Die Adresse kommt in einen Dialog; der Anwender klickt, der Browser öffnet die Datei.

Kürzer geht der Dialog mit view.add(new view.Link(url, 'PDF anzeigen')) — siehe view.

pdf.render() — Formular nach PDF

pdf.render() braucht keinen Parameter. Es liest die aktuelle Formular-Definition aus ctx.formId und rendert mit ctx.form als Werten:

var p = pdf.render();
log.info("PDF-Größe: " + p.bytes.length + " Bytes");
log.info("Default-Filename: " + p.defaultFilename);

Optional nimmt pdf.render(lang) eine Ausdrucks-Sprache entgegen ("de"/"sv"/"en" bzw. jede im Formular gepflegte Sprache) — Feld-Labels, Seiten-Titel sowie ${i18n:…}- und ${date|…}-Makros folgen dann dieser Sprache. Vorrang: lang-Argument → ctx.docLang → ctx.lang (siehe ctx.docLang). Dasselbe Argument kennt auch mp.addOutput(name, format, type, cid, lang) für den PDF-Anhang einer Mail.

var url = pdf.render("sv").deliver("Utskrift.pdf");   // schwedischer Ausdruck

Vorbedingungen:

  • ctx.formId muss gesetzt sein. In der Skript-Konsole ohne Formular-Bezug läuft das nicht und produziert eine sprechende Fehlermeldung.
  • Gedruckt wird der aktuelle Stand von ctx.form — auch ungespeicherte Änderungen und Formulare ohne eigene Tabelle.

Was im PDF erscheint:

Element Im PDF?
Eingabefelder mit Wert ja
Detail-Tabellen (kc:childTable) ja, als Tabelle
kc:image / kc:svg ja, eingebettet
kc:text ja, als Leittext
kc:section ja, als Überschrift mit Trennlinie
kc:button nein — nur Bedienelement
kc:spacer nein — nur Abstand im Formular
kc:link nein — im Druck nicht anklickbar

Mehr zu Aufbau, Kopf-/Fußzeilen und Seitenumbrüchen: Druck & PDF.

Das Dokument — was ein Erzeuger zurückgibt

pdf.render() gibt kein rohes Byte-Array zurück, sondern ein Dokument: die fertige Datei im Speicher, die sich selbst ausliefern kann.

Member Typ Zweck
.bytes byte[] der rohe Dateiinhalt
.name String vorgeschlagener Dateiname
.defaultFilename String Alias auf .name (ältere Schreibweise)
.mime String Dateityp
.deliver() → URL unter dem vorgeschlagenen Namen ausliefern
.deliver(dateiname) → URL mit explizitem Namen
.deliver({ name, mime }) → URL Optionen-Map

Dasselbe Dokument gibt es auch von docx und zip

Damit sich alle Wege gleich lesen, liefern auch docx.document(…) und zip.archive(…) dieses Objekt:

pdf.render().deliver('Antrag.pdf');
docx.document(vorlage, daten).deliver('Brief.docx');
zip.archive(dateien).deliver('unterlagen.zip');

Die älteren docx.render(…) und zip.create(…) geben weiterhin rohe Bytes zurück — praktisch, wenn du sie direkt an mail.send({attachments:[{bytes: …}]}) weiterreichst. Beide Wege bleiben.

Einmal erzeugen, zweimal nutzen — Mail und Download

Die Bytes lassen sich ohne erneutes Erzeugen mehrfach verwenden:

var p = pdf.render();
var name = "Bestellung-" + ctx.instanceId + ".pdf";

// 1. PDF an Mail anhängen
var mp = new mail.MessageMultipart();
mp.addText("Bitte siehe Anhang.");
mp.addBytes(name, p.bytes, "application/pdf", null);

var msg = new mail.Message();
msg.to      = ctx.user.email;
msg.subject = "Bestellbestätigung";
msg.content = mp;
mail.send(msg);

// 2. Gleichzeitig im Browser anzeigen
view.add(new view.Link(p.deliver(name), "PDF öffnen"));

Rohe Bytes ausliefern: delivery.file(bytes, …)

Wenn du andere Bytes ausliefern willst — z. B. eine selbst zusammengebaute CSV oder eine Datei, die du gerade aus fs[…] gelesen hast:

var csvBytes = new csv.StandardPrinter().println(["a", "b"]).toByteArray();
var url = delivery.file(csvBytes, { name: "Export.csv", mime: "text/csv" });

Frühere Schreibweise: delivery.deliver(…)

Der Aufruf hieß delivery.deliver(…) und nannte damit zweimal dasselbe. Er funktioniert unverändert weiter — file ist nur der kürzere Name für denselben Vorgang.

Optionen:

Schlüssel Default Bedeutung
name "download" Dateiname beim Speichern
filename — Alias für name (akzeptiert, wenn name fehlt)
mime "application/octet-stream" Dateityp (MIME)

Bequemere Overloads:

var bytes = new csv.StandardPrinter().println(["a", "b"]).toByteArray();

delivery.file(bytes, "Export.csv");           // nur Name; MIME=octet-stream
delivery.file(bytes);                         // beides Default — selten gewollt

Adresse und Gültigkeit

Die zurückgegebene URL ist relativ:

/api/deliveries/<random-token>/<filename>
  • Der Token ist eine zufällige, schwer zu erratende ID — nur wer die URL hat, kann die Datei abrufen.
  • Der Dateiname am Ende bestimmt nur den Namen beim Speichern.
  • Die Adresse ist relativ; in einem Link (<a href="…">) ergänzt der Browser den Server selbst.
  • Gültigkeit: Nach 30 Minuten (einstellbar über kcforms.delivery.ttl-seconds) wird die Datei verworfen; ein späterer Klick endet mit „nicht gefunden" (404).

Fehlerbilder

Symptom Ursache
pdf.render(): ctx.formId ist nicht gesetzt Skript läuft ohne Formular-Bezug (Konsole, Job, REST-Service)
pdf.render(): Formular '...' nicht gefunden formId zeigt auf eine gelöschte Definition
404 beim Klick auf den Download-Link Adresse abgelaufen — PDF neu erzeugen
Leeres PDF Kein Wert in ctx.form und keine Felder mit Default — Skript hat ggf. nichts gesetzt

Sicherheit

  • Download-Adressen sind nicht an eine Anmeldung gebunden — wer die Adresse kennt, bekommt die Datei. Deshalb ist der Token zufällig und die Gültigkeit kurz.
  • Die Dateien liegen nur im Arbeitsspeicher; nach einem Neustart des Servers sind alle Download-Adressen ungültig. Wer eine Datei dauerhaft braucht, legt sie als Dateianlage am Datensatz ab oder schreibt sie mit fs weg.
  • Im Browser angezeigt werden nur PDF, PNG, JPEG, GIF, WebP sowie reiner Text und CSV. Alle anderen Typen — auch HTML und SVG — kommen als Download. Grund: Den Typ bestimmt das Skript; würde es einen hochgeladenen Anhang als HTML ausliefern, liefe dessen Inhalt sonst mit der Sitzung des Betrachters auf der KCForms-Adresse.