Files
ink-notes/docs/FILE_FORMAT.md
T
haraldandClaude Opus 5.5 3c793fe659 feat: Endlose Fläche als Seitenanordnung (Issue #30, Schritt 2)
Ein endloses Notizbuch hat eine einzige Seite von 10 × 10 m; Eingabe,
Treffertests, Undo und Speichern bleiben dadurch unverändert. Die
Ansicht zeigt bei 100 % einen DIN-A4-Rahmen in der Mitte, Bilder und
Textfelder richten sich nach A4. Wählbar beim Anlegen und im ☰-Menü,
umschalten nur bei einer Seite ohne PDF bzw. zurück, solange alles im
Startausschnitt liegt. Seiten-Knöpfe sind auf der endlosen Fläche aus.

Der Export enthält nur den beschriebenen Bereich mit 10 mm Rand, auf
das Musterraster gelegt; Bilder einer Auswahl rastern nur noch den
Ausschnitt statt der ganzen Seite. Formatversion 9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:14:45 +02:00

9.4 KiB
Raw 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 9.


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": 9,
  "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,
  "layout": "Paged"
}
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
layout Anordnung der Seiten: Paged (Seite für Seite), Continuous (durchgehend untereinander) oder Endless (endlose Fläche, siehe unten). Seit 0.28; fehlt das Feld, gilt die frühere globale Einstellung continuous_scroll aus ui.json

Endlose Notizbücher (Version 9) haben genau eine Seite der Größe [100000.0, 100000.0] (10 × 10 m). Der Startausschnitt ist ein DIN-A4-Rahmen in ihrer Mitte (ab [48950, 48515]); Koordinaten bleiben positiv. Beim Export wird nur der beschriebene Bereich mit 10 mm Rand zur PDF-Seite.


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]). Höchstens 2 × 2 m, außer der endlosen Seite [100000, 100000]
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)
9 0.29 Seitenanordnung je Notizbuch (layout im Manifest), endlose Notizbücher

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.