From 42a0d2d8b1a76f31d716f6a6ffe91496761e8821 Mon Sep 17 00:00:00 2001 From: s4luorth Date: Tue, 1 Sep 2026 15:01:07 +0200 Subject: [PATCH] Kuvertbeschriftung: Absender oben links + Empfaenger unten rechts (opt-in) generateEnvelopeSVG rendert bei gesetztem Absender den Absender klein oben links und den Empfaenger unten rechts (sonst unveraendert). Absender wird ueber die Auftrags-/ Vorschau-Pipeline durchgereicht (metadata.envelopeSender, envelope_sender), fetchOrder laedt das Feld, Kuvert wird bei Absender frisch gerendert statt 1:1 aus der Vorschau. Co-Authored-By: Claude Opus 4.8 --- src/api/controllers/directus-controller.js | 2 ++ src/api/controllers/order-controller.js | 7 +++-- src/api/controllers/preview-controller.js | 2 +- src/lib/svg-generator.js | 33 +++++++++++++++++++--- src/services/directus-service.js | 2 +- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/api/controllers/directus-controller.js b/src/api/controllers/directus-controller.js index e5d7609..6df91b9 100644 --- a/src/api/controllers/directus-controller.js +++ b/src/api/controllers/directus-controller.js @@ -167,6 +167,8 @@ async function generateFromDirectus(req, res, next) { previewSession: order.preview_session || null, // Eingegebener Brieftext → als brieftext.txt im Ausgabeordner ablegen. rawText: order.text_template || null, + // Absender fürs Kuvert (manuelle Aufträge): oben links, Empfänger unten rechts. + envelopeSender: order.envelope_sender || null, }, }); diff --git a/src/api/controllers/order-controller.js b/src/api/controllers/order-controller.js index b007da1..a392c66 100644 --- a/src/api/controllers/order-controller.js +++ b/src/api/controllers/order-controller.js @@ -311,8 +311,9 @@ async function runGeneration({ orderNumber, letters, metadata }) { const filepath = isEnv ? path.join(envelopesDir, filename) : path.join(outputDir, filename); // 1:1 aus der Vorschau übernehmen, wenn für dieses Dokument eine - // Vorschau-Datei existiert. - if (previewSession) { + // Vorschau-Datei existiert. Ausnahme: Kuvert mit Absender – das wird frisch + // gerendert, damit der Absender (oben links) sicher drauf ist. + if (previewSession && !(isEnv && metadata.envelopeSender)) { const cached = path.join(previewsRoot, previewSession, filename); try { await fs.access(cached); @@ -329,7 +330,7 @@ async function runGeneration({ orderNumber, letters, metadata }) { if (isEnv) { svgResult = generateEnvelopeSVG( scriptalizedText, letter.format, letter.envelopeType, - { font: letter.font, realisticHandwriting: letter.realisticHandwriting ?? false, fontScale: letter.fontScale } + { font: letter.font, realisticHandwriting: letter.realisticHandwriting ?? false, fontScale: letter.fontScale, sender: metadata.envelopeSender || '' } ); } else { svgResult = generateLetterSVG( diff --git a/src/api/controllers/preview-controller.js b/src/api/controllers/preview-controller.js index 40ec39e..ced7834 100644 --- a/src/api/controllers/preview-controller.js +++ b/src/api/controllers/preview-controller.js @@ -167,7 +167,7 @@ async function generateBatch(req, res, next) { if (letter.type === "envelope") { const svgResult = generateEnvelopeSVG( scriptalizedText, letter.format, letter.envelopeType, - { font: letter.font, realisticHandwriting: letter.realisticHandwriting ?? false, fontScale: letter.fontScale } + { font: letter.font, realisticHandwriting: letter.realisticHandwriting ?? false, fontScale: letter.fontScale, sender: letter.sender || '' } ); if (svgResult.overflow) hasOverflow = true; const filename = `envelope_${pad}.svg`; diff --git a/src/lib/svg-generator.js b/src/lib/svg-generator.js index 0ca756a..0ca65ad 100644 --- a/src/lib/svg-generator.js +++ b/src/lib/svg-generator.js @@ -294,8 +294,10 @@ function generateEnvelopeSVG(text, format, type, options = {}) { startX = 0; // Dummy-Wert, wird für jede Zeile überschrieben } else { - // type === 'recipient' / 'free': Links, max. 60% der Umschlagsbreite - const maxWidthPx = Math.min(widthPx - marginLeftPx - 20, widthPx * 0.6); + // type === 'recipient' / 'free'. Ist ein Absender gesetzt, sitzt der + // Empfänger UNTEN RECHTS (klassisches Kuvert), sonst wie bisher oben links. + const hatAbsender = !!String(options.sender || '').trim(); + const maxWidthPx = Math.min(widthPx - marginLeftPx - 20, widthPx * (hatAbsender ? 0.48 : 0.6)); lines = wrapTextToLinesByWidth({ text: String(text || ''), @@ -304,8 +306,13 @@ function generateEnvelopeSVG(text, format, type, options = {}) { fontSizePx, }); - startX = marginLeftPx; - startY = marginTopPx + fontSizePx; + if (hatAbsender) { + startX = Math.max(marginLeftPx, Math.round(widthPx * 0.48)); + startY = Math.max(marginTopPx + fontSizePx, heightPx - marginTopPx - lines.length * lineHeightPx + fontSizePx); + } else { + startX = marginLeftPx; + startY = marginTopPx + fontSizePx; + } } let paths = []; @@ -347,6 +354,24 @@ function generateEnvelopeSVG(text, format, type, options = {}) { }); } + // Absender oben links (kleiner) – nur wenn gesetzt und kein zentrierter Freitext. + const absender = String(options.sender || '').trim(); + if (absender && type !== 'custom') { + const absSize = fontSizePx * 0.62; + const absLH = lineHeightPx * 0.62; + const absLines = absender.split('\n').map((l) => l.replace(/\s+$/, '')); + const absPaths = layoutTextToPaths({ + lines: absLines, + fontFileName: fontConfig.file, + fontSizePx: absSize, + startX: marginLeftPx, + startY: marginTopPx + absSize, + lineHeightPx: absLH, + realisticHandwriting: options.realisticHandwriting ?? false, + }); + paths = [...paths, ...absPaths]; + } + const pathElements = paths .map( (p) => `