Initial commit
This commit is contained in:
228
README.md
228
README.md
@@ -1,3 +1,227 @@
|
||||
# libreoffice-synopse-builder
|
||||
# Synopse aus Änderungsverfolgung (LibreOffice Writer)
|
||||
|
||||
Erstellt eine Synopse aus zwei Writer-Dokumenten oder aus einem Dokument mit aktiver Änderungsverfolgung
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user