From 7930c177ee754c33bd21f60222a16629621b2b38 Mon Sep 17 00:00:00 2001 From: s4luorth Date: Wed, 30 Sep 2026 10:53:19 +0200 Subject: [PATCH] A6-Greifrand an die lange Seite + A5/DIN-lang (Volltext) A6: Greifrand liegt jetzt IMMER an der langen (148mm) Seite - A6 quer (a6l) unten (neuer margin_bottom-Mechanismus haelt untere Zeilen frei, 11->10), A6 hoch (a6p) rechts. Neue Funktion getEffectiveLineLimit() deckelt das Zeilenlimit zusaetzlich geometrisch; A4/A5 unberuehrt. A5 (a5p 148x210, a5l 210x148) und DIN lang komplett beschrieben (dinlang_karte 210x105) als Brief-Fallback-Formate ergaenzt. Co-Authored-By: Claude Opus 4.8 --- src/lib/page-layout.js | 36 ++++++++++++++++++++++++++++++++---- src/lib/svg-generator.js | 10 +++++----- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/lib/page-layout.js b/src/lib/page-layout.js index c307afc..b9a6ba4 100644 --- a/src/lib/page-layout.js +++ b/src/lib/page-layout.js @@ -6,23 +6,32 @@ const FONT_SIZE_PX = 26; // im Speicher gehalten (stündlich / manuell aufgefrischt via applyRenderFormats). // Ist Directus nicht erreichbar oder ein Format nicht hinterlegt, greifen die // eingebauten Defaults – die Generierung läuft also immer. -const DEFAULT_LINE_LIMITS = { a4: 27, a6p: 14, a6l: 11 }; +const DEFAULT_LINE_LIMITS = { a4: 27, a5p: 19, a5l: 13, a6p: 14, a6l: 11, dinlang_karte: 11 }; const DEFAULT_FORMAT_LAYOUTS = { a4: { marginTopMm: 25, marginLeftMm: 20, lineHeightFactor: 1.35 }, - a6p: { marginTopMm: 8, marginLeftMm: 8, lineHeightFactor: 1.3 }, - a6l: { marginTopMm: 8, marginLeftMm: 8, marginRightMm: 14, lineHeightFactor: 1.2 }, + a5p: { marginTopMm: 20, marginLeftMm: 15, lineHeightFactor: 1.35 }, + a5l: { marginTopMm: 20, marginLeftMm: 15, lineHeightFactor: 1.35 }, + dinlang_karte: { marginTopMm: 8, marginLeftMm: 10, lineHeightFactor: 1.2 }, + // A6-Greifrand IMMER an der langen (148 mm) Seite: + // • a6p (hoch, 105×148): lange Seite = rechts → marginRightMm (Breite) + // • a6l (quer, 148×105): lange Seite = unten → marginBottomMm (Zeilen) + a6p: { marginTopMm: 8, marginLeftMm: 8, marginRightMm: 14, lineHeightFactor: 1.3 }, + a6l: { marginTopMm: 8, marginLeftMm: 8, marginBottomMm: 14, lineHeightFactor: 1.2 }, din_lang: { marginTopMm: 50, marginLeftMm: 10, lineHeightFactor: 1.2 }, c6: { marginTopMm: 60, marginLeftMm: 10, lineHeightFactor: 1.2 }, }; const DEFAULT_PAGE_FORMATS = { a4: { widthMm: 210, heightMm: 297 }, + a5p: { widthMm: 148, heightMm: 210 }, + a5l: { widthMm: 210, heightMm: 148 }, + dinlang_karte: { widthMm: 210, heightMm: 105 }, a6p: { widthMm: 105, heightMm: 148 }, a6l: { widthMm: 148, heightMm: 105 }, din_lang: { widthMm: 220, heightMm: 110 }, c6: { widthMm: 162, heightMm: 114 }, }; // Art des Formats (brief/umschlag/postkarte) – steuert, WAS generiert wird. -const DEFAULT_KINDS = { a4: 'brief', a6p: 'brief', a6l: 'brief', din_lang: 'umschlag', c6: 'umschlag' }; +const DEFAULT_KINDS = { a4: 'brief', a5p: 'brief', a5l: 'brief', a6p: 'brief', a6l: 'brief', dinlang_karte: 'brief', din_lang: 'umschlag', c6: 'umschlag' }; // Live-Cache (wird von applyRenderFormats ersetzt). Start = Defaults. let LINE_LIMITS = { ...DEFAULT_LINE_LIMITS }; @@ -56,6 +65,8 @@ function applyRenderFormats(rows) { }; const mr = r.margin_right_mm ?? alt.marginRightMm; if (mr != null && mr !== '') layout.marginRightMm = Number(mr); + const mb = r.margin_bottom_mm ?? alt.marginBottomMm; + if (mb != null && mb !== '') layout.marginBottomMm = Number(mb); fl[k] = layout; if (r.line_limit != null && r.line_limit !== '') ll[k] = Number(r.line_limit); } @@ -110,6 +121,7 @@ function getLayoutForFormat(format) { marginTopPx: mmToPx(layoutConfig.marginTopMm), marginLeftPx: mmToPx(layoutConfig.marginLeftMm), marginRightPx: layoutConfig.marginRightMm ? mmToPx(layoutConfig.marginRightMm) : null, + marginBottomPx: layoutConfig.marginBottomMm ? mmToPx(layoutConfig.marginBottomMm) : 0, lineHeightPx: FONT_SIZE_PX * layoutConfig.lineHeightFactor, }; } @@ -143,6 +155,21 @@ function getLineLimit(format) { return LINE_LIMITS[normalizedFormat] || LINE_LIMITS.a4; } +// Effektives Zeilenlimit: das kleinere aus (a) konfiguriertem line_limit, +// gegenläufig zur Schriftgröße skaliert, und (b) geometrischer Kapazität, die +// einen unteren Greifrand (marginBottomMm) frei hält. Dadurch reserviert A6 quer +// (marginBottomMm=14) unten Freiraum für den rollengeführten Plotter, ohne dass +// die anderen Formate (ohne Bottom-Rand) berührt werden. +function getEffectiveLineLimit(format, fontScale = 1) { + const fs = Math.max(0.8, Math.min(1.5, Number(fontScale) || 1)); + const konfiguriert = Math.max(1, Math.round(getLineLimit(format) / fs)); + const { heightPx } = getPageDimensions(format); + const { marginTopPx, marginBottomPx, lineHeightPx } = getLayoutForFormat(format); + const nutzbarPx = heightPx - marginTopPx - (marginBottomPx || 0); + const geo = Math.max(1, Math.floor(nutzbarPx / (lineHeightPx * fs))); + return Math.min(konfiguriert, geo); +} + module.exports = { PX_PER_MM, FONT_SIZE_PX, @@ -150,6 +177,7 @@ module.exports = { getLayoutForFormat, getPageDimensions, getLineLimit, + getEffectiveLineLimit, getFormatKind, escapeXml, applyRenderFormats, diff --git a/src/lib/svg-generator.js b/src/lib/svg-generator.js index 4ecf2ce..68b192d 100644 --- a/src/lib/svg-generator.js +++ b/src/lib/svg-generator.js @@ -2,7 +2,7 @@ const { FONT_SIZE_PX, getLayoutForFormat, getPageDimensions, - getLineLimit, + getEffectiveLineLimit, escapeXml, mmToPx } = require('./page-layout'); @@ -140,7 +140,7 @@ function generateLetterSVG(text, format, options = {}) { .join('\n '); // Größere Schrift → weniger Zeilen passen aufs Blatt (Limit gegenläufig skalieren). - const lineLimit = Math.max(1, Math.round(getLineLimit(format) / fontScale)); + const lineLimit = getEffectiveLineLimit(format, fontScale); return { svg: `