Files
libreoffice-synopse-builder/README.md
2026-09-11 12:47:00 +02:00

10 KiB
Raw Blame History

Synopse aus Änderungsverfolgung (LibreOffice Writer)

Erzeugt aus einem Writer-Dokument mit nachverfolgten Änderungen ein neues Dokument mit einer Synopse-Tabelle.

  • Spalte links: Ursprungsfassung (alle Änderungen verworfen)
  • Rechte Seite: wahlweise in drei Darstellungen
Modus Aufbau geeignet für
kumuliert eine Spalte mit der Endfassung Abnahme einer konsolidierten Fassung
gestapelt eine Spalte, darin die Personen untereinander, optional mit Endfassung als Abschluss viele Beteiligte
getrennt je Person eine eigene Spalte, optional gefolgt von der Endfassung zwei bis drei Beteiligte

Optional wird in beiden mehrspaltigen Modi zusätzlich die Endfassung ausgewiesen: im gestapelten Modus als letzter Eintrag jeder Zelle, bei getrennter Darstellung als zusätzliche Spalte ganz rechts. Sie zeigt den Wortlaut, der beschlossen werden soll — ungekürzt und ohne Streichungen, da diese in derselben Zeile ohnehin sichtbar sind. Einfügungen bleiben markiert, damit erkennbar ist, was gegenüber der Ursprungsfassung neu ist.

Im gestapelten Modus erscheint pro Zeile nur, wer dort tatsächlich etwas geändert oder kommentiert hat. Jeder Eintrag beginnt mit einer dünnen Trennlinie und dem Namen in der Autorenfarbe. Wer an einer Stelle nur kommentiert hat, wird mit dem Zusatz „nur Anmerkung“ geführt und bekommt keine Textfassung sie wäre mit der Ursprungsfassung identisch.

Streichungen in der Endfassung

Die Endfassung zeigt keinen durchgestrichenen Text — sonst wäre sie kein lesbarer Beschlusstext. Damit Streichungen trotzdem erkennbar bleiben, wird nach Fall unterschieden:

Fall Darstellung
Text unmittelbar ersetzt nur die Neufassung, hervorgehoben
Abschnitt ersatzlos gestrichen kursiver Hinweis „ersatzlos gestrichen"
Teil eines Satzes gestrichen letztes verbliebenes Wort davor und erstes danach fett hervorgehoben

Der dritte Fall macht die Nahtstelle sichtbar, ohne den Satz zu zerreißen:

Ursprung    Bei diesem Satz wird der Mittelteil, der eigentlich nicht
            gebraucht wird, weggelassen.

Endfassung  Bei diesem Satz wird der **Mittelteil weggelassen.**

Die Logik steckt in _clean_runs(). „Unmittelbar ersetzt" heißt dabei: der nächste vorhandene Nachbar der Streichung ist eine Einfügung — dann trägt diese bereits die Markierung und es wird nichts zusätzlich hervorgehoben.

Kürzung unveränderter Passagen

Damit die Zellen auch bei langen Absätzen lesbar bleiben, lassen sich unveränderte Passagen auf den Kontext um die Änderung kürzen (voreingestellt 40 Zeichen je Seite, an der Wortgrenze geschnitten, gekennzeichnet mit ). Die Kürzung wirkt auf die Änderungsseite; die Spalte „Ursprungsfassung“ bleibt vollständig, damit der Bezugstext nachlesbar ist.

Ursprung   Die Feuerwehr … hält für den Grundschutz … bereit, die
           regelmäßig geprüft werden. rückt aus. Die Feuerwehr …

Anna       … die regelmäßig geprüft werden. r̶ü̶c̶k̶t̶ fährt aus. Die …

Die Fassung einer Person ist dabei jeweils diejenige, die entstünde, wenn ausschließlich deren Änderungen übernommen würden. Einfügungen sind unterstrichen und in der Autorenfarbe dargestellt, Löschungen optional durchgestrichen.

Anmerkungen

Erklärungsbedürftige Änderungen lassen sich kommentieren. Berücksichtigt werden beide Quellen, die Writer dafür anbietet:

  • Kommentare (Einfügen ▸ Kommentar, Strg+Alt+C) wahlweise an einer Stelle oder über einen markierten Bereich
  • Kommentare zu einer Änderung (Bearbeiten ▸ Änderungsverwaltung ▸ Verwalten ▸ Bearbeiten…), also das Feld RedlineComment der Redline

Die Anmerkung erscheint unterhalb des Textes in derselben Zelle, bei getrennter Darstellung in der Spalte der jeweiligen Person, im gestapelten Modus unter deren Eintrag, bei kumulierter Darstellung gesammelt in der Spalte „Endfassung“. Die Spalte „Ursprungsfassung“ bleibt frei von Anmerkungen.

Optisch abgesetzt wird über vier gleichzeitig wirkende Merkmale, damit die Unterscheidung auch im Schwarzweißdruck trägt:

Merkmal Wirkung
senkrechter Balken links Absatzrahmen LeftBorder in Autorenfarbe
flächige Hinterlegung ParaBackColor, auf 10 % aufgehellte Autorenfarbe
Einzug und Abstand ParaLeftMargin, ParaTopMargin
kleinerer Schriftgrad 8,5 pt gegenüber 10 pt im Fließtext

Zur Abgrenzung davon nutzt die Personen-Überschrift im gestapelten Modus eine waagerechte Linie (TopBorder) ohne Hinterlegung Balken links bedeutet also stets „Anmerkung“, Linie oben „neue Person“.

Der Kopf der Anmerkung nennt in Autorenfarbe und fett den Namen, bei Änderungskommentaren ergänzt um „– zur Änderung“, bei erledigten Kommentaren um „(erledigt)“. Bei Kommentaren über einen Textbereich wird die kommentierte Stelle optional als kursives Zitat (gekürzt auf 80 Zeichen) vorangestellt.

Zweite Funktion: zwei Dokumente vergleichen

Synopse ▸ Zwei Dokumente vergleichen… listet alle geöffneten Writer-Dokumente auf. Auszuwählen sind Quellfassung und Endfassung; das gerade aktive Dokument ist als Endfassung vorbelegt.

Spalte Inhalt
links Textstellen der Quellfassung, entfallene Wörter durchgestrichen (rot)
Mitte Textstellen der Endfassung, neue Wörter unterstrichen (blau)
rechts Kommentare aus der Endfassung

Die dritte Spalte entfällt automatisch, wenn die Endfassung keine Kommentare enthält. Alternativ lassen sich die Kommentare unter die Textstelle in der mittleren Spalte setzen, dann bleibt es bei zwei Spalten.

Enthält eines der Dokumente selbst nachverfolgte Änderungen, wird dessen Fassung „alle Änderungen übernommen" verglichen.

Zuordnung

Anders als bei der Änderungsverfolgung gibt es hier keine Redlines, aus denen sich die Zuordnung ablesen ließe sie wird in zwei Stufen berechnet:

  1. Absatzebene difflib.SequenceMatcher über die normalisierten Absatztexte. Innerhalb geänderter Abschnitte werden Absätze über ihre Ähnlichkeit gepaart (MATCH_MIN = 0.30, Suchfenster LOOKAHEAD = 3); was darunter bleibt, gilt als entfallener bzw. neuer Absatz und bekommt eine einseitige Zeile.
  2. Wortebene ein zweiter Durchlauf innerhalb jedes Absatzpaares markiert die abweichenden Wörter.

Die beiden Schwellwerte stehen am Kopf von compare.py. Bei stark umformulierten Texten kann MATCH_MIN zu senken sinnvoll sein, bei stark wiederholenden Texten (Formulare, Tabellen) eher zu erhöhen.

Bauen und Installieren

./build.sh
unopkg add --force synopse-0.6.1.oxt

Danach im Writer: Menü Synopse ▸ Synopse erzeugen…

Zum Entfernen:

unopkg remove de.example.synopse

Aufbau

Datei Zweck
synopse_handler.py UNO-Komponente (Protocol Handler), Einstiegspunkt
pythonpath/synopse/redline_model.py Extraktion von Segmenten und Anmerkungen
pythonpath/synopse/builder.py Aufbau des Zieldokuments (Tabelle, Formatierung)
pythonpath/synopse/compare.py Vergleich zweier Dokumente (difflib, zweistufig)
pythonpath/synopse/dialog.py Optionsdialog
Addons.xcu, ProtocolHandler.xcu Menüeintrag und Registrierung

Modell

Der Text wird einmal linear durchlaufen. Dabei wird ein Stack der gerade offenen Redlines geführt; jedes Textstück (Segment) merkt sich diesen Stack. Aus dem Stack lässt sich jede Fassung ableiten:

Zustand des Segments Ursprung Endfassung Fassung von X
unverändert ja ja ja
eingefügt von X nein ja ja
eingefügt von Y nein ja nein
gelöscht von X ja nein nein
gelöscht von Y ja nein ja
von Y eingefügt, von X gelöscht nein nein nein

Absätze werden zu einer Tabellenzeile zusammengefasst, wenn die Absatzmarke selbst Teil einer Änderung ist. Dadurch bleiben linke und rechte Spalte auch bei zusammengeführten oder geteilten Absätzen auf gleicher Höhe.

Das Quelldokument bleibt unverändert

Für die Extraktion muss RedlineDisplayType kurzzeitig auf „Einfügungen und Löschungen anzeigen" stehen nur dann liefert die Portion-Enumeration auch den gelöschten Text. extract_blocks() merkt sich deshalb den vorherigen Wert sowie isModified() und stellt beides in einem finally-Block wieder her.

Der Zahlenwert dieser Anzeigeart wird gemessen, nicht geraten: Liefert der Typmanager die Konstante nicht, probiert _display_all_value() die Werte 0 bis 3 durch und nimmt den, bei dem getText().getString() am längsten ist denn nur die Anzeige mit Einfügungen und Löschungen enthält beides. Das Ergebnis wird für die Sitzung gemerkt.

Hintergrund: Die Reihenfolge der Konstanten ist nicht über alle Versionen gleich. Ein geratener Wert kann REMOVED treffen, und dann werden stillschweigend alle Einfügungen ausgeblendet samt der Kommentaranker, die darin liegen.

Hinweis zu UNO-Konstanten

Alle UNO-Konstanten werden über redline_model.const() aufgelöst, das bei einem Fehlschlag auf den dokumentierten Zahlenwert zurückfällt. Grund: löst uno.getConstantByName() beim Import eines Moduls eine RuntimeException aus, bricht der Python-Loader die Registrierung der gesamten Erweiterung ab sichtbar als „Couldn't load … for reason com.sun.star.text.RedlineDisplayType .INSERTED_AND_REMOVED". Zur Importzeit darf deshalb nichts stehen, das den Typmanager zwingend braucht. Gleiches gilt für uno.Enum() und uno.createUnoStruct(), die über enum() bzw. _line_struct() laufen.

Bekannte Grenzen

  • Reine Formatänderungen (RedlineType == "Format") werden ignoriert.
  • Antworten auf Kommentare erscheinen als eigenständige Anmerkungen; die Verkettung über ParentName wird nicht ausgewertet.
  • Personen, die ausschließlich kommentiert haben, erhalten bei getrennter Darstellung eine eigene Spalte. Deren Textfassung entspricht dann der Ursprungsfassung.
  • Absätze aus Tabellenzellen des Quelldokuments werden in die Zeilenfolge eingereiht, die Tabellenstruktur geht dabei verloren.
  • Kopf-/Fußzeilen, Fußnoten und Rahmen werden nicht durchlaufen.
  • Beim Dokumentvergleich werden reine Formatunterschiede nicht erkannt verglichen wird der Text.
  • Verschobene Absätze erscheinen beim Dokumentvergleich als entfallen und neu, nicht als Verschiebung.
  • Absatzformate (Überschriftenebene, Nummerierung) werden nicht übernommen.
  • Verschachtelte Redlines über RedlineSuccessorData werden nur insoweit berücksichtigt, wie LibreOffice sie als überlappende Portions ausliefert.