Eine kleine Desktop-App, um Urkunden aus fertigen Vorlagen auszufüllen, als PDF zu speichern oder direkt zu drucken. Alle Texte, Wappen/Bilder und die Hintergrundfarbe lassen sich anpassen. Ausgefüllte Urkunden können gespeichert und später wieder geöffnet werden.
Wer die App einsetzt, baut sich daraus eine eigene Variante - mit eigenen
Vorlagen, eigener Optik und eigenem Installer. Mitgeliefert ist die Variante
standard.
Die App funktioniert komplett offline (Schriften sind eingebettet).
Jede Karte startet eine neue Urkunde aus dieser Vorlage; eine eigene Variante kann weitere Karten hinzufügen, ohne den Kern anzufassen.
Die Vorschau rechts zieht bei jeder Änderung sofort nach - ganz ohne eigenen Aktualisieren-Schritt.
- App starten.
- Unter „Neue Urkunde erstellen" eine Vorlage anklicken (Neu erstellen).
- Links die Felder ausfüllen - die Vorschau rechts aktualisiert sich sofort.
- Bild wählen… tauscht ein Wappen gegen eine eigene Bilddatei; Standard setzt es zurück.
- Oben einen Titel eingeben und Speichern (zum späteren Wiederöffnen unter „Meine Urkunden").
- Als PDF speichern → Speicherort wählen; das PDF öffnet sich anschließend automatisch. Oder Drucken → normaler Druckdialog.
Die fertige Urkunde ist ein randloses A4-Blatt (Hochformat).
Für einen Wettkampf steht die Teilnehmerliste meist schon vorher fest. Statt jede Urkunde einzeln auszufüllen:
- Eine Urkunde wie gewohnt öffnen und alles ausfüllen, was für alle gleich ist (Anlass, Datum, Unterschriften). Diese Urkunde ist die Vorlage für die ganze Serie.
- Serie aus Liste… anklicken.
- Muster-CSV speichern… - die Datei hat für jedes Feld eine Spalte und eine Beispielzeile mit deinen Werten.
- Die Datei in Excel öffnen, je Teilnehmer eine Zeile ausfüllen, die Beispielzeile überschreiben oder löschen, und als CSV speichern.
- CSV wählen… - die App zeigt, wie viele Zeilen sie gelesen hat, welche Spalten sie erkannt hat und was auffällig ist, dazu die Vorschau der ersten Urkunde mit echten Daten. Mit Letzte Zeile lässt sich prüfen, ob eine Summen- oder Kommentarzeile am Listenende mitgeraten ist.
- Ein PDF mit allen Urkunden, Einzeldateien… in einen Ordner oder direkt Drucken. Bei Einzeldateien wird vorher gewählt, aus welchen Spalten sich der Dateiname jeder Urkunde zusammensetzt.
Spalten, die die App nicht kennt (etwa eine Startnummer), werden ignoriert. Fehlt eine Spalte, bleibt der Wert aus deiner Vorlage stehen - und ebenso, wenn eine Zelle leer bleibt: dann gilt, was du in der Urkunde eingestellt hast. Soll ein Feld auf allen Urkunden leer sein (etwa die Punktzahl zum Eintragen von Hand), lass es schon in der Vorlage leer.
Nennt eine Vorlage mehrere Teilnehmer (beim Pokalkampf ein bis vier Mensch-Hund-Paare), steht auch dafür eine Spalte in der Muster-CSV: je Zeile die Anzahl. So stehen Einzel- und Zweierteams in derselben Serie, ohne dass auf der Urkunde des Einzelteams eine leere zweite Namenszeile bleibt. Steht dort keine oder keine sinnvolle Zahl, meldet die App die Zeile, bevor sie druckt.
Die Standard-Variante bringt drei Vorlagen mit, die sich eine Hausoptik teilen (doppelter Zierrahmen, Eckrauten, gemeinsame Schriften):
- Vereinsjubiläum (zwei Wappen nebeneinander)
- Geburtstag (mit Name des Mitglieds)
- Hochzeit (Brautpaar)
Farben, Rahmen und Texte lassen sich je Variante ändern, ohne die Vorlagen anzufassen - siehe „Eine neue Variante hinzufügen".
npm install
npm run dev # Variante standard
VARIANT=<id> npm run dev # andere Variantenpm run build:linux:standard # AppImage + .deb (Ordner dist/)
npm run build:win:standard # NSIS-Installer .exe (unter Windows, oder mit Wine)src/
main.js Electron-Hauptprozess: Fenster, PDF/Druck, Dialoge, Variantenwahl
preload.js sichere Brücke (window.api)
main/storage.js Speichern der Urkunden (JSON im userData-Ordner)
main/variant.js Varianten-Lookup und Feld-Merge (Shell, Vorlage, Overrides)
renderer/ Oberfläche: Galerie, Formular, Live-Vorschau
templates/
base.css gemeinsame Hausoptik der geteilten Vorlagen (hier zentral ändern)
shell.html geteilter Rahmen + Unterschriftsblock
shell.fields.json Felder, die die geteilte Shell mitbringt (Kopf, Unterschriften)
<vorlage>/
template.html Inhaltslayout mit data-field-Platzhaltern
manifest.json Felder der Vorlage (Beschriftung, Typ, Standardwert)
variants/
<id>/
variant.json Name, Vorlagenliste, Text-Overrides
shell.html eigener Rahmen (optional, sonst templates/shell.html)
shell.fields.json Felder, die die Shell mitbringt (Kopf, Unterschriften)
base.css eigene Optik (optional, ersetzt templates/base.css ganz)
assets/ Logos und Bilder dieser Variante
templates/<vorlage>/ eigene Vorlagen; gleichnamige gewinnen über templates/<vorlage>/
build/
builder.<id>.yml Installer-Metadaten dieser Variante
assets/
fonts/fonts.css lokal eingebettete Schriften (auto-generiert)
test/ automatisierte Tests (node --test)
tools/ Hilfsskripte für Entwicklung und Build, z. B. fetch-fonts.mjs
docs/ Screenshots für dieses README
- Ordner
templates/<neue-id>/anlegen mittemplate.htmlundmanifest.json(am einfachsten eine vorhandene Vorlage kopieren und anpassen). - In
template.htmlnur die innere Inhaltsspalte angeben; jedes editierbare Element bekommt eindata-field="schlüssel". Rahmen, Eckrauten und Unterschriften kommen automatisch ausshell.html/base.css. - In
manifest.jsondie Felder beschreiben. Diekeys müssen zu dendata-field-Werten passen. Feld-Typen:text,textarea,number- Textinhaltselect- Auswahl (mitoptions)image- Bild tauschen (Datei → wird als Base64 gespeichert)checkbox- blendet das zugehörigedata-field-Element ein/ausrange- Schieberegler; setzt entweder eine CSS-Eigenschaft des Elements ("styleProp":"gap","unit":"px") oder - mit"cssVar":"--name"- eine CSS-Variable auf der Urkunde (z. B. für Abstände, keindata-fieldnötig).rangemit"bind":"count"- Anzahl: dasdata-field-Element ist ein Container, dessen Kinderdata-count="1","2", … tragen; sichtbar bleiben so viele, wie eingestellt sind (etwa für ein Teilnehmerpaar, das je nach Urkunde ein- oder zweizeilig steht).selectmit"bind":"switch"- Entweder-oder: dasdata-field-Element ist ein Container, dessen Kinderdata-case="…"tragen; sichtbar ist das Kind, dessendata-casezum gewählten Wert passt (etwa eine Prüfung, die mit einer Punktzahl oder nur mit „bestanden" endet). Die Kinder dürfen eigenedata-field-Elemente enthalten.
- Optional Felder in einen ausklappbaren Block legen:
"group":"<id>"am Feld und den Block unter"groups": { "<id>": { "label":"…", "collapsed":true } }beschreiben (siehe die „Abstände"-Gruppe der vorhandenen Vorlagen). - Die neue
idin dietemplates-Liste der jeweiligenvariant.jsoneinsortieren (keineindex.jsonmehr - die Reihenfolge steht pro Variante). - Wappen-Kopf, Unterschriften und Hintergrundwahl gehören nicht zur Vorlage, sondern
kommen aus
shell.fields.json(geteilt oder der Variante). Eine Vorlage justiert nur deren Standardwerte übershellDefaultsim eigenenmanifest.json.
Abstände zwischen Abschnitten werden über CSS-Variablen gesteuert: die Abschnitte
nutzen margin-top: calc(var(--mt-xyz, Basis) * var(--gap-scale, 1)). Ein range-Feld mit
"cssVar":"--mt-xyz" regelt den Einzelabstand, eines mit "cssVar":"--gap-scale" den
Gesamtfaktor.
Leer gebliebene Textfelder tragen im gerenderten Markup ein data-leer. Damit
zeichnet eine Vorlage oder eine Variante ein Feld ohne Wert anders als eines mit - etwa
als Linie zum Eintragen von Hand, die verschwindet, sobald im Editor etwas eingetragen
wird (.blank[data-leer] { border-bottom: 1px solid … }).
Der Editor und das Formular bauen sich automatisch aus dem Manifest - am Programmcode muss nichts geändert werden.
-
variants/<id>/variant.jsonanlegen:name,templates(Reihenfolge der Karten in der Galerie), optionaloverrides. -
Reicht die bestehende Optik, werden Shell und
base.cssvontemplates/geerbt, samt der neutralen Vorgaben austemplates/shell.fields.json. Eigene Wappen und Rollenbezeichnungen kommen entweder überoverridesoder über eine eigeneshell.fields.jsonim Varianten-Ordner. Ein eigenes Wappen braucht zusätzlich einen Ordnervariants/<id>/assets/mit der Bilddatei, auf die der Override zeigt. -
Eigene Optik:
shell.html,shell.fields.jsonundbase.cssin den Varianten-Ordner legen.base.csswird ganz ersetzt, muss also alle Token und Klassen tragen, die Vorlagen und Shell benutzen:- Vorlagen:
--accent,--ornament,--ink,--muted,--hair,--bg,.divider,.signatures - Shell:
.urkunde(Seitenmaße, Schrift,position: relative),.frame-outer,.frame-inner,.corner,.content,.emblem-head,.emblem-head .emblem
Fehlt eine Klasse, bleibt das beim Testen unbemerkt (
npm testprüft keine Optik) und zeigt sich erst am Blatt: kein Rahmen, Wappen in Originalgröße. - Vorlagen:
-
Eigene Vorlagen unter
variants/<id>/templates/<vorlage>/anlegen. Liegt dort eine Vorlage mit dem Namen einer geteilten, gewinnt die eigene. -
Einzelne Texte einer geteilten Vorlage ändern:
overridesinvariant.json, nach Vorlagen-ID und Feldschlüssel. Ein unbekannter Schlüssel ist ein Fehler beim Start. -
build/builder.<id>.ymlnach dem Muster einer bestehenden anlegen: eigenerappId, eigenerproductName,extraMetadata.variant: <id>und die eigene Variante infiles.filesist eine positive Liste - fremde Varianten bleiben schon dadurch draußen, dass sie nicht aufgeführt sind. Ein!-Muster braucht nur, wer eine geteilte Vorlage aus dem Paket halten will, die die eigene Variante gar nicht anbietet. -
Skripte
build:win:<id>undbuild:linux:<id>inpackage.jsonergänzen. -
npm test- der Konsistenz-Test prüft die neue Variante automatisch mit.
Entwicklung mit einer anderen Variante: VARIANT=<id> npm run dev. Ohne
Angabe läuft standard.
Wo die Urkunden liegen: jede Variante speichert in einem eigenen Ordner,
benannt nach der Varianten-ID (urkunden-editor-<id> im Konfigurationsordner
des Benutzers). Das gilt in der Entwicklung wie im Installer und hängt
ausdrücklich nicht am Produktnamen: Electron leitet den Ordner sonst aus dem
Feld name der package.json ab, und das ist für alle Varianten dasselbe -
eine Urkunde der einen Variante tauchte dann bei der anderen unter „Meine
Urkunden" auf.
Die Variante, die eine Version ohne Varianten fortsetzt, trägt
"vorgaengerSpeicher": true in ihrer variant.json und übernimmt deren
Ordner einmalig.
Eigene Varianten müssen nicht im Projektordner liegen:
URKUNDEN_VARIANTS=/pfad/zu/meinen/varianten VARIANT=meinverein npm run devGesucht wird in dieser Reihenfolge: URKUNDEN_VARIANTS (mehrere Pfade wie in
PATH getrennt), dann das Ressourcen-Verzeichnis der gepackten App, dann das
eigene variants/. Die Installer dieses Projekts nehmen ihre Variante über
files mit ins Paket und finden sie deshalb über den letzten Pfad; das
Ressourcen-Verzeichnis deckt den Fall ab, dass eine Variante per
extraResources daneben gelegt wird. So oder so braucht die fertige App
keinen Pfad nach außen.
npm run test:e2e # Variante standard
npm run test:e2e <id> # andere VarianteStartet das Programm, öffnet die erste Vorlage, ändert ein Textfeld und prüft,
dass die Änderung in der Vorschau ankommt. Das lässt sich nur am laufenden
Programm prüfen: die Vorschau ist ein iframe, und ob der Renderer hineinschreiben
darf, entscheidet sich erst zur Laufzeit - eine zu strenge sandbox hat genau
das schon einmal stillschweigend abgeschaltet. Braucht eine Bildschirmanzeige
und läuft deshalb nicht in npm test mit.
assets/fonts/fonts.css enthält EB Garamond + Cormorant Garamond als eingebettete
woff2 (latin). node tools/fetch-fonts.mjs holt sie neu von Google Fonts und schreibt
die Datei komplett neu; im Normalbetrieb nicht nötig.
MIT, siehe LICENSE.
Die eingebetteten Schriften EB Garamond und Cormorant Garamond stehen unter
der SIL Open Font License 1.1, siehe assets/fonts/OFL.txt.

