Drucken: eindeutige Dateinamen für PDFs und Einzelbilder

Zwei Druckbogen desselben Tages hießen beide `2026-09-01_druckbogen.pdf`. Der
Browser hängte „(1)" an; wer sie in denselben Ordner oder aufs NAS legte, verlor
den ersten. Genau der Fehler, der im August für die Bibliotheksbilder behoben
wurde — im Druckmodul stand er noch.

Druckdateien folgen jetzt derselben Regel wie alles andere:

    2026-09-01_143207_druck-9-teile_a4_4eca.pdf

Datum, Uhrzeit auf die Sekunde, was auf dem Bogen liegt, das Papier, ein Kürzel.
Das Kürzel ist die Kennung des Verlaufseintrags — eine PDF-Datei lässt sich also
ihrem Eintrag unter „Zuletzt gedruckt" zuordnen. Dafür entsteht die Kennung vor
dem Dateinamen und wird an `recordRun` durchgereicht.

Einzelbilder erben das Motiv ihrer Quelle:
`2026-09-01_143207_lea-badge_130x180_25b3.jpg` statt `2026-09-01_1318.jpg`.
Dafür gibt `loadSourceNamed` neben den Bilddaten auch Namen und Kennung zurück.

Die Oberfläche erfindet keinen eigenen Namen mehr. Der Server schickt einen, der
Client liest ihn aus `Content-Disposition` — es gab zwei Wahrheiten, und die des
Clients war die schlechtere: Sie enthielt nur das Datum. Aus dem Kopf gelesene
Namen werden entschärft, bevor sie in `a.download` landen.

`sheetFilename` in printsource.ts ist entfallen; die Regeln stehen jetzt
vollständig in `naming.ts`, wo sie schon für die Bibliothek stehen.

12 neue Tests, 123 grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNhLunz586g6DeYMa7Qsd1
This commit is contained in:
tillandClaude Opus 5 committed 2026-09-01 15:31:23 +00:00
1 parent 841ff41076
commit a87587c273
9 files changed
+249 -19

No files matched your search

+6 -2
View File
@@ -7,6 +7,7 @@ import {
import { fitsOnSheet, overflowPlacement, tilePlan, tileSheet, type Oversize } from '../lib/printoversize';
import { baseCrop, clampCrop, centred, limitsFor, type CropRel, type FitMode } from '../lib/cropmath';
import PrintRuns from './PrintRuns';
import { nameAusKopf } from '../lib/naming';
/* --------------------------------------------------------------------------
Drucken — Bilder ohne KI auf exakte Maße bringen und mehrere davon mit
@@ -382,7 +383,10 @@ export default function PrintApp() {
});
if (!res.ok) { notify((await res.json().catch(() => ({}))).error || 'Fehlgeschlagen.', true); return; }
const blob = await res.blob();
download(blob, `klarbild-druck-${new Date().toISOString().slice(0, 10)}.pdf`);
// Den Namen schickt der Server mit — er trägt Zeitstempel und das Kürzel
// des Verlaufseintrags. Hier einen eigenen zu bauen hieße, zwei Bogen
// desselben Tages wieder gleich zu nennen.
download(blob, nameAusKopf(res.headers.get('Content-Disposition'), 'klarbild-druck.pdf'));
const n = Number(res.headers.get('X-Klarbild-Pages') || 0);
const hinweis = res.headers.get('X-Klarbild-Notes');
const liefer = res.headers.get('X-Klarbild-Delivery-Msg');
@@ -401,7 +405,7 @@ export default function PrintApp() {
body: JSON.stringify({ src: c.src, crop: c.crop, wMm: s.w, hMm: s.h, dpi, ext, name: c.name, fit: c.fit, bg: c.bg }),
});
if (!res.ok) { notify((await res.json().catch(() => ({}))).error || 'Fehlgeschlagen.', true); return; }
download(await res.blob(), `${(c.name || 'bild').replace(/\.[^.]+$/, '')}_${labelMm(s.w, s.h).replace(/[^0-9]+/g, 'x')}.${ext}`);
download(await res.blob(), nameAusKopf(res.headers.get('Content-Disposition'), `bild.${ext}`));
const real = res.headers.get('X-Klarbild-Real-Dpi');
notify(res.headers.get('X-Klarbild-Dpi-Ok') === '0'
? `Heruntergeladen — Achtung: die Quelle reicht nur für ca. ${real} dpi.`
+4 -1
View File
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useState } from 'react';
import { nameAusKopf } from '../lib/naming';
/**
* Die letzten Drucke.
@@ -74,7 +75,9 @@ export default function PrintRuns({ onLaden }: { onLaden?: (config: any) => void
const blob = await res.blob();
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `klarbild-druck-${new Date().toISOString().slice(0, 10)}.pdf`;
// Der Wiederholungsdruck ist eine **neue** Datei mit neuem Zeitstempel und
// neuem Kürzel — er überschreibt den ersten Bogen also nicht.
a.download = nameAusKopf(res.headers.get('Content-Disposition'), 'klarbild-druck.pdf');
document.body.appendChild(a); a.click(); a.remove();
setTimeout(() => URL.revokeObjectURL(a.href), 4000);
sagen('PDF neu erzeugt. Beim Drucken „Tatsächliche Größe / 100 %" wählen.');