17 KiB
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_SPOOLERundRAW_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_FOohne 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/<templateName>/...
Zielplattform
- Java 25
- Maven 3.9 oder neuer empfohlen
- Windows, Linux oder macOS
- Windows-Dienstbetrieb über WinSW
Build
mvn clean package
Ergebnis:
target/printservice-standalone.jar
Start als Konsole
Windows:
java -Dprintservice.config.dir=C:\printservice -jar target\printservice-standalone.jar --host=0.0.0.0 --port=8080
Linux:
java -Dprintservice.config.dir=/opt/printservice -jar target/printservice-standalone.jar --host=0.0.0.0 --port=8080
Danach:
http://localhost:8080/
http://localhost:8080/api/diagnostics/system
Konfigurationsstruktur
Empfohlen unter Windows:
C:\printservice
├── auth.json
├── printers.json
├── queue
│ └── print-jobs.json
├── templates
│ └── <templateName>
│ ├── template.json
│ ├── document.*
│ ├── fields.json
│ ├── sample-data.json
│ └── print-request.json
├── spool
├── archive
├── preview
└── logs
Ohne -Dprintservice.config.dir nutzt die Anwendung:
%USERPROFILE%\.printservice
Windows-Service mit WinSW
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:
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.
{
"enabled": false,
"protectPrint": true,
"protectRenderedDownload": true,
"protectPreview": false,
"protectAdmin": false,
"headerName": "X-Printservice-Token",
"tokens": []
}
Empfohlener Ablauf im Webfrontend
- Webfrontend öffnen.
- Bereich Authentifizierung, Benutzer und Token.
- Erst einen Admin-User anlegen, Scope
ALL. - Token kopieren und sicher speichern.
- Danach Auth aktivieren.
- Optional
protectAdminaktivieren. - Für Maschinen/Clients eigene User mit Scope
PRINTanlegen.
Token werden serverseitig nur als SHA-256-Hash gespeichert. Das Klartext-Token wird nur einmal bei der Erstellung zurückgegeben.
REST: Benutzer/Token anlegen
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:
{
"userName": "line01",
"displayName": "Linie 01",
"tokenName": "line01-print-token",
"enabled": true,
"scopes": ["PRINT", "PREVIEW"]
}
Antwort enthält das Token einmalig:
{
"userName": "line01",
"tokenName": "line01-print-token",
"token": "NUR_EINMAL_SICHTBAR"
}
REST: Auth einschalten
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
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:
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
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
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
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
curl http://localhost:8080/api/queue/JOB_ID \
-H "X-Printservice-Token: YOUR_PRINT_TOKEN"
Redruck
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:
{
"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.
{
"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.
{
"alias": "LABEL_LINE_01",
"type": "RAW_TCP",
"host": "192.168.10.50",
"port": 9100,
"language": "ZPL",
"description": "Zebra Etikettendrucker direkt per TCP"
}
REST-Endpunkte
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
<img src="{{barcode:CODE_128:serialNumber:360x90}}">
<img src="{{qr:serialNumber}}">
<img src="{{data_matrix:serialNumber:120x120}}">
ZPL direkt:
^FO40,145^BCN,80,Y,N,N^FD{{serialNumber}}^FS
^FO40,260^BQN,2,6^FDLA,{{serialNumber}}^FS
HTML-Tabelle
<table>
<tbody>
{{#each:items}}
<tr>
<td>{{text:pos}}</td>
<td>{{text:partNumber}}</td>
<td>{{text:description}}</td>
<td>{{text:quantity}}</td>
<td><img src="{{barcode:CODE_128:serialNumber:360x90}}"></td>
</tr>
{{/each}}
</tbody>
</table>
PDF_TEMPLATE-Tabelle
{
"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:
{
"renderMode": "DOCX",
"docxConverter": "DOCX4J_FO"
}
Ablauf:
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_TEMPLATEoderHTML -> PDFweiterhin robuster und meist schneller
Konverterstatus abrufen
curl http://localhost:8080/api/docx/converters
DOCX4J_FO mit einer Vorlage validieren
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:
{
"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
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
{
"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_TCPfür ZPL/EPL-Etikettendrucker verwenden.PDF_TEMPLATEfür feste A4-Formulare verwenden.HTML -> PDFfür flexible Tabellenformulare verwenden.- Authentifizierung mindestens für
/api/printaktivieren. - Admin-Funktionen nach Initialeinrichtung schützen.
- Für Maschinen/Stationen separate Tokens mit eigenem User verwenden.
C:\printserviceregelmäßig sichern, besonderstemplates,printers.json,auth.jsonundqueue/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 <br>, <img>, fehlende </td> oder ein UTF-8-BOM vor <html> kann sonst mit einem XML-Fehler abbrechen, zum Beispiel:
Markup im Dokument vor dem Root-Element muss ordnungsgemäß formatiert sein.
Der Service führt deshalb vor dem Rendern aus:
HTML-Vorlage + JSON-Daten
→ Platzhalter und Tabellenblöcke rendern
→ BOM/ungültige Steuerzeichen entfernen
→ HTML-Fragmente bei Bedarf in <html><body>...</body></html> einbetten
→ Browser-HTML mit jsoup zu XHTML normalisieren
→ openhtmltopdf → PDF
→ Druck oder Download
Zusätzlich gibt es einen Validierungsendpunkt:
POST /api/html/validate
Beispiel:
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:
{
"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 <br>, <hr> oder <img> ohne schließenden Slash.
Validierung vor dem Druck:
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
config-example/templates/lieferschein_pdf_simple/
├── template.json
├── document.pdf
├── fields.json
├── sample-data.json
└── print-request.json
DOCX-Lieferscheinvorlage
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:
{{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:
- Diagnose laden.
- Liste A pruefen und bei Bedarf einen Spooler-/CUPS-Drucker als
WINDOWS_SPOOLERuebernehmen. - Fuer Zebra/Sato/CAB/Etikettendrucker den Button B. RAW_TCP fuer ZPL/EPL nutzen.
- Fuer Windows-Serverinstallation den Button C. Windows-Kommandos oeffnen.
Druckwarteschlange leeren
Die Druckwarteschlange des Printservice ist eine eigene JSON-basierte Historie unter:
<configDir>/queue/print-jobs.json
Sie ist nicht die Windows-Spooler-Warteschlange. Manuell komplett leeren:
curl -X DELETE http://localhost:8080/api/queue \
-H "X-Printservice-Token: YOUR_ADMIN_TOKEN"
Nur Jobs aelter als X Tage loeschen:
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.
{
"enabled": false,
"storeRequestsForReprint": false,
"autoCleanupEnabled": false,
"retentionDays": 0,
"maxStoredJobs": 0
}
Speichern per REST:
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.
{
"enabled": true,
"storeRequestsForReprint": false,
"autoCleanupEnabled": true,
"retentionDays": 14,
"maxStoredJobs": 1000
}
Queue-Einstellungen abrufen
curl -X GET http://localhost:8080/api/queue/settings \
-H "X-Printservice-Token: YOUR_ADMIN_TOKEN"
Queue-Einstellungen speichern
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