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:
pdf.render()rendert das aktuelle Formular nach PDF..deliver(dateiname)legt die Bytes zum Abruf bereit und liefert die Adresse.- 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.
Vorbedingungen:
ctx.formIdmuss 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:
- 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
fsweg. - 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.