OPENOFFICE-DOKUMENTE SELBST MODIFIZIEREN - EINE ANREGUNG MIT BEISPIELEN PYCON 2012, LEIPZIG
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
OpenOffice-Dokumente selbst modifizieren Eine Anregung mit Beispielen PyCon 2012, Leipzig Reinhard Wobst r.wobst@gmx.de R.Wobst, OpenOffice-Dokumente selbst modifizieren 1/18
1. Anliegen ODF (Open Document Format) ist ein schönes Format, und ich arbeite gern mit LibreOffice ☺ Leider ist unakzeptabel viel Handarbeit nötig, wenn man viele Dokumente in gleicher Weise bearbeiten muss oder ein Dokument an vielen Stellen zu modifizieren ist und das nicht mit einfachem Suchen+Ersetzen oder Makros erledigt werden kann, oder Fehler wie nicht aufgelöste Querverweise gesucht werden müssen. Es gibt bereits eine Reihe Tools, die Open/LibreOffice steuern (wie pyUNO) oder ODF-Dokumente bearbeiten (vgl. 8), aber diese sind schwierig zu nutzen, oder noch in Entwicklung, oder leisten nicht das Gewünschte. R.Wobst, OpenOffice-Dokumente selbst modifizieren 2/18
Dabei sind ODF-Files nichts anderes als zip-Archive von XML-Files, wie man z.B. mit dem vim leicht sehen kann - BEISPIEL: t.zip Idee: Das Modifizieren vor allem von content.xml so bequem wie möglich machen und das spezielle Anliegen selbst programmieren. R.Wobst, OpenOffice-Dokumente selbst modifizieren 3/18
2. Vorgehen content.xml wird mit xml.etree.ElementTree geparsed man stützt sich auf die recht gut lesbare ODF-Dokumentation [3] Vorgehen vorerst vorwiegend heuristisch, Analyse des Formats zuerst mit Editor, dann mittels Standard Auf Optimierungen (RAM, CPU) braucht man kaum Rücksicht zu nehmen vorerst nur Lösen konkreter Probleme, daraus könnte sich später ein bequemeres Interface ergeben. R.Wobst, OpenOffice-Dokumente selbst modifizieren 4/18
2.1. Klasse ResolveODF Die Klasse übernimmt nur das Bereitstellen und Rückschreiben eines XML- Baums; die Modifikation des etree's erfolgt durch das aufrufende Programm. Methoden: __init__(ODF_file, zipentry='content.xml') Konstruktor getIterator() Liefert Iterator über den etree-Baum, kann mehrfach gerufen werden und liefert dabei unabhängige Bäume - vgl. 6. R.Wobst, OpenOffice-Dokumente selbst modifizieren 5/18
writeBack(backup=True) schreibt den ggf. modifizierten etree-Baum zurück, überschreibt das alte ODF-File und legt optional noch ein Backup ODF_file.bck an. 2.2. Besonderheiten zu beachten! Es muss einen Eintrag mimetype im zip-Archiv geben mit einem Eintrag wie application/vnd.oasis.opendocument.text - aber dieser darf nicht komprimiert sein, damit der Filetyp erkannt werden kann. Bis Python2.6 konnte man die Kompressionsmethode bei ZipFile.writestr() nicht angeben, daher wird mit einem temporären File gearbeitet: R.Wobst, OpenOffice-Dokumente selbst modifizieren 6/18
fdo = zipfile.ZipFile(self.name, 'w', zipfile.ZIP_DEFLATED) for mem in self.members: if mem == 'mimetype': # the zipfile.write method forces a # named file as argument _fd = tempfile.NamedTemporaryFile() _fd.write(self.entries[mem]) _fd.seek(0) #! fdo.write(_fd.name, 'mimetype',\ zipfile.ZIP_STORED) _fd.close() else: fdo.writestr(mem, self.entries[mem]) ... fd.seek(0): temporäres File muss gelesen, darf aber noch nicht geschlossen werden! R.Wobst, OpenOffice-Dokumente selbst modifizieren 7/18
3. Beispiel: Tabellenspalten vertauschen Sicher von beschränktem praktischen Wert: In einem Office-Writer-File sollen bei allen Tabellen die ersten beiden Spalten vertauscht werden: handle = ResolveODF(sys.argv[1]) ... for elem in handle.getIterator(): if stripns(elem.tag) == 'table-row': ch = elem.getchildren() h = ch[0]; ch[0] = ch[1]; ch[1] = h handle.writeBack() VORFÜHRUNG: Tabcolex.py R.Wobst, OpenOffice-Dokumente selbst modifizieren 8/18
Hinderlich ist, dass beim Tagname immer die Namensräume (bei ODF recht lang) vorangesetzt werden - hier per einfacher Funktion stripns() herausgefiltert. Bemerkungen Ab Python2.7 kann elem.getchildren() auch durch list(elem) ersetzt werden. Der ElementTree-Baum sollte beim Parsen nicht modifiziert werden, aber es funktioniert hier ☺ (vgl. a. 5). Ebenso könnte man nach Spalten sortieren. Das kann LibreOffice auch - aber nicht mehr bei lexikografischer Sortierung nach mehreren Spalten oder schwierigen Sortierkriterien. Sortieren nach Zeilen wird schwieriger. R.Wobst, OpenOffice-Dokumente selbst modifizieren 9/18
4. Beispiel: manuelle Änderungen finden "Änderungen von Hand" (Setzen von Fonts, Auszeichnung wie kursiv, fett, Fontfarbe) mitten im Text sind eine weit verbreitete Unsitte, die geforderte Korrektur wird sehr teuer bis fast unmöglich. Spätestens ein Setzer verlangt konsequente Anwendung von Styles, auch bei Zeichen! → Wünschenswert wäre wenigstens eine Markierung der "Sünden", Korrektur von Hand ist sowieso nötig. Das Skript FindManual.py leistet das: VORFÜHRUNG Hier wird heuristisch ausgenutzt, dass die automatisch generierten Textstyles Namen der Form Tnn haben, nn = Ziffernfolge, haben (nicht Teil R.Wobst, OpenOffice-Dokumente selbst modifizieren 10/18
vom Standard!), und Formatierungen mitten im Text mit dem Tag span markiert sind. Leider können damit von Hand markierte Absätze nicht so einfach erfasst werden. R.Wobst, OpenOffice-Dokumente selbst modifizieren 11/18
5. Beispiel: Unerwünschte Kapitel löschen Dieses Beispiel ist nicht trivial und zeigt prinzipielle Probleme des ODF- Designs: Kapitel sind hierarchisch strukturiert, das XML-File spiegelt das nicht wieder. Anliegen ist, alle Kapitel einer vorgebenen Gliederungsebene, deren Überschrift einen gegeben Text enthält, zu löschen - samt aller Unterkapitel. Das ist mit dem Navigator nicht möglich; man muss die zu löschenden Kapitel von Hand markieren und löschen - keine Option bei 100 Dokumenten. R.Wobst, OpenOffice-Dokumente selbst modifizieren 12/18
Es gibt drei Probleme: ElementTree erlaubt das Löschen von Knoten nur vom Elternknoten aus, dieser wieder aber nicht mitgeführt. Der Überschriftstext muss nicht das Text-Attribut des Überschrifts- Knotens sein, wenn dort ein z.B. Bookmark gesetzt ist (wird auch automatisch eingefügt). Die fehlende Hierarchie im XML, wie oben erwähnt. Das zwingt zu Tricks, wie in Remove.py gezeigt: VORFÜHRUNG Das Löschen wird über ein Flag delete vorgemerkt, das via Überschriften-Level ein- und ausgeschaltet wird. Die Elternknoten werden hier recht umständlich gesucht. Problem: Im Beispiel wird eine Referenz zerstört - Anregung für ... R.Wobst, OpenOffice-Dokumente selbst modifizieren 13/18
6. Beispiel: Nicht aufgelöste Referenzen markieren Referenzen werden intern durch Tags wie bookmark-start gesetzt, der automatisch vergebene Wert steht im Attribut name. Der Verweis auf die Referenz geschieht in Tags bookmark-ref, ebenfalls über das Attribut name. Dazu muss das Dokument zweimal geparsed werden - das ist mit der Methode getIterator() kein Problem, da sie auch mehrfach aufgerufen werden kann. VORFÜHRUNG: MissingRef.py Anmerkung: Der Text "Referenz nicht gefunden" kann nicht gesucht werden, er wird von LibreOffice erst bei der Anzeige erzeugt. R.Wobst, OpenOffice-Dokumente selbst modifizieren 14/18
7. Anregungen, Probleme Besonders einfach: Textstellen oder Attribute (z.B. Farben, Schnitt) finden und ändern - auch Anpassung an vorgegebene Farbtabelle bei großer Zahl von Dokumenten ist denkbar. Änderung kann kontextabhängig sein (z.B. nur bestimmte Tabellen sortieren) - vgl. Beispiel in 5. Modifikation der Baumstruktur ist u.U. heikel und nicht ganz einfach; ganz sicher ist das parallele Erzeugen eines neuen Baums (der handle.tree ersetzt). Es ist hinderlich, dass LibreOffice automatische Styles ohne Notwendigkeit einfügt - daher auch ein "XXX" zuviel im Ergebnis von FindManual.py. R.Wobst, OpenOffice-Dokumente selbst modifizieren 15/18
Es wäre schön, wenn es ElementTree-Methoden gäbe, die bei der Suche Namensräume ausblenden. Oder man wenigstens an eine Liste von Paaren Kürzel-Namensraum herankäme. Draw-Dokumente sind leider nicht so einfach zu bearbeiten - intern werden zu oft neues Styles erzeugt, oder es ist zusätzliche Intelligenz nötig (etwa beim Umwandeln von Linien- in Kurvenverbinder). Ebenso ist die Font-Verwaltung nicht trivial. Weder ODF-Standard noch dessen Umsetzung sind "strikt". Derzeit sollte man immer prüfen, ob die eigenen Annahmen z.B. über Stylenamen zutreffen. Zunächst mehr Erfahrungen und Anregungen aus der Praxis gesucht, erst dann Entwicklung von Tools - oder auch z.B. von OOopy, s. 8. Wunder soll man nicht erwarten: Für optimale Spaltenbreiten bei Tabellen gibt es z.B. keinen optimalen Algorithmus ☹. R.Wobst, OpenOffice-Dokumente selbst modifizieren 16/18
8. Konkurrenz Makros zielen in anderen Richtung pyUNO ist kompliziert, wenig dokumentiert, dient zur "Fernsteuerung von LibO/OO"; wird z.B. vom Konvertierungstool unoconv genutzt. ODFpy als Paket in Distr, sehr schlechte Doku, Ergebnis z.B. von odf2xhtml unakzeptabel: VORFÜHRUNG OOopy ist offenbar noch rudimentär (http://ooopy.sourceforge.net/), obwohl Substitutionen implementiert sind. ? (noch zahlreiche andere Referenzen, aber bisher nichts Nützliches gefunden) R.Wobst, OpenOffice-Dokumente selbst modifizieren 17/18
9. Links [1] Quellcode und Dokumentation: https://bitbucket.org/ReinhardW/resolveodf [2] Einführung in ODF: http://www.langintro.com/odfdom_tutorials/odf_internals.html [3] ODF Standard: https://www.oasis-open.org/ > Standards > ODF1.1 > Open Document v1.1 as PDF (der Standard von 1.2 ist in mehrere Teile zerlegt) R.Wobst, OpenOffice-Dokumente selbst modifizieren 18/18
Sie können auch lesen