feat(LamNI,flomni): add PSI logo to every PDF report page footer

Downloaded from https://intranet.psi.ch/themes/custom/design/logo.svg
(no gradients/filters/embedded fonts -- confirmed fpdf2 renders it
directly, no PNG conversion needed) and stored as psi_logo.svg in
OMNY_shared. Added TomoQueueMixin._add_psi_footer(), which overrides
the fpdf instance's footer() callback (PDFWriter/BECPDF live in
bec_lib, a separate repo with no public API for this) so the logo
repeats on every page -- unlike the header logo, which is drawn once
inline, a report can span multiple pages (e.g. a long
at_each_angle_hook source dump). Also drops the microseconds from the
existing "BEC, <timestamp>" footer text while reimplementing it, per
request ("ms precision is not needed"). Verified end-to-end with
bec_lib's real PDFWriter: a 300-line hook source forced a 7-page
report, with the logo correctly repeating on every page.
This commit is contained in:
x01dc
2026-07-27 17:48:57 +02:00
parent 6c06b63ece
commit 79f257f072
3 changed files with 36 additions and 0 deletions
@@ -2231,6 +2231,7 @@ class LamNI(TomoQueueMixin, LamNIAlignmentMixin, LamNIOpticsMixin, LamniGuiTools
hook_source = self._active_hook_source()
user_target = os.path.expanduser(f"~/data/raw/documentation/tomo_scan_ID_{self.tomo_id}.pdf")
with PDFWriter(user_target) as file:
self._add_psi_footer(file)
# PDFWriter (bec_lib) has no public image API -- reach into its
# underlying fpdf object directly. logo_w chosen to keep the
# header modest relative to the A4 page width (210mm).
@@ -39,6 +39,7 @@ import builtins
import datetime
import inspect
import json
import os
import threading
import uuid
from typing import Callable
@@ -481,6 +482,39 @@ class TomoQueueMixin:
threading.Thread(target=_run, name="PdfUpload", daemon=True).start()
def _add_psi_footer(self, pdf_writer) -> None:
"""Add the PSI logo to every page's footer of a PDFWriter report.
PDFWriter/BECPDF (bec_lib, a separate repo) has no public API for
this, and its footer() is fpdf's own automatic per-page callback
(unlike the header logo, which is drawn once inline right after
opening the PDFWriter) -- a report can span multiple pages (e.g. a
long at_each_angle_hook source dump), so the logo needs to repeat
on each one. Fully replaces bec_lib's own footer() (rather than
calling it and adding to it) so the timestamp can drop the
microseconds str(datetime.datetime.now()) includes -- same visual
layout/font otherwise (see bec_lib/pdf_writer.py's BECPDF.footer()).
"""
psi_logo = os.path.join(os.path.dirname(os.path.abspath(__file__)), "psi_logo.svg")
if not os.path.exists(psi_logo):
return
from fpdf import XPos, YPos
pdf = pdf_writer._pdf
def _footer_with_logo():
pdf.set_y(-15)
pdf.image(psi_logo, x=pdf.l_margin, y=pdf.h - 22, h=6)
pdf.set_font("Courier", "", 8)
pdf.set_text_color(128)
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
pdf.cell(0, 10, f"BEC, {timestamp}", 0, new_x=XPos.RIGHT, new_y=YPos.TOP, align="L")
pdf.cell(
0, 10, "Page " + str(pdf.page_no()), 0, new_x=XPos.RIGHT, new_y=YPos.TOP, align="R"
)
pdf.footer = _footer_with_logo
# ── command-job action registry / dispatch ──────────────────────────────
def _validate_action_kwargs(self, action_name: str, kwargs: dict) -> None:
@@ -3862,6 +3862,7 @@ class Flomni(
f"~/data/raw/documentation/tomo_scan_ID_{self.tomo_id}.pdf"
)
with PDFWriter(user_target) as file:
self._add_psi_footer(file)
# PDFWriter (bec_lib) has no public image API -- reach into its
# underlying fpdf object directly. logo_w chosen to keep the
# header modest relative to the A4 page width (210mm).