Files
ink-notes/docs/FILE_FORMAT.md
haraldandClaude Opus 5.5 61c0c903a4 feat: Achsenkreuz aus einem „+“ in einem Zug (Issue #20)
- Formerkennung (Halten oder Formen-Werkzeug): Ein „+“ in einem Strich wird zum
  Achsenkreuz – waagerechte und senkrechte Achse, Pfeilspitzen an allen vier
  Enden; die Arme dürfen unterschiedlich lang sein
- Gespeichert als Koordinatensystem mit dem neuen optionalen Feld
  negative_ends: ältere Versionen zeigen es als L-Koordinatensystem, kein neues
  Dateiformat nötig
- Auch im PDF-, PNG- und SVG-Export mit vier Pfeilspitzen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:42:03 +02:00

8.7 KiB
Raw Permalink Blame History

Dateiformat von ink-notes

Dieses Dokument beschreibt, wo und wie ink-notes Notizbücher speichert, und die Versionen des Dateiformats. Aktuell ist Version 8.


1. Ordner

~/.local/share/ink-notes/              ($XDG_DATA_HOME/ink-notes)
├── notebooks/
│   ├── library.json                   Ordner der Notizbücher (siehe unten)
│   ├── <notizbuch-id>/
│   │   ├── manifest.json              Metadaten und Seitenreihenfolge
│   │   ├── manifest.json.bak          Kopie des vorherigen Manifests
│   │   ├── page_1.json                eine Datei je Seite
│   │   ├── page_2.json
│   │   ├── page_7.json.corrupt-<ts>   beiseitegelegte beschädigte Seite (falls vorgekommen)
│   │   ├── assets/                    eingefügte Bilder und importierte PDFs
│   │   │   ├── <sha256>.png
│   │   │   └── <sha256>.pdf
│   │   └── .lock                      Sperrdatei, solange das Notizbuch geöffnet ist
│   └── .trash/                        gelöschte Notizbücher (<id>_<zeitstempel>)
└── recovery/                          Notfall-Sicherungen ungespeicherter Seiten

~/.config/ink-notes/ui.json            ($XDG_CONFIG_HOME) Einstellungen der Oberfläche
  • Notizbuch-IDs werden aus dem Titel und dem Zeitpunkt gebildet (z. B. mathematik_1775460000); das erste Notizbuch heißt default.
  • Assets sind nach dem SHA-256-Hash ihres Inhalts benannt; dasselbe Bild wird nur einmal gespeichert. Bilder werden als PNG oder (unverändert) JPEG abgelegt, PDFs unverändert.
  • Alle Dateien werden mit Rechten 0600, Ordner mit 0700 angelegt.

Ordner (library.json)

Ordner wie „3. Semester › Mathe“ sind rein logisch: Die Notizbücher bleiben in ihren Verzeichnissen, library.json hält nur fest, welcher Ordner welches Notizbuch enthält. Umbenennen, Verschieben (samt Inhalt) und Löschen von Ordnern ändern deshalb nie eine Notizbuch-Datei.

{
  "folders": ["3. Semester", "3. Semester/Mathe", "4. Semester"],
  "notebooks": { "analysis_1775460000": "3. Semester/Mathe" }
}
  • Ordner sind Pfade mit / als Trenner; Namen enthalten selbst kein / und höchstens 80 Zeichen.
  • Notizbücher ohne Eintrag liegen auf der obersten Ebene.
  • Wird ein Ordner gelöscht, rücken seine Notizbücher und Unterordner eine Ebene nach oben.
  • Fehlt die Datei oder ist sie unlesbar, liegen alle Notizbücher auf der obersten Ebene; fehlende übergeordnete Ordner werden beim Laden ergänzt.

2. Manifest (manifest.json)

{
  "format_version": 8,
  "id": "vorlesung_1775460000",
  "title": "Vorlesung",
  "created_at": 1775460000,
  "modified_at": 1775463600,
  "current_page_index": 0,
  "pages": [
    { "id": 1, "background": "Blank", "title": "Einführung", "size": [2970.0, 1671.0] },
    { "id": 2, "background": "Grid5mm" }
  ],
  "page_ids": [1, 2],
  "next_stroke_id": 42,
  "next_page_id": 3
}
Feld Bedeutung
format_version Formatversion (siehe Abschnitt 5)
id, title ID (= Ordnername) und Anzeigename
created_at, modified_at UNIX-Zeit in Sekunden
current_page_index zuletzt geöffnete Seite
pages Seiten in ihrer Reihenfolge: id, background, optional title und size (siehe unten) – damit Seitenübersicht und Ansicht sie kennen, ohne jede Seite zu laden
page_ids dieselbe Reihenfolge als reine ID-Liste (für ältere Versionen)
next_stroke_id, next_page_id nächste freie IDs

3. Seite (page_<id>.json)

Jede Seite liegt in einer eigenen Datei (kompaktes JSON, hier zur Lesbarkeit umbrochen):

{
  "format_version": 8,
  "id": 1,
  "background": "Blank",
  "title": "Einführung",
  "size": [2970.0, 1671.0],
  "pdf_background": { "asset": "<sha256>.pdf", "page": 0, "width_pt": 960.0, "height_pt": 540.0 },
  "strokes": [ … ]
}
Feld Bedeutung
background Muster: Blank, Grid5mm, Lined, DotGrid
title Seitentitel (optional)
size Seitengröße [breite, höhe] in Seiteneinheiten (10 = 1 mm); fehlt bei DIN A4 hoch ([2100, 2970])
pdf_background importierte PDF-Seite als Hintergrund (optional): Datei, Seitennummer ab 0, Größe in Punkt
strokes alle Objekte der Seite, von unten nach oben

3.1 Objekte (strokes)

Jedes Objekt hat eine id, eine color (RGBA, linear, Werte 0–1) und samples: Punkte mit x, y (Seiteneinheiten), p (Druck 0–1) und t (Zeit in Sekunden). Die Dreiecke für die Anzeige werden beim Laden aus den Punkten berechnet und nicht gespeichert.

Tinte

{ "id": 7, "color": [0.12, 0.12, 0.14, 1.0],
  "samples": [{ "x": 350.2, "y": 420.5, "p": 0.65, "t": 12.345 }, …],
  "tool": "pen", "width": 5.0, "finalized": true }
  • tool: pen, highlighter oder shape
  • width: Strichbreite in Seiteneinheiten (5.0 = 0,5 mm)
  • finalized: Der Strich wurde nach dem Absetzen nachbearbeitet (Glättung, Ecken, Endkappen)

Formen haben zusätzlich shape, z. B.:

"shape": { "Rectangle": { "corners": [[500, 600], [800, 600], [800, 800], [500, 800]] } }

Mögliche Formen: Line { start, end }, Arrow { start, end, double }, Circle { center, radius }, Ellipse { center, radius_x, radius_y, rotation }, Rectangle { corners }, Triangle { corners }, Rhombus { corners }, Polygon { vertices }, CoordinateSystem { origin, x_axis_end, y_axis_end, with_arrows, negative_ends? }, Octahedron { … }. Punkte sind [x, y]. Beim Achsenkreuz enthält negative_ends die Enden der x- und der y-Achse auf der anderen Seite des Ursprungs; ältere Versionen ignorieren das Feld und zeigen ein L-förmiges Koordinatensystem.

Bilder haben image (Dateiname in assets/); die vier samples sind die Ecken oben links, oben rechts, unten rechts, unten links:

{ "id": 9, "color": [1, 1, 1, 1], "samples": [ …4 Ecken… ], "width": 0.0, "image": "<sha256>.png" }

Textfelder haben text; die vier samples sind wie bei Bildern die Ecken des Felds:

"text": { "content": "Überschrift\nzweite Zeile", "size": 42.3, "font": "Sans", "wrap_width": 1500.0 }
  • size: Schriftgröße (Em) in Seiteneinheiten (12 pt ≈ 42,3)
  • font: Sans (Ubuntu Light) oder Mono (Hack)
  • wrap_width: Umbruchbreite in Seiteneinheiten; die Ecken bilden das Feld dieser Breite (und der Höhe des gesetzten Texts) ab, Drehung und Skalierung stecken in den Ecken

4. Sicherheit beim Speichern

Atomares Schreiben

  1. Der Inhalt wird in <datei>.tmp.<pid>.<nanosekunden> geschrieben und mit fsync auf den Datenträger gebracht.
  2. rename ersetzt die alte Datei in einem Schritt.
  3. Danach wird auch der Ordner mit fsync gesichert.

Ein Absturz während des Speicherns hinterlässt deshalb entweder die alte oder die neue Datei, nie eine halbe.

Beschädigte Dateien

  • Lässt sich eine Seite nicht lesen, wird sie nach page_<id>.json.corrupt-<zeitstempel> verschoben (nicht gelöscht) und die Seite schreibgeschützt angezeigt.
  • Ist manifest.json beschädigt oder fehlt, wird manifest.json.bak verwendet.

Gelöschte Seiten

Eine gelöschte Seite verschwindet aus manifest.json; ihre Datei page_<id>.json bleibt im Ordner liegen. Seitenkennungen werden nie wiederverwendet (next_page_id), die Datei kann also mit keiner neuen Seite kollidieren. Wird die Seite in derselben Sitzung wiederhergestellt, schreibt die App sie neu.

Neuere Versionen

Dateien mit einer höheren format_version als der unterstützten werden nicht geöffnet – so kann eine ältere App-Version keine Inhalte verlieren, die sie nicht kennt.

Migration

Beim ersten Speichern einer unversionierten Datei (Version 0) wird vorher eine Kopie *.v0.bak angelegt.


5. Versionen

Version App Änderung
0 – ohne format_version
1 – format_version, Hintergründe im Manifest
2 – tool, width, finalized an Strichen; kompaktes JSON
3 0.3.7 Formen Ellipse und Arrow
4 0.5 Bilder (image, Ordner assets/)
5 0.6.1 importierte PDF-Seiten (pdf_background)
6 0.7 Seitentitel (title in Seite und Manifest)
7 0.10 Textfelder (text)
8 0.11 Seitengröße je Seite (size in Seite und Manifest)

Jede neue Version ändert nur Ergänzungen; ältere Dateien werden ohne Umwandlung gelesen und beim nächsten Speichern mit der aktuellen Versionsnummer geschrieben.


6. Warum JSON

Ein Binärformat (z. B. postcard) würde Dateien um weitere 20–30 % verkleinern. JSON bleibt trotzdem, weil es sich ansehen, vergleichen und im Notfall von Hand reparieren lässt und das Speichern einer Seite auch bei vielen Strichen deutlich unter einer Millisekunde dauert.