diff --git a/app/plan-watermark.php b/app/plan-watermark.php
index ae1119e..6d42ab0 100644
--- a/app/plan-watermark.php
+++ b/app/plan-watermark.php
@@ -85,6 +85,19 @@ function plan_watermark_logo_svg(): string
. '';
}
+/**
+ * Beschriftung des Wasserzeichens: die Marketing-Domain ohne Schema
+ * ("kaffeeliste.de"). Laesst sie sich nicht ermitteln, bleibt der Name der
+ * App uebrig. Wird von der Web-Ansicht und vom PDF-Ausdruck gemeinsam
+ * genutzt, damit beide dieselbe Adresse nennen.
+ */
+function plan_watermark_label(): string
+{
+ $url = app_marketing_url();
+
+ return $url !== '' ? (string)preg_replace('#^https?://#i', '', $url) : 'Kaffeeliste';
+}
+
/**
* Markup des Wasserzeichens. Ohne erreichbare Marketing-Domain (CLI,
* ungewoehnliche Hosts) bleibt es bei Logo und Namen ohne Link - ein
@@ -93,7 +106,7 @@ function plan_watermark_logo_svg(): string
function plan_watermark_html(): string
{
$url = app_marketing_url();
- $label = $url !== '' ? preg_replace('#^https?://#i', '', $url) : 'Kaffeeliste';
+ $label = plan_watermark_label();
$inner = plan_watermark_logo_svg()
. '' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '';
diff --git a/docs/billing.md b/docs/billing.md
index 78464da..bbe782b 100644
--- a/docs/billing.md
+++ b/docs/billing.md
@@ -338,7 +338,8 @@ eingeschaltet; ab einem bezahlten Tarif entscheidet der Mandant selbst
Umgesetzte Dateien:
```text
-app/plan-watermark.php (Sichtbarkeit + Markup)
+app/plan-watermark.php (Sichtbarkeit, Markup, Beschriftung)
+exportKaffeeliste.php (Marke im PDF-Fuss, gezeichnet statt eingebettet)
app/bootstrap.php (app_marketing_url())
app/saas-auth.php (Speichern, Zwang im kostenlosen Tarif)
mandant-einstellungen.php (Schalter + Hinweis)
@@ -363,6 +364,14 @@ scripts/http-smoke.php, scripts/check-m3-settings-flow.php (Regressionstests)
- **Keine Schreibzugriffe im Seitenaufbau.** Bewusst nicht
`billing_fetch_or_init()`, das eine fehlende Zeile anlegen würde; die
Prüfung ist eine reine Leseabfrage und wird pro Request zwischengespeichert.
+- **Im PDF mitgezeichnet.** Der Ausdruck traegt dieselbe Marke unten
+ rechts (`MyCustomPDFWithWatermark::planWatermark()`), nach derselben
+ Regel wie die Web-Ansicht. Die Tasse wird dort mit TCPDFs Zeichen-
+ befehlen gemalt: `ImageSVG()` braucht die XML-Extension, ein Rasterbild
+ GD - beides ist auf einem Webspace nicht garantiert. Steht die Marke,
+ reserviert `export_page_row_budget_mm()` 6 mm am Seitenfuss, damit die
+ Tabelle nicht hineinlaeuft (bei gesetzter Fusszeile deckt deren Band sie
+ bereits ab).
- **Logo als Inline-SVG.** Spart einen Request, bleibt auf jedem Bildschirm
scharf und übernimmt die Farbe der Umgebung. Ein eigenes Bild lässt sich
in `plan_watermark_logo_svg()` durch ein `
` ersetzen.
diff --git a/exportKaffeeliste.php b/exportKaffeeliste.php
index e79aaba..5ba96f7 100644
--- a/exportKaffeeliste.php
+++ b/exportKaffeeliste.php
@@ -35,6 +35,11 @@ $watermarkText = trim((string)($settings['pdf_watermark_text'] ?? ''));
$footerText = trim((string)($settings['pdf_footer_text'] ?? ''));
$watermarkLogoPath = tenant_logo_path((string)($settings['pdf_watermark_logo'] ?? '')) ?? '';
+// Hinweis auf die Kaffeeliste: im Ausdruck nach derselben Regel wie in der
+// Web-Ansicht - im kostenlosen Tarif fest, sonst nach Mandant-Einstellung.
+require_once __DIR__ . '/app/plan-watermark.php';
+$planWatermarkLabel = plan_watermark_visible($pdo, $tenantId) ? plan_watermark_label() : '';
+
// Vorder-/Rueckseite erst oberhalb der gemeinsamen Schwelle aus app/ledger.php
// (LEDGER_SINGLE_PAGE_MAX_MEMBERS): bis einschliesslich 50 Personen bleibt der
// Ausdruck bewusst einseitig.
@@ -70,7 +75,7 @@ $isMultiPage = ledger_uses_two_sided_sheet($memberCount);
* minus oberem/unterem Rand, Titelzeile, Spaltenkopf und optional
* reservierter Fusszeile bzw. Vorder-/Rueckseiten-Hinweiszeile.
*/
-function export_page_row_budget_mm(bool $hasFooter, bool $hasSeitenHinweis): float
+function export_page_row_budget_mm(bool $hasFooter, bool $hasSeitenHinweis, bool $hasPlanWatermark = false): float
{
// Kleiner zusaetzlicher Sicherheitspuffer obendrauf (der zeilenweise
// Cell()-Overhead wird bereits separat in export_compute_layout()
@@ -78,6 +83,10 @@ function export_page_row_budget_mm(bool $hasFooter, bool $hasSeitenHinweis): flo
$budget = 297.0 - 5.0 - 5.0 - EXPORT_TITLE_ROW_MM - EXPORT_HEADER_ROW_MM - 2.0;
if ($hasFooter) {
$budget -= 12.0;
+ } elseif ($hasPlanWatermark) {
+ // Der Hinweis auf die Kaffeeliste braucht deutlich weniger Platz als
+ // eine Fusszeile - steht beides, deckt deren Band ihn mit ab.
+ $budget -= 6.0;
}
if ($hasSeitenHinweis) {
$budget -= EXPORT_HINT_ROW_MM;
@@ -119,6 +128,7 @@ class MyCustomPDFWithWatermark extends TCPDF {
public string $watermarkText = '';
public string $watermarkLogoPath = '';
public string $footerText = '';
+ public string $planWatermarkLabel = '';
public function Header() {
$hasLogo = $this->watermarkLogoPath !== '' && is_file($this->watermarkLogoPath);
@@ -163,15 +173,69 @@ class MyCustomPDFWithWatermark extends TCPDF {
}
public function Footer() {
- if (trim($this->footerText) === '') {
+ if (trim($this->footerText) !== '') {
+ $this->SetY(-15);
+ $this->SetFont('helvetica', '', 8);
+ $this->SetTextColor(120, 120, 120);
+ $this->Cell(0, 10, $this->footerText, 0, 0, 'C');
+ $this->SetTextColor(0, 0, 0);
+ }
+
+ $this->planWatermark();
+ }
+
+ /**
+ * Hinweis auf die Kaffeeliste unten rechts - dasselbe Zeichen wie in der
+ * Web-Ansicht, nur fuer den Ausdruck. Das Tassen-Symbol wird mit TCPDFs
+ * eigenen Zeichenbefehlen gemalt statt als SVG oder Bild eingebettet:
+ * ImageSVG braucht die XML-Extension, ein Rasterbild braucht GD - beides
+ * ist auf einem Webspace nicht garantiert, gezeichnete Linien dagegen
+ * funktionieren immer.
+ */
+ protected function planWatermark(): void
+ {
+ if (trim($this->planWatermarkLabel) === '') {
return;
}
- $this->SetY(-15);
- $this->SetFont('helvetica', '', 8);
- $this->SetTextColor(120, 120, 120);
- $this->Cell(0, 10, $this->footerText, 0, 0, 'C');
+ $rechterRand = $this->getPageWidth() - 5.0;
+ // Text und Symbol teilen sich eine gedachte Zeile: Cell() zentriert
+ // den Schriftzug zuverlaessig in deren Hoehe, die Tasse wird auf
+ // dieselbe Mittellinie gezeichnet. Text() waere hier die schlechtere
+ // Wahl - sein y-Bezugspunkt haengt an Schriftmetriken und ergab in
+ // der Probe einen um Millimeter versetzten Schriftzug.
+ $zeilenHoehe = 4.0;
+ $zeileOben = $this->getPageHeight() - 5.0 - $zeilenHoehe;
+ $mitte = $zeileOben + ($zeilenHoehe / 2);
+
+ $this->SetFont('helvetica', '', 7);
+ $this->SetTextColor(130, 130, 130);
+ $textBreite = $this->GetStringWidth($this->planWatermarkLabel);
+
+ $tasseBreite = 2.6;
+ $tasseHoehe = 2.2;
+ $abstand = 1.0;
+ $henkelBreite = 1.0;
+ $tasseX = $rechterRand - $textBreite - $abstand - $tasseBreite - $henkelBreite;
+ $tasseY = $mitte - ($tasseHoehe / 2);
+
+ $this->SetDrawColor(130, 130, 130);
+ $this->SetLineWidth(0.2);
+
+ // Tassenkoerper: nur unten abgerundet ('0011' = unten rechts/links).
+ $this->RoundedRect($tasseX, $tasseY, $tasseBreite, $tasseHoehe, 0.7, '0011', 'D');
+ // Henkel: halber Bogen an der rechten Seite.
+ $this->Ellipse($tasseX + $tasseBreite, $mitte, 0.8, 0.6, 0, 270, 90, 'D');
+ // Zwei Dampffaehnchen ueber der Tasse.
+ $this->Line($tasseX + 0.7, $tasseY - 1.1, $tasseX + 0.7, $tasseY - 0.4);
+ $this->Line($tasseX + 1.7, $tasseY - 1.1, $tasseX + 1.7, $tasseY - 0.4);
+
+ $this->SetXY($rechterRand - $textBreite, $zeileOben);
+ $this->Cell($textBreite, $zeilenHoehe, $this->planWatermarkLabel, 0, 0, 'R');
+
$this->SetTextColor(0, 0, 0);
+ $this->SetDrawColor(0, 0, 0);
+ $this->SetLineWidth(0.2);
}
}
@@ -196,7 +260,11 @@ function export_render_page(
int $adminRowHeightPx,
?string $seitenHinweis
): void {
- $rowBudgetMm = export_page_row_budget_mm(trim($pdf->footerText) !== '', $seitenHinweis !== null);
+ $rowBudgetMm = export_page_row_budget_mm(
+ trim($pdf->footerText) !== '',
+ $seitenHinweis !== null,
+ trim($pdf->planWatermarkLabel) !== ''
+ );
[$targetRows, $rowHeight] = export_compute_layout(count($teilnehmer), $showEmptyRows, $adminRowHeightPx, $rowBudgetMm);
// Titelzeile: Name/Guthaben-Breite + Strichebreite, Hintergrundfarbe je nach Seite.
@@ -261,6 +329,7 @@ $pdf = new MyCustomPDFWithWatermark(PDF_PAGE_ORIENTATION, 'mm', 'A4', true, 'UTF
$pdf->watermarkText = $watermarkText;
$pdf->watermarkLogoPath = $watermarkLogoPath;
$pdf->footerText = $footerText;
+$pdf->planWatermarkLabel = $planWatermarkLabel;
$pdf->SetHeaderData("", 0, "Kaffeestrichliste", "");
$pdf->SetPrintFooter(true);
$pdf->SetMargins('5', '5', '5');