Hallo Carlo, hallo Hubert,
vielen Dank für eure Rückmeldungen.
@Carlo: Dass das auf den ersten Blick nach wildem Geklicke aussieht, kann ich nachvollziehen. Es handelt sich nicht um ein fertiges Tool, sondern um einen Prototyp – bewusst offen und bewusst roh gehalten. Was man im Video nicht sieht: Die Basisfunktionen laufen bereits erstaunlich stabil – seitenbasiertes Layout, wiederholbare Bereiche (z. B. Artikel), Wasserzeichen, Summen – alles direkt im Browser, komplett auf JSON-Basis.
@Hubert: Du hast meine Motivation genau erkannt. Ich möchte kein überladenes Monster-Tool, keine externen Abhängigkeiten und keine laufenden Lizenzkosten. Mein Ziel ist eine schlanke, leicht verständliche Lösung, die man als Entwickler vollständig selbst kontrollieren und anpassen kann. Genau so etwas fehlt am Markt. Und der entscheidende Punkt: Der Designer soll so einfach bedienbar sein, dass auch Endanwender selbst Änderungen vornehmen können – z. B. Positionen verschieben oder Texte anpassen.
Das System besteht aus reinem HTML und JavaScript und lädt die Daten über eine lokale JSON-Datei (z. B. rechnung.json). Es ist komplett eigenständig und statisch aufgebaut:
– Kein Server notwendig – kann direkt per `file://` oder über einen beliebigen Webserver geöffnet werden
– Volle Portabilität – ideal für den Einsatz auf USB-Stick, im Kioskmodus oder in einem eingebetteten WebView
– Die gesamte Logik für Layout, Seitenumbruch und Formatierung läuft clientseitig
– Datenquellen wie DBF können vorgelagert in JSON umgewandelt oder dynamisch eingebunden werden
Das Ganze funktioniert also auch problemlos in einer WebView-Steuerung oder jedem eingebetteten Browser. Es läuft offline – ohne Server – und ist damit extrem flexibel für interne Berichte oder, wie bei mir, für Anwendungen in der Hotelautomation.
Ein Demo-Projekt bereite ich gerade vor.
Viele Grüße
Otto
PS: Die Idee zum Designer entstand übrigens durch dieses Posting hier im Forum:
viewtopic.php?p=150371#p150371
Günter hatte darin nach einer Möglichkeit gefragt, vor mehreren Etiketten auf einer A4-Seite einmalig einen variablen Textblock zu drucken – also quasi einen Seitenkopf.
Die Diskussion dazu war spannend, aber zeigte auch: Selbst mit Tools wie List & Label ist das nicht so einfach umzusetzen, wie es eigentlich sein sollte.
Genau das war für mich der Auslöser, ein eigenes System zu entwickeln, das solche Anforderungen nativ unterstützt:
Ein Bericht besteht aus flexiblen Bereichen (Kopf, Etiketten, Summen, Wasserzeichen), die logisch und seitenbasiert kombiniert werden können – ohne Umwege, ohne Tricks.
So entstand innerhalb von 14 Tagen die Basisversion meines HTML/JS-Designers, mit der ich genau solche Fälle abbilden kann:
– Etiketten pro Seite
– Textblöcke vor den Etiketten
– Summenblöcke am Ende
– alles JSON-basiert und vollständig im Browser lauffähig.
Code: Alles auswählen
async function loadReportConfig() {
const res = await fetch("rechnung.json");
report = await res.json();
// 📄 Etikettendaten vorbereiten
reportData = {
rechnungsnummer: "RE-2024-0012",
datum: "2024-06-03", // 🔁 neu
kunde: {
name: "Max Mustermann",
strasse: "Teststraße 12", // 🔁 neu
plz: "9999", // 🔁 neu
ort: "Musterstadt" // 🔁 neu
},
artikelListe: [
{ artikel: "Apfel", preis: "1.50" },
{ artikel: "Brot", preis: "2.00" },
{ artikel: "Käse", preis: "3.75" },
{ artikel: "Wurst", preis: "4.10" },
{ artikel: "Milch", preis: "1.20" },
{ artikel: "Kaffee", preis: "5.00" },
{ artikel: "Zucker", preis: "1.10" },
{ artikel: "Butter", preis: "2.80" },
{ artikel: "Eier", preis: "2.30" },
{ artikel: "Müsli", preis: "3.90" }
],
summe_netto: "27.65", // optional, für MwSt-Anzeige
summe_mwst: "5.53", // optional
summe_brutto: "33.18" // optional
};
json:
Code: Alles auswählen
{
"paper": {
"top1": 20,
"top2": 45,
"pageHeight": 297
},
"areas": [
{
"id": "area7458",
"title": "Logo",
"top": 0,
"height": 50,
"printCondition": "always",
"topDependsOnPrevious": false,
"disallowChildren": false,
"items": [
{
"type": "text",
"x": 100,
"y": 5,
"value": "MyLogo",
"fontSize": 60,
"bold": false,
"color": "#d9913f",
"align": "left"
}
]
},
{
"id": "kopf",
"title": "Rechnungskopf",
"top": 70,
"height": 40,
"topDependsOnPrevious": true,
"items": [
{
"type": "text",
"x": 0,
"y": 2,
"value": "Rechnungsnummer: {rechnungsnummer}",
"fontSize": 10,
"bold": true
},
{
"type": "text",
"x": 0,
"y": 8,
"value": "Datum: {datum}",
"fontSize": 10
},
{
"type": "text",
"x": 0,
"y": 16,
"value": "An: {kunde.name}",
"fontSize": 10
},
{
"type": "text",
"x": 0,
"y": 22,
"value": "{kunde.strasse}",
"fontSize": 10
},
{
"type": "text",
"x": 0,
"y": 28,
"value": "{kunde.plz} {kunde.ort}",
"fontSize": 10
}
],
"printCondition": "",
"disallowChildren": false
},
{
"id": "wasser",
"title": "Wasserzeichen",
"top": 150,
"height": 100,
"disallowChildren": true,
"items": [
{
"type": "text",
"x": 100,
"y": 20,
"value": "KOPIE",
"fontSize": 70,
"bold": true,
"color": "#50ce58",
"align": "left",
"fontFamily": "Courier"
}
],
"printCondition": "",
"topDependsOnPrevious": false,
"repeatFor": "artikelListe",
"columns": 1
},
{
"id": "einleitung",
"title": "Überschrift / Einleitung",
"height": 15,
"printCondition": "always",
"topDependsOnPrevious": true,
"items": [
{
"type": "text",
"x": 0,
"y": 2,
"value": "Vielen Dank für Ihre Bestellung",
"fontSize": 11,
"bold": true
}
],
"top": 0
},
{
"id": "artikelblock",
"title": "Artikelblock",
"repeatFor": "artikelListe",
"columns": 1,
"labelWidth": 160,
"height": 8,
"topDependsOnPrevious": true,
"items": [
{
"type": "text",
"x": 0,
"y": 2,
"value": "{artikel}",
"fontSize": 10,
"bold": true
},
{
"type": "text",
"x": 90,
"y": 2,
"value": "{preis} €",
"fontSize": 10
}
],
"top": 225,
"printCondition": "",
"disallowChildren": false
},
{
"id": "summe",
"title": "Summenbereich",
"top": 200,
"height": 20,
"topDependsOnPrevious": false,
"items": [
{
"type": "text",
"x": 0,
"y": 2,
"value": "Netto: {summe_netto} €",
"fontSize": 10
},
{
"type": "text",
"x": 60,
"y": 2,
"value": "MwSt (20 %): {summe_mwst} €",
"fontSize": 10
},
{
"type": "text",
"x": 120,
"y": 2,
"value": "Brutto: {summe_brutto} €",
"fontSize": 10,
"bold": true
}
]
},
{
"id": "endtext",
"title": "Rechnungs-Endtext",
"top": 325,
"height": 45,
"topDependsOnPrevious": true,
"items": [
{
"type": "text",
"x": 0,
"y": 2,
"value": "Vielen Dank für Ihren Einkauf!\n\nBitte überweisen Sie den Gesamtbetrag innerhalb von 14 Tagen auf folgendes Konto:\nEmpfänger: Hotel Bergland\nIBAN: ATxx xxxx xxxx xxxx xxxx\nBIC: XXXXXXXX\nVerwendungszweck: Rechnungsnummer {rechnungsnummer}\n\nBei Fragen stehen wir Ihnen gerne zur Verfügung.\nMit freundlichen Grüßen\nIhr Team vom Hotel Bergland",
"fontSize": 9
}
]
}
]
}