# Printservice Standalone Java 25 v2.5 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.5 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.5 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.5: 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. ## v2.5: Uebersichtlicher Windows-Assistent und Queue-Datenschutz ### Windows-/Drucker-Assistent im Webfrontend Der Bereich **A. Gefundene Windows-Spooler-/CUPS-Drucker** ist jetzt als breite, scrollbar lesbare Liste umgesetzt. Die Bereiche **B. RAW_TCP fuer ZPL/EPL** und **C. Windows-Kommandos** wurden in eigene Modal-Dialoge verschoben. Dadurch bleiben lange Windows-Druckernamen lesbar und der Anwender wird Schritt fuer Schritt gefuehrt: 1. Diagnose laden. 2. Liste A pruefen und bei Bedarf einen Spooler-/CUPS-Drucker als `WINDOWS_SPOOLER` uebernehmen. 3. Fuer Zebra/Sato/CAB/Etikettendrucker den Button **B. RAW_TCP fuer ZPL/EPL** nutzen. 4. Fuer Windows-Serverinstallation den Button **C. Windows-Kommandos** oeffnen. ### Druckwarteschlange leeren Die Druckwarteschlange des Printservice ist eine eigene JSON-basierte Historie unter: ```text /queue/print-jobs.json ``` Sie ist nicht die Windows-Spooler-Warteschlange. Manuell komplett leeren: ```bash curl -X DELETE http://localhost:8080/api/queue \ -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" ``` Nur Jobs aelter als X Tage loeschen: ```bash curl -X POST "http://localhost:8080/api/queue/cleanup?olderThanDays=30" \ -H "Content-Type: application/json" \ -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" \ -d "{}" ``` ### Queue aus Datenschutzgruenden deaktivieren Wenn keine Druckhistorie gespeichert werden soll, kann die Queue deaktiviert werden. Drucken funktioniert weiter, aber Jobs werden nicht gespeichert und Redruck ist nicht moeglich. ```json { "enabled": false, "storeRequestsForReprint": false, "autoCleanupEnabled": false, "retentionDays": 0, "maxStoredJobs": 0 } ``` Speichern per REST: ```bash curl -X PUT http://localhost:8080/api/queue/settings \ -H "Content-Type: application/json" \ -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" \ -d @src/main/resources/examples/rest-disable-queue-for-privacy-request.json ``` ### Queue aktiv, aber ohne Originalrequest Fuer eine reduzierte Historie kann die Queue aktiv bleiben, ohne den kompletten Druckrequest zu speichern. Dann bleiben Metadaten wie Zeit, User, Drucker, Vorlage und Status erhalten, aber kein Redruck aus der Historie. ```json { "enabled": true, "storeRequestsForReprint": false, "autoCleanupEnabled": true, "retentionDays": 14, "maxStoredJobs": 1000 } ``` ### Queue-Einstellungen abrufen ```bash curl -X GET http://localhost:8080/api/queue/settings \ -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" ``` ### Queue-Einstellungen speichern ```bash curl -X PUT http://localhost:8080/api/queue/settings \ -H "Content-Type: application/json" \ -H "X-Printservice-Token: YOUR_ADMIN_TOKEN" \ -d @src/main/resources/examples/rest-update-queue-settings-request.json ```