docs: README und Dokumentation auf den aktuellen Stand
README, ARCHITECTURE.md und FILE_FORMAT.md beschreiben jetzt alle Funktionen bis v0.11 (Text, Bilder, PDF-Import, Seitenformate, Lineal, durchgehendes Scrollen, Musterfarben, Dateiformat-Version 8). Werbliche Formulierungen durch überprüfbare Angaben ersetzt, auch in Paket- beschreibung, Desktop-Datei, AppStream-Metadaten und Release-Notizen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+154
-117
@@ -1,153 +1,190 @@
|
||||
# Architektur & Technische Dokumentation von ink-notes
|
||||
# Architektur von ink-notes
|
||||
|
||||
Dieses Dokument beschreibt die interne Architektur, mathematische Modelle und Rendering-Pipelines von `ink-notes`.
|
||||
Dieses Dokument beschreibt, wie ink-notes intern aufgebaut ist: Eingabe und Strichverarbeitung, Rendering, Seiten und Ansicht, Formerkennung, Speichern und Export. Das Speicherformat steht in [`FILE_FORMAT.md`](FILE_FORMAT.md).
|
||||
|
||||
---
|
||||
|
||||
## 1. Die Inking-Pipeline
|
||||
## 1. Überblick
|
||||
|
||||
ink-notes ist ein einzelnes Programm mit einem Event-Loop (`winit`) im Haupt-Thread. Dazu kommen drei Hintergrund-Threads:
|
||||
|
||||
| Thread | Aufgabe |
|
||||
| :--- | :--- |
|
||||
| Haupt-Thread | Eingaben, Datenmodell, Rendering (`wgpu`), Oberfläche (`egui`) |
|
||||
| Autosave | Seiten und Manifest auf die Festplatte schreiben (`autosave.rs`) |
|
||||
| PDF-Rendering | Importierte PDF-Seiten zu Texturen rendern (`pdf_doc.rs`, `hayro`) |
|
||||
| Export | PDF/PNG/SVG-Export, damit die Oberfläche nicht hängt (`main.rs`) |
|
||||
|
||||
Stifteingaben kommen über `octotablet` (Tablet-Protokoll von Wayland bzw. X11), Maus und Touch über `winit`. Die Oberfläche (Werkzeugleiste, Menüs, Dialoge, Textbearbeitung) ist mit `egui` gebaut und wird im selben Render-Durchgang über das Blatt gezeichnet.
|
||||
|
||||
---
|
||||
|
||||
## 2. Vom Stift zum Strich
|
||||
|
||||
```
|
||||
[Hardware Stylus / Touch / Maus]
|
||||
│
|
||||
▼
|
||||
[winit Event-Loop]
|
||||
│ (tx, ty, pressure, timestamp)
|
||||
▼
|
||||
[Viewport Invertierung] (screen_to_page: Umrechnung in DIN-A4-Einheiten)
|
||||
│
|
||||
▼
|
||||
[1€-Filter] (Jitter-Beseitigung bei langsamen Bewegungen)
|
||||
│
|
||||
▼
|
||||
[Centripetal Catmull-Rom] (C1-stetige Spline-Interpolation, alpha = 0.5)
|
||||
│
|
||||
▼
|
||||
[Mesh-Tessellierung] (Drucksensitive Dreiecksstreifen via Normalen)
|
||||
│
|
||||
▼
|
||||
[wgpu GPU-Buffer] (Vertex- & Index-Buffer im VRAM)
|
||||
[Stift / Touch / Maus]
|
||||
│ Bildschirmposition, Druck, Zeit
|
||||
▼
|
||||
[Bildschirm → Seitenkoordinaten] (viewport.rs)
|
||||
│
|
||||
▼
|
||||
[Druckkurve] (settings.rs, vom Nutzer einstellbar)
|
||||
│
|
||||
▼
|
||||
[1€-Filter] (filter.rs)
|
||||
│
|
||||
▼
|
||||
[Catmull-Rom-Spline + Dreiecksstreifen] (spline.rs) → Live-Puffer auf der GPU
|
||||
│ Stift abgehoben
|
||||
▼
|
||||
[Nachbearbeitung] (spline.rs: finalize_stroke)
|
||||
│
|
||||
▼
|
||||
[Strich auf der Seite + Undo-Eintrag] (document.rs, history.rs)
|
||||
```
|
||||
|
||||
### 1.1. 1€-Filter (One-Euro-Filter)
|
||||
Stift- und Touch-Eingaben unterliegen bei niedrigen Geschwindigkeiten oft Mikrozittern (Jitter). Standard-Tiefpassfilter verursachen jedoch bei schnellen Schwüngen eine störende Verzögerung (Latenz).
|
||||
### 2.1 1€-Filter
|
||||
Langsame Bewegungen zittern, schnelle sollen nicht nachhinken. Der 1€-Filter (`filter.rs`) passt seine Grenzfrequenz an die Geschwindigkeit an:
|
||||
|
||||
Der 1€-Filter (`filter.rs`) löst dieses Dilemma mit einer adaptiven Grenzfrequenz $f_c$:
|
||||
$$f_c = f_{c,\min} + \beta \cdot |\dot{x}|$$
|
||||
- Bei geringer Geschwindigkeit ($|\dot{x}| \approx 0$) dominiert $f_{c,\min}$, wodurch Zittern maximal geglättet wird.
|
||||
- Bei schnellen Zügen wächst $\beta \cdot |\dot{x}|$ an, wodurch der Filter durchlässig wird und Latenz eliminiert.
|
||||
|
||||
### 1.2. Centripetal Catmull-Rom Splines
|
||||
Um Ecken zu vermeiden und eine organische Schriftführung zu erzielen, werden Abtastpunkte mit einem zentripetalen Catmull-Rom-Spline (`spline.rs`) interpoliert ($\alpha = 0.5$):
|
||||
$$t_{i+1} = t_i + ||P_{i+1} - P_i||^\alpha$$
|
||||
Diese Parametrisierung garantiert:
|
||||
- Keine Selbstüberschneidungen bei scharfen Wendungen.
|
||||
- Keine unerwünschten Cusp-Schleifen.
|
||||
- Perfekte $C^1$-Stetigkeit an den Stützstellen.
|
||||
Bei langsamer Bewegung glättet er stark, bei schneller kaum.
|
||||
|
||||
### 1.3. Drucksensitive Dreiecksstreifen (Tessellierung)
|
||||
Aus den interpolierten Spline-Punkten mit Radius $r_i = f(\text{pressure}_i)$ wird ein Triangle-Strip erzeugt:
|
||||
1. Richtungsvektor $T_i = P_{i+1} - P_i$.
|
||||
2. Senkrechter Normalenvektor $N_i = (-T_{i,y}, T_{i,x}) / ||T_i||$.
|
||||
3. An inneren Stützstellen werden Normalen benachbarter Segmente gemittelt (Winkelhalbierende).
|
||||
4. Eckpunkte des Dreiecksstreifens:
|
||||
$$V_{i,\text{left}} = P_i + N_i \cdot r_i, \quad V_{i,\text{right}} = P_i - N_i \cdot r_i$$
|
||||
### 2.2 Zentripetaler Catmull-Rom-Spline
|
||||
Die gefilterten Punkte werden mit einem zentripetalen Catmull-Rom-Spline ($\alpha = 0{,}5$) interpoliert:
|
||||
|
||||
### 1.4. Zwei-Stufen-Modell (Live-Glättung vs. akausale Post-Stroke-Finalisierung)
|
||||
Um minimale Latenz beim Zeichnen mit maximaler Präzision und Ecken-Erhaltung zu verbinden, arbeitet `ink-notes` mit einem strikten Zwei-Stufen-Modell:
|
||||
$$t_{i+1} = t_i + \lVert P_{i+1} - P_i \rVert^{\alpha}$$
|
||||
|
||||
1. **Live-Phase (Kausal, Zero-Latency):**
|
||||
- Eingehende Punkte durchlaufen in Echtzeit den 1€-Filter (`filter.rs`).
|
||||
- `spline.rs` tesselliert inkrementell temporäre Triangle-Strips für die unverzögerte Bildschirmdarstellung im GPU-Live-Buffer.
|
||||
2. **Commit-Phase (Akausal, `finalize_stroke`):**
|
||||
- Beim Absetzen des Stifts wird der gesamte Strich akausal analysiert:
|
||||
- **Druckglättung:** Glättung des Druckverlaufs $p$ über ein gewichtetes 3-Punkt-Fenster $[0.25, 0.5, 0.25]$ zur Ausblendung von Digitalisierer-Stufen.
|
||||
- **RDP-Dezimierung:** Ausdünnung kollinearer Zwischenpunkte mit Ramer-Douglas-Peucker ($\epsilon \approx 0.075\text{ mm} = 0.75\text{ Einheiten}$).
|
||||
- **Ecken-Erkennung (Corner Preservation):** Bei Richtungsänderungen $> 65^\circ$ wird die $C^1$-Spline-Kontinuität zu $C^0$ aufgebrochen, sodass Ecken (z. B. 90°-L-Knicke oder Zickzack-Linien) gestochen scharf bleiben und nicht rundgeschliffen werden.
|
||||
- **Runde Kappen & Miter-Joins:** Generierung sauberer runder Kappen an Start und Ende des Triangle-Strips.
|
||||
- Der temporäre Live-Buffer wird vollständig entleert und der bereinigte `Stroke` in den persistenten Puffer der Seite überführt.
|
||||
Diese Parametrisierung erzeugt bei engen Kurven keine Schlaufen oder Spitzen und ist an den Stützstellen $C^1$-stetig.
|
||||
|
||||
### 2.3 Dreiecksstreifen
|
||||
Aus den Spline-Punkten mit Radius $r_i$ (aus Druck und Strichstärke) entsteht ein Dreiecksstreifen:
|
||||
|
||||
$$V_{i,\text{links}} = P_i + N_i \cdot r_i, \qquad V_{i,\text{rechts}} = P_i - N_i \cdot r_i$$
|
||||
|
||||
$N_i$ ist die Normale der Laufrichtung; an inneren Punkten wird über benachbarte Segmente gemittelt.
|
||||
|
||||
### 2.4 Zwei Stufen
|
||||
1. **Während des Schreibens:** Filter und Spline arbeiten nur mit bisherigen Punkten; der Strich wird sofort in einen eigenen Live-Puffer geschrieben.
|
||||
2. **Beim Absetzen** wird der ganze Strich noch einmal bearbeitet (`finalize_stroke`):
|
||||
- Druckverlauf mit dem Fenster $[0{,}25;\ 0{,}5;\ 0{,}25]$ glätten,
|
||||
- Punkte mit Ramer-Douglas-Peucker ausdünnen ($\varepsilon = 0{,}75$ Einheiten $= 0{,}075$ mm),
|
||||
- an Knicken über 65° den Spline auftrennen, damit Ecken nicht abgerundet werden,
|
||||
- runde Endkappen anfügen.
|
||||
|
||||
Danach wird der Live-Puffer geleert und der Strich in die Seite übernommen.
|
||||
|
||||
### 2.5 Lineal
|
||||
Beginnt ein Strich nahe einer Linealkante (`ruler.rs`), werden alle seine Punkte auf diese Kante projiziert, bis der Stift abgehoben wird.
|
||||
|
||||
---
|
||||
|
||||
## 2. Rendering & Kamera-Pipeline (`wgpu`)
|
||||
## 3. Koordinaten, Seiten und Ansicht
|
||||
|
||||
### 2.1. Virtuelles Koordinatensystem
|
||||
Die Anwendung rechnet intern in virtuellen DIN-A4-Koordinaten:
|
||||
- Breite: **2100 Einheiten** (210 mm)
|
||||
- Höhe: **2970 Einheiten** (297 mm)
|
||||
- 1 mm entspricht exakt **10 Einheiten**.
|
||||
### 3.1 Seitenkoordinaten
|
||||
Gerechnet wird in Seiteneinheiten: **10 Einheiten = 1 mm**. Eine DIN-A4-Seite ist 2100 × 2970 Einheiten groß. Jede Seite hat ihre eigene Größe (`Page::size`): Standard ist A4 hoch; beim PDF-Import bekommen Querformat-Seiten 2970 Einheiten Breite und eine Höhe nach Seitenverhältnis (`document::pdf_page_size`). Neue Seiten übernehmen die Größe der aktuellen.
|
||||
|
||||
### 2.2. Viewport & Projektionsmatrix (`viewport.rs`)
|
||||
Der Vertex-Shader transformiert DIN-A4-Koordinaten über eine Orthogonal-Projektion kombiniert mit Pan und Zoom in Normalized Device Coordinates (NDC, $[-1, 1]$):
|
||||
### 3.2 Maßstab und Kamera
|
||||
- `PageViewport` (`document.rs`) passt einen Rahmen, in den alle Seiten des Notizbuchs passen (`Notebook::fit_box`), ins Fenster ein. Daraus ergibt sich ein Maßstab (Bildschirmpixel je Einheit), der für alle Seiten gilt. Die aktuelle Seite liegt waagerecht zentriert und oben im Rahmen. Den Platz der Werkzeugleiste rechnet der Viewport heraus.
|
||||
- `ViewportState` (`viewport.rs`) ergänzt Zoom (25 % bis 1000 %) und Verschiebung (Pan). Beim Zoomen bleibt der Punkt unter dem Mauszeiger stehen:
|
||||
|
||||
```wgsl
|
||||
struct CameraUniform {
|
||||
transform: mat4x4<f32>,
|
||||
};
|
||||
```
|
||||
|
||||
Die Projektionsmatrix $M$ wird berechnet aus:
|
||||
1. Skalierung des DIN-A4-Papiers auf den Viewport unter Berücksichtigung des Seitenverhältnisses.
|
||||
2. Anwenden des Zoom-Faktors $z \in [0.25, 5.0]$.
|
||||
3. Translation durch den Pan-Offset $(p_x, p_y)$.
|
||||
|
||||
### 2.3. Cursor-zentrierter Zoom
|
||||
Beim Zoomen mit dem Mausrad wird der neue Pan-Offset so korrigiert, dass der Punkt unter dem Cursor fixiert bleibt:
|
||||
$$\vec{p}_{\text{neu}} = \vec{c} - (\vec{c} - \vec{p}_{\text{alt}}) \cdot \frac{z_{\text{neu}}}{z_{\text{alt}}}$$
|
||||
|
||||
### 2.4. Render-Passes
|
||||
In jedem Frame werden zwei Hauptpässe durchlaufen:
|
||||
1. **Pass 1 (Canvas):**
|
||||
- Hintergrund-Quad und Linien/Gitter (Instanced oder Batch Vertex-Buffer).
|
||||
- Gespeicherte Striche und Geometrie-Meshes.
|
||||
- Aktiver Live-Strich (Ephemeral Buffer).
|
||||
- Auswahl-Overlay & Transformationsgriffe.
|
||||
2. **Pass 2 (egui UI):**
|
||||
- Floating Glassmorphism-Toolbar.
|
||||
- Farb- und Werkzeug-Popovers.
|
||||
- Modal-Dokumentenmanager.
|
||||
- Im Shader wird jeder Punkt mit `transform_scale` und `transform_offset` (aus `CameraUniforms`) direkt in Gerätekoordinaten umgerechnet.
|
||||
|
||||
### 3.3 Durchgehende Ansicht
|
||||
Alle Koordinaten beziehen sich auf die **aktuelle Seite**. `PageStack` (`viewport.rs`) legt die übrigen Seiten mit 8 mm Abstand darunter bzw. darüber, waagerecht zur aktuellen Seite zentriert. Jede sichtbare Nachbarseite wird mit einer eigenen Kamera gezeichnet, die um die Lage der Seite verschoben ist (höchstens 12 Nachbarseiten).
|
||||
|
||||
Nach dem Scrollen wird die Seite in der Fenstermitte zur aktuellen Seite; die Kamera wird dabei so verschoben, dass sich das Bild nicht bewegt. Während eines Strichs, einer Auswahl, beim Ziehen des Lineals oder beim Schreiben von Text wird nicht gewechselt. Wer auf einer sichtbaren Nachbarseite zu zeichnen beginnt, wechselt zuerst auf diese Seite.
|
||||
|
||||
---
|
||||
|
||||
## 3. Geometrieerkennung & Shape-Fitting (`geometry.rs`)
|
||||
## 4. Rendering
|
||||
|
||||
Die Erkennung erfolgt rein analytisch und deterministisch ohne schwere ML-Frameworks:
|
||||
Ein Frame besteht aus einem Render-Durchgang auf die Fensterfläche (`canvas.rs`):
|
||||
|
||||
### 3.1. Ramer-Douglas-Peucker (RDP)
|
||||
Reduziert den Rohpfad auf signifikante Eckpunkte mit konfigurierbarem Toleranzabstand $\epsilon$.
|
||||
1. Für jede sichtbare Nachbarseite: Papier und Muster, Bilder/PDF-Seite/Text, Tinte.
|
||||
2. Für die aktuelle Seite: Papier, Muster (Karo, Linien, Punkte), Bildebene, gespeicherte Striche, Live-Strich.
|
||||
3. Die `egui`-Oberfläche: Werkzeugleiste, Menüs, Auswahlrahmen und Griffe, Lineal, Textbearbeitung.
|
||||
|
||||
### 3.2. Erkennungsregeln:
|
||||
- **Gerade:** Verhältnis von Sehnenlänge ($||P_{\text{end}} - P_{\text{start}}||$) zur Pfad-Bogenlänge $> 0.92$.
|
||||
- **Rechteck:** RDP liefert 4 dominante Ecken; Kantenwinkel liegen zwischen $75^\circ$ und $105^\circ$.
|
||||
- **Kreis / Ellipse:** Pfad ist geschlossen ($||P_{\text{start}} - P_{\text{end}}|| < 0.1 \times \text{Umfang}$); Standardabweichung der Radien vom Mittelpunkt ist minimal ($< 0.12 \times \bar{r}$).
|
||||
- **Koordinatensystem:** Zwei orthogonale Schenkel mit Schnittpunkt nahe dem Start/Ende; Winkel $80^\circ$ bis $100^\circ$ wird exakt auf $90^\circ$ eingerastet; positive Achsenenden erhalten Pfeilspitzen.
|
||||
- **Dreiecke / Rhomben / Oktaeder:** Spezifische Eckpunkt- und Symmetrieanalysen der RDP-Kontur.
|
||||
### 4.1 Bildebene
|
||||
`image_layer.rs` zeichnet texturierte Vierecke unter der Tinte: importierte PDF-Seiten, eingefügte Bilder und Textfelder. Texturen werden beim ersten Bedarf geladen, bekommen Mipmaps und werden freigegeben, wenn mehr als 384 MB belegt sind.
|
||||
- **PDF-Seiten** rendert ein Hintergrund-Thread mit `hayro` (längste Kante 2560 Pixel, beim starken Hineinzoomen 5120 Pixel). Bis die schärfere Fassung fertig ist, bleibt die vorhandene sichtbar.
|
||||
- **Textfelder** werden mit `ab_glyph` gerastert, in einer Auflösung, die dem Zoom folgt (in Vierteln einer Zweierpotenz, höchstens 8 Pixel je Einheit). Der Texturschlüssel enthält einen Hash von Inhalt, Schrift und Farbe; jede Änderung erzeugt eine neue Textur.
|
||||
|
||||
### 3.3. Draw-and-Hold Erkennung
|
||||
Hält der Anwender den Stift am Ende eines Strichs für $> 400\text{ ms}$ innerhalb eines Radius von $< 3\text{ px}$ ruhig, wird die Form im fliegenden Wechsel als Vektorform visualisiert und beim Abheben des Stifts fixiert.
|
||||
### 4.2 Papier- und Musterfarben
|
||||
Papierfarbe (`PaperTheme`) und Musterfarbe (`PatternColor`) stehen in `background.rs`. Alle Farben sind linear; die Fensterfläche ist sRGB, die Umrechnung übernimmt die GPU. Auf dunklem Papier kann der Shader dunkle Tinte aufhellen (`ink_params` in `CameraUniforms`), gespeichert werden immer die Originalfarben.
|
||||
|
||||
---
|
||||
|
||||
## 4. Persistenz & Autosave-Architektur (`storage.rs`, `autosave.rs`)
|
||||
## 5. Objekte auf der Seite
|
||||
|
||||
Alles auf einer Seite ist ein `Stroke` (`document.rs`):
|
||||
- **Tinte:** Abtastpunkte, Werkzeug (Stift, Textmarker, Form), Breite, Farbe; die Dreiecke werden beim Laden aus den Punkten neu berechnet.
|
||||
- **Formen:** zusätzlich eine `RecognizedShape` (siehe Abschnitt 6).
|
||||
- **Bilder und Textfelder:** vier Eckpunkte (oben links, oben rechts, unten rechts, unten links) plus Verweis auf die Bilddatei bzw. ein `TextBox`. Weil die Ecken normale Punkte sind, wirken Verschieben, Drehen und Skalieren der Auswahl ohne Sonderfälle.
|
||||
|
||||
### 5.1 Textfelder
|
||||
`text.rs` setzt den Text selbst: Zeilenumbruch an Leerzeichen (zu lange Wörter zeichenweise), Unterschneidung, Tabulator = 4 Leerzeichen. Bildschirm-Textur, PDF-Export und Bearbeitung verwenden dieselben Schriften (Ubuntu Light und Hack aus den eingebauten `egui`-Schriften) und denselben Umbruch. Bearbeitet wird in einem `egui`-Textfeld an der Stelle des Felds; danach wird die Höhe des Felds an den Text angepasst, Lage, Drehung und Skalierung bleiben.
|
||||
|
||||
### 5.2 Rückgängig
|
||||
Jede Seite hat einen eigenen Verlauf (`history.rs`): Hinzufügen, Entfernen, Ersetzen und Ändern von Strichen. Verschieben/Drehen/Skalieren einer Auswahl, ein Radierzug mit dem Teilstrich-Radierer und eine Textbearbeitung sind jeweils ein Schritt.
|
||||
|
||||
---
|
||||
|
||||
## 6. Formerkennung
|
||||
|
||||
`geometry.rs` erkennt Formen rein geometrisch, ohne Machine Learning. Geschlossene Formen (Kreis, Ellipse, Dreieck, Rechteck, Raute) treten gegeneinander an; gewählt wird die, deren Umriss am besten zu den Punkten passt. Danach folgen Vielecke, Pfeile, Koordinatensysteme und Linien. Übernommen wird eine Form nur, wenn der Strich im Mittel höchstens 4 % seiner Diagonale von ihr abweicht – sonst bleibt er Handschrift.
|
||||
|
||||
- **Linie:** Verhältnis von Abstand Anfang–Ende zur Strichlänge über 0,92.
|
||||
- **Kreis, Ellipse:** Kreis- bzw. Ellipsen-Fit über die Punkte.
|
||||
- **Rechteck, Dreieck, Raute, Vieleck, Pfeil:** Analyse der mit Douglas-Peucker vereinfachten Kontur (Eckenzahl, Winkel, Seitenverhältnisse).
|
||||
- **Koordinatensystem:** zwei etwa rechtwinklige Achsen; der Winkel wird auf 90° gesetzt.
|
||||
- **Oktaeder:** Skizze einer Doppelpyramide als 3D-Kantenmodell; nur, wenn kein einfacher Umriss passt.
|
||||
|
||||
**Halten am Strichende:** Bleibt der Stift über 400 ms innerhalb von 3 Pixeln ruhig, wird die erkannte Form schon vor dem Absetzen angezeigt und beim Absetzen übernommen. Mit dem Formen-Werkzeug wird jeder Strich beim Absetzen geprüft.
|
||||
|
||||
---
|
||||
|
||||
## 7. Speichern
|
||||
|
||||
```
|
||||
[UI / Inking Event]
|
||||
│
|
||||
▼
|
||||
[mark_page_dirty]
|
||||
│
|
||||
▼
|
||||
[AutosaveController] ──(1.5s Debounce)──► [Hintergrund-Thread (MPSC)]
|
||||
│
|
||||
▼
|
||||
[Temp-Datei *.tmp schreiben]
|
||||
│
|
||||
▼
|
||||
[Atomares std::fs::rename]
|
||||
│
|
||||
▼
|
||||
[manifest.json / page_<id>.json]
|
||||
[Änderung]
|
||||
│ mark_page_dirty / mark_manifest_dirty
|
||||
▼
|
||||
[AutosaveController] ── 1,5 s nach der letzten Änderung, spätestens nach 10 s ──► [Autosave-Thread]
|
||||
│
|
||||
▼
|
||||
[temporäre Datei schreiben + fsync]
|
||||
│
|
||||
▼
|
||||
[rename über die alte Datei, Ordner-fsync]
|
||||
```
|
||||
|
||||
- **Keine UI-Blockierung:** Disk-I/O erfolgt vollständig in einem dedizierten Worker-Thread.
|
||||
- **Atomarität:** Daten werden stets zuerst in `.json.tmp` geschrieben und via `fs::rename` atomar ersetzt.
|
||||
- **Verlustfreier Notizbuchwechsel:** Bei `switch_notebook_dir` wird synchron `flush()` aufgerufen, um alle ungespeicherten Daten des aktuellen Dokuments sicherzustellen, bevor der Pfad gewechselt wird.
|
||||
- Der Haupt-Thread schreibt nie selbst; er übergibt fertige Seitendaten an den Autosave-Thread.
|
||||
- Schlägt ein Schreibvorgang fehl, wird die Seite mit wachsendem Abstand (1 s bis 30 s) erneut versucht; der Status wird rot.
|
||||
- `flush()` schreibt alles Ausstehende und wartet darauf. Es läuft bei `Strg + S`, beim Wechsel des Notizbuchs und beim Beenden. Schlägt es beim Beenden fehl, bleibt die App offen und bietet Wiederholen oder einen Notfall-Export der ungespeicherten Seiten an (`~/.local/share/ink-notes/recovery/`).
|
||||
- Seiten werden erst beim Anzeigen geladen (Lazy Loading); Titel, Hintergrund und Größe stehen im Manifest, damit Seitenübersicht und durchgehende Ansicht sie vorher kennen.
|
||||
- Ein zweites Fenster mit demselben Notizbuch öffnet es schreibgeschützt (Sperrdatei).
|
||||
|
||||
---
|
||||
|
||||
## 8. Export
|
||||
|
||||
`export.rs` erzeugt das PDF mit `printpdf`. Jede Seite bekommt ihre eigene Größe und diese Ebenen:
|
||||
|
||||
1. **Background:** Karo, Linien oder Punkte in der gewählten Musterfarbe (immer auf weißem Papier).
|
||||
2. **PDF** (nur im Notfall): importierte PDF-Seite als Bild, falls sie sich nicht als Vektor übernehmen lässt.
|
||||
3. **Images:** eingefügte Bilder (höchstens 300 dpi).
|
||||
4. **Text:** Textfelder als echter Text mit eingebetteter Schrift (durchsuchbar, kopierbar). Jedes Feld liegt in einem eigenen Koordinatensystem, das eine Transformationsmatrix auf seine Ecken abbildet.
|
||||
5. **Ink:** Striche als gefüllte Polygone, Formen als Pfade.
|
||||
|
||||
Importierte PDF-Seiten werden anschließend als Form-XObject unter die Seite gelegt (`pdf_merge.rs`), bleiben also Vektorgrafik. Seitentitel werden zu Lesezeichen.
|
||||
|
||||
PNG und SVG einer Seite (`page_export.rs`) entstehen aus diesem PDF: `hayro` rastert es bzw. wandelt es in SVG um. So sehen alle Exporte gleich aus.
|
||||
|
||||
---
|
||||
|
||||
## 9. Updates
|
||||
|
||||
`updater.rs` fragt die Gitea-Releases ab, vergleicht die Version (SemVer) und lädt auf Wunsch das passende Paket. Vor der Installation prüft er die Minisign-Signatur von `SHA256SUMS.txt` gegen den eingebauten Schlüssel und danach die SHA-256-Prüfsumme des Pakets. Ein AppImage wird per `rename` ersetzt; ein `.deb`-Paket wird mit `xdg-open` an die Softwareinstallation des Systems übergeben. Details im README unter „Sicherheit der Updates“.
|
||||
|
||||
Reference in New Issue
Block a user