| README.md | ||
Doc-Factory_LibreOffice_CrossPlatform
Erzeugt Dokumente aus ODT-Vorlagen, ersetzt ggf. Platzhalter und exportiert nach PDF. Läuft unter Windows und Linux.
Anlass
Überblick
Mein Job ist die Pflege und Weiterentwicklung eines .NET-Framework-Projekts auf Windows (als Linux-Fanboy 😢). Auslöser waren Überlegungen über die öffentlich bekannte Standardisierung auf ODF und PDF/UA als Dokumentenformate im Deutschland-Stack. Da die benannte Software für die öffentliche Verwaltung ist, ist die früher oder später betroffen. Weiter habe ich an der Stelle die Chance, einen über Jahre entstandenen Wildwuchs an Dokumenterstellung (die Software dürfte schon Bier trinken und bei der nächsten Bundestagswahl auch wählen) zu vereinheitlichen, modernisieren und damit die Wartung und Pflege (meinen Job) zu vereinfachen.
Die Idee, die ich hier umsetze, ist eine, die im gerade benannten Kontext entstand, die ich aber verworfen habe. Ich finde diese Idee aber vom persönlichen Geschmack und vom technischen Anspruch interessanter. Darum baue ich die jetzt als Hobby, auch wenn ich damit absehbar weit über das ursprüngliche Ziel hinausschieße.
Warum mache ich es, obwohl ich über das ursprüngliche Ziel hinausschieße?
- weil ich Bock drauf hab
- weil ich FLOSS mag
- weil ich Lust habe, was zu basteln, was größer ist als das, was ich im 9to5 sehe
Kurzer Ausflug zur Entscheidungsfindung
Für das .NET-Framework-Projekt ausschlaggebend waren folgende Punkte, es nicht so wie hier zu machen:
- LibreOffice hätte eine neue Abhängigkeit ins Projekt gebracht, die auch gepflegt werden muss (Job wird nicht leichter)
- das Projekt nutzt (und bezahlt) bereits Tools von DevExpress (keine neuen Kosten)
- im Projekt werden bereits Dokumente mit DevExpress auf mehrere Arten verarbeitet (ich hab schon Ähnliches gebaut und weiß, dass es läuft)
- die DevExpress-Tools werden als Lib auf dem bestehenden Weg der Auslieferung verteilt, LibreOffice hätte zusätzlich irgendwie ausgerollt und gepflegt werden müssen und hätte mit Pech auch noch Versions- und andere Konflikte mit anderen LibreOffice-Installationen beim Kunden verursacht.
Wo ich den Boden der Vernunft verlasse und der Grund "leider geil" wird
Dieses Repo skaliert ohne Grund viel weiter als nötig: parallele LibreOffice-Instanzen mit getrennten Profilen, im Container, am Ende im Cluster über völlig an den Haaren herbeigezogene zehntausende Dokumente. Der Skalierungsteil ist Selbstzweck und hiermit als solcher dokumentiert.
Der Kern des Problems
Beim ersten Suchen und Suchenlassen (ja, ich nutze KI-Tools) bin ich da gelandet, dass LibreOffice nicht mehrere Instanzen parallel betreiben kann. Das wäre tricky geworden, wenn User in der Client-App auch ihr eigenes LibreOffice gleichzeitig genutzt hätten oder auf dem Server zwei Prozesse (meiner und ggf. einer, den ich bei der Entwicklung gar nicht kenne) gleichzeitig LibreOffice genutzt hätten. Dass nur eine Instanz laufen kann, stimmt nur bedingt, und weil ich noch andere wilde Überlegungen hatte, hat mir ein Claude-Chat irgendwann gesagt "geht doch!" (okay, okay, das ist das tldr). Wie kam ich/Claude drauf? Ich habe überlegt, ob ich LibreOffice nicht einfach als Lib ins Projekt laden kann oder ggf. bauen kann und so nutzbar machen kann (das wäre je nach Ansatz auch mit der Lizenz ein spannendes Ding geworden).
Wie arbeitet LibreOffice per default? Was Claude mir Schönes dazu gesagt hat:
Es ist immer ein separater soffice-Prozess. Startest du mehrere davon, zeigen alle auf dasselbe User-Profil unter ~/.config/libreoffice/4/user beziehungsweise %APPDATA%\LibreOffice\4\user. Dort liegt eine .lock-Datei. Die zweite Instanz sieht sie, meldet ihren Job an die erste an und beendet sich. Nach außen sieht das nach Parallelität aus, tatsächlich arbeitet eine einzige Instanz alles nacheinander ab. Bei --convert-to kommt es schlimmer: die zweite Instanz schreibt oft keine Ausgabedatei und liefert trotzdem Exit-Code 0.
Der Ausweg ist ein Schalter.
-env:UserInstallation=file:///var/lib/lopool/1
Damit bekommt jede Instanz ihr eigenes Profil und damit ihr eigenes Lock. Zwei Details entscheiden dabei über Erfolg oder stilles Scheitern: es muss eine file:-URL sein, ein nackter Pfad wird ignoriert, und der Parameter gehört vor die Dateiargumente.
Das gilt auch ohne Parallelbetrieb. Läuft die Anwendung als Windows-Dienst oder unter IIS, zeigt %APPDATA% ins Systemprofil unter C:\Windows\System32\config\systemprofile und ist je nach Rechten schreibgeschützt. Dann startet LibreOffice gar nicht erst und meldet nichts Verwertbares.
Status
Um nicht alles in einem Zug durchziehen zu müssen, soll das Projekt in Schritten entstehen, und jeder endet mit etwas Benutzbarem, auch wenn ich danach Lust bekomme, etwas anderem zu folgen.
- 0 Beweis. Zwei Aufrufe, einmal mit und einmal ohne
UserInstallation. Klärt, ob die Prämisse trägt. - 1 Ein Dokument, seriell. ODT laden, Platzhalter ersetzen, PDF schreiben. Bereits ein brauchbares Werkzeug.
- 2 Port zu Windows. Wäre das im 9to5 nützlich oder bricht die Idee am Stack?
- 3 Viele Dokumente, seriell, mit Messung. Wie lange dauert eigentlich pro Doc ein LibreOffice-Start? Wie lange dauert eine serielle Verarbeitung von vielen in einer Instanz?
- 4 Parallel auf einem Host. Wie viele Instanzen kann ich eigentlich auf einem Host laufen lassen?
- 5 Container. Rootless unter Podman.
- 6 Kubernetes. Kubernetes Job. Prepare to be wild.
- 7 Kubernetes Cluster. Kubernetes Job wtf
Die Reihenfolge ist so gewählt, wie ich Lust drauf habe.
Plattformunterschiede
Unter Windows gibt es vier Punkte, an denen es anders läuft als unter Linux.
soffice.com statt soffice.exe. Die .exe ist als GUI-Anwendung gelinkt, kehrt sofort zurück und liefert weder Exit-Code noch Konsolenausgabe. Für Automatisierung nimmst du die .com aus demselben Verzeichnis. Bei direktem Process.Start geht auch die .exe, wenn du selbst auf Exit wartest, stdout bleibt dann leer.
Pfadformat. file:///C:/lopool/1, also drei Slashes und Forward-Slashes im Rest. Leerzeichen im Pool-Pfad vermeiden.
Rendering über GDI. Bei grafiklastigen Dokumenten spürbar langsamer als unter Linux, bei reinem ODT nach PDF kaum relevant.
Dienstkontext. Siehe oben, das Systemprofil ist die häufigste Ursache für einen Start ohne Fehlermeldung.
Platzhalter
Das Ersetzen läuft ohne LibreOffice. Ein ODT ist ein ZIP mit content.xml darin, FODT verzichtet sogar auf den ZIP-Teil und würde für Platzhalter den Job sogar beschleunigen (mal schauen, was ich draus mache). Also reichen ZipArchive und XDocument, vllt. auch string.Replace. LibreOffice bleibt für den PDF-Export zuständig, also für den Teil, der ohnehin nicht anders geht.
Lizenz
LibreOffice steht unter MPL 2.0. Dieses Projekt lädt es nur dynamisch beziehungsweise ruft es als Prozess auf und ändert keine LibreOffice-Quelldateien.