Hinweis auf die Kaffeeliste auch im PDF-Ausdruck
Die Marke stand bisher nur in der Web-Ansicht; auf dem Ausdruck, den die Mitglieder taeglich vor sich haben, fehlte sie. Sie erscheint jetzt unten rechts im Seitenfuss - nach derselben Regel wie im Web: im kostenlosen Tarif fest, sonst nach Mandant-Einstellung. Die Tasse wird mit TCPDFs Zeichenbefehlen gemalt statt als SVG oder Bild eingebettet: ImageSVG braucht die XML-Extension, ein Rasterbild GD - beides ist auf einem Webspace nicht garantiert. Der Schriftzug sitzt in einer Cell() statt in Text(): dessen y-Bezug haengt an Schriftmetriken und ergab in der Probe einen um Millimeter versetzten Schriftzug. Steht die Marke, reserviert export_page_row_budget_mm() 6 mm am Seitenfuss, damit die Tabelle nicht hineinlaeuft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+14
-1
@@ -85,6 +85,19 @@ function plan_watermark_logo_svg(): string
|
|||||||
. '</svg>';
|
. '</svg>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
* Markup des Wasserzeichens. Ohne erreichbare Marketing-Domain (CLI,
|
||||||
* ungewoehnliche Hosts) bleibt es bei Logo und Namen ohne Link - ein
|
* 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
|
function plan_watermark_html(): string
|
||||||
{
|
{
|
||||||
$url = app_marketing_url();
|
$url = app_marketing_url();
|
||||||
$label = $url !== '' ? preg_replace('#^https?://#i', '', $url) : 'Kaffeeliste';
|
$label = plan_watermark_label();
|
||||||
$inner = plan_watermark_logo_svg()
|
$inner = plan_watermark_logo_svg()
|
||||||
. '<span class="plan-watermark-text">' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '</span>';
|
. '<span class="plan-watermark-text">' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '</span>';
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -338,7 +338,8 @@ eingeschaltet; ab einem bezahlten Tarif entscheidet der Mandant selbst
|
|||||||
Umgesetzte Dateien:
|
Umgesetzte Dateien:
|
||||||
|
|
||||||
```text
|
```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/bootstrap.php (app_marketing_url())
|
||||||
app/saas-auth.php (Speichern, Zwang im kostenlosen Tarif)
|
app/saas-auth.php (Speichern, Zwang im kostenlosen Tarif)
|
||||||
mandant-einstellungen.php (Schalter + Hinweis)
|
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
|
- **Keine Schreibzugriffe im Seitenaufbau.** Bewusst nicht
|
||||||
`billing_fetch_or_init()`, das eine fehlende Zeile anlegen würde; die
|
`billing_fetch_or_init()`, das eine fehlende Zeile anlegen würde; die
|
||||||
Prüfung ist eine reine Leseabfrage und wird pro Request zwischengespeichert.
|
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
|
- **Logo als Inline-SVG.** Spart einen Request, bleibt auf jedem Bildschirm
|
||||||
scharf und übernimmt die Farbe der Umgebung. Ein eigenes Bild lässt sich
|
scharf und übernimmt die Farbe der Umgebung. Ein eigenes Bild lässt sich
|
||||||
in `plan_watermark_logo_svg()` durch ein `<img>` ersetzen.
|
in `plan_watermark_logo_svg()` durch ein `<img>` ersetzen.
|
||||||
|
|||||||
+76
-7
@@ -35,6 +35,11 @@ $watermarkText = trim((string)($settings['pdf_watermark_text'] ?? ''));
|
|||||||
$footerText = trim((string)($settings['pdf_footer_text'] ?? ''));
|
$footerText = trim((string)($settings['pdf_footer_text'] ?? ''));
|
||||||
$watermarkLogoPath = tenant_logo_path((string)($settings['pdf_watermark_logo'] ?? '')) ?? '';
|
$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
|
// Vorder-/Rueckseite erst oberhalb der gemeinsamen Schwelle aus app/ledger.php
|
||||||
// (LEDGER_SINGLE_PAGE_MAX_MEMBERS): bis einschliesslich 50 Personen bleibt der
|
// (LEDGER_SINGLE_PAGE_MAX_MEMBERS): bis einschliesslich 50 Personen bleibt der
|
||||||
// Ausdruck bewusst einseitig.
|
// Ausdruck bewusst einseitig.
|
||||||
@@ -70,7 +75,7 @@ $isMultiPage = ledger_uses_two_sided_sheet($memberCount);
|
|||||||
* minus oberem/unterem Rand, Titelzeile, Spaltenkopf und optional
|
* minus oberem/unterem Rand, Titelzeile, Spaltenkopf und optional
|
||||||
* reservierter Fusszeile bzw. Vorder-/Rueckseiten-Hinweiszeile.
|
* 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
|
// Kleiner zusaetzlicher Sicherheitspuffer obendrauf (der zeilenweise
|
||||||
// Cell()-Overhead wird bereits separat in export_compute_layout()
|
// 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;
|
$budget = 297.0 - 5.0 - 5.0 - EXPORT_TITLE_ROW_MM - EXPORT_HEADER_ROW_MM - 2.0;
|
||||||
if ($hasFooter) {
|
if ($hasFooter) {
|
||||||
$budget -= 12.0;
|
$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) {
|
if ($hasSeitenHinweis) {
|
||||||
$budget -= EXPORT_HINT_ROW_MM;
|
$budget -= EXPORT_HINT_ROW_MM;
|
||||||
@@ -119,6 +128,7 @@ class MyCustomPDFWithWatermark extends TCPDF {
|
|||||||
public string $watermarkText = '';
|
public string $watermarkText = '';
|
||||||
public string $watermarkLogoPath = '';
|
public string $watermarkLogoPath = '';
|
||||||
public string $footerText = '';
|
public string $footerText = '';
|
||||||
|
public string $planWatermarkLabel = '';
|
||||||
|
|
||||||
public function Header() {
|
public function Header() {
|
||||||
$hasLogo = $this->watermarkLogoPath !== '' && is_file($this->watermarkLogoPath);
|
$hasLogo = $this->watermarkLogoPath !== '' && is_file($this->watermarkLogoPath);
|
||||||
@@ -163,15 +173,69 @@ class MyCustomPDFWithWatermark extends TCPDF {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function Footer() {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->SetY(-15);
|
$rechterRand = $this->getPageWidth() - 5.0;
|
||||||
$this->SetFont('helvetica', '', 8);
|
// Text und Symbol teilen sich eine gedachte Zeile: Cell() zentriert
|
||||||
$this->SetTextColor(120, 120, 120);
|
// den Schriftzug zuverlaessig in deren Hoehe, die Tasse wird auf
|
||||||
$this->Cell(0, 10, $this->footerText, 0, 0, 'C');
|
// 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->SetTextColor(0, 0, 0);
|
||||||
|
$this->SetDrawColor(0, 0, 0);
|
||||||
|
$this->SetLineWidth(0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +260,11 @@ function export_render_page(
|
|||||||
int $adminRowHeightPx,
|
int $adminRowHeightPx,
|
||||||
?string $seitenHinweis
|
?string $seitenHinweis
|
||||||
): void {
|
): 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);
|
[$targetRows, $rowHeight] = export_compute_layout(count($teilnehmer), $showEmptyRows, $adminRowHeightPx, $rowBudgetMm);
|
||||||
|
|
||||||
// Titelzeile: Name/Guthaben-Breite + Strichebreite, Hintergrundfarbe je nach Seite.
|
// 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->watermarkText = $watermarkText;
|
||||||
$pdf->watermarkLogoPath = $watermarkLogoPath;
|
$pdf->watermarkLogoPath = $watermarkLogoPath;
|
||||||
$pdf->footerText = $footerText;
|
$pdf->footerText = $footerText;
|
||||||
|
$pdf->planWatermarkLabel = $planWatermarkLabel;
|
||||||
$pdf->SetHeaderData("", 0, "Kaffeestrichliste", "");
|
$pdf->SetHeaderData("", 0, "Kaffeestrichliste", "");
|
||||||
$pdf->SetPrintFooter(true);
|
$pdf->SetPrintFooter(true);
|
||||||
$pdf->SetMargins('5', '5', '5');
|
$pdf->SetMargins('5', '5', '5');
|
||||||
|
|||||||
Reference in New Issue
Block a user