# 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.