Do it in two steps from the coach: (1) build the PDF, (2) attach it to the same instance so that the Document List control on the coach shows it right away.
Server side (recommended) - a service flow with a Java integration (Apache PDFBox jar as a managed server file) builds the PDF from the coach data and creates the document:
// Java: InvoicePdf.build(String json) -> Base64 PDF (PDFBox)
public static String build(String json) throws Exception {
JSONObject inv = new JSONObject(json);
PDDocument doc = new PDDocument(); PDPage page = new PDPage(PDRectangle.A4); doc.addPage(page);
PDPageContentStream cs = new PDPageContentStream(doc, page);
cs.setFont(PDType1Font.HELVETICA_BOLD, 16); cs.beginText(); cs.newLineAtOffset(50, 790); cs.showText("Invoice " + inv.getString("number")); cs.endText();
float y = 750; cs.setFont(PDType1Font.HELVETICA, 11);
for (Object o : inv.getJSONArray("lines")) { JSONObject l = (JSONObject) o;
cs.beginText(); cs.newLineAtOffset(50, y); cs.showText(l.getString("item") + " " + l.getString("qty") + " x " + l.getString("price")); cs.endText(); y -= 16; }
cs.close(); ByteArrayOutputStream out = new ByteArrayOutputStream(); doc.save(out); doc.close();
return Base64.getEncoder().encodeToString(out.toByteArray());
}// service flow "Create invoice PDF": input tw.local.invoice (BO), output tw.local.docId
// 1. script: tw.local.json = JSON.stringify(tw.local.invoice.toJSON ? tw.local.invoice.toJSON() : tw.local.invoice);
// 2. Java integration: pdfBase64 = InvoicePdf.build(json)
// 3. script: attach to the current instance (BPM document store)
var doc = tw.system.createDocument(
"Invoice-" + tw.local.invoice.number + ".pdf", // name
"application/pdf", // MIME type
tw.local.pdfBase64, true, // content, isBase64
tw.system.currentProcessInstanceID); // attach to this instance (BAW 20+: optional properties list as last argument)
tw.local.docId = doc.id;
In the CSHS call that service flow from a button (a Service Call control with the invoice object as input); on its result event refresh the Document List control (${DocList}.refresh()) - the new PDF appears and can be opened / downloaded.
Client side only (no Java): jsPDF + html2canvas in a custom coach view turn the rendered form into a PDF blob, then ${Uploader}.uploadFile(blob, "invoice.pdf") with the UI Toolkit File Uploader stores it as a document of the instance. Good for quick wins; layout is what the browser rendered.
ECM (FileNet) instead of the BPM document store: replace step 3 by the ECM Create Document integration (System Data / Content toolkit: tw.system.ecm / "Create Document" service) with the folder of the case or a fixed folder, and use the ECM Document List control on the coach.
References