commit 1d8ca0b908a1c27c6003368b72df7de1373c2130 Author: chris Date: Tue Jun 9 22:13:00 2026 +0200 Initialer Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a5a2467 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +target/ +*.class +*.log +*.tmp +.idea/ +.vscode/ +*.iml +.DS_Store +Thumbs.db + +# Runtime/config - use config-example/ for examples +printservice/ +logs/ +spool/ +preview/ +archive/ +printers.json +auth.json +templates/ + +# Windows service local install copy +service/*.exe diff --git a/README.md b/README.md new file mode 100644 index 0000000..80a9e58 --- /dev/null +++ b/README.md @@ -0,0 +1,586 @@ +# Printservice Standalone Java 25 v2.3 + +Eigenständiger Druckservice ohne GlassFish/Payara. Die Anwendung läuft als normale Java-Applikation oder über WinSW als Windows-Dienst und stellt ein Webfrontend plus REST-API bereit. + +## Funktionen + +- eigener HTTP-Server aus dem JDK +- Webfrontend unter `http://localhost:8080/` +- REST-API unter `http://localhost:8080/api` +- Drucker-Aliase für `WINDOWS_SPOOLER` und `RAW_TCP` +- Windows-/LocalSystem-Assistent für Dienstbetrieb +- HTML-Vorlagen mit Online-Editor +- HTML-Tabellen über `{{#each:items}} ... {{/each}}` +- HTML -> PDF -> Druck +- PDF als Vorlage mit Koordinatenfeldern (`PDF_TEMPLATE`) +- PDF-Tabellen aus JSON-Arrays +- ZPL/EPL/Text RAW-Druck +- DOCX-Vorlagen rendern und herunterladen +- DOCX -> PDF -> Druck optional über `DOCX4J_FO` ohne LibreOffice +- Barcode, QR-Code, DataMatrix und weitere ZXing-Formate +- optionale Authentifizierung: wenn deaktiviert, kann jeder per REST drucken +- Benutzer-/Tokenverwaltung im Webfrontend +- persistente Druckjob-Historie / Druckwarteschlange für Redruck +- Filterung der Druckhistorie nach User, Status, Drucker und Vorlage +- Admin-Vollansicht der Warteschlange mit `full=true` +- produktionsnahe Vorlagenstruktur unter `templates//...` + +## Zielplattform + +- Java 25 +- Maven 3.9 oder neuer empfohlen +- Windows, Linux oder macOS +- Windows-Dienstbetrieb über WinSW + +## Build + +```bash +mvn clean package +``` + +Ergebnis: + +```text +target/printservice-standalone.jar +``` + +## Start als Konsole + +Windows: + +```cmd +java -Dprintservice.config.dir=C:\printservice -jar target\printservice-standalone.jar --host=0.0.0.0 --port=8080 +``` + +Linux: + +```bash +java -Dprintservice.config.dir=/opt/printservice -jar target/printservice-standalone.jar --host=0.0.0.0 --port=8080 +``` + +Danach: + +```text +http://localhost:8080/ +http://localhost:8080/api/diagnostics/system +``` + +## Konfigurationsstruktur + +Empfohlen unter Windows: + +```text +C:\printservice +├── auth.json +├── printers.json +├── queue +│ └── print-jobs.json +├── templates +│ └── +│ ├── template.json +│ ├── document.* +│ ├── fields.json +│ ├── sample-data.json +│ └── print-request.json +├── spool +├── archive +├── preview +└── logs +``` + +Ohne `-Dprintservice.config.dir` nutzt die Anwendung: + +```text +%USERPROFILE%\.printservice +``` + +## Windows-Service mit WinSW + +```powershell +mvn clean package +.\scripts\install-service.ps1 -InstallDir C:\printservice-app -ConfigDir C:\printservice -Port 8080 +``` + +Danach `WinSW-x64.exe` als `C:\printservice-app\printservice.exe` ablegen und starten: + +```powershell +cd C:\printservice-app +.\printservice.exe install +.\printservice.exe start +.\printservice.exe status +``` + +## Authentifizierung + +Standardmäßig ist Auth deaktiviert. Dann kann jeder, der `/api/print` erreicht, drucken. Das ist für Tests praktisch, aber produktiv nur in geschlossenen Netzen sinnvoll. + +```json +{ + "enabled": false, + "protectPrint": true, + "protectRenderedDownload": true, + "protectPreview": false, + "protectAdmin": false, + "headerName": "X-Printservice-Token", + "tokens": [] +} +``` + +### Empfohlener Ablauf im Webfrontend + +1. Webfrontend öffnen. +2. Bereich **Authentifizierung, Benutzer und Token**. +3. Erst einen Admin-User anlegen, Scope `ALL`. +4. Token kopieren und sicher speichern. +5. Danach Auth aktivieren. +6. Optional `protectAdmin` aktivieren. +7. Für Maschinen/Clients eigene User mit Scope `PRINT` anlegen. + +Token werden serverseitig nur als SHA-256-Hash gespeichert. Das Klartext-Token wird nur einmal bei der Erstellung zurückgegeben. + +### REST: Benutzer/Token anlegen + +```bash +curl -X POST http://localhost:8080/api/auth/users \ + -H "Content-Type: application/json" \ + -d @src/main/resources/examples/rest-create-auth-user-request.json +``` + +Beispiel: + +```json +{ + "userName": "line01", + "displayName": "Linie 01", + "tokenName": "line01-print-token", + "enabled": true, + "scopes": ["PRINT", "PREVIEW"] +} +``` + +Antwort enthält das Token einmalig: + +```json +{ + "userName": "line01", + "tokenName": "line01-print-token", + "token": "NUR_EINMAL_SICHTBAR" +} +``` + +### REST: Auth einschalten + +```bash +curl -X PUT http://localhost:8080/api/auth/config \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: ADMIN_TOKEN" \ + -d @src/main/resources/examples/rest-update-auth-config-enabled-request.json +``` + +### REST: Drucken mit Token + +```bash +curl -X POST http://localhost:8080/api/print \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" \ + -d @src/main/resources/examples/rest-print-raw-tcp-zpl-request.json +``` + +Alternativ: + +```text +Authorization: Bearer YOUR_PRINT_TOKEN +``` + +## Druckwarteschlange / Druckhistorie / Redruck + +Jeder echte Druckjob wird unter `queue/print-jobs.json` gespeichert. Gespeichert werden u. a. Job-ID, Benutzer, Drucker, Vorlage, Render-Modus, Status und der Original-PrintRequest. Dadurch kann der Job später erneut gedruckt werden. + +### Eigene Jobs abrufen + +```bash +curl "http://localhost:8080/api/queue?limit=100" \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" +``` + +Wenn Auth aktiv ist, sieht ein normaler Benutzer nur seine eigenen Jobs. + +### Vollständige Liste abrufen + +```bash +curl "http://localhost:8080/api/queue?full=true&limit=500" \ + -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" +``` + +`full=true` benötigt Scope `ADMIN` oder `ALL`. + +### Nach User/Status/Vorlage filtern + +```bash +curl "http://localhost:8080/api/queue?full=true&user=line01&status=SUBMITTED&templateName=serial_label_zpl" \ + -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" +``` + +### Einzelnen Job abrufen + +```bash +curl http://localhost:8080/api/queue/JOB_ID \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" +``` + +### Redruck + +```bash +curl -X POST http://localhost:8080/api/queue/JOB_ID/reprint \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" \ + -d @src/main/resources/examples/rest-reprint-request.json +``` + +Beispiel: + +```json +{ + "copies": 1, + "printerAlias": "LABEL_LINE_01", + "dataOverride": { + "reprintReason": "Etikett beschädigt" + } +} +``` + +Ohne `printerAlias` und ohne `dataOverride` wird der Originaljob unverändert erneut gesendet. + +## Druckerarten + +### WINDOWS_SPOOLER + +Für PDF, HTML->PDF, PDF_TEMPLATE und normale Office-/A4-Drucker. + +```json +{ + "alias": "OFFICE_A4", + "type": "WINDOWS_SPOOLER", + "systemName": "HP_LaserJet_A4", + "description": "A4 Drucker über Windows Spooler" +} +``` + +### RAW_TCP + +Für schnelle Etikettendrucker mit ZPL/EPL/ESC-POS/PCL. + +```json +{ + "alias": "LABEL_LINE_01", + "type": "RAW_TCP", + "host": "192.168.10.50", + "port": 9100, + "language": "ZPL", + "description": "Zebra Etikettendrucker direkt per TCP" +} +``` + +## REST-Endpunkte + +```text +GET /api/diagnostics/system +GET /api/printers/diagnostics +GET /api/printers/scan +GET /api/printers +POST /api/printers +DELETE /api/printers/{alias} +GET /api/templates/storage +GET /api/templates +GET /api/templates/{name} +POST /api/templates +PUT /api/templates/{name} +DELETE /api/templates/{name} +GET /api/barcodes/formats +GET /api/auth/status +GET /api/auth/me +GET /api/auth/users +POST /api/auth/users +DELETE /api/auth/users/{idOrUserName} +GET /api/auth/config +PUT /api/auth/config +POST /api/auth/reload +POST /api/print/preview +POST /api/print/preview/html +POST /api/print/rendered +POST /api/print +GET /api/docx/converters +POST /api/docx/validate +GET /api/queue +GET /api/queue/{jobId} +POST /api/queue/{jobId}/reprint +``` + +## Render-Modi + +| Modus | Bedeutung | +|---|---| +| `AUTO` | Modus wird anhand des Vorlagentyps ermittelt | +| `RAW` | Text/ZPL/EPL direkt ausgeben | +| `PDF` | HTML nach PDF rendern | +| `PDF_TEMPLATE` | PDF-Vorlage mit Koordinatenfeldern befüllen | +| `DOCX` | DOCX befüllen und als DOCX bereitstellen | + +## Barcode-Platzhalter in HTML/ZPL + +```html + + + +``` + +ZPL direkt: + +```zpl +^FO40,145^BCN,80,Y,N,N^FD{{serialNumber}}^FS +^FO40,260^BQN,2,6^FDLA,{{serialNumber}}^FS +``` + +## HTML-Tabelle + +```html + + + {{#each:items}} + + + + + + + + {{/each}} + +
{{text:pos}}{{text:partNumber}}{{text:description}}{{text:quantity}}
+``` + +## PDF_TEMPLATE-Tabelle + +```json +{ + "name": "positionsTable", + "type": "TABLE", + "source": "items", + "page": 1, + "x": 18, + "y": 78, + "width": 174, + "height": 125, + "rowHeight": 13, + "headerHeight": 8, + "maxRows": 9, + "overflow": "CUT", + "fontSize": 7, + "border": true, + "columns": [ + { "title": "Pos.", "field": "pos", "width": 12, "align": "CENTER" }, + { "title": "Artikel", "field": "partNumber", "width": 29 }, + { "title": "Barcode", "type": "BARCODE", "barcodeType": "CODE_128", "field": "serialNumber", "width": 39 } + ] +} +``` + +## DOCX -> PDF -> Druck ohne LibreOffice: DOCX4J_FO + +Ab v2.3 gibt es zusätzlich zum externen LibreOffice-Weg den Java-only-Konverter: + +```json +{ + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO" +} +``` + +Ablauf: + +```text +DOCX-Vorlage +-> Platzhalter/Barcode/QR im DOCX füllen +-> docx4j-export-fo wandelt DOCX nach XSL-FO +-> Apache FOP erzeugt PDF +-> PDFBox druckt das PDF +``` + +Vorteile: + +- keine LibreOffice-Installation notwendig +- läuft komplett in der Java-Anwendung +- gut für einfache DOCX-Vorlagen mit Text, einfachen Tabellen, Bildern und Kopf-/Fußzeilen +- geeignet als optionaler, serverinterner Konverter + +Grenzen: + +- Layouttreue ist nicht so hoch wie bei Word/LibreOffice +- komplexe Word-Layouts, Shapes, Textfelder, SmartArt, exakte Seitenumbrüche und komplizierte Tabellen müssen pro Vorlage geprüft werden +- für Produktionsdruck ist `PDF_TEMPLATE` oder `HTML -> PDF` weiterhin robuster und meist schneller + +### Konverterstatus abrufen + +```bash +curl http://localhost:8080/api/docx/converters +``` + +### DOCX4J_FO mit einer Vorlage validieren + +```bash +curl -X POST "http://localhost:8080/api/docx/validate?converter=DOCX4J_FO" \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-validate-docx4j-fo-request.json +``` + +Beispielrequest: + +```json +{ + "printerAlias": "OFFICE_A4", + "templateName": "serienbegleitschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "includeRenderedBase64": true, + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456", + "workorderNumber": "WO-100200" + } +} +``` + +Die Validierung prüft technisch, ob DOCX -> PDF erfolgreich erzeugt werden kann. Die Layout-Qualität muss anschließend visuell anhand des erzeugten PDFs geprüft werden. + +### DOCX mit DOCX4J_FO drucken + +```bash +curl -X POST http://localhost:8080/api/print \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-print-docx4j-fo-request.json +``` + +```json +{ + "printerAlias": "OFFICE_A4", + "templateName": "serienbegleitschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "copies": 1, + "pdfDpi": 300, + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456", + "workorderNumber": "WO-100200" + } +} +``` + +Empfehlung: `DOCX4J_FO` als optionale Alternative einbauen, aber pro Vorlage in der Druckfreigabe validieren. Für feste Formulare bleibt `PDF_TEMPLATE`; für dynamische Tabellen bleibt `HTML -> PDF`; für Etiketten bleibt `RAW_TCP/ZPL` die erste Wahl. + +## Produktionsempfehlungen + +- `RAW_TCP` für ZPL/EPL-Etikettendrucker verwenden. +- `PDF_TEMPLATE` für feste A4-Formulare verwenden. +- `HTML -> PDF` für flexible Tabellenformulare verwenden. +- Authentifizierung mindestens für `/api/print` aktivieren. +- Admin-Funktionen nach Initialeinrichtung schützen. +- Für Maschinen/Stationen separate Tokens mit eigenem User verwenden. +- `C:\printservice` regelmäßig sichern, besonders `templates`, `printers.json`, `auth.json` und `queue/print-jobs.json`. + + +## HTML→PDF: robuste XHTML-Normalisierung ab v2.3 + +Der HTML→PDF-Druckpfad normalisiert HTML-Vorlagen jetzt vor dem PDF-Renderer automatisch zu XHTML. Hintergrund: `openhtmltopdf` ist deutlich strenger als ein Browser. Normales Browser-HTML wie `
`, ``, fehlende `` oder ein UTF-8-BOM vor `` kann sonst mit einem XML-Fehler abbrechen, zum Beispiel: + +```text +Markup im Dokument vor dem Root-Element muss ordnungsgemäß formatiert sein. +``` + +Der Service führt deshalb vor dem Rendern aus: + +```text +HTML-Vorlage + JSON-Daten +→ Platzhalter und Tabellenblöcke rendern +→ BOM/ungültige Steuerzeichen entfernen +→ HTML-Fragmente bei Bedarf in ... einbetten +→ Browser-HTML mit jsoup zu XHTML normalisieren +→ openhtmltopdf → PDF +→ Druck oder Download +``` + +Zusätzlich gibt es einen Validierungsendpunkt: + +```http +POST /api/html/validate +``` + +Beispiel: + +```bash +curl -X POST http://localhost:8080/api/html/validate \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-validate-html-pdf-request.json +``` + +Eine erfolgreiche Antwort enthält unter anderem: + +```json +{ + "ok": true, + "templateName": "lieferschein_html_table", + "renderMode": "PDF", + "message": "HTML was rendered and normalized to XHTML successfully. It can be passed to openhtmltopdf." +} +``` + +Wenn die Vorlage versehentlich JSON oder PDF-Inhalt enthält, gibt der Service nun eine verständlichere Fehlermeldung aus, statt nur den SAX/XML-Fehler des Renderers weiterzugeben. + + +## v2.4: Lieferschein-Demos und HTML->PDF Fehlerbehebung + +### HTML->PDF Fehler "Markup im Dokument vor dem Root-Element" +Der HTML->PDF Pfad normalisiert HTML jetzt vor dem openhtmltopdf Renderer. Dadurch werden typische Ursachen abgefangen: UTF-8 BOM, HTML-Fragmente ohne Root-Element, nicht XML-konforme Tags wie `
`, `
` oder `` ohne schließenden Slash. + +Validierung vor dem Druck: + +```bash +curl -X POST http://localhost:8080/api/html/validate \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-validate-html-pdf-request.json +``` + +### Einfache PDF-Lieferscheinvorlage + +```text +config-example/templates/lieferschein_pdf_simple/ +├── template.json +├── document.pdf +├── fields.json +├── sample-data.json +└── print-request.json +``` + +### DOCX-Lieferscheinvorlage + +```text +config-example/templates/lieferschein_docx/ +├── template.json +├── document.docx +├── sample-data.json +├── print-request.json +└── validate-request.json +``` + +Barcode-/QR-Platzhalter in DOCX müssen alleine in einem Absatz stehen: + +```text +{{barcode:CODE_128:serialNumber:360x90}} +{{qr:serialNumber}} +``` + +Für Druck ohne LibreOffice verwendet das Beispiel `docxConverter=DOCX4J_FO`. Jede DOCX-Vorlage sollte vor produktiver Nutzung validiert werden. diff --git a/config-example/queue/print-jobs.json b/config-example/queue/print-jobs.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/config-example/queue/print-jobs.json @@ -0,0 +1 @@ +[] diff --git a/docs/DOCX4J_FO_VALIDIERUNG_v2.2.pdf b/docs/DOCX4J_FO_VALIDIERUNG_v2.2.pdf new file mode 100644 index 0000000..c946bbd Binary files /dev/null and b/docs/DOCX4J_FO_VALIDIERUNG_v2.2.pdf differ diff --git a/docs/HTML_PDF_FEHLERBEHEBUNG_v2.3.pdf b/docs/HTML_PDF_FEHLERBEHEBUNG_v2.3.pdf new file mode 100644 index 0000000..0615fee --- /dev/null +++ b/docs/HTML_PDF_FEHLERBEHEBUNG_v2.3.pdf @@ -0,0 +1,105 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/BaseFont /ZapfDingbats /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +5 0 obj +<< +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font +>> +endobj +6 0 obj +<< +/Contents 11 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +7 0 obj +<< +/Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +8 0 obj +<< +/PageMode /UseNone /Pages 10 0 R /Type /Catalog +>> +endobj +9 0 obj +<< +/Author (\(anonymous\)) /CreationDate (D:20260609194150+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260609194150+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False +>> +endobj +10 0 obj +<< +/Count 2 /Kids [ 6 0 R 7 0 R ] /Type /Pages +>> +endobj +11 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2299 +>> +stream +Gat=+CN%tI')h6*0lR^i[l*%R;6"*\\@-16B=G\Pk0je3=XX&Z$HY8ZUOc!8oKcl5qSXho"F(TUH!$+:Yr*:TKB+*'=%5:IXXMM,@)]!AM@WD%lO\nb4L2&`WA\iV-X/9cqE?.g?Egd/%W53/4U7)3"RUGDITkOjVWp'#bg.s$P(k]&9,c5iR0N\B`FiHMd0A]5GZg82HqZGMl-2+=d-mglXE^U7fjK@>*TC,?^*k0aVN>!ZR"p$PF/8MUh*+JRDTM'a<$dLf47@U4'ed<'9:/k,RNC9=0<"O@+bl^cc64-;n$8r9LtaCJ@(hIi=JP#SPFZ(#@,TMR%hPS!*U>$06)#:Goe7i/[4`o?X1lWP[Y1*hiBD?BmtWd;iN7nM$\olBl)%"\6&240"l4[N,Hnrt.VUb9ejo@okN^h#YK6JPfPLfRkW_k"2T;Fa<=]GJS>'psFP@*D+/9W0ZUkc%7_i'mW4-rR,A7C/,2F@::Jc3?\78)N+0ruVcl[/QNk6hWWHS>iX^JSJV^F0VOoWjZ6ceR^&p_'jU)^n4E8+*D?+AIqMVcUCp5WVlB&@.5%%,XuZtbJ&>q1i6(l>Pf+9h5J6gSG9#]0sEHm&g%M;.0i&&PcF;<@Oq@XTm>$P=Whn3,tL_@<3'tm6?;3HCIn)kqGdBV<=U9jIkp4#T4l+j/F6-6P@i@'G)E^K6/f6a^oX^"A*^Da:7iGB=?G'=!Xc%PJ,-ZIR/=CuaY8'Q_7Zb^<52[laEKRhDZ>&:&tWOsdZgHe"$qp9n(@l'huc?SisZ!FOJGi"QmWGjGP)I>"*;I\OfPYLH%6dV8qg+?[U)6N`n:Am1]A1n&n)==@t"iH)".WJ--$sCP6A5'?7n<6Nq7:^.+ZB9/7F!Tg*EKDDMOn9.p5>^^QVV(&W`T&0Ydj9u`9IhGONo&JbTkLGr?[;Yk_<(pHW))%aYkP*]dqkHJ?`UM"50Yq[>*O>Y'_Kn'/i%6uH\JnGQk\6PmoE`!VVf4H6D3T$ZX:-(fYmT2RGRH]0ZqcRDJAP+Kg.C7Tl::W?hU*/((*32"Vgi/H#))>)Wl9O^knS0[)VeXHU*HO?oi];>2*#!I6?9g>*d\G4?5]dJc(nm]YhIBjH8MnFtXhol#AE$VMtc3)?so%?7YMUOP'R#8%C*P%*C.mZm"W.<7BiC7H-!OpsdbUrHf49c^*Aeb%j!9,sr`O>S/@0'M+g*/!!D)Q>2dcGTY_+^K%jHCh8_P"/bS.c24uB(,s!6>jnc`iP,P#BaKVoND[nt[\AIF^CWNrn,Zm9V89#7U^AHD_WY>QcDUf]+fCg$54!4N?Y#Cr6IS$6>4@bVj:9+LbbLinH!UF[F*F?CO-GYf&Fb//L-nT2duLnM$0;:j+i.A#IsEU6E6"XE6jio.qek)8_ZQ2_T8-?4f3eFab7&HS.B>"[8RBbXoC+Ou!r#K8M"6hTeB#BiO^Zbt#MZ[a@&?qM\(J&O\>l!9L]-pR!9-@j9U-Ci!T^:t?hB#DDe$3HXdPRt"`idd73r?USUR8km#UL2_X+QD^Ogendstream +endobj +12 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 507 +>> +stream +GasbU_2ctu&;KY%MZ6dT$#u.e/2pD)5Ur_*#VNg#aWrT8S6^Ks!`-RpK\^\4!KE=7cMZQn4tFQLC4$d;n34nYXb'_#YFs;l7D?ZpI.f6mIlg^]8lfYh+Np(u1/9XIa_(p[B9n]_2aV[$/fo!%=K]YkHUtHl_,;D'mp2u0ZFH!)EfI?f%`s$&\#*h$j4q,GkEXDm`E3^1L7eYCSS$o?L@D$`SSjEGjFXL@+TI;r-0CaN)MUYbEM5AJ=1X(NO29$3"ituK57DrcLD(OuW$F&J6eK"ZnOdJXpXn`fI:+-[TuGJ^WEZOV8X.3aS&*YBL?.(X0RGQtbNdX@jELTUXHdoJ[a_\T\pDl"@V;&E.)jFmbEq'?aDs*#C`,-51u,Kc7qY1*"C'm;56iB4GD:+c18:ZCl1b!Vjb94L>faueoJh6(N>='\p;;PT)mgW&ml0!&hhF46dQ)irE)"C3k(@(@a5V+SWOqiZA.c.<&O4A>~>endstream +endobj +xref +0 13 +0000000000 65535 f +0000000061 00000 n +0000000122 00000 n +0000000229 00000 n +0000000341 00000 n +0000000424 00000 n +0000000529 00000 n +0000000734 00000 n +0000000939 00000 n +0000001008 00000 n +0000001288 00000 n +0000001354 00000 n +0000003745 00000 n +trailer +<< +/ID +[<5fc3771a4bb5cad6c6937da174857a27><5fc3771a4bb5cad6c6937da174857a27>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 9 0 R +/Root 8 0 R +/Size 13 +>> +startxref +4343 +%%EOF diff --git a/docs/LIEFERSCHEIN_DEMOS_v2.4.pdf b/docs/LIEFERSCHEIN_DEMOS_v2.4.pdf new file mode 100644 index 0000000..276604f --- /dev/null +++ b/docs/LIEFERSCHEIN_DEMOS_v2.4.pdf @@ -0,0 +1,74 @@ +%PDF-1.3 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +5 0 obj +<< +/PageMode /UseNone /Pages 7 0 R /Type /Catalog +>> +endobj +6 0 obj +<< +/Author (anonymous) /CreationDate (D:20260609194716+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260609194716+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (unspecified) /Title (Printservice Lieferschein-Demos v2.4) /Trapped /False +>> +endobj +7 0 obj +<< +/Count 1 /Kids [ 4 0 R ] /Type /Pages +>> +endobj +8 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 712 +>> +stream +Gat%`Df;+!&B0s8)kA$AkZCie*h7U2H.#h$45)$h!]OV%ifu.\@^o]^f_#>3)'B<[iLfBL9)C@O$lQpQY3`BNF45GWn.l<`!Y$(Mpcd_HFDrHAba.EVVso>E4=75-KiLPG[om:iW-<3HR9UO6SLWR@=7;[9LH(LMB4L?`0:eR,/;Q]5B]>._=IMRMR6n&V.BbnIcg%CEO[XSXgNHO:[c^-nDf@dhX)VL5O3+3L\Kr::%e\_2K?%G"R.l84#ihfZ+a7\->\f0tA8es62d$P;9lZP[1;cC2kU]gNf5l2)'6.LHo2>-?=%%R4rmDO&C~>endstream +endobj +xref +0 9 +0000000000 65535 f +0000000061 00000 n +0000000102 00000 n +0000000209 00000 n +0000000321 00000 n +0000000524 00000 n +0000000592 00000 n +0000000881 00000 n +0000000940 00000 n +trailer +<< +/ID +[<3a690545ba6eb066f85a2dabe118b139><3a690545ba6eb066f85a2dabe118b139>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 6 0 R +/Root 5 0 R +/Size 9 +>> +startxref +1742 +%%EOF diff --git a/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION.md b/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION.md new file mode 100644 index 0000000..98ef839 --- /dev/null +++ b/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION.md @@ -0,0 +1,540 @@ +# Printservice Standalone Java 25 v2.3 + +Eigenständiger Druckservice ohne GlassFish/Payara. Die Anwendung läuft als normale Java-Applikation oder über WinSW als Windows-Dienst und stellt ein Webfrontend plus REST-API bereit. + +## Funktionen + +- eigener HTTP-Server aus dem JDK +- Webfrontend unter `http://localhost:8080/` +- REST-API unter `http://localhost:8080/api` +- Drucker-Aliase für `WINDOWS_SPOOLER` und `RAW_TCP` +- Windows-/LocalSystem-Assistent für Dienstbetrieb +- HTML-Vorlagen mit Online-Editor +- HTML-Tabellen über `{{#each:items}} ... {{/each}}` +- HTML -> PDF -> Druck +- PDF als Vorlage mit Koordinatenfeldern (`PDF_TEMPLATE`) +- PDF-Tabellen aus JSON-Arrays +- ZPL/EPL/Text RAW-Druck +- DOCX-Vorlagen rendern und herunterladen +- DOCX -> PDF -> Druck optional über `DOCX4J_FO` ohne LibreOffice +- Barcode, QR-Code, DataMatrix und weitere ZXing-Formate +- optionale Authentifizierung: wenn deaktiviert, kann jeder per REST drucken +- Benutzer-/Tokenverwaltung im Webfrontend +- persistente Druckjob-Historie / Druckwarteschlange für Redruck +- Filterung der Druckhistorie nach User, Status, Drucker und Vorlage +- Admin-Vollansicht der Warteschlange mit `full=true` +- produktionsnahe Vorlagenstruktur unter `templates//...` + +## Zielplattform + +- Java 25 +- Maven 3.9 oder neuer empfohlen +- Windows, Linux oder macOS +- Windows-Dienstbetrieb über WinSW + +## Build + +```bash +mvn clean package +``` + +Ergebnis: + +```text +target/printservice-standalone.jar +``` + +## Start als Konsole + +Windows: + +```cmd +java -Dprintservice.config.dir=C:\printservice -jar target\printservice-standalone.jar --host=0.0.0.0 --port=8080 +``` + +Linux: + +```bash +java -Dprintservice.config.dir=/opt/printservice -jar target/printservice-standalone.jar --host=0.0.0.0 --port=8080 +``` + +Danach: + +```text +http://localhost:8080/ +http://localhost:8080/api/diagnostics/system +``` + +## Konfigurationsstruktur + +Empfohlen unter Windows: + +```text +C:\printservice +├── auth.json +├── printers.json +├── queue +│ └── print-jobs.json +├── templates +│ └── +│ ├── template.json +│ ├── document.* +│ ├── fields.json +│ ├── sample-data.json +│ └── print-request.json +├── spool +├── archive +├── preview +└── logs +``` + +Ohne `-Dprintservice.config.dir` nutzt die Anwendung: + +```text +%USERPROFILE%\.printservice +``` + +## Windows-Service mit WinSW + +```powershell +mvn clean package +.\scripts\install-service.ps1 -InstallDir C:\printservice-app -ConfigDir C:\printservice -Port 8080 +``` + +Danach `WinSW-x64.exe` als `C:\printservice-app\printservice.exe` ablegen und starten: + +```powershell +cd C:\printservice-app +.\printservice.exe install +.\printservice.exe start +.\printservice.exe status +``` + +## Authentifizierung + +Standardmäßig ist Auth deaktiviert. Dann kann jeder, der `/api/print` erreicht, drucken. Das ist für Tests praktisch, aber produktiv nur in geschlossenen Netzen sinnvoll. + +```json +{ + "enabled": false, + "protectPrint": true, + "protectRenderedDownload": true, + "protectPreview": false, + "protectAdmin": false, + "headerName": "X-Printservice-Token", + "tokens": [] +} +``` + +### Empfohlener Ablauf im Webfrontend + +1. Webfrontend öffnen. +2. Bereich **Authentifizierung, Benutzer und Token**. +3. Erst einen Admin-User anlegen, Scope `ALL`. +4. Token kopieren und sicher speichern. +5. Danach Auth aktivieren. +6. Optional `protectAdmin` aktivieren. +7. Für Maschinen/Clients eigene User mit Scope `PRINT` anlegen. + +Token werden serverseitig nur als SHA-256-Hash gespeichert. Das Klartext-Token wird nur einmal bei der Erstellung zurückgegeben. + +### REST: Benutzer/Token anlegen + +```bash +curl -X POST http://localhost:8080/api/auth/users \ + -H "Content-Type: application/json" \ + -d @src/main/resources/examples/rest-create-auth-user-request.json +``` + +Beispiel: + +```json +{ + "userName": "line01", + "displayName": "Linie 01", + "tokenName": "line01-print-token", + "enabled": true, + "scopes": ["PRINT", "PREVIEW"] +} +``` + +Antwort enthält das Token einmalig: + +```json +{ + "userName": "line01", + "tokenName": "line01-print-token", + "token": "NUR_EINMAL_SICHTBAR" +} +``` + +### REST: Auth einschalten + +```bash +curl -X PUT http://localhost:8080/api/auth/config \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: ADMIN_TOKEN" \ + -d @src/main/resources/examples/rest-update-auth-config-enabled-request.json +``` + +### REST: Drucken mit Token + +```bash +curl -X POST http://localhost:8080/api/print \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" \ + -d @src/main/resources/examples/rest-print-raw-tcp-zpl-request.json +``` + +Alternativ: + +```text +Authorization: Bearer YOUR_PRINT_TOKEN +``` + +## Druckwarteschlange / Druckhistorie / Redruck + +Jeder echte Druckjob wird unter `queue/print-jobs.json` gespeichert. Gespeichert werden u. a. Job-ID, Benutzer, Drucker, Vorlage, Render-Modus, Status und der Original-PrintRequest. Dadurch kann der Job später erneut gedruckt werden. + +### Eigene Jobs abrufen + +```bash +curl "http://localhost:8080/api/queue?limit=100" \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" +``` + +Wenn Auth aktiv ist, sieht ein normaler Benutzer nur seine eigenen Jobs. + +### Vollständige Liste abrufen + +```bash +curl "http://localhost:8080/api/queue?full=true&limit=500" \ + -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" +``` + +`full=true` benötigt Scope `ADMIN` oder `ALL`. + +### Nach User/Status/Vorlage filtern + +```bash +curl "http://localhost:8080/api/queue?full=true&user=line01&status=SUBMITTED&templateName=serial_label_zpl" \ + -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" +``` + +### Einzelnen Job abrufen + +```bash +curl http://localhost:8080/api/queue/JOB_ID \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" +``` + +### Redruck + +```bash +curl -X POST http://localhost:8080/api/queue/JOB_ID/reprint \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" \ + -d @src/main/resources/examples/rest-reprint-request.json +``` + +Beispiel: + +```json +{ + "copies": 1, + "printerAlias": "LABEL_LINE_01", + "dataOverride": { + "reprintReason": "Etikett beschädigt" + } +} +``` + +Ohne `printerAlias` und ohne `dataOverride` wird der Originaljob unverändert erneut gesendet. + +## Druckerarten + +### WINDOWS_SPOOLER + +Für PDF, HTML->PDF, PDF_TEMPLATE und normale Office-/A4-Drucker. + +```json +{ + "alias": "OFFICE_A4", + "type": "WINDOWS_SPOOLER", + "systemName": "HP_LaserJet_A4", + "description": "A4 Drucker über Windows Spooler" +} +``` + +### RAW_TCP + +Für schnelle Etikettendrucker mit ZPL/EPL/ESC-POS/PCL. + +```json +{ + "alias": "LABEL_LINE_01", + "type": "RAW_TCP", + "host": "192.168.10.50", + "port": 9100, + "language": "ZPL", + "description": "Zebra Etikettendrucker direkt per TCP" +} +``` + +## REST-Endpunkte + +```text +GET /api/diagnostics/system +GET /api/printers/diagnostics +GET /api/printers/scan +GET /api/printers +POST /api/printers +DELETE /api/printers/{alias} +GET /api/templates/storage +GET /api/templates +GET /api/templates/{name} +POST /api/templates +PUT /api/templates/{name} +DELETE /api/templates/{name} +GET /api/barcodes/formats +GET /api/auth/status +GET /api/auth/me +GET /api/auth/users +POST /api/auth/users +DELETE /api/auth/users/{idOrUserName} +GET /api/auth/config +PUT /api/auth/config +POST /api/auth/reload +POST /api/print/preview +POST /api/print/preview/html +POST /api/print/rendered +POST /api/print +GET /api/docx/converters +POST /api/docx/validate +GET /api/queue +GET /api/queue/{jobId} +POST /api/queue/{jobId}/reprint +``` + +## Render-Modi + +| Modus | Bedeutung | +|---|---| +| `AUTO` | Modus wird anhand des Vorlagentyps ermittelt | +| `RAW` | Text/ZPL/EPL direkt ausgeben | +| `PDF` | HTML nach PDF rendern | +| `PDF_TEMPLATE` | PDF-Vorlage mit Koordinatenfeldern befüllen | +| `DOCX` | DOCX befüllen und als DOCX bereitstellen | + +## Barcode-Platzhalter in HTML/ZPL + +```html + + + +``` + +ZPL direkt: + +```zpl +^FO40,145^BCN,80,Y,N,N^FD{{serialNumber}}^FS +^FO40,260^BQN,2,6^FDLA,{{serialNumber}}^FS +``` + +## HTML-Tabelle + +```html + + + {{#each:items}} + + + + + + + + {{/each}} + +
{{text:pos}}{{text:partNumber}}{{text:description}}{{text:quantity}}
+``` + +## PDF_TEMPLATE-Tabelle + +```json +{ + "name": "positionsTable", + "type": "TABLE", + "source": "items", + "page": 1, + "x": 18, + "y": 78, + "width": 174, + "height": 125, + "rowHeight": 13, + "headerHeight": 8, + "maxRows": 9, + "overflow": "CUT", + "fontSize": 7, + "border": true, + "columns": [ + { "title": "Pos.", "field": "pos", "width": 12, "align": "CENTER" }, + { "title": "Artikel", "field": "partNumber", "width": 29 }, + { "title": "Barcode", "type": "BARCODE", "barcodeType": "CODE_128", "field": "serialNumber", "width": 39 } + ] +} +``` + +## DOCX -> PDF -> Druck ohne LibreOffice: DOCX4J_FO + +Ab v2.3 gibt es zusätzlich zum externen LibreOffice-Weg den Java-only-Konverter: + +```json +{ + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO" +} +``` + +Ablauf: + +```text +DOCX-Vorlage +-> Platzhalter/Barcode/QR im DOCX füllen +-> docx4j-export-fo wandelt DOCX nach XSL-FO +-> Apache FOP erzeugt PDF +-> PDFBox druckt das PDF +``` + +Vorteile: + +- keine LibreOffice-Installation notwendig +- läuft komplett in der Java-Anwendung +- gut für einfache DOCX-Vorlagen mit Text, einfachen Tabellen, Bildern und Kopf-/Fußzeilen +- geeignet als optionaler, serverinterner Konverter + +Grenzen: + +- Layouttreue ist nicht so hoch wie bei Word/LibreOffice +- komplexe Word-Layouts, Shapes, Textfelder, SmartArt, exakte Seitenumbrüche und komplizierte Tabellen müssen pro Vorlage geprüft werden +- für Produktionsdruck ist `PDF_TEMPLATE` oder `HTML -> PDF` weiterhin robuster und meist schneller + +### Konverterstatus abrufen + +```bash +curl http://localhost:8080/api/docx/converters +``` + +### DOCX4J_FO mit einer Vorlage validieren + +```bash +curl -X POST "http://localhost:8080/api/docx/validate?converter=DOCX4J_FO" \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-validate-docx4j-fo-request.json +``` + +Beispielrequest: + +```json +{ + "printerAlias": "OFFICE_A4", + "templateName": "serienbegleitschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "includeRenderedBase64": true, + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456", + "workorderNumber": "WO-100200" + } +} +``` + +Die Validierung prüft technisch, ob DOCX -> PDF erfolgreich erzeugt werden kann. Die Layout-Qualität muss anschließend visuell anhand des erzeugten PDFs geprüft werden. + +### DOCX mit DOCX4J_FO drucken + +```bash +curl -X POST http://localhost:8080/api/print \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-print-docx4j-fo-request.json +``` + +```json +{ + "printerAlias": "OFFICE_A4", + "templateName": "serienbegleitschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "copies": 1, + "pdfDpi": 300, + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456", + "workorderNumber": "WO-100200" + } +} +``` + +Empfehlung: `DOCX4J_FO` als optionale Alternative einbauen, aber pro Vorlage in der Druckfreigabe validieren. Für feste Formulare bleibt `PDF_TEMPLATE`; für dynamische Tabellen bleibt `HTML -> PDF`; für Etiketten bleibt `RAW_TCP/ZPL` die erste Wahl. + +## Produktionsempfehlungen + +- `RAW_TCP` für ZPL/EPL-Etikettendrucker verwenden. +- `PDF_TEMPLATE` für feste A4-Formulare verwenden. +- `HTML -> PDF` für flexible Tabellenformulare verwenden. +- Authentifizierung mindestens für `/api/print` aktivieren. +- Admin-Funktionen nach Initialeinrichtung schützen. +- Für Maschinen/Stationen separate Tokens mit eigenem User verwenden. +- `C:\printservice` regelmäßig sichern, besonders `templates`, `printers.json`, `auth.json` und `queue/print-jobs.json`. + + +## HTML→PDF: robuste XHTML-Normalisierung ab v2.3 + +Der HTML→PDF-Druckpfad normalisiert HTML-Vorlagen jetzt vor dem PDF-Renderer automatisch zu XHTML. Hintergrund: `openhtmltopdf` ist deutlich strenger als ein Browser. Normales Browser-HTML wie `
`, ``, fehlende `` oder ein UTF-8-BOM vor `` kann sonst mit einem XML-Fehler abbrechen, zum Beispiel: + +```text +Markup im Dokument vor dem Root-Element muss ordnungsgemäß formatiert sein. +``` + +Der Service führt deshalb vor dem Rendern aus: + +```text +HTML-Vorlage + JSON-Daten +→ Platzhalter und Tabellenblöcke rendern +→ BOM/ungültige Steuerzeichen entfernen +→ HTML-Fragmente bei Bedarf in ... einbetten +→ Browser-HTML mit jsoup zu XHTML normalisieren +→ openhtmltopdf → PDF +→ Druck oder Download +``` + +Zusätzlich gibt es einen Validierungsendpunkt: + +```http +POST /api/html/validate +``` + +Beispiel: + +```bash +curl -X POST http://localhost:8080/api/html/validate \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-validate-html-pdf-request.json +``` + +Eine erfolgreiche Antwort enthält unter anderem: + +```json +{ + "ok": true, + "templateName": "lieferschein_html_table", + "renderMode": "PDF", + "message": "HTML was rendered and normalized to XHTML successfully. It can be passed to openhtmltopdf." +} +``` + +Wenn die Vorlage versehentlich JSON oder PDF-Inhalt enthält, gibt der Service nun eine verständlichere Fehlermeldung aus, statt nur den SAX/XML-Fehler des Renderers weiterzugeben. diff --git a/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION.pdf b/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION.pdf new file mode 100644 index 0000000..eee42c8 Binary files /dev/null and b/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION.pdf differ diff --git a/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION_v2.2.pdf b/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION_v2.2.pdf new file mode 100644 index 0000000..eee42c8 Binary files /dev/null and b/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION_v2.2.pdf differ diff --git a/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION_v2.4.md b/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION_v2.4.md new file mode 100644 index 0000000..80a9e58 --- /dev/null +++ b/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION_v2.4.md @@ -0,0 +1,586 @@ +# Printservice Standalone Java 25 v2.3 + +Eigenständiger Druckservice ohne GlassFish/Payara. Die Anwendung läuft als normale Java-Applikation oder über WinSW als Windows-Dienst und stellt ein Webfrontend plus REST-API bereit. + +## Funktionen + +- eigener HTTP-Server aus dem JDK +- Webfrontend unter `http://localhost:8080/` +- REST-API unter `http://localhost:8080/api` +- Drucker-Aliase für `WINDOWS_SPOOLER` und `RAW_TCP` +- Windows-/LocalSystem-Assistent für Dienstbetrieb +- HTML-Vorlagen mit Online-Editor +- HTML-Tabellen über `{{#each:items}} ... {{/each}}` +- HTML -> PDF -> Druck +- PDF als Vorlage mit Koordinatenfeldern (`PDF_TEMPLATE`) +- PDF-Tabellen aus JSON-Arrays +- ZPL/EPL/Text RAW-Druck +- DOCX-Vorlagen rendern und herunterladen +- DOCX -> PDF -> Druck optional über `DOCX4J_FO` ohne LibreOffice +- Barcode, QR-Code, DataMatrix und weitere ZXing-Formate +- optionale Authentifizierung: wenn deaktiviert, kann jeder per REST drucken +- Benutzer-/Tokenverwaltung im Webfrontend +- persistente Druckjob-Historie / Druckwarteschlange für Redruck +- Filterung der Druckhistorie nach User, Status, Drucker und Vorlage +- Admin-Vollansicht der Warteschlange mit `full=true` +- produktionsnahe Vorlagenstruktur unter `templates//...` + +## Zielplattform + +- Java 25 +- Maven 3.9 oder neuer empfohlen +- Windows, Linux oder macOS +- Windows-Dienstbetrieb über WinSW + +## Build + +```bash +mvn clean package +``` + +Ergebnis: + +```text +target/printservice-standalone.jar +``` + +## Start als Konsole + +Windows: + +```cmd +java -Dprintservice.config.dir=C:\printservice -jar target\printservice-standalone.jar --host=0.0.0.0 --port=8080 +``` + +Linux: + +```bash +java -Dprintservice.config.dir=/opt/printservice -jar target/printservice-standalone.jar --host=0.0.0.0 --port=8080 +``` + +Danach: + +```text +http://localhost:8080/ +http://localhost:8080/api/diagnostics/system +``` + +## Konfigurationsstruktur + +Empfohlen unter Windows: + +```text +C:\printservice +├── auth.json +├── printers.json +├── queue +│ └── print-jobs.json +├── templates +│ └── +│ ├── template.json +│ ├── document.* +│ ├── fields.json +│ ├── sample-data.json +│ └── print-request.json +├── spool +├── archive +├── preview +└── logs +``` + +Ohne `-Dprintservice.config.dir` nutzt die Anwendung: + +```text +%USERPROFILE%\.printservice +``` + +## Windows-Service mit WinSW + +```powershell +mvn clean package +.\scripts\install-service.ps1 -InstallDir C:\printservice-app -ConfigDir C:\printservice -Port 8080 +``` + +Danach `WinSW-x64.exe` als `C:\printservice-app\printservice.exe` ablegen und starten: + +```powershell +cd C:\printservice-app +.\printservice.exe install +.\printservice.exe start +.\printservice.exe status +``` + +## Authentifizierung + +Standardmäßig ist Auth deaktiviert. Dann kann jeder, der `/api/print` erreicht, drucken. Das ist für Tests praktisch, aber produktiv nur in geschlossenen Netzen sinnvoll. + +```json +{ + "enabled": false, + "protectPrint": true, + "protectRenderedDownload": true, + "protectPreview": false, + "protectAdmin": false, + "headerName": "X-Printservice-Token", + "tokens": [] +} +``` + +### Empfohlener Ablauf im Webfrontend + +1. Webfrontend öffnen. +2. Bereich **Authentifizierung, Benutzer und Token**. +3. Erst einen Admin-User anlegen, Scope `ALL`. +4. Token kopieren und sicher speichern. +5. Danach Auth aktivieren. +6. Optional `protectAdmin` aktivieren. +7. Für Maschinen/Clients eigene User mit Scope `PRINT` anlegen. + +Token werden serverseitig nur als SHA-256-Hash gespeichert. Das Klartext-Token wird nur einmal bei der Erstellung zurückgegeben. + +### REST: Benutzer/Token anlegen + +```bash +curl -X POST http://localhost:8080/api/auth/users \ + -H "Content-Type: application/json" \ + -d @src/main/resources/examples/rest-create-auth-user-request.json +``` + +Beispiel: + +```json +{ + "userName": "line01", + "displayName": "Linie 01", + "tokenName": "line01-print-token", + "enabled": true, + "scopes": ["PRINT", "PREVIEW"] +} +``` + +Antwort enthält das Token einmalig: + +```json +{ + "userName": "line01", + "tokenName": "line01-print-token", + "token": "NUR_EINMAL_SICHTBAR" +} +``` + +### REST: Auth einschalten + +```bash +curl -X PUT http://localhost:8080/api/auth/config \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: ADMIN_TOKEN" \ + -d @src/main/resources/examples/rest-update-auth-config-enabled-request.json +``` + +### REST: Drucken mit Token + +```bash +curl -X POST http://localhost:8080/api/print \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" \ + -d @src/main/resources/examples/rest-print-raw-tcp-zpl-request.json +``` + +Alternativ: + +```text +Authorization: Bearer YOUR_PRINT_TOKEN +``` + +## Druckwarteschlange / Druckhistorie / Redruck + +Jeder echte Druckjob wird unter `queue/print-jobs.json` gespeichert. Gespeichert werden u. a. Job-ID, Benutzer, Drucker, Vorlage, Render-Modus, Status und der Original-PrintRequest. Dadurch kann der Job später erneut gedruckt werden. + +### Eigene Jobs abrufen + +```bash +curl "http://localhost:8080/api/queue?limit=100" \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" +``` + +Wenn Auth aktiv ist, sieht ein normaler Benutzer nur seine eigenen Jobs. + +### Vollständige Liste abrufen + +```bash +curl "http://localhost:8080/api/queue?full=true&limit=500" \ + -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" +``` + +`full=true` benötigt Scope `ADMIN` oder `ALL`. + +### Nach User/Status/Vorlage filtern + +```bash +curl "http://localhost:8080/api/queue?full=true&user=line01&status=SUBMITTED&templateName=serial_label_zpl" \ + -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" +``` + +### Einzelnen Job abrufen + +```bash +curl http://localhost:8080/api/queue/JOB_ID \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" +``` + +### Redruck + +```bash +curl -X POST http://localhost:8080/api/queue/JOB_ID/reprint \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" \ + -d @src/main/resources/examples/rest-reprint-request.json +``` + +Beispiel: + +```json +{ + "copies": 1, + "printerAlias": "LABEL_LINE_01", + "dataOverride": { + "reprintReason": "Etikett beschädigt" + } +} +``` + +Ohne `printerAlias` und ohne `dataOverride` wird der Originaljob unverändert erneut gesendet. + +## Druckerarten + +### WINDOWS_SPOOLER + +Für PDF, HTML->PDF, PDF_TEMPLATE und normale Office-/A4-Drucker. + +```json +{ + "alias": "OFFICE_A4", + "type": "WINDOWS_SPOOLER", + "systemName": "HP_LaserJet_A4", + "description": "A4 Drucker über Windows Spooler" +} +``` + +### RAW_TCP + +Für schnelle Etikettendrucker mit ZPL/EPL/ESC-POS/PCL. + +```json +{ + "alias": "LABEL_LINE_01", + "type": "RAW_TCP", + "host": "192.168.10.50", + "port": 9100, + "language": "ZPL", + "description": "Zebra Etikettendrucker direkt per TCP" +} +``` + +## REST-Endpunkte + +```text +GET /api/diagnostics/system +GET /api/printers/diagnostics +GET /api/printers/scan +GET /api/printers +POST /api/printers +DELETE /api/printers/{alias} +GET /api/templates/storage +GET /api/templates +GET /api/templates/{name} +POST /api/templates +PUT /api/templates/{name} +DELETE /api/templates/{name} +GET /api/barcodes/formats +GET /api/auth/status +GET /api/auth/me +GET /api/auth/users +POST /api/auth/users +DELETE /api/auth/users/{idOrUserName} +GET /api/auth/config +PUT /api/auth/config +POST /api/auth/reload +POST /api/print/preview +POST /api/print/preview/html +POST /api/print/rendered +POST /api/print +GET /api/docx/converters +POST /api/docx/validate +GET /api/queue +GET /api/queue/{jobId} +POST /api/queue/{jobId}/reprint +``` + +## Render-Modi + +| Modus | Bedeutung | +|---|---| +| `AUTO` | Modus wird anhand des Vorlagentyps ermittelt | +| `RAW` | Text/ZPL/EPL direkt ausgeben | +| `PDF` | HTML nach PDF rendern | +| `PDF_TEMPLATE` | PDF-Vorlage mit Koordinatenfeldern befüllen | +| `DOCX` | DOCX befüllen und als DOCX bereitstellen | + +## Barcode-Platzhalter in HTML/ZPL + +```html + + + +``` + +ZPL direkt: + +```zpl +^FO40,145^BCN,80,Y,N,N^FD{{serialNumber}}^FS +^FO40,260^BQN,2,6^FDLA,{{serialNumber}}^FS +``` + +## HTML-Tabelle + +```html + + + {{#each:items}} + + + + + + + + {{/each}} + +
{{text:pos}}{{text:partNumber}}{{text:description}}{{text:quantity}}
+``` + +## PDF_TEMPLATE-Tabelle + +```json +{ + "name": "positionsTable", + "type": "TABLE", + "source": "items", + "page": 1, + "x": 18, + "y": 78, + "width": 174, + "height": 125, + "rowHeight": 13, + "headerHeight": 8, + "maxRows": 9, + "overflow": "CUT", + "fontSize": 7, + "border": true, + "columns": [ + { "title": "Pos.", "field": "pos", "width": 12, "align": "CENTER" }, + { "title": "Artikel", "field": "partNumber", "width": 29 }, + { "title": "Barcode", "type": "BARCODE", "barcodeType": "CODE_128", "field": "serialNumber", "width": 39 } + ] +} +``` + +## DOCX -> PDF -> Druck ohne LibreOffice: DOCX4J_FO + +Ab v2.3 gibt es zusätzlich zum externen LibreOffice-Weg den Java-only-Konverter: + +```json +{ + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO" +} +``` + +Ablauf: + +```text +DOCX-Vorlage +-> Platzhalter/Barcode/QR im DOCX füllen +-> docx4j-export-fo wandelt DOCX nach XSL-FO +-> Apache FOP erzeugt PDF +-> PDFBox druckt das PDF +``` + +Vorteile: + +- keine LibreOffice-Installation notwendig +- läuft komplett in der Java-Anwendung +- gut für einfache DOCX-Vorlagen mit Text, einfachen Tabellen, Bildern und Kopf-/Fußzeilen +- geeignet als optionaler, serverinterner Konverter + +Grenzen: + +- Layouttreue ist nicht so hoch wie bei Word/LibreOffice +- komplexe Word-Layouts, Shapes, Textfelder, SmartArt, exakte Seitenumbrüche und komplizierte Tabellen müssen pro Vorlage geprüft werden +- für Produktionsdruck ist `PDF_TEMPLATE` oder `HTML -> PDF` weiterhin robuster und meist schneller + +### Konverterstatus abrufen + +```bash +curl http://localhost:8080/api/docx/converters +``` + +### DOCX4J_FO mit einer Vorlage validieren + +```bash +curl -X POST "http://localhost:8080/api/docx/validate?converter=DOCX4J_FO" \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-validate-docx4j-fo-request.json +``` + +Beispielrequest: + +```json +{ + "printerAlias": "OFFICE_A4", + "templateName": "serienbegleitschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "includeRenderedBase64": true, + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456", + "workorderNumber": "WO-100200" + } +} +``` + +Die Validierung prüft technisch, ob DOCX -> PDF erfolgreich erzeugt werden kann. Die Layout-Qualität muss anschließend visuell anhand des erzeugten PDFs geprüft werden. + +### DOCX mit DOCX4J_FO drucken + +```bash +curl -X POST http://localhost:8080/api/print \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-print-docx4j-fo-request.json +``` + +```json +{ + "printerAlias": "OFFICE_A4", + "templateName": "serienbegleitschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "copies": 1, + "pdfDpi": 300, + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456", + "workorderNumber": "WO-100200" + } +} +``` + +Empfehlung: `DOCX4J_FO` als optionale Alternative einbauen, aber pro Vorlage in der Druckfreigabe validieren. Für feste Formulare bleibt `PDF_TEMPLATE`; für dynamische Tabellen bleibt `HTML -> PDF`; für Etiketten bleibt `RAW_TCP/ZPL` die erste Wahl. + +## Produktionsempfehlungen + +- `RAW_TCP` für ZPL/EPL-Etikettendrucker verwenden. +- `PDF_TEMPLATE` für feste A4-Formulare verwenden. +- `HTML -> PDF` für flexible Tabellenformulare verwenden. +- Authentifizierung mindestens für `/api/print` aktivieren. +- Admin-Funktionen nach Initialeinrichtung schützen. +- Für Maschinen/Stationen separate Tokens mit eigenem User verwenden. +- `C:\printservice` regelmäßig sichern, besonders `templates`, `printers.json`, `auth.json` und `queue/print-jobs.json`. + + +## HTML→PDF: robuste XHTML-Normalisierung ab v2.3 + +Der HTML→PDF-Druckpfad normalisiert HTML-Vorlagen jetzt vor dem PDF-Renderer automatisch zu XHTML. Hintergrund: `openhtmltopdf` ist deutlich strenger als ein Browser. Normales Browser-HTML wie `
`, ``, fehlende `` oder ein UTF-8-BOM vor `` kann sonst mit einem XML-Fehler abbrechen, zum Beispiel: + +```text +Markup im Dokument vor dem Root-Element muss ordnungsgemäß formatiert sein. +``` + +Der Service führt deshalb vor dem Rendern aus: + +```text +HTML-Vorlage + JSON-Daten +→ Platzhalter und Tabellenblöcke rendern +→ BOM/ungültige Steuerzeichen entfernen +→ HTML-Fragmente bei Bedarf in ... einbetten +→ Browser-HTML mit jsoup zu XHTML normalisieren +→ openhtmltopdf → PDF +→ Druck oder Download +``` + +Zusätzlich gibt es einen Validierungsendpunkt: + +```http +POST /api/html/validate +``` + +Beispiel: + +```bash +curl -X POST http://localhost:8080/api/html/validate \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-validate-html-pdf-request.json +``` + +Eine erfolgreiche Antwort enthält unter anderem: + +```json +{ + "ok": true, + "templateName": "lieferschein_html_table", + "renderMode": "PDF", + "message": "HTML was rendered and normalized to XHTML successfully. It can be passed to openhtmltopdf." +} +``` + +Wenn die Vorlage versehentlich JSON oder PDF-Inhalt enthält, gibt der Service nun eine verständlichere Fehlermeldung aus, statt nur den SAX/XML-Fehler des Renderers weiterzugeben. + + +## v2.4: Lieferschein-Demos und HTML->PDF Fehlerbehebung + +### HTML->PDF Fehler "Markup im Dokument vor dem Root-Element" +Der HTML->PDF Pfad normalisiert HTML jetzt vor dem openhtmltopdf Renderer. Dadurch werden typische Ursachen abgefangen: UTF-8 BOM, HTML-Fragmente ohne Root-Element, nicht XML-konforme Tags wie `
`, `
` oder `` ohne schließenden Slash. + +Validierung vor dem Druck: + +```bash +curl -X POST http://localhost:8080/api/html/validate \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-validate-html-pdf-request.json +``` + +### Einfache PDF-Lieferscheinvorlage + +```text +config-example/templates/lieferschein_pdf_simple/ +├── template.json +├── document.pdf +├── fields.json +├── sample-data.json +└── print-request.json +``` + +### DOCX-Lieferscheinvorlage + +```text +config-example/templates/lieferschein_docx/ +├── template.json +├── document.docx +├── sample-data.json +├── print-request.json +└── validate-request.json +``` + +Barcode-/QR-Platzhalter in DOCX müssen alleine in einem Absatz stehen: + +```text +{{barcode:CODE_128:serialNumber:360x90}} +{{qr:serialNumber}} +``` + +Für Druck ohne LibreOffice verwendet das Beispiel `docxConverter=DOCX4J_FO`. Jede DOCX-Vorlage sollte vor produktiver Nutzung validiert werden. diff --git a/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION_v2.4.pdf b/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION_v2.4.pdf new file mode 100644 index 0000000..6ac2f92 Binary files /dev/null and b/docs/PRINTSERVICE_STANDALONE_DOKUMENTATION_v2.4.pdf differ diff --git a/examples/curl-print-authenticated.sh b/examples/curl-print-authenticated.sh new file mode 100644 index 0000000..7386b11 --- /dev/null +++ b/examples/curl-print-authenticated.sh @@ -0,0 +1,10 @@ +curl -X POST http://localhost:8080/api/print \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: CHANGE_ME_PRINT_TOKEN" \ + -d '{ + "printerAlias":"LABEL_LINE_01", + "templateName":"serial_label_zpl", + "renderMode":"RAW", + "copies":1, + "data":{"serialNumber":"SNR000001","partNumber":"A123456"} + }' diff --git a/examples/rest-print-authenticated-request.http b/examples/rest-print-authenticated-request.http new file mode 100644 index 0000000..97a6308 --- /dev/null +++ b/examples/rest-print-authenticated-request.http @@ -0,0 +1,14 @@ +POST http://localhost:8080/api/print +Content-Type: application/json +X-Printservice-Token: CHANGE_ME_PRINT_TOKEN + +{ + "printerAlias": "LABEL_LINE_01", + "templateName": "serial_label_zpl", + "renderMode": "RAW", + "copies": 1, + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456" + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..62eabbe --- /dev/null +++ b/pom.xml @@ -0,0 +1,118 @@ + + + 4.0.0 + + de.hehl + printservice-standalone + 2.4.0 + jar + + Printservice Standalone Java 25 + Eigenstaendige Java-25-Printservice-Applikation mit Webfrontend, REST API, Drucker-Aliasen, HTML/PDF/DOCX/ZPL Vorlagen, PDF_TEMPLATE-Koordinaten, Tabellen, Barcode/QR, RAW_TCP, Windows-Service-Skripten und Druck-Authentifizierung, Benutzer-/Tokenverwaltung und persistenter Druckjob-Historie fuer Reprint und optionaler DOCX4J-FO-DOCX-nach-PDF-Konvertierung. + + + 25 + UTF-8 + 3.5.4 + 1.1.37 + 5.5.1 + 3.0.5 + 2.20.0 + 11.5.14 + 1.18.3 + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + + + + com.google.zxing + core + ${zxing.version} + + + com.google.zxing + javase + ${zxing.version} + + + + io.github.openhtmltopdf + openhtmltopdf-pdfbox + ${openhtmltopdf.version} + + + + org.jsoup + jsoup + ${jsoup.version} + + + + org.apache.pdfbox + pdfbox + ${pdfbox.version} + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + org.docx4j + docx4j-export-fo + ${docx4j.version} + + + org.docx4j + docx4j-JAXB-ReferenceImpl + ${docx4j.version} + + + + + printservice-standalone + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.1 + + ${maven.compiler.release} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.1 + + + package + shade + + false + + + de.hehl.printservice.standalone.PrintServiceApplication + + + + + + + + + diff --git a/scripts/install-service.ps1 b/scripts/install-service.ps1 new file mode 100644 index 0000000..8563d14 --- /dev/null +++ b/scripts/install-service.ps1 @@ -0,0 +1,22 @@ +param( + [string]$InstallDir = "C:\printservice-app", + [string]$ConfigDir = "C:\printservice", + [int]$Port = 8080, + [string]$Jar = "$PSScriptRoot\..\target\printservice-standalone.jar" +) +New-Item -ItemType Directory -Force -Path $InstallDir, "$InstallDir\logs", $ConfigDir | Out-Null +Copy-Item $Jar "$InstallDir\printservice-standalone.jar" -Force +Copy-Item "$PSScriptRoot\..\service\printservice.xml" "$InstallDir\printservice.xml" -Force +(Get-Content "$InstallDir\printservice.xml") ` + -replace 'C:\\printservice', ($ConfigDir -replace '\\','\\') ` + -replace '--port=8080', "--port=$Port" | Set-Content "$InstallDir\printservice.xml" -Encoding UTF8 +Write-Host "Bitte WinSW-x64.exe als $InstallDir\printservice.exe ablegen, falls noch nicht vorhanden." +if (Test-Path "$InstallDir\printservice.exe") { + Push-Location $InstallDir + .\printservice.exe install + .\printservice.exe start + Pop-Location +} else { + Write-Host "Download WinSW, benenne es in printservice.exe um und fuehre danach aus:" + Write-Host "cd $InstallDir; .\printservice.exe install; .\printservice.exe start" +} diff --git a/scripts/start-console.bat b/scripts/start-console.bat new file mode 100644 index 0000000..e6e0881 --- /dev/null +++ b/scripts/start-console.bat @@ -0,0 +1,6 @@ +@echo off +setlocal +set BASE=%~dp0.. +if not defined PRINTSERVICE_CONFIG_DIR set PRINTSERVICE_CONFIG_DIR=C:\printservice +if not defined PRINTSERVICE_PORT set PRINTSERVICE_PORT=8080 +java -Dprintservice.config.dir=%PRINTSERVICE_CONFIG_DIR% -jar "%BASE%\target\printservice-standalone.jar" --host=0.0.0.0 --port=%PRINTSERVICE_PORT% diff --git a/scripts/start-console.ps1 b/scripts/start-console.ps1 new file mode 100644 index 0000000..1800c37 --- /dev/null +++ b/scripts/start-console.ps1 @@ -0,0 +1,6 @@ +param( + [string]$ConfigDir = "C:\printservice", + [int]$Port = 8080, + [string]$Jar = "$PSScriptRoot\..\target\printservice-standalone.jar" +) +java "-Dprintservice.config.dir=$ConfigDir" -jar $Jar --host=0.0.0.0 --port=$Port diff --git a/scripts/uninstall-service.ps1 b/scripts/uninstall-service.ps1 new file mode 100644 index 0000000..9126d4c --- /dev/null +++ b/scripts/uninstall-service.ps1 @@ -0,0 +1,9 @@ +param([string]$InstallDir = "C:\printservice-app") +Push-Location $InstallDir +if (Test-Path ".\printservice.exe") { + .\printservice.exe stop + .\printservice.exe uninstall +} else { + Write-Host "printservice.exe nicht gefunden. WinSW-Datei pruefen." +} +Pop-Location diff --git a/service/printservice.xml b/service/printservice.xml new file mode 100644 index 0000000..49ba4d7 --- /dev/null +++ b/service/printservice.xml @@ -0,0 +1,16 @@ + + PrintserviceStandalone + Printservice Standalone + Eigenständiger Java-25 Printservice mit Webfrontend und REST API. + %BASE%\runtime\bin\java.exe + -Dprintservice.config.dir=C:\printservice -jar "%BASE%\printservice-standalone.jar" --host=0.0.0.0 --port=8080 + %BASE%\logs + + 10485760 + 10 + + + Automatic + 15 sec + 1 sec + diff --git a/src/main/resources/examples/lieferschein_docx_rendered_example.docx b/src/main/resources/examples/lieferschein_docx_rendered_example.docx new file mode 100644 index 0000000..8f48d90 Binary files /dev/null and b/src/main/resources/examples/lieferschein_docx_rendered_example.docx differ diff --git a/src/main/resources/examples/lieferschein_docx_rendered_example.pdf b/src/main/resources/examples/lieferschein_docx_rendered_example.pdf new file mode 100644 index 0000000..ad3b5db Binary files /dev/null and b/src/main/resources/examples/lieferschein_docx_rendered_example.pdf differ diff --git a/src/main/resources/examples/lieferschein_docx_template.docx b/src/main/resources/examples/lieferschein_docx_template.docx new file mode 100644 index 0000000..81f2b25 Binary files /dev/null and b/src/main/resources/examples/lieferschein_docx_template.docx differ diff --git a/src/main/resources/examples/lieferschein_html_table.html.tpl b/src/main/resources/examples/lieferschein_html_table.html.tpl new file mode 100644 index 0000000..8ee8148 --- /dev/null +++ b/src/main/resources/examples/lieferschein_html_table.html.tpl @@ -0,0 +1,58 @@ + + + + + Lieferschein HTML Tabelle + + + +
+
+

Lieferschein

+
Nr.: {{text:deliveryNumber}}
+
Datum: {{text:deliveryDate}}
+
+ QR +
+ +
Kunde: {{text:customer.name}}
+ + + + + + + + + + + + + + {{#each:items}} + + + + + + + + + {{/each}} + +
Pos.ArtikelBezeichnungMengeSeriennummerBarcode
{{text:pos}}{{text:partNumber}}{{text:description}}{{text:quantity}}{{text:serialNumber}}Barcode
+ + diff --git a/src/main/resources/examples/lieferschein_html_table_rendered_example.pdf b/src/main/resources/examples/lieferschein_html_table_rendered_example.pdf new file mode 100644 index 0000000..ff055d6 --- /dev/null +++ b/src/main/resources/examples/lieferschein_html_table_rendered_example.pdf @@ -0,0 +1,74 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +5 0 obj +<< +/PageMode /UseNone /Pages 7 0 R /Type /Catalog +>> +endobj +6 0 obj +<< +/Author (\(anonymous\)) /CreationDate (D:20260608180457+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260608180457+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False +>> +endobj +7 0 obj +<< +/Count 1 /Kids [ 4 0 R ] /Type /Pages +>> +endobj +8 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1266 +>> +stream +Gb"/'9lo&I&A@C2i2C8-gKe4WeQ^h)m^,a;`&dUTfW_L1Uo1RVSa!?HK%(TJ#u[04g)5_lBa?i6?!nKG#a.NbW;XE4PWGG8mscNM>"rlL0sI*OWje2>+!Xr3fn'SI=aA>dOhH^;DJZqS]9+eoMH#=36DtWQ9W3*2UDP*l';U+uUW/YLFIS`^lh`N@^Jg?"C@XufoeGIA8%Ee'@J#9f4kn\%j4'N6?,5gDdSD`LhnTc)]lq80k?)"rSS?UA$k]K+bhfEkO5G>Vc)[2K0E=/T,BjUnF^Et$Q\4/>T4b-#(Y+kEQh)h'<3KAM'4;(o&1MB>ph3C2&:*[(UUL1K5'7q/,dJ7i=]rI0L=M\%@+___9iAI(HJr-+gb!+k*LpYs"`9b^jagb#6(8(Hlr&gmH[:GF'1Y:Dn*>7I\(?L\&g3%XE%CBTI@'Zu)q$uFIQ7@-C8Ru5k>de9rS\k#A(c7I@XW-sT5O?u6kW(7;&bV7eC:7j,I1u2pM833XZI_bL0D)XpV.[0q/]FU^8,A,VYbFXSP=ag$BI`SU#];D59[1%kO*j[.;1==)o:bUZ].#hKCja)Sl%"T_7$]:C/M)iMEd=cUrF:5Zid&=440B-`.9l2WGF',!p9cU@V^'%.#X*ZU?I]D^lGqgOn!rAJh@sOK!)$42D\1ZABLPFjNsHJRsk\6m4DF.2L_dZuBE&pBASTaVhDa\BO3TH8Cf^49inX4tJW[%D7*_&$tDtSj$+L2tJ8YAuHl"=D*@d04h!pLPXjHRiATHk[@j/i5Eb7$u9Gu)Bq,s1F_3T=:"Pl!sd`T-n-lK`N9:^_4&BKSBEBKXdt53=OTP^KGcV'mlo-;'gQm7njH&t+s)F<\&8/H^PHY`(:Jg-,>8-^bNsNU2k'H>%9]_Z?b5DbA5,o)1TNM\K_-DuYendstream +endobj +xref +0 9 +0000000000 65535 f +0000000061 00000 n +0000000102 00000 n +0000000209 00000 n +0000000321 00000 n +0000000524 00000 n +0000000592 00000 n +0000000872 00000 n +0000000931 00000 n +trailer +<< +/ID +[<03ba57a25334291279fbbf61957007c7><03ba57a25334291279fbbf61957007c7>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 6 0 R +/Root 5 0 R +/Size 9 +>> +startxref +2288 +%%EOF diff --git a/src/main/resources/examples/lieferschein_pdf_fields.json b/src/main/resources/examples/lieferschein_pdf_fields.json new file mode 100644 index 0000000..1b4a393 --- /dev/null +++ b/src/main/resources/examples/lieferschein_pdf_fields.json @@ -0,0 +1,128 @@ +{ + "coordinateUnit": "mm", + "coordinateOrigin": "TOP_LEFT", + "pdfFields": [ + { + "name": "serialNumber", + "type": "TEXT", + "value": "{{serialNumber}}", + "page": 1, + "x": 35, + "y": 52, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "partNumber", + "type": "TEXT", + "value": "{{partNumber}}", + "page": 1, + "x": 35, + "y": 60, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "workorderNumber", + "type": "TEXT", + "value": "{{workorderNumber}}", + "page": 1, + "x": 35, + "y": 68, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "quantity", + "type": "TEXT", + "value": "{{quantity}}", + "page": 1, + "x": 35, + "y": 76, + "width": 70, + "height": 6, + "fontSize": 10, + "align": "RIGHT" + }, + { + "name": "customer", + "type": "TEXT", + "value": "{{customer.name}}", + "page": 1, + "x": 35, + "y": 84, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "serialBarcode", + "type": "BARCODE", + "barcodeType": "CODE_128", + "value": "{{serialNumber}}", + "page": 1, + "x": 25, + "y": 94, + "width": 90, + "height": 18 + }, + { + "name": "serialQr", + "type": "QR_CODE", + "value": "{{serialNumber}}", + "page": 1, + "x": 155, + "y": 38, + "width": 32, + "height": 32 + }, + { + "name": "item1Pos", + "type": "TEXT", + "value": "{{items.0.pos}}", + "page": 1, + "x": 22, + "y": 142, + "width": 15, + "height": 5, + "fontSize": 9 + }, + { + "name": "item1Material", + "type": "TEXT", + "value": "{{items.0.material}}", + "page": 1, + "x": 40, + "y": 142, + "width": 55, + "height": 5, + "fontSize": 9 + }, + { + "name": "item1Description", + "type": "TEXT", + "value": "{{items.0.description}}", + "page": 1, + "x": 100, + "y": 142, + "width": 60, + "height": 5, + "fontSize": 9 + }, + { + "name": "item1Quantity", + "type": "TEXT", + "value": "{{items.0.quantity}}", + "page": 1, + "x": 165, + "y": 142, + "width": 20, + "height": 5, + "fontSize": 9, + "align": "RIGHT" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/examples/lieferschein_pdf_simple_fields.json b/src/main/resources/examples/lieferschein_pdf_simple_fields.json new file mode 100644 index 0000000..99733c0 --- /dev/null +++ b/src/main/resources/examples/lieferschein_pdf_simple_fields.json @@ -0,0 +1,128 @@ +{ + "coordinateUnit": "mm", + "coordinateOrigin": "TOP_LEFT", + "pdfFields": [ + { + "name": "deliveryNumber", + "type": "TEXT", + "value": "{{deliveryNumber}}", + "page": 1, + "x": 55, + "y": 30, + "width": 55, + "height": 8, + "fontSize": 10 + }, + { + "name": "deliveryDate", + "type": "TEXT", + "value": "{{deliveryDate}}", + "page": 1, + "x": 145, + "y": 30, + "width": 47, + "height": 8, + "fontSize": 10 + }, + { + "name": "customerName", + "type": "TEXT", + "value": "{{customer.name}}", + "page": 1, + "x": 45, + "y": 50, + "width": 147, + "height": 8, + "fontSize": 10 + }, + { + "name": "customerAddress", + "type": "TEXT", + "value": "{{customer.address}}", + "page": 1, + "x": 45, + "y": 63, + "width": 147, + "height": 8, + "fontSize": 9 + }, + { + "name": "workorderNumber", + "type": "TEXT", + "value": "{{workorderNumber}}", + "page": 1, + "x": 55, + "y": 87, + "width": 55, + "height": 8, + "fontSize": 10 + }, + { + "name": "partNumber", + "type": "TEXT", + "value": "{{partNumber}}", + "page": 1, + "x": 55, + "y": 101, + "width": 55, + "height": 8, + "fontSize": 10 + }, + { + "name": "quantity", + "type": "TEXT", + "value": "{{quantity}}", + "page": 1, + "x": 145, + "y": 101, + "width": 47, + "height": 8, + "fontSize": 10, + "align": "RIGHT" + }, + { + "name": "serialNumber", + "type": "TEXT", + "value": "{{serialNumber}}", + "page": 1, + "x": 55, + "y": 115, + "width": 70, + "height": 8, + "fontSize": 10 + }, + { + "name": "serialBarcode", + "type": "BARCODE", + "barcodeType": "CODE_128", + "value": "{{serialNumber}}", + "page": 1, + "x": 18, + "y": 135, + "width": 100, + "height": 25 + }, + { + "name": "serialQr", + "type": "QR_CODE", + "value": "{{serialNumber}}", + "page": 1, + "x": 145, + "y": 125, + "width": 35, + "height": 35 + }, + { + "name": "note", + "type": "TEXT", + "value": "{{note}}", + "page": 1, + "x": 18, + "y": 205, + "width": 174, + "height": 18, + "fontSize": 9, + "multiline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/examples/lieferschein_pdf_simple_rendered_example.pdf b/src/main/resources/examples/lieferschein_pdf_simple_rendered_example.pdf new file mode 100644 index 0000000..ddef77d --- /dev/null +++ b/src/main/resources/examples/lieferschein_pdf_simple_rendered_example.pdf @@ -0,0 +1,80 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 4 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/BaseFont /Times-Roman /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +5 0 obj +<< +/Contents 9 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +6 0 obj +<< +/PageMode /UseNone /Pages 8 0 R /Type /Catalog +>> +endobj +7 0 obj +<< +/Author (anonymous) /CreationDate (D:20260609194637+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260609194637+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (unspecified) /Title (Lieferschein PDF_TEMPLATE Demo - gerendert) /Trapped /False +>> +endobj +8 0 obj +<< +/Count 1 /Kids [ 5 0 R ] /Type /Pages +>> +endobj +9 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1941 +>> +stream +Gatn)gMRrh&;KZH'YJ77R-".j]=9j8Xck)lJ6Xp"Tu==5`7h(=#mL1tSR%(>bAsOR*$8`Eq9ZVPSf.R49/RHQ>Ioa8it#)GMGV%flY'E]9oM^e1X%89(VIt2Z:jsO1+'Q#NBE(n?@u&_/goQ(+bg"D.kS#T=V)QYL8T)VtooL4Pd%!\9=nEet9$4_&/U2\h^U[:%YR95=7[lcop8B:i0C\pco(sNUG'37=V'"JppKGBS(K#OkZ$PRj==_[)K@mZfNE;n5?9hpK047E.3$WP3_80#h:pW#u3/$KbfR]]gL1k.OE5O">TT5<$e6,g;?lc0N=bo&/"Z99#W%%WoF-K&6dC-kE%5hT75eo.Z,3,m4&?ndn$!8&AFr$s9J;&8j_]GV5Dd`h;S_(0O,1f+BgS+uU^JGRHIUO9(r.6NV>43oq6:k"L&LCk@>?fa!-mXpX'U!/(9>d;+lp-G[0V'*.3t*TZb5C3HT_Wu6W?C(>QP+d,5]T!03n]:b[F!8#\HWCatlFb_n+-;.KhHO-8lY49t$)[NUK^)Z_Jk<$p\`k[d,[[]&mjPs];-NCQr8"jFg.."YA1Z;-I-H*=)*>%M;K@-r.ALhbfYgV#)ACA/'Z$S9U,Vnd>.GNom#ZneYTOMV9AKHXd_\\"D4db#Nlb[ROcmJA^D.k?4%o&XLo;t;#),#!`o-;6T_>>_@HTG&gVBo,)bC8*s04J?:Nig('t&OYXf>0MS(^&TLeELp]a/t(53sdrB`paT^ikOXKkp!Sje7XB.rrh>GnA2?P'#q^b<>r.1VphHX)R-PAQ+=ga;5u>#*Bi?3DfAJ7mHtV`<0UJR>p\&[4mo^Knm?O4Tu):;9;l@%gWU=XB,a=e!aTO!p<,4eXosO2."cF4]4W]C+.+]omm*d/`YPih8c#r=Hrkj/5YT1eQe:qhS2NPB73FRZ!eXgIROhD\P*jbH@s7*:n`7E`ZHZOK+e*qZDLHfD:-!Pu9\*<6o[[Qa4bJ-*(qL?ujAp)]k2YdkU&"G3~>endstream +endobj +xref +0 10 +0000000000 65535 f +0000000061 00000 n +0000000112 00000 n +0000000219 00000 n +0000000331 00000 n +0000000440 00000 n +0000000643 00000 n +0000000711 00000 n +0000001006 00000 n +0000001065 00000 n +trailer +<< +/ID +[<756205e546b68225b6eb780b6d82060b><756205e546b68225b6eb780b6d82060b>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 7 0 R +/Root 6 0 R +/Size 10 +>> +startxref +3097 +%%EOF diff --git a/src/main/resources/examples/lieferschein_pdf_simple_template.pdf b/src/main/resources/examples/lieferschein_pdf_simple_template.pdf new file mode 100644 index 0000000..2e05dca --- /dev/null +++ b/src/main/resources/examples/lieferschein_pdf_simple_template.pdf @@ -0,0 +1,74 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +5 0 obj +<< +/PageMode /UseNone /Pages 7 0 R /Type /Catalog +>> +endobj +6 0 obj +<< +/Author (anonymous) /CreationDate (D:20260609194637+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260609194637+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (unspecified) /Title (Lieferschein PDF_TEMPLATE Demo) /Trapped /False +>> +endobj +7 0 obj +<< +/Count 1 /Kids [ 4 0 R ] /Type /Pages +>> +endobj +8 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 997 +>> +stream +Gau1-gN(as&;KZF'RL,p?$&88;Gpo"/Vi='/fBjhJn]$;fkIA5l+80%[ZcH3daBD/.$<\"I9QSQM$&ZShL87@JGfUW^#d$5,W"?c'dQ:gOGo:EjosLVR#bF-!5TS1kQS5XGHj"q-DV5]X[2!$LEqXf7iBQ"<0JDg(!e'!$-A*#f)YShI"q(8E7-`,?@d5[[Up9Ae+i=>jP]g^dHfin`fugo#DZk.VHK&4<6T#1V6D\u?1U(%2r\d2.Q3s-8)lGeTTqj;f0'N:dSJ^W\?YdHP?.gF[Gm7hmsPId1BGBe[6c=1]r<$MoeO9f0FS`ecoWOX_;V\edX;2YRhVreq0EsXWpZ'aa-!]?_&Gu@:0Cl!b?5k6q0blS;/n*JAY#C?U0-RRN>9#V%l]0^<6XfBA/q]c/&<#/)&7W?B[@(+cj7BYa$)r.l*As3<]&t('+KT*FUZkVk=fZq;IP4mlQ4G++1Df[pC%;UGfHKAGF7fg\CIa*=AR#G6`X=!-]chLf+MCq9kf`\;n2b0beI)d^>;+dXYbdCKj!rrN&R-#6m8(J6>"1_3ZUTE_&NnYs%LXj7BLu+YniU715>7taqJAi16c*K0O>s0b]h!hAQ>)E$s9>$@us^RWgt>L',QbpD<,fmmco;"m`hf75TPd):@5,g/F'Cao0jNlA*p/_5Wc0;V*NAgNgZ/"c"%?5QA57@gMb?K_sOqPqeMT8F]Mj54M/a[OPDosc&oMolnX?6D2B_Xmr\*8r!rC)7XI^.bc%NJhIf8O@34D[lb!XaFA<*K.Fdu&pendstream +endobj +xref +0 9 +0000000000 65535 f +0000000061 00000 n +0000000102 00000 n +0000000209 00000 n +0000000321 00000 n +0000000524 00000 n +0000000592 00000 n +0000000875 00000 n +0000000934 00000 n +trailer +<< +/ID +[] +% ReportLab generated PDF document -- digest (opensource) + +/Info 6 0 R +/Root 5 0 R +/Size 9 +>> +startxref +2021 +%%EOF diff --git a/src/main/resources/examples/lieferschein_pdf_table_fields.json b/src/main/resources/examples/lieferschein_pdf_table_fields.json new file mode 100644 index 0000000..cc3b5f7 --- /dev/null +++ b/src/main/resources/examples/lieferschein_pdf_table_fields.json @@ -0,0 +1,111 @@ +{ + "coordinateUnit": "mm", + "coordinateOrigin": "TOP_LEFT", + "pdfFields": [ + { + "name": "deliveryNumber", + "type": "TEXT", + "value": "{{deliveryNumber}}", + "page": 1, + "x": 38, + "y": 35, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "customerName", + "type": "TEXT", + "value": "{{customer.name}}", + "page": 1, + "x": 38, + "y": 43, + "width": 90, + "height": 6, + "fontSize": 10 + }, + { + "name": "deliveryDate", + "type": "TEXT", + "value": "{{deliveryDate}}", + "page": 1, + "x": 38, + "y": 51, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "deliveryQr", + "type": "QR_CODE", + "value": "{{deliveryNumber}}", + "page": 1, + "x": 162, + "y": 25, + "width": 28, + "height": 28 + }, + { + "name": "positionsTable", + "type": "TABLE", + "source": "items", + "page": 1, + "x": 18, + "y": 78, + "width": 174, + "height": 125, + "rowHeight": 13, + "headerHeight": 8, + "maxRows": 9, + "overflow": "CUT", + "fontSize": 7, + "border": true, + "drawHeader": true, + "repeatHeader": true, + "padding": 1.5, + "headerBackground": "#EEEEEE", + "headerColor": "#000000", + "gridColor": "#000000", + "columns": [ + { + "title": "Pos.", + "field": "pos", + "width": 12, + "align": "CENTER" + }, + { + "title": "Artikel", + "field": "partNumber", + "width": 29, + "align": "LEFT" + }, + { + "title": "Bezeichnung", + "field": "description", + "width": 47, + "align": "LEFT", + "multiline": true + }, + { + "title": "Menge", + "field": "quantity", + "width": 16, + "align": "RIGHT" + }, + { + "title": "SNR", + "field": "serialNumber", + "width": 31, + "align": "LEFT" + }, + { + "title": "Barcode", + "type": "BARCODE", + "barcodeType": "CODE_128", + "field": "serialNumber", + "width": 39 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/examples/lieferschein_pdf_table_fields_newpage.json b/src/main/resources/examples/lieferschein_pdf_table_fields_newpage.json new file mode 100644 index 0000000..d0ee3e9 --- /dev/null +++ b/src/main/resources/examples/lieferschein_pdf_table_fields_newpage.json @@ -0,0 +1,111 @@ +{ + "coordinateUnit": "mm", + "coordinateOrigin": "TOP_LEFT", + "pdfFields": [ + { + "name": "deliveryNumber", + "type": "TEXT", + "value": "{{deliveryNumber}}", + "page": 1, + "x": 38, + "y": 35, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "customerName", + "type": "TEXT", + "value": "{{customer.name}}", + "page": 1, + "x": 38, + "y": 43, + "width": 90, + "height": 6, + "fontSize": 10 + }, + { + "name": "deliveryDate", + "type": "TEXT", + "value": "{{deliveryDate}}", + "page": 1, + "x": 38, + "y": 51, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "deliveryQr", + "type": "QR_CODE", + "value": "{{deliveryNumber}}", + "page": 1, + "x": 162, + "y": 25, + "width": 28, + "height": 28 + }, + { + "name": "positionsTable", + "type": "TABLE", + "source": "items", + "page": 1, + "x": 18, + "y": 78, + "width": 174, + "height": 125, + "rowHeight": 13, + "headerHeight": 8, + "maxRows": 9, + "overflow": "NEW_PAGE", + "fontSize": 7, + "border": true, + "drawHeader": true, + "repeatHeader": true, + "padding": 1.5, + "headerBackground": "#EEEEEE", + "headerColor": "#000000", + "gridColor": "#000000", + "columns": [ + { + "title": "Pos.", + "field": "pos", + "width": 12, + "align": "CENTER" + }, + { + "title": "Artikel", + "field": "partNumber", + "width": 29, + "align": "LEFT" + }, + { + "title": "Bezeichnung", + "field": "description", + "width": 47, + "align": "LEFT", + "multiline": true + }, + { + "title": "Menge", + "field": "quantity", + "width": 16, + "align": "RIGHT" + }, + { + "title": "SNR", + "field": "serialNumber", + "width": 31, + "align": "LEFT" + }, + { + "title": "Barcode", + "type": "BARCODE", + "barcodeType": "CODE_128", + "field": "serialNumber", + "width": 39 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/examples/lieferschein_pdf_table_rendered_example.pdf b/src/main/resources/examples/lieferschein_pdf_table_rendered_example.pdf new file mode 100644 index 0000000..b3d2a6f --- /dev/null +++ b/src/main/resources/examples/lieferschein_pdf_table_rendered_example.pdf @@ -0,0 +1,85 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/BitsPerComponent 8 /ColorSpace /DeviceRGB /Filter [ /ASCII85Decode /FlateDecode ] /Height 290 /Length 3456 /Subtype /Image + /Type /XObject /Width 290 +>> +stream +Gb"0M5n_rq&4=k#s3f$?S%+1cZ>6UJ\VkrKR3URe1H7UpSOJYmi"#C0#?/)kEn3%4-(1M*ab+g<9CUVWWT"ESN$=DrepmP1W1+O9h?0com0CsI:9e(OY'ohCZotJ;0q)lA<'u%nL`cKaX?fB1U*ab*l[=I?o6G^X.V<:a\_3i&YNG\GGcYR*'WcsGjI2^l0AY.SpbZ+$L1]'#;iQF?iNL?d&2.lPrCr;TbAfEO)]kbjYe(BC.o?9+)VL$j]fr)(a?fmbnbY7]40saYc)nsNPgR+Do?"7K=`\r44B=9NNe[riF5)jFS1='cHAu^'7R=3rHBVaFmEOO9lIF",FHU$W+NO+W$msCXJhtnksD4GJ8Ic'amqqb,ZDrYZ/p\UO0h=lB@I2cf<^#/QHl+Zmjmb9_Ukl&6Y8b]ojEo4mi7dp(7:V-#nPl!Aa2L<.,H.%-0Vqeb$[aFu#c]ddD5NTV=Z]3i1MmMs8!,h^GJ/`tUi5T+.\FGmdDZ"'r)h/a``a8q[nWs8or+0j:I6#Fd4-Qj>AN#fGU"lT/Y79A.V>-KYAlGZCb)I>8g@2dQMQ]T_2O-n.go-@=%Gr,$pmP/o;lLJRN>hWHn@_\VY=W:`PtIb*W0+7mh]&EeRT*ltW>*L7)Yr+V]hsZj8S'fF8tk%=Ofo5O)l<=u<+](.'ZuCRq:b>O;2VPKAN#fGU"lT/Y79A.V>-KYAlGZCb)I>8RT0'"Pa]9;Q*U3J8ln3j)l7.b9;QGgO4';Smma;0gJTJic_GUDj1A0'p9mrCk4:!,q=nilR_:;;V.OX=V//>VdZom^7]G(q;CS*]UNGVX]2<>:[T(E3o-do?phl=b]e,MTq)h8N5/%u4AoC>=Cd(XpBA^86=^N+(2u@3*j6Kj1b.Q<]^,BS@p'KktR(:m/HQ->c%Z^=h99H<-gsYB@=XNL?8aZa]JtX@Qo/lY^&K1H52CDr^.dOik>fh7bdZDYD?7G;FU>V/#oT)`-Mgjt$4)21DfiRIN-*'muu!^Y$QMGaeF8^/U)!Ch%'>c$T\%Wl?^#0f,D5^4J"l;nA7_T]2Sa2b>AbM[Qhtc-MpO^,#IC#os^2rg+h])Atd!)X22O2s>0(;*)D_cI#CgF-tsPOH6.k1$KCei5)Rsg]Ce(BqKRJWT3eP(rshY-',mb?CE9T;d'p>r+!f[WZT:#>U*V!d!ieP(rshY-',mb?CE9T;d'p>r+!f[WZT:#>U*V!d!ieP(rshY-',mb?CE9YOS)Ub],rpQ^0?:6BGdCJ#PF=%LT5m-AeOFJE2Ag.8nDC,TX1Y>_59rL2/(l4m4'.JnF^,2MJ7PVuFCOIAnsfBX.87_bh-Oa;`2Cs'IuVarcC25Q;^/#6cUp@UI8]$[]1D5:J]24LnTf.#.-ISFN$oa7\OP^U5?O`'f,8fOa28Bn$ulZJ),UL-sR8A2lcZTc*&n!ij3H6_=XNO(g_<:?=HP^4S3\W*pZ^/XQjnW\>Ehmn`Q5/("bS+2_h,N$%kZTc*&n!ij3H6_=XNO(g_<:?=HP^4S3\W*pZ^/XQjnW\>Ehmn`Q5/("bS+2_h,N$%kZTc*&n!ij3H6_=XNO(g_<:?=HP^4S3\[4(7Z]aT98fOa2:hFG%S'4L)o-eIO?9t!UNGgR&WQq9GOa71BQ8ZJ!XJ@n1l9%ZXWE2o.C#b],8uhJ>[c,EBmlO]/Z]1C92JVS.ou6dOCh)VfE$IBbslgR*8(UW0H7?`_St2J/Q8W3NDQ([El`NLArfC@!GC`VRepA*1=])S8UiYs'tuR!ESE`i`3HXO#*KAtIbUZ)c@jlSGl,RHHJClmh\IqTNbck.-KFI48*jmcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$OLc8s;'-mcur9ft@#9I$GQLzzzzzzzzzzzz!;QHo'r)09~>endstream +endobj +5 0 obj +<< +/Contents 9 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /XObject << +/FormXob.8de85583b9e7bfa26ef5e1750cf0a554 4 0 R +>> +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +6 0 obj +<< +/PageMode /UseNone /Pages 8 0 R /Type /Catalog +>> +endobj +7 0 obj +<< +/Author (anonymous) /CreationDate (D:20260608163430+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260608163430+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (unspecified) /Title (untitled) /Trapped /False +>> +endobj +8 0 obj +<< +/Count 1 /Kids [ 5 0 R ] /Type /Pages +>> +endobj +9 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2534 +>> +stream +Gat%gbB"U]&r5rP.ra:RehGqkhlKB=')_JttF^7=Y/X/l.mB5,ucc`qMS[i2`A!NhsZ5p#jec.6Mj#/BP\ngUrSBoVs'.="6Oggnhn)D5\lcnublT:LURcJn&GI`QSo32ls\SlT^l@,mR]m&INIIZ'GbdRHr?N.W`lrSba8SYAdE7mjPWthL)O,d\uM#"Xje]j%*.eOA-"K,GqVa=N6Q1iT96JW&fWg>&YT1)R_l5Xi2U0lMV3N1KhV6\doA*@GOKoU'-B]qjPbM'O9kYI](ip"EScll$AS3BEop-]Apd<3seAQlQqka0P$c/S`WC;5'7N*qp61g/GRZBs-(qo/nB3D#RMR/bcMp"0[_;lF4nlK+nt"k4L%Z!L!:;]Nfkq;q?:A=B+VWplAlX_=KWDa\b,b3Z+WLO$Gdgohfgf?_Iotn-gID,X7Q!J:7cT'1N!=j4N0JXnO7nL2VML.E(r,JpFH+b4NZq9N,\\:dUf8AV[3sI^c)G9V<"ogPWLhk-K%PHqCC!S`S!1/qCCb-0fLS47!FlEB`^"?:nke6MCSX]C6^.-c?u335:b61?c:t(&#I-Q!PHh_h6Bc,k@;MgXdEhscFQUuJ$,uRJ;2H-GV6,It)bR;P9g0jO^Ku7)K7t)Gf2U`i4Z2%RE5Tl@N-u;!-8t@@q*BO+CZ$r80k6g;>c4de_EjfU4-A@+uA4I2*:!7LaCg1P,>_dKqbt@+I7E`@G/+$f#s_<`E7H<8V?VGNrrLq-W,$\"CRdrce8k=g!'M_*=>*=CjuSmdE,(R$F/->&@QfqK!H19Cb-;3Yj=CVC1L3X/'*+j)_dTVjZH0D9e$mO4)sm/J?Ag&YkHF!Jlm1:KIorNT^3p(9:9;dZ=:#V^"q%e,E7?:[sd#oW,t0\(uN+\D?$0rY!ht?@=u=0Qj$;]q,-fAu)nWbg-EIkm(gUZ)8W)Qm(6n>c*3IV^UOY&6ldM@\'l,7>rf.KFJ"ebAm`jLh4NXi:IJV7Qi7`Ju'!9:J&.#MS!FG>]Ps&LiMfboMU'L_PLXO[<09,D[pl"L1CU+I8'\KIq3IZ),!`YUi9U><4YR)^M6r>+*p:cE\hbLd.n7YbQAGmQNPY/McPtYTBjbNH8TW4G)Q$.1#bbejFgR[@)t=%8MsAT-4Sj)d3LZF8MsLu-KRTMgU"Y!!f/0HCh`Q-Cj?/Xn$P1&(?4XmoSl\3+HPF>;>6nW;miZHB/eQ#k?Jakul7`KJ*eZg1pMUV/I0X/\C`k;'HHrie0gSnW'7d3jJZtu#K:qQL#[:O95cYP@/>(`(bP`!&jbH89H[#MnTr\dff\r15?[\"0eCXEYK.LS#NW8I#O(-]s/XDendstream +endobj +xref +0 10 +0000000000 65535 f +0000000061 00000 n +0000000102 00000 n +0000000209 00000 n +0000000321 00000 n +0000003968 00000 n +0000004234 00000 n +0000004302 00000 n +0000004563 00000 n +0000004622 00000 n +trailer +<< +/ID +[] +% ReportLab generated PDF document -- digest (opensource) + +/Info 7 0 R +/Root 6 0 R +/Size 10 +>> +startxref +7247 +%%EOF diff --git a/src/main/resources/examples/lieferschein_pdf_table_template.pdf b/src/main/resources/examples/lieferschein_pdf_table_template.pdf new file mode 100644 index 0000000..45bb8b2 --- /dev/null +++ b/src/main/resources/examples/lieferschein_pdf_table_template.pdf @@ -0,0 +1,74 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +5 0 obj +<< +/PageMode /UseNone /Pages 7 0 R /Type /Catalog +>> +endobj +6 0 obj +<< +/Author (anonymous) /CreationDate (D:20260608163430+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260608163430+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (unspecified) /Title (PDF Tabellenvorlage Lieferschein) /Trapped /False +>> +endobj +7 0 obj +<< +/Count 1 /Kids [ 4 0 R ] /Type /Pages +>> +endobj +8 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 672 +>> +stream +Gat%`9lJ`N&;KZL'm"2lAF#fb1_PR3JK@.Y1pM?m&HY(F/$D7Y1sNf(fFT_c.'5%G@C5L8ccj[:6H"q$hU;IDPJH@q"Gr5a5\cu9)*fjcB]9Z6@F?s("&X&cJI+nHhB^F_WDAlCI(r8^;Gc)LaqqM)ul$^5e>UMZ?=Tuq?c(K:2V`\g!(G-79$\Q_k!2HqedgG4+pmT`YMM%*^@@b;::;nO_.Qb[Br@PsddG/SrPht/2@j_tbD\T\@C(iZ.;_uP'EmgCdg0mU!p5@([DjrGSo'&!K6+[4?:,hKRaVftF*9]Q9&c)Do:>n_m4?@=)]NlVnn?$C<$^>>XMu1[G7Yuh>;c_&-#!Wu$.*nmNgSJ_tkeQYioH@JV9@l&eF-]jbY&q8S_im&e<*b>-KMuTWjuWa?\-p!,[_qoN=m"a)FWbTsr+MA@7b)aHe#9"Fb./SsLYrMO[&s[ZU9AaW9;oJ!J7g?1?<1%Bh/ZYq=j_ocp&;1i]K.i9r4UH^Y9To7b4lP$;S^6n&iD;q~>endstream +endobj +xref +0 9 +0000000000 65535 f +0000000061 00000 n +0000000102 00000 n +0000000209 00000 n +0000000321 00000 n +0000000524 00000 n +0000000592 00000 n +0000000877 00000 n +0000000936 00000 n +trailer +<< +/ID +[<721e7688792119f7e9da1c836b6aa329><721e7688792119f7e9da1c836b6aa329>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 6 0 R +/Root 5 0 R +/Size 9 +>> +startxref +1698 +%%EOF diff --git a/src/main/resources/examples/lieferschein_pdf_template.pdf b/src/main/resources/examples/lieferschein_pdf_template.pdf new file mode 100644 index 0000000..ef235ee --- /dev/null +++ b/src/main/resources/examples/lieferschein_pdf_template.pdf @@ -0,0 +1,74 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +5 0 obj +<< +/PageMode /UseNone /Pages 7 0 R /Type /Catalog +>> +endobj +6 0 obj +<< +/Author (anonymous) /CreationDate (D:20260608150433+00'00') /Creator (anonymous) /Keywords () /ModDate (D:20260608150433+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (unspecified) /Title (PDF-Koordinatenvorlage Lieferschein) /Trapped /False +>> +endobj +7 0 obj +<< +/Count 1 /Kids [ 4 0 R ] /Type /Pages +>> +endobj +8 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 914 +>> +stream +Gat=i92?K]&:j6F'm"1'(m!S:80L-\[QM"[L]VS5dX=O?NN3!T:7P&2"Ge1XPdO!@R;X.<6/_CQo`%_hS7Z_&6pLe+"AgfVf`dmY".h?P^CChS%Ot;,C%TD0+#J!bU_."B@8:kI0u2$Vfb8mR@8A"%SNW-GkF2%&(EUCrA=RI$^M%FY$#2!;0h:?s(Hi?B!kh']Tb`#gPnVW8MTMA,=D?oW>:V33O+W:2(W82O(OE[+dt_2C9]k`%E=O'2A&j1+MA,`\%Y=;k]]paimJ8q5AfcoC>)`PFar"KN/q!qb'A[^j@D)(bj5iLX$!Ja-=pbK4UQWB:&9rpVEmM<'[pKH'-iYD"iZk3]pE,7)T9T@sg,94kJf7iaZ*UZVin3NjmY-^Pc6)oT[3f4I@Qn5(XoNjXQA&fEcTEW"tmM-#0.Isp^4&cob:FRFkp]T\d*J$?['D)Q>MiE$kA5u-`"KG!9N+QUAHc)>V^q#F(oR82/((f&:%-,@W30[c]!SN6jEFh)#=)RZ9NOqfWr'Ho&H^N4V&%uZ,8QK/@:jWXPZbq)c?+T$G0GB#gj%cK8*bq'CEd;ZWcRlie;.!f+tDDn*A9h8jNYc"AdpH&f_"%YoX>l~>endstream +endobj +xref +0 9 +0000000000 65535 f +0000000061 00000 n +0000000102 00000 n +0000000209 00000 n +0000000321 00000 n +0000000524 00000 n +0000000592 00000 n +0000000880 00000 n +0000000939 00000 n +trailer +<< +/ID +[<3c3495ea3f083feb5cec8e62223c0531><3c3495ea3f083feb5cec8e62223c0531>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 6 0 R +/Root 5 0 R +/Size 9 +>> +startxref +1943 +%%EOF diff --git a/src/main/resources/examples/rest-create-admin-user-request.json b/src/main/resources/examples/rest-create-admin-user-request.json new file mode 100644 index 0000000..796de4b --- /dev/null +++ b/src/main/resources/examples/rest-create-admin-user-request.json @@ -0,0 +1,7 @@ +{ + "userName": "admin", + "displayName": "Administrator", + "tokenName": "admin-token", + "enabled": true, + "scopes": ["ALL"] +} diff --git a/src/main/resources/examples/rest-create-auth-user-request.json b/src/main/resources/examples/rest-create-auth-user-request.json new file mode 100644 index 0000000..ac930ff --- /dev/null +++ b/src/main/resources/examples/rest-create-auth-user-request.json @@ -0,0 +1,7 @@ +{ + "userName": "line01", + "displayName": "Linie 01", + "tokenName": "line01-print-token", + "enabled": true, + "scopes": ["PRINT", "PREVIEW"] +} diff --git a/src/main/resources/examples/rest-create-docx-lieferschein-template-request.json b/src/main/resources/examples/rest-create-docx-lieferschein-template-request.json new file mode 100644 index 0000000..c114e95 --- /dev/null +++ b/src/main/resources/examples/rest-create-docx-lieferschein-template-request.json @@ -0,0 +1,46 @@ +{ + "name": "lieferschein_docx", + "documentName": "Lieferschein DOCX Vorlage", + "templateType": "DOCX", + "contentType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "sourceFileName": "lieferschein_docx_template.docx", + "documentFile": "document.docx", + "sampleData": { + "deliveryNumber": "LS-2026-0001", + "deliveryDate": "09.06.2026", + "customer": { + "name": "Muster GmbH", + "address": "Musterstraße 1, 12345 Musterstadt" + }, + "workorderNumber": "WO-100200", + "partNumber": "A123456", + "quantity": "120", + "serialNumber": "SNR000001", + "note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen.", + "description": "Lieferschein Demo-Artikel" + }, + "examplePrintRequest": { + "printerAlias": "OFFICE_A4", + "templateName": "lieferschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "copies": 1, + "pdfDpi": 300, + "includeRenderedBase64": false, + "data": { + "deliveryNumber": "LS-2026-0001", + "deliveryDate": "09.06.2026", + "customer": { + "name": "Muster GmbH", + "address": "Musterstraße 1, 12345 Musterstadt" + }, + "workorderNumber": "WO-100200", + "partNumber": "A123456", + "quantity": "120", + "serialNumber": "SNR000001", + "note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen.", + "description": "Lieferschein Demo-Artikel" + } + }, + "contentBase64": "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" +} \ No newline at end of file diff --git a/src/main/resources/examples/rest-create-html-table-template-request.json b/src/main/resources/examples/rest-create-html-table-template-request.json new file mode 100644 index 0000000..428ecf9 --- /dev/null +++ b/src/main/resources/examples/rest-create-html-table-template-request.json @@ -0,0 +1,10 @@ +{ + "name": "lieferschein_html_table", + "documentName": "lieferschein_html_table", + "sourceFileName": "lieferschein_html_table.html.tpl", + "description": "HTML-Lieferschein mit dynamischer Tabelle, Barcode je Zeile und QR-Code im Kopf", + "contentType": "text/html", + "templateType": "HTML", + "encoding": "UTF-8", + "content": "\n\n\n \n Lieferschein HTML Tabelle\n \n\n\n
\n
\n

Lieferschein

\n
Nr.: {{text:deliveryNumber}}
\n
Datum: {{text:deliveryDate}}
\n
\n \"QR\"\n
\n\n
Kunde: {{text:customer.name}}
\n\n \n \n \n \n \n \n \n \n \n \n \n \n {{#each:items}}\n \n \n \n \n \n \n \n \n {{/each}}\n \n
Pos.ArtikelBezeichnungMengeSeriennummerBarcode
{{text:pos}}{{text:partNumber}}{{text:description}}{{text:quantity}}{{text:serialNumber}}\"Barcode\"
\n\n\n" +} \ No newline at end of file diff --git a/src/main/resources/examples/rest-create-pdf-simple-lieferschein-template-request.json b/src/main/resources/examples/rest-create-pdf-simple-lieferschein-template-request.json new file mode 100644 index 0000000..cd9dd5f --- /dev/null +++ b/src/main/resources/examples/rest-create-pdf-simple-lieferschein-template-request.json @@ -0,0 +1,169 @@ +{ + "name": "lieferschein_pdf_simple", + "documentName": "Lieferschein PDF einfach", + "templateType": "PDF_TEMPLATE", + "contentType": "application/pdf", + "sourceFileName": "lieferschein_pdf_simple_template.pdf", + "documentFile": "document.pdf", + "coordinateUnit": "mm", + "coordinateOrigin": "TOP_LEFT", + "pdfFields": [ + { + "name": "deliveryNumber", + "type": "TEXT", + "value": "{{deliveryNumber}}", + "page": 1, + "x": 55, + "y": 30, + "width": 55, + "height": 8, + "fontSize": 10 + }, + { + "name": "deliveryDate", + "type": "TEXT", + "value": "{{deliveryDate}}", + "page": 1, + "x": 145, + "y": 30, + "width": 47, + "height": 8, + "fontSize": 10 + }, + { + "name": "customerName", + "type": "TEXT", + "value": "{{customer.name}}", + "page": 1, + "x": 45, + "y": 50, + "width": 147, + "height": 8, + "fontSize": 10 + }, + { + "name": "customerAddress", + "type": "TEXT", + "value": "{{customer.address}}", + "page": 1, + "x": 45, + "y": 63, + "width": 147, + "height": 8, + "fontSize": 9 + }, + { + "name": "workorderNumber", + "type": "TEXT", + "value": "{{workorderNumber}}", + "page": 1, + "x": 55, + "y": 87, + "width": 55, + "height": 8, + "fontSize": 10 + }, + { + "name": "partNumber", + "type": "TEXT", + "value": "{{partNumber}}", + "page": 1, + "x": 55, + "y": 101, + "width": 55, + "height": 8, + "fontSize": 10 + }, + { + "name": "quantity", + "type": "TEXT", + "value": "{{quantity}}", + "page": 1, + "x": 145, + "y": 101, + "width": 47, + "height": 8, + "fontSize": 10, + "align": "RIGHT" + }, + { + "name": "serialNumber", + "type": "TEXT", + "value": "{{serialNumber}}", + "page": 1, + "x": 55, + "y": 115, + "width": 70, + "height": 8, + "fontSize": 10 + }, + { + "name": "serialBarcode", + "type": "BARCODE", + "barcodeType": "CODE_128", + "value": "{{serialNumber}}", + "page": 1, + "x": 18, + "y": 135, + "width": 100, + "height": 25 + }, + { + "name": "serialQr", + "type": "QR_CODE", + "value": "{{serialNumber}}", + "page": 1, + "x": 145, + "y": 125, + "width": 35, + "height": 35 + }, + { + "name": "note", + "type": "TEXT", + "value": "{{note}}", + "page": 1, + "x": 18, + "y": 205, + "width": 174, + "height": 18, + "fontSize": 9, + "multiline": true + } + ], + "sampleData": { + "deliveryNumber": "LS-2026-0001", + "deliveryDate": "09.06.2026", + "customer": { + "name": "Muster GmbH", + "address": "Musterstraße 1, 12345 Musterstadt" + }, + "workorderNumber": "WO-100200", + "partNumber": "A123456", + "quantity": "120", + "serialNumber": "SNR000001", + "note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen." + }, + "examplePrintRequest": { + "printerAlias": "OFFICE_A4", + "templateName": "lieferschein_pdf_simple", + "renderMode": "PDF_TEMPLATE", + "copies": 1, + "pdfDpi": 300, + "includeRenderedBase64": false, + "data": { + "deliveryNumber": "LS-2026-0001", + "deliveryDate": "09.06.2026", + "customer": { + "name": "Muster GmbH", + "address": "Musterstraße 1, 12345 Musterstadt" + }, + "workorderNumber": "WO-100200", + "partNumber": "A123456", + "quantity": "120", + "serialNumber": "SNR000001", + "note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen." + } + }, + "contentBase64": "JVBERi0xLjQKJZOMi54gUmVwb3J0TGFiIEdlbmVyYXRlZCBQREYgZG9jdW1lbnQgKG9wZW5zb3VyY2UpCjEgMCBvYmoKPDwKL0YxIDIgMCBSIC9GMiAzIDAgUgo+PgplbmRvYmoKMiAwIG9iago8PAovQmFzZUZvbnQgL0hlbHZldGljYSAvRW5jb2RpbmcgL1dpbkFuc2lFbmNvZGluZyAvTmFtZSAvRjEgL1N1YnR5cGUgL1R5cGUxIC9UeXBlIC9Gb250Cj4+CmVuZG9iagozIDAgb2JqCjw8Ci9CYXNlRm9udCAvSGVsdmV0aWNhLUJvbGQgL0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcgL05hbWUgL0YyIC9TdWJ0eXBlIC9UeXBlMSAvVHlwZSAvRm9udAo+PgplbmRvYmoKNCAwIG9iago8PAovQ29udGVudHMgOCAwIFIgL01lZGlhQm94IFsgMCAwIDU5NS4yNzU2IDg0MS44ODk4IF0gL1BhcmVudCA3IDAgUiAvUmVzb3VyY2VzIDw8Ci9Gb250IDEgMCBSIC9Qcm9jU2V0IFsgL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdCj4+IC9Sb3RhdGUgMCAvVHJhbnMgPDwKCj4+IAogIC9UeXBlIC9QYWdlCj4+CmVuZG9iago1IDAgb2JqCjw8Ci9QYWdlTW9kZSAvVXNlTm9uZSAvUGFnZXMgNyAwIFIgL1R5cGUgL0NhdGFsb2cKPj4KZW5kb2JqCjYgMCBvYmoKPDwKL0F1dGhvciAoYW5vbnltb3VzKSAvQ3JlYXRpb25EYXRlIChEOjIwMjYwNjA5MTk0NjM3KzAwJzAwJykgL0NyZWF0b3IgKGFub255bW91cykgL0tleXdvcmRzICgpIC9Nb2REYXRlIChEOjIwMjYwNjA5MTk0NjM3KzAwJzAwJykgL1Byb2R1Y2VyIChSZXBvcnRMYWIgUERGIExpYnJhcnkgLSBcKG9wZW5zb3VyY2VcKSkgCiAgL1N1YmplY3QgKHVuc3BlY2lmaWVkKSAvVGl0bGUgKExpZWZlcnNjaGVpbiBQREZfVEVNUExBVEUgRGVtbykgL1RyYXBwZWQgL0ZhbHNlCj4+CmVuZG9iago3IDAgb2JqCjw8Ci9Db3VudCAxIC9LaWRzIFsgNCAwIFIgXSAvVHlwZSAvUGFnZXMKPj4KZW5kb2JqCjggMCBvYmoKPDwKL0ZpbHRlciBbIC9BU0NJSTg1RGVjb2RlIC9GbGF0ZURlY29kZSBdIC9MZW5ndGggOTk3Cj4+CnN0cmVhbQpHYXUxLWdOKGFzJjtLWkYnUkwscD8kJjg4O0dwbyIvVmk9Jy9mQmpoSm5dJDtma0lBNWwrODAlW1pjSDNkYUJELy4kPFwiSTlRU1FNJCZaU2hMODdASkdmVVdeI2QkNSxXIj9jJ2RROmdPR286RWpvc0xWUiNiRi0hNVRTMWtRUzVYR0hqInEtRFY1XVhbMiEkTEVxWGY3aUJRIjwwSkRnKCFlJyEkLUEqI2YpWVNoSSJxKDhFNy1gLD9AZDVbW1VwOUFlK2k9PmpQXWdeZEhmaW5gZnVnbyNEWmsuVkhLJjQ8NlQjMVY2RFx1PzFVKCUyclxkMi5RM3MtOClsR2VUVHFqO2YwJ046ZFNKXldcP1lkSFA/LmdGW0dtN2htc1BJZDFCR0JlWzZjPTFdcjwkTW9lTzlmMEZTYGVjb1dPWF87VlxlZFg7MllSaFZyZXEwRXNYV3BaJ2FhLSFdP18mR3VAOjBDbCFiPzVrNnEwYmxTOy9uKkpBWSNDP1UwLVJSTj45I1YlbF0wXjw2WGZCQS9xXWMvJjwjLykmN1c/QltAKCtjajdCWWEkKXIubCpBczM8XSZ0KCcrS1QqRlVaa1ZrPWZacTtJUDRtbFE0RysrMURmW3BDJTtVR2ZIS0FHRjdmZ1xDSWEqPUFSI0c2YFg9IS1dY2hMZitNQ3E5a2ZgXDtuMmIwYmVJKWRePjsrZFhZYmRDS2ohcnJOJlItIzZtOChKNj4iMV8zWlVURV8mTm5ZcyVMWGo3Qkx1K1luaVU3MTU+N3RhcUpBaTE2YypLME8+czBiXWghaEFRPilFJHM5PiRAdXNeUldndD5MJyxRYnBEPCxmbW1jbzsibWBoZjc1VFBkKTpANSxnL0YnQ2FvMGpObEEqcC9fNVdjMDtWKk5BZ05nWi8iYyIlPzVRQTU3QGdNYj9LX3NPcVBxZU1UOEZdTWo1NE0vYVtPUERvc2Mmb01vbG5YPzZEMkJfWG1yXCo4ciFyQyk3WElePFUoKlZcQVExNCpuU1ZiRD04dSd0Z1trZ1w4ZGpec1socFRETlhyakRpU11qWksvM15QbW5rcGhxdEI5JSZaWzwwZ0UjQEk6RUdHLDUhMnRManAuTGVPLkl1Vyw2Im0sb2staW5oSz1RcFVZMDY5XCVeMDw9RklXcHBScSZuVjIqN1UzMjs/Uj84OzJDVUZMbnRXYVY+LmJjJU5KaElmOE9AMzREW2xiIVhhRkE8KksuRmR1JnA8dF1FVmM1czAjK25dYmhSJWJiLiRoOkNjNyRJWk1camhMSW9eK11vLVJYaTdaMlRhM1hETTxiP3FGJn4+ZW5kc3RyZWFtCmVuZG9iagp4cmVmCjAgOQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwNjEgMDAwMDAgbiAKMDAwMDAwMDEwMiAwMDAwMCBuIAowMDAwMDAwMjA5IDAwMDAwIG4gCjAwMDAwMDAzMjEgMDAwMDAgbiAKMDAwMDAwMDUyNCAwMDAwMCBuIAowMDAwMDAwNTkyIDAwMDAwIG4gCjAwMDAwMDA4NzUgMDAwMDAgbiAKMDAwMDAwMDkzNCAwMDAwMCBuIAp0cmFpbGVyCjw8Ci9JRCAKWzxhYzA4MzlmM2UwZTNjNGMzYWNhOGRmNGVjM2E0OWNkMj48YWMwODM5ZjNlMGUzYzRjM2FjYThkZjRlYzNhNDljZDI+XQolIFJlcG9ydExhYiBnZW5lcmF0ZWQgUERGIGRvY3VtZW50IC0tIGRpZ2VzdCAob3BlbnNvdXJjZSkKCi9JbmZvIDYgMCBSCi9Sb290IDUgMCBSCi9TaXplIDkKPj4Kc3RhcnR4cmVmCjIwMjEKJSVFT0YK" +} \ No newline at end of file diff --git a/src/main/resources/examples/rest-create-pdf-template-request.json b/src/main/resources/examples/rest-create-pdf-template-request.json new file mode 100644 index 0000000..937f4e5 --- /dev/null +++ b/src/main/resources/examples/rest-create-pdf-template-request.json @@ -0,0 +1,136 @@ +{ + "name": "lieferschein_pdf", + "documentName": "lieferschein_pdf", + "sourceFileName": "lieferschein_pdf_template.pdf", + "description": "Beispiel: PDF als Vorlage plus Koordinatenfelder für Text, Barcode und QR-Code.", + "contentType": "application/pdf", + "templateType": "PDF", + "encoding": "UTF-8", + "contentBase64": "JVBERi0xLjQKJZOMi54gUmVwb3J0TGFiIEdlbmVyYXRlZCBQREYgZG9jdW1lbnQgKG9wZW5zb3VyY2UpCjEgMCBvYmoKPDwKL0YxIDIgMCBSIC9GMiAzIDAgUgo+PgplbmRvYmoKMiAwIG9iago8PAovQmFzZUZvbnQgL0hlbHZldGljYSAvRW5jb2RpbmcgL1dpbkFuc2lFbmNvZGluZyAvTmFtZSAvRjEgL1N1YnR5cGUgL1R5cGUxIC9UeXBlIC9Gb250Cj4+CmVuZG9iagozIDAgb2JqCjw8Ci9CYXNlRm9udCAvSGVsdmV0aWNhLUJvbGQgL0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcgL05hbWUgL0YyIC9TdWJ0eXBlIC9UeXBlMSAvVHlwZSAvRm9udAo+PgplbmRvYmoKNCAwIG9iago8PAovQ29udGVudHMgOCAwIFIgL01lZGlhQm94IFsgMCAwIDU5NS4yNzU2IDg0MS44ODk4IF0gL1BhcmVudCA3IDAgUiAvUmVzb3VyY2VzIDw8Ci9Gb250IDEgMCBSIC9Qcm9jU2V0IFsgL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdCj4+IC9Sb3RhdGUgMCAvVHJhbnMgPDwKCj4+IAogIC9UeXBlIC9QYWdlCj4+CmVuZG9iago1IDAgb2JqCjw8Ci9QYWdlTW9kZSAvVXNlTm9uZSAvUGFnZXMgNyAwIFIgL1R5cGUgL0NhdGFsb2cKPj4KZW5kb2JqCjYgMCBvYmoKPDwKL0F1dGhvciAoYW5vbnltb3VzKSAvQ3JlYXRpb25EYXRlIChEOjIwMjYwNjA4MTUwNDMzKzAwJzAwJykgL0NyZWF0b3IgKGFub255bW91cykgL0tleXdvcmRzICgpIC9Nb2REYXRlIChEOjIwMjYwNjA4MTUwNDMzKzAwJzAwJykgL1Byb2R1Y2VyIChSZXBvcnRMYWIgUERGIExpYnJhcnkgLSBcKG9wZW5zb3VyY2VcKSkgCiAgL1N1YmplY3QgKHVuc3BlY2lmaWVkKSAvVGl0bGUgKFBERi1Lb29yZGluYXRlbnZvcmxhZ2UgTGllZmVyc2NoZWluKSAvVHJhcHBlZCAvRmFsc2UKPj4KZW5kb2JqCjcgMCBvYmoKPDwKL0NvdW50IDEgL0tpZHMgWyA0IDAgUiBdIC9UeXBlIC9QYWdlcwo+PgplbmRvYmoKOCAwIG9iago8PAovRmlsdGVyIFsgL0FTQ0lJODVEZWNvZGUgL0ZsYXRlRGVjb2RlIF0gL0xlbmd0aCA5MTQKPj4Kc3RyZWFtCkdhdD1pOTI/S10mOmo2RidtIjEnKG0hUzo4MEwtXFtRTSJbTF1WUzVkWD1PP05OMyFUOjdQJjIiR2UxWFBkTyFAUjtYLjw2L19DUW9gJV9oUzdaXyY2cExlKyJBZ2ZWZmBkbVkiLmg/UF5DQ2hTJU90OyxDJVREMCsjSiFiVV8uIkJAODprSTB1MiRWZmI4bVJAOEEiJVNOVy1Ha0YyJSYoRVVDckE9UkkkPGwxYm00QTgoWCpaZFtUXyhqYCpAXThfNFpqOnJoJ1JrZickKm8zL2ZFMGJTQiRUMGErJ0xDWmcqUm8uQj5eTSVGWSQjMiE7MGg6P3MoSGk/QiFraCddVGJgI2dQblZXOE1UTUEsPUQ/b1c+OlYzM08rVzoyKFc4Mk8oT0VbK2R0XzJDOV1rYCVFPU8nMkEmajErTUEsYFwlWT07a11dcGFpbUo4cTVBZmNvQz4pYFBGYXIiS04vcSFxYidBW15qQEQpKGJqNWlMWCQhSmEtPXBiSzRVUVdCOiY5cnBWRW1NPCdbcEtIJy1pWUQiaVo8TDdwckcrLCcxblY9XVcoW1o1ajlAL0RXK2Fjb1k3NTZiUGBkVXEwRD03XTs+azNdcEUsNylUOVRAc2csOTRrPFgkL3MoP0MkVGYjLzg0VTVhTWlLOztUJDkmWEBkZGMvWjNjaU9eWT1zMlhBb2RIXHA+SmY3aWFaKlVaVmluM05qbVktXlBjNilvVFszZjRJQFFuNShYb05qWFFBJmZFY1RFVyJ0bU0tIzAuPFVaUDA7azhrKWMnVllVc2o1V1kzST9Qbz1FV20nbFRlYi5BOG9aOzQ7SnJCU3RlbGM4dVQ6J1VYYjwzVSMsWjtUSDRVVEdrV3AsUVVMRltWMW04RDAoKixQUyhNLXFWJ2hAZ2hnVzRHPUIkRWI1RWlnSk9ZK3RxPklzcF40JmNvYjpGUkZrcF1UXGQqSiQ/WydEKVE+TWlFJGtBNXUtYCJLRyE5TitRVUFIYyk+Vl5xI0Yob1I4Mi8oKGYmOiUtLEBXMzBbY10hU042akU8bjctLzciOVpzMHJuNnMuUj5GaCkjPSlSWjlOT3FmV3InSG8mSF5ONFYmJXVaLDhRSy9AOmpXWFBaYnEpYz8rVCRHMEdCI2dqJWNLOCpicSdDRWQ7WldjUmxpZTsuIWYrdEREbipBOWg4ak5ZYyJBZHBIJmZfIiVZb1g+bH4+ZW5kc3RyZWFtCmVuZG9iagp4cmVmCjAgOQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwNjEgMDAwMDAgbiAKMDAwMDAwMDEwMiAwMDAwMCBuIAowMDAwMDAwMjA5IDAwMDAwIG4gCjAwMDAwMDAzMjEgMDAwMDAgbiAKMDAwMDAwMDUyNCAwMDAwMCBuIAowMDAwMDAwNTkyIDAwMDAwIG4gCjAwMDAwMDA4ODAgMDAwMDAgbiAKMDAwMDAwMDkzOSAwMDAwMCBuIAp0cmFpbGVyCjw8Ci9JRCAKWzwzYzM0OTVlYTNmMDgzZmViNWNlYzhlNjIyMjNjMDUzMT48M2MzNDk1ZWEzZjA4M2ZlYjVjZWM4ZTYyMjIzYzA1MzE+XQolIFJlcG9ydExhYiBnZW5lcmF0ZWQgUERGIGRvY3VtZW50IC0tIGRpZ2VzdCAob3BlbnNvdXJjZSkKCi9JbmZvIDYgMCBSCi9Sb290IDUgMCBSCi9TaXplIDkKPj4Kc3RhcnR4cmVmCjE5NDMKJSVFT0YK", + "coordinateUnit": "mm", + "coordinateOrigin": "TOP_LEFT", + "pdfFields": [ + { + "name": "serialNumber", + "type": "TEXT", + "value": "{{serialNumber}}", + "page": 1, + "x": 35, + "y": 52, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "partNumber", + "type": "TEXT", + "value": "{{partNumber}}", + "page": 1, + "x": 35, + "y": 60, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "workorderNumber", + "type": "TEXT", + "value": "{{workorderNumber}}", + "page": 1, + "x": 35, + "y": 68, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "quantity", + "type": "TEXT", + "value": "{{quantity}}", + "page": 1, + "x": 35, + "y": 76, + "width": 70, + "height": 6, + "fontSize": 10, + "align": "RIGHT" + }, + { + "name": "customer", + "type": "TEXT", + "value": "{{customer.name}}", + "page": 1, + "x": 35, + "y": 84, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "serialBarcode", + "type": "BARCODE", + "barcodeType": "CODE_128", + "value": "{{serialNumber}}", + "page": 1, + "x": 25, + "y": 94, + "width": 90, + "height": 18 + }, + { + "name": "serialQr", + "type": "QR_CODE", + "value": "{{serialNumber}}", + "page": 1, + "x": 155, + "y": 38, + "width": 32, + "height": 32 + }, + { + "name": "item1Pos", + "type": "TEXT", + "value": "{{items.0.pos}}", + "page": 1, + "x": 22, + "y": 142, + "width": 15, + "height": 5, + "fontSize": 9 + }, + { + "name": "item1Material", + "type": "TEXT", + "value": "{{items.0.material}}", + "page": 1, + "x": 40, + "y": 142, + "width": 55, + "height": 5, + "fontSize": 9 + }, + { + "name": "item1Description", + "type": "TEXT", + "value": "{{items.0.description}}", + "page": 1, + "x": 100, + "y": 142, + "width": 60, + "height": 5, + "fontSize": 9 + }, + { + "name": "item1Quantity", + "type": "TEXT", + "value": "{{items.0.quantity}}", + "page": 1, + "x": 165, + "y": 142, + "width": 20, + "height": 5, + "fontSize": 9, + "align": "RIGHT" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/examples/rest-create-pdf-template-table-request.json b/src/main/resources/examples/rest-create-pdf-template-table-request.json new file mode 100644 index 0000000..a86d5d1 --- /dev/null +++ b/src/main/resources/examples/rest-create-pdf-template-table-request.json @@ -0,0 +1,118 @@ +{ + "name": "lieferschein_pdf_table", + "documentName": "lieferschein_pdf_table", + "sourceFileName": "lieferschein_pdf_table_template.pdf", + "description": "PDF_TEMPLATE Beispiel mit dynamischer Tabelle, Barcode-Spalte und QR-Code", + "contentType": "application/pdf", + "templateType": "PDF", + "coordinateUnit": "mm", + "coordinateOrigin": "TOP_LEFT", + "contentBase64": "JVBERi0xLjQKJZOMi54gUmVwb3J0TGFiIEdlbmVyYXRlZCBQREYgZG9jdW1lbnQgKG9wZW5zb3VyY2UpCjEgMCBvYmoKPDwKL0YxIDIgMCBSIC9GMiAzIDAgUgo+PgplbmRvYmoKMiAwIG9iago8PAovQmFzZUZvbnQgL0hlbHZldGljYSAvRW5jb2RpbmcgL1dpbkFuc2lFbmNvZGluZyAvTmFtZSAvRjEgL1N1YnR5cGUgL1R5cGUxIC9UeXBlIC9Gb250Cj4+CmVuZG9iagozIDAgb2JqCjw8Ci9CYXNlRm9udCAvSGVsdmV0aWNhLUJvbGQgL0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcgL05hbWUgL0YyIC9TdWJ0eXBlIC9UeXBlMSAvVHlwZSAvRm9udAo+PgplbmRvYmoKNCAwIG9iago8PAovQ29udGVudHMgOCAwIFIgL01lZGlhQm94IFsgMCAwIDU5NS4yNzU2IDg0MS44ODk4IF0gL1BhcmVudCA3IDAgUiAvUmVzb3VyY2VzIDw8Ci9Gb250IDEgMCBSIC9Qcm9jU2V0IFsgL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdCj4+IC9Sb3RhdGUgMCAvVHJhbnMgPDwKCj4+IAogIC9UeXBlIC9QYWdlCj4+CmVuZG9iago1IDAgb2JqCjw8Ci9QYWdlTW9kZSAvVXNlTm9uZSAvUGFnZXMgNyAwIFIgL1R5cGUgL0NhdGFsb2cKPj4KZW5kb2JqCjYgMCBvYmoKPDwKL0F1dGhvciAoYW5vbnltb3VzKSAvQ3JlYXRpb25EYXRlIChEOjIwMjYwNjA4MTYzNDMwKzAwJzAwJykgL0NyZWF0b3IgKGFub255bW91cykgL0tleXdvcmRzICgpIC9Nb2REYXRlIChEOjIwMjYwNjA4MTYzNDMwKzAwJzAwJykgL1Byb2R1Y2VyIChSZXBvcnRMYWIgUERGIExpYnJhcnkgLSBcKG9wZW5zb3VyY2VcKSkgCiAgL1N1YmplY3QgKHVuc3BlY2lmaWVkKSAvVGl0bGUgKFBERiBUYWJlbGxlbnZvcmxhZ2UgTGllZmVyc2NoZWluKSAvVHJhcHBlZCAvRmFsc2UKPj4KZW5kb2JqCjcgMCBvYmoKPDwKL0NvdW50IDEgL0tpZHMgWyA0IDAgUiBdIC9UeXBlIC9QYWdlcwo+PgplbmRvYmoKOCAwIG9iago8PAovRmlsdGVyIFsgL0FTQ0lJODVEZWNvZGUgL0ZsYXRlRGVjb2RlIF0gL0xlbmd0aCA2NzIKPj4Kc3RyZWFtCkdhdCVgOWxKYE4mO0taTCdtIjJsQUYjZmIxX1BSM0pLQC5ZMXBNP20mSFkoRi8kRDdZMXNOZihmRlRfYy4nNSVHQEM1TDhjY2pbOjZIInEkaFU7SURQSkhAcSJHcjVhNVxjdTkpKmZqY0JdOTxHZjVbJWwqb18zUUBBSl1VPVRmTzJfSFNONiQxTUFiZD5aNkBGP3MoIiZYJmNKSStuSGhCXkZfV0RBbENJKHI4XjtHYylMYXFxTSl1bCReNWU+VU1aPz1UdXE/YyhLOjJWYFxnIShHLTc5JFxRX2shMkhxZWRnRzQrcG1UYFlNTSUqXkBAYjs6OjtuT18uUWJbQnJAUHNkZEcvU3JQaHQvMkBqX3RiRFxUXEBDKGlaLjtfdVAnRW1nQ2RnMG1VIXA1QChbRGpyR1NvJyYhSzYrWzQ/OixoS1JhVmZ0Rio5XVE5JmMpRG86Pm5fbTQ/QD0pXU5sVm5uPyRDPCRePj5YTXUxW0c3WXVoPjs8LzZQOHBcMC4kK0AhaCc9YilxMFRoPSlsLyZUKXIyP2AsL1FCc0duISJNWUgucj1LSXFWLDtidVUuJEYoRXVKIV4/LldbLCNTNFhqRydLNSIjXltLYFlpXDFfbVBRcE1kMWNpT14jY21nJjQ6bi4yKEE+Y18mLSMhV3UkLipubU5nU0pfdGtlUVlpb0hASlY5QGwmZUYtXWpiWSZxOFNfaW0mZTwqYj4tS011VFdqdVdhP1wtcCEsW19xb049bSJhKUZXYlRzcitNQUA3YilhSGUjOSJGYi4vU3NMWXJNT1smc1taVTlBYVc5O29KIUo3Zz8xPzwxJUJoL1pZcT1qX29jcCY7MWldSy5pOXI0VUheWTlUbzdiNGxQJDtTXjZuJmlEO3F+PmVuZHN0cmVhbQplbmRvYmoKeHJlZgowIDkKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDYxIDAwMDAwIG4gCjAwMDAwMDAxMDIgMDAwMDAgbiAKMDAwMDAwMDIwOSAwMDAwMCBuIAowMDAwMDAwMzIxIDAwMDAwIG4gCjAwMDAwMDA1MjQgMDAwMDAgbiAKMDAwMDAwMDU5MiAwMDAwMCBuIAowMDAwMDAwODc3IDAwMDAwIG4gCjAwMDAwMDA5MzYgMDAwMDAgbiAKdHJhaWxlcgo8PAovSUQgCls8NzIxZTc2ODg3OTIxMTlmN2U5ZGExYzgzNmI2YWEzMjk+PDcyMWU3Njg4NzkyMTE5ZjdlOWRhMWM4MzZiNmFhMzI5Pl0KJSBSZXBvcnRMYWIgZ2VuZXJhdGVkIFBERiBkb2N1bWVudCAtLSBkaWdlc3QgKG9wZW5zb3VyY2UpCgovSW5mbyA2IDAgUgovUm9vdCA1IDAgUgovU2l6ZSA5Cj4+CnN0YXJ0eHJlZgoxNjk4CiUlRU9GCg==", + "pdfFields": [ + { + "name": "deliveryNumber", + "type": "TEXT", + "value": "{{deliveryNumber}}", + "page": 1, + "x": 38, + "y": 35, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "customerName", + "type": "TEXT", + "value": "{{customer.name}}", + "page": 1, + "x": 38, + "y": 43, + "width": 90, + "height": 6, + "fontSize": 10 + }, + { + "name": "deliveryDate", + "type": "TEXT", + "value": "{{deliveryDate}}", + "page": 1, + "x": 38, + "y": 51, + "width": 70, + "height": 6, + "fontSize": 10 + }, + { + "name": "deliveryQr", + "type": "QR_CODE", + "value": "{{deliveryNumber}}", + "page": 1, + "x": 162, + "y": 25, + "width": 28, + "height": 28 + }, + { + "name": "positionsTable", + "type": "TABLE", + "source": "items", + "page": 1, + "x": 18, + "y": 78, + "width": 174, + "height": 125, + "rowHeight": 13, + "headerHeight": 8, + "maxRows": 9, + "overflow": "CUT", + "fontSize": 7, + "border": true, + "drawHeader": true, + "repeatHeader": true, + "padding": 1.5, + "headerBackground": "#EEEEEE", + "headerColor": "#000000", + "gridColor": "#000000", + "columns": [ + { + "title": "Pos.", + "field": "pos", + "width": 12, + "align": "CENTER" + }, + { + "title": "Artikel", + "field": "partNumber", + "width": 29, + "align": "LEFT" + }, + { + "title": "Bezeichnung", + "field": "description", + "width": 47, + "align": "LEFT", + "multiline": true + }, + { + "title": "Menge", + "field": "quantity", + "width": 16, + "align": "RIGHT" + }, + { + "title": "SNR", + "field": "serialNumber", + "width": 31, + "align": "LEFT" + }, + { + "title": "Barcode", + "type": "BARCODE", + "barcodeType": "CODE_128", + "field": "serialNumber", + "width": 39 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/examples/rest-create-raw-tcp-printer-request.json b/src/main/resources/examples/rest-create-raw-tcp-printer-request.json new file mode 100644 index 0000000..16f9b3c --- /dev/null +++ b/src/main/resources/examples/rest-create-raw-tcp-printer-request.json @@ -0,0 +1,8 @@ +{ + "alias": "LABEL_LINE_01", + "type": "RAW_TCP", + "host": "192.168.10.50", + "port": 9100, + "language": "ZPL", + "description": "Zebra Etikettendrucker direkt per TCP" +} diff --git a/src/main/resources/examples/rest-create-windows-spooler-printer-request.json b/src/main/resources/examples/rest-create-windows-spooler-printer-request.json new file mode 100644 index 0000000..f0564e7 --- /dev/null +++ b/src/main/resources/examples/rest-create-windows-spooler-printer-request.json @@ -0,0 +1,6 @@ +{ + "alias": "OFFICE_A4", + "type": "WINDOWS_SPOOLER", + "systemName": "HP_LaserJet_A4", + "description": "A4 Drucker ueber Windows Spooler" +} diff --git a/src/main/resources/examples/rest-diagnostics-system-response-example.json b/src/main/resources/examples/rest-diagnostics-system-response-example.json new file mode 100644 index 0000000..fa6e2dc --- /dev/null +++ b/src/main/resources/examples/rest-diagnostics-system-response-example.json @@ -0,0 +1,12 @@ +{ + "osName": "Windows Server 2022", + "windows": true, + "javaUser": "SYSTEM", + "likelyLocalSystem": true, + "spoolerPrinterCount": 0, + "recommendations": [ + "GlassFish läuft wahrscheinlich als LocalSystem. Benutzergebundene Drucker werden meistens nicht gefunden.", + "Für PDF/HTML/PDF_TEMPLATE: Drucker lokal/serverweit installieren oder maschinenweite Printserver-Verbindung verwenden.", + "Für ZPL/EPL: RAW_TCP-Alias verwenden und direkt an IP:9100 senden." + ] +} diff --git a/src/main/resources/examples/rest-docx-converters.curl.txt b/src/main/resources/examples/rest-docx-converters.curl.txt new file mode 100644 index 0000000..ec7d478 --- /dev/null +++ b/src/main/resources/examples/rest-docx-converters.curl.txt @@ -0,0 +1 @@ +curl http://localhost:8080/api/docx/converters diff --git a/src/main/resources/examples/rest-list-queue-filter-user.curl.txt b/src/main/resources/examples/rest-list-queue-filter-user.curl.txt new file mode 100644 index 0000000..4aff599 --- /dev/null +++ b/src/main/resources/examples/rest-list-queue-filter-user.curl.txt @@ -0,0 +1,2 @@ +curl "http://localhost:8080/api/queue?full=true&user=line01&status=SUBMITTED&templateName=serial_label_zpl" \ + -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" diff --git a/src/main/resources/examples/rest-list-queue-full-admin.curl.txt b/src/main/resources/examples/rest-list-queue-full-admin.curl.txt new file mode 100644 index 0000000..141d1a1 --- /dev/null +++ b/src/main/resources/examples/rest-list-queue-full-admin.curl.txt @@ -0,0 +1,2 @@ +curl "http://localhost:8080/api/queue?full=true&limit=500&includeRequest=false" \ + -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" diff --git a/src/main/resources/examples/rest-list-queue-own.curl.txt b/src/main/resources/examples/rest-list-queue-own.curl.txt new file mode 100644 index 0000000..5ae1fa2 --- /dev/null +++ b/src/main/resources/examples/rest-list-queue-own.curl.txt @@ -0,0 +1,2 @@ +curl http://localhost:8080/api/queue?limit=100 \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" diff --git a/src/main/resources/examples/rest-print-docx-lieferschein-request.json b/src/main/resources/examples/rest-print-docx-lieferschein-request.json new file mode 100644 index 0000000..dc7ee28 --- /dev/null +++ b/src/main/resources/examples/rest-print-docx-lieferschein-request.json @@ -0,0 +1,23 @@ +{ + "printerAlias": "OFFICE_A4", + "templateName": "lieferschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "copies": 1, + "pdfDpi": 300, + "includeRenderedBase64": false, + "data": { + "deliveryNumber": "LS-2026-0001", + "deliveryDate": "09.06.2026", + "customer": { + "name": "Muster GmbH", + "address": "Musterstraße 1, 12345 Musterstadt" + }, + "workorderNumber": "WO-100200", + "partNumber": "A123456", + "quantity": "120", + "serialNumber": "SNR000001", + "note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen.", + "description": "Lieferschein Demo-Artikel" + } +} \ No newline at end of file diff --git a/src/main/resources/examples/rest-print-docx4j-fo-request.json b/src/main/resources/examples/rest-print-docx4j-fo-request.json new file mode 100644 index 0000000..24efdad --- /dev/null +++ b/src/main/resources/examples/rest-print-docx4j-fo-request.json @@ -0,0 +1,16 @@ +{ + "printerAlias": "OFFICE_A4", + "templateName": "serienbegleitschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "copies": 1, + "pdfDpi": 300, + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456", + "workorderNumber": "WO-100200", + "customer": { + "name": "Muster GmbH" + } + } +} diff --git a/src/main/resources/examples/rest-print-html-table-request.json b/src/main/resources/examples/rest-print-html-table-request.json new file mode 100644 index 0000000..ed9db73 --- /dev/null +++ b/src/main/resources/examples/rest-print-html-table-request.json @@ -0,0 +1,37 @@ +{ + "printerAlias": "OFFICE_A4", + "templateName": "lieferschein_html_table", + "renderMode": "PDF", + "copies": 1, + "pdfDpi": 300, + "data": { + "deliveryNumber": "LS-2026-0001", + "deliveryDate": "08.06.2026", + "customer": { + "name": "Muster GmbH" + }, + "items": [ + { + "pos": "1", + "partNumber": "A123456", + "description": "Gehäuse Oberteil", + "quantity": "10", + "serialNumber": "SNR000001" + }, + { + "pos": "2", + "partNumber": "B987654", + "description": "Elektronikmodul", + "quantity": "5", + "serialNumber": "SNR000002" + }, + { + "pos": "3", + "partNumber": "C555111", + "description": "Dichtung", + "quantity": "20", + "serialNumber": "SNR000003" + } + ] + } +} diff --git a/src/main/resources/examples/rest-print-pdf-simple-lieferschein-request.json b/src/main/resources/examples/rest-print-pdf-simple-lieferschein-request.json new file mode 100644 index 0000000..f807b0e --- /dev/null +++ b/src/main/resources/examples/rest-print-pdf-simple-lieferschein-request.json @@ -0,0 +1,21 @@ +{ + "printerAlias": "OFFICE_A4", + "templateName": "lieferschein_pdf_simple", + "renderMode": "PDF_TEMPLATE", + "copies": 1, + "pdfDpi": 300, + "includeRenderedBase64": false, + "data": { + "deliveryNumber": "LS-2026-0001", + "deliveryDate": "09.06.2026", + "customer": { + "name": "Muster GmbH", + "address": "Musterstraße 1, 12345 Musterstadt" + }, + "workorderNumber": "WO-100200", + "partNumber": "A123456", + "quantity": "120", + "serialNumber": "SNR000001", + "note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen." + } +} \ No newline at end of file diff --git a/src/main/resources/examples/rest-print-pdf-template-request.json b/src/main/resources/examples/rest-print-pdf-template-request.json new file mode 100644 index 0000000..95c9cc0 --- /dev/null +++ b/src/main/resources/examples/rest-print-pdf-template-request.json @@ -0,0 +1,24 @@ +{ + "printerAlias": "OFFICE_A4", + "templateName": "lieferschein_pdf", + "renderMode": "PDF_TEMPLATE", + "copies": 1, + "pdfDpi": 300, + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456", + "workorderNumber": "WO-100200", + "quantity": "120", + "customer": { + "name": "Muster GmbH" + }, + "items": [ + { + "pos": "10", + "material": "A123456", + "description": "Baugruppe rechts", + "quantity": "120" + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/examples/rest-print-pdf-template-table-request.json b/src/main/resources/examples/rest-print-pdf-template-table-request.json new file mode 100644 index 0000000..712c55c --- /dev/null +++ b/src/main/resources/examples/rest-print-pdf-template-table-request.json @@ -0,0 +1,52 @@ +{ + "printerAlias": "OFFICE_A4", + "templateName": "lieferschein_pdf_table", + "renderMode": "PDF_TEMPLATE", + "copies": 1, + "pdfDpi": 300, + "includeRenderedBase64": false, + "data": { + "deliveryNumber": "LS-2026-0001", + "deliveryDate": "08.06.2026", + "customer": { + "name": "Muster GmbH" + }, + "items": [ + { + "pos": "1", + "partNumber": "A123456", + "description": "Gehäuse Oberteil", + "quantity": "10", + "serialNumber": "SNR000001" + }, + { + "pos": "2", + "partNumber": "B987654", + "description": "Elektronikmodul", + "quantity": "5", + "serialNumber": "SNR000002" + }, + { + "pos": "3", + "partNumber": "C555111", + "description": "Dichtungssatz EPDM", + "quantity": "20", + "serialNumber": "SNR000003" + }, + { + "pos": "4", + "partNumber": "D444222", + "description": "Sensorleitung vorkonfektioniert", + "quantity": "7", + "serialNumber": "SNR000004" + }, + { + "pos": "5", + "partNumber": "E777888", + "description": "Befestigungsclip", + "quantity": "30", + "serialNumber": "SNR000005" + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/examples/rest-print-raw-tcp-zpl-request.json b/src/main/resources/examples/rest-print-raw-tcp-zpl-request.json new file mode 100644 index 0000000..7dac6e6 --- /dev/null +++ b/src/main/resources/examples/rest-print-raw-tcp-zpl-request.json @@ -0,0 +1,12 @@ +{ + "printerAlias": "LABEL_LINE_01", + "templateName": "serial_label_zpl", + "renderMode": "RAW", + "copies": 1, + "encoding": "UTF-8", + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456", + "workorderNumber": "WO-100200" + } +} diff --git a/src/main/resources/examples/rest-queue-response-example.json b/src/main/resources/examples/rest-queue-response-example.json new file mode 100644 index 0000000..0261a36 --- /dev/null +++ b/src/main/resources/examples/rest-queue-response-example.json @@ -0,0 +1,25 @@ +{ + "total": 1, + "returned": 1, + "filteredToCurrentUser": true, + "currentUser": "line01", + "jobs": [ + { + "jobId": "31b5a0e2-6c99-4098-9c6c-8aa80d7e2231", + "status": "SUBMITTED", + "createdAt": "2026-06-09T07:12:15Z", + "updatedAt": "2026-06-09T07:12:16Z", + "userName": "line01", + "displayName": "Linie 01", + "printerAlias": "LABEL_LINE_01", + "systemName": "192.168.10.50:9100", + "templateName": "serial_label_zpl", + "documentName": "Serienetikett ZPL", + "renderMode": "RAW", + "contentType": "text/plain", + "copies": 1, + "renderedBytes": 164, + "message": "Print job submitted" + } + ] +} diff --git a/src/main/resources/examples/rest-reprint-request.json b/src/main/resources/examples/rest-reprint-request.json new file mode 100644 index 0000000..3a8ca6a --- /dev/null +++ b/src/main/resources/examples/rest-reprint-request.json @@ -0,0 +1,7 @@ +{ + "copies": 1, + "printerAlias": "LABEL_LINE_01", + "dataOverride": { + "reprintReason": "Etikett beschädigt" + } +} diff --git a/src/main/resources/examples/rest-reprint.curl.txt b/src/main/resources/examples/rest-reprint.curl.txt new file mode 100644 index 0000000..22e9bdc --- /dev/null +++ b/src/main/resources/examples/rest-reprint.curl.txt @@ -0,0 +1,4 @@ +curl -X POST http://localhost:8080/api/queue/JOB_ID/reprint \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_PRINT_TOKEN" \ + -d @rest-reprint-request.json diff --git a/src/main/resources/examples/rest-update-auth-config-enabled-request.json b/src/main/resources/examples/rest-update-auth-config-enabled-request.json new file mode 100644 index 0000000..6e96f00 --- /dev/null +++ b/src/main/resources/examples/rest-update-auth-config-enabled-request.json @@ -0,0 +1,8 @@ +{ + "enabled": true, + "protectPrint": true, + "protectRenderedDownload": true, + "protectPreview": false, + "protectAdmin": true, + "headerName": "X-Printservice-Token" +} diff --git a/src/main/resources/examples/rest-validate-docx-lieferschein-request.json b/src/main/resources/examples/rest-validate-docx-lieferschein-request.json new file mode 100644 index 0000000..312141a --- /dev/null +++ b/src/main/resources/examples/rest-validate-docx-lieferschein-request.json @@ -0,0 +1,20 @@ +{ + "templateName": "lieferschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "includeRenderedBase64": false, + "data": { + "deliveryNumber": "LS-2026-0001", + "deliveryDate": "09.06.2026", + "customer": { + "name": "Muster GmbH", + "address": "Musterstraße 1, 12345 Musterstadt" + }, + "workorderNumber": "WO-100200", + "partNumber": "A123456", + "quantity": "120", + "serialNumber": "SNR000001", + "note": "Lieferung frei Rampe. Bitte Ware bei Annahme prüfen.", + "description": "Lieferschein Demo-Artikel" + } +} \ No newline at end of file diff --git a/src/main/resources/examples/rest-validate-docx4j-fo-request.json b/src/main/resources/examples/rest-validate-docx4j-fo-request.json new file mode 100644 index 0000000..a7bbf39 --- /dev/null +++ b/src/main/resources/examples/rest-validate-docx4j-fo-request.json @@ -0,0 +1,15 @@ +{ + "printerAlias": "OFFICE_A4", + "templateName": "serienbegleitschein_docx", + "renderMode": "DOCX", + "docxConverter": "DOCX4J_FO", + "includeRenderedBase64": true, + "data": { + "serialNumber": "SNR000001", + "partNumber": "A123456", + "workorderNumber": "WO-100200", + "customer": { + "name": "Muster GmbH" + } + } +} diff --git a/src/main/resources/examples/rest-validate-docx4j-fo.curl.txt b/src/main/resources/examples/rest-validate-docx4j-fo.curl.txt new file mode 100644 index 0000000..9d422d1 --- /dev/null +++ b/src/main/resources/examples/rest-validate-docx4j-fo.curl.txt @@ -0,0 +1,4 @@ +curl -X POST "http://localhost:8080/api/docx/validate?converter=DOCX4J_FO" \ + -H "Content-Type: application/json" \ + -H "X-Printservice-Token: YOUR_TOKEN" \ + -d @src/main/resources/examples/rest-validate-docx4j-fo-request.json diff --git a/src/main/resources/examples/rest-validate-html-pdf-request.json b/src/main/resources/examples/rest-validate-html-pdf-request.json new file mode 100644 index 0000000..690675b --- /dev/null +++ b/src/main/resources/examples/rest-validate-html-pdf-request.json @@ -0,0 +1,20 @@ +{ + "templateName": "lieferschein_html_table", + "renderMode": "PDF", + "data": { + "deliveryNumber": "LS-2026-0001", + "deliveryDate": "08.06.2026", + "customer": { + "name": "Muster GmbH" + }, + "items": [ + { + "pos": "1", + "partNumber": "A123456", + "description": "Gehäuse Oberteil", + "quantity": "10", + "serialNumber": "SNR000001" + } + ] + } +} diff --git a/src/main/resources/examples/serial_label.zpl.tpl b/src/main/resources/examples/serial_label.zpl.tpl new file mode 100644 index 0000000..8a38351 --- /dev/null +++ b/src/main/resources/examples/serial_label.zpl.tpl @@ -0,0 +1,7 @@ +^XA +^CF0,35 +^FO40,40^FDSerial: {{serialNumber}}^FS +^FO40,90^FDPart: {{partNumber}}^FS +^FO40,140^FDOrder: {{workorderNumber}}^FS +^FO40,200^BCN,100,Y,N,N^FD{{serialNumber}}^FS +^XZ diff --git a/src/main/resources/examples/serial_label_zpl_barcode_qr.zpl.tpl b/src/main/resources/examples/serial_label_zpl_barcode_qr.zpl.tpl new file mode 100644 index 0000000..dfa28ea --- /dev/null +++ b/src/main/resources/examples/serial_label_zpl_barcode_qr.zpl.tpl @@ -0,0 +1,7 @@ +^XA +^CF0,35 +^FO40,40^FDSerial: {{serialNumber}}^FS +^FO40,90^FDPart: {{partNumber}}^FS +^FO40,145^BCN,80,Y,N,N^FD{{serialNumber}}^FS +^FO40,260^BQN,2,6^FDLA,{{serialNumber}}^FS +^XZ diff --git a/src/main/resources/examples/serienbegleitschein.docx b/src/main/resources/examples/serienbegleitschein.docx new file mode 100644 index 0000000..cb30cd4 Binary files /dev/null and b/src/main/resources/examples/serienbegleitschein.docx differ diff --git a/src/main/resources/examples/warenanhaenger.html.tpl b/src/main/resources/examples/warenanhaenger.html.tpl new file mode 100644 index 0000000..34aca70 --- /dev/null +++ b/src/main/resources/examples/warenanhaenger.html.tpl @@ -0,0 +1,12 @@ + + +Warenanhänger + +

Warenanhänger

+

Serial: {{text:serialNumber}}

+

Part: {{text:partNumber}}

+

Order: {{text:workorderNumber}}

+
Barcode
+
QR
+ + diff --git a/src/main/resources/examples/warenanhaenger_pdf.html.tpl b/src/main/resources/examples/warenanhaenger_pdf.html.tpl new file mode 100644 index 0000000..337fb3d --- /dev/null +++ b/src/main/resources/examples/warenanhaenger_pdf.html.tpl @@ -0,0 +1,23 @@ + + + + + + + +
+

Warenanhänger

+

Serial: {{text:serialNumber}}

+

Part: {{text:partNumber}}

+

Order: {{text:workorderNumber}}

+ Barcode +
+ QR +
+ + diff --git a/src/main/resources/web/index.html b/src/main/resources/web/index.html new file mode 100644 index 0000000..5a25e5a --- /dev/null +++ b/src/main/resources/web/index.html @@ -0,0 +1,399 @@ + + + + + + Printservice Standalone + + + +
+

Printservice Standalone

+
Eigenständige Java-25-Applikation · REST Druckservice · Drucker-Aliase · HTML→PDF mit Tabellen · PDF-Koordinatenvorlagen · DOCX-Vorlagen · ZPL/EPL-Rohdruck · Barcode · QR-Code · Ordnerbasierte Vorlagenspeicherung
+
+
+

Status

Bereit.
+
+

Authentifizierung, Benutzer und Token

+
+ Authentifizierung ist optional. Wenn sie deaktiviert ist, kann jeder, der den REST-Endpunkt erreicht, drucken. Wenn sie aktiviert wird, kann der Benutzer im Webfrontend angelegt werden und erhält ein Token. Das Token wird als Header X-Printservice-Token oder Authorization: Bearer ... gesendet. +
+
+
+

Token im Browser nutzen

+ + + +
Noch nicht geladen.
+
+
+

Auth einschalten / ausschalten

+
+
+
+
+ + +
Empfehlung: zuerst Admin-Benutzer anlegen, Token speichern, dann Auth aktivieren.
+
+
+

Benutzer / Token anlegen

+ + + + + + +
+
+

Benutzerliste

+
UserNameTokenScopesAktiv
+

Neues Token - nur einmal sichtbar

+
Noch kein Token erzeugt.
+
+ +
+

Speicherstruktur

+
+ Vorlagen werden produktionsnah als Ordner gespeichert: templates/<templateName>/template.json, document.*, fields.json, sample-data.json und print-request.json. Dadurch bleiben PDF/DOCX/HTML-Dateien auffindbar, sicherbar und separat versionierbar. +
+ +
Noch nicht geladen.
+
+ +
+

1. Windows-/Server-Drucker-Assistent

+
+ Der Assistent erkennt, ob der Service unter Windows und wahrscheinlich unter LocalSystem laeuft. Danach zeigt er die passenden Wege: gefundene Spooler-Drucker nutzen, lokale TCP/IP-Drucker serverweit installieren, Printserver-Drucker maschinenweit verbinden oder ZPL/EPL direkt per RAW_TCP drucken. +
+ + +
Noch keine Diagnose geladen.
+
+
+

A. Gefundene Windows-Spooler-/CUPS-Drucker

+
Geeignet fuer PDF, HTML->PDF, PDF_TEMPLATE und auch RAW-Druck ueber installierte Treiber.
+
SystemnameStandardAktion
+
+
+

B. RAW_TCP fuer ZPL/EPL

+
Schnellster Weg fuer Etikettendrucker. Umgeht den Windows-Spooler und funktioniert auch bei LocalSystem.
+ + + + +
+
+

C. Windows-Kommandos

+
Diese Hinweise werden vom Server geliefert und passen zum Dienstkontext.
+
Noch nicht geladen.
+
Noch nicht geladen.
+
+
+
+ +
+

2. Eindeutigen Druckernamen zuweisen

+
+
+
+
+
+
+ + +
AliasTypZielAktiv
+
+ +
+

3. Dokumentenvorlage einchecken oder online erstellen

+
+ Schnellster Druckpfad: ZPL/EPL direkt als Rohdaten oder PDF_TEMPLATE für fertige PDF-Vorlagen mit Koordinatenfeldern. HTML→PDF bleibt für Webvorlagen nutzbar und unterstützt Tabellen mit JSON-Wiederholungsblöcken wie {{#each:items}}. + PDF-Koordinatenfelder nutzen x/y/width/height in pt oder mm; Text, Barcode und QR werden direkt ins PDF gestempelt. +
+
+
+ + + + + + + + + +
+
+ + + +
+
+
+
+
+
+
+ +
+
+ + + + + + + + +
+
+ +
+ + +
Der Designer ist für HTML-Vorlagen gedacht. DOCX bitte in Word oder LibreOffice bearbeiten und erneut einchecken.
+
+
+

Eingecheckte Vorlagen

+
NameDokumentTypSpeicherGrößeGeändert
+

Barcode-Arten

Noch nicht geladen.
+
+ +
+

4. JSON übergeben, Vorschau erzeugen und drucken

+
+
+ + +
+
+
+
+
+ + + + + + + + + +
+
+

Antwort / gerenderter Inhalt

Noch kein Druckjob.
+

HTML-Vorschau

+
+
+
+ +
+

5. Druckwarteschlange / Druckhistorie und Redruck

+
+ Der Printservice speichert jeden gesendeten Druckjob unter queue/print-jobs.json. Damit kann ein Benutzer seine eigenen Jobs erneut drucken. Ein Admin kann mit full=true die volle Liste abrufen und filtern. +
+
+
+
+
+
+
+
+
+

+
+ +
ZeitStatusUserDruckerVorlageKopienJob-ID
+

Job-Details

Noch kein Job ausgewählt.
+
+ +
+ + +