Files
TieMeasureFlow/packaging/windows/pdf/LEGGIMI.md
T

8.5 KiB

Report PDF nel pacchetto Windows

Questa cartella contiene quello che serve perché i report PDF di TieMeasureFlow (src/backend/services/report_service.py: Jinja2, Plotly con Kaleido 1.2.0, WeasyPrint 68.1) funzionino su Windows, dentro il pacchetto, senza Internet. Riferimento: TieMeasureFlow#78 (parte del pacchetto Windows, madre #76).

Come funziona

Un report SPC passa da due componenti nativi che su Windows non arrivano con pip:

  1. WeasyPrint disegna il PDF e carica a runtime le DLL di Pango/GTK (libgobject-2.0-0, libpango-1.0-0, libharfbuzz-0, libharfbuzz-subset-0, libfontconfig-1, libpangoft2-1.0-0 e le loro dipendenze).
  2. Kaleido 1.x trasforma i grafici Plotly in SVG pilotando un browser Chromium già installato (non porta più Chromium con sé). Su ogni Windows 10/11 c'è Edge.

Le DLL: prendi-gtk.ps1

powershell -NoProfile -ExecutionPolicy Bypass -File packaging\windows\pdf\prendi-gtk.ps1 -Destinazione <cartella>
  • Scarica da repo.msys2.org (ucrt64) 21 pacchetti pinnati con sha256 (nome file e hash stanno nello script), verifica l'hash, estrae solo le 27 DLL necessarie, etc\fonts di fontconfig e le licenze.
  • Idempotente: se la cartella è già pronta (file .gtk-pin uguale ai pin dello script) esce subito.
  • Se un hash non coincide fallisce e non lascia mezza cartella.
  • I pacchetti scaricati stanno in %LOCALAPPDATA%\Tielogic\tmf-gtk-cache (-Cache per cambiarla).
  • Serve Internet solo alla build. Estrae .zst con tar.exe di Windows (verificato: bsdtar 3.8.9 con zstd); se tar.exe non sa di zstd ripiega su uv run --with zstandard.
  • Gira da utente non admin con PowerShell 5.1.
  • ⚠️ repo.msys2.org tiene solo le versioni recenti: se un file pinnato sparisce (404) vanno aggiornati nome e sha256 dal db https://repo.msys2.org/mingw/ucrt64/ucrt64.db e rifatto il test. Conservare la cache (Wasabi o NAS) evita la corsa contro il repository.

Il piano C copia <cartella> in P\gtk\ (P = cartella programma).

Variabili d'ambiente (le imposta avvio.py, piano C)

Variabile Valore Perché
WEASYPRINT_DLL_DIRECTORIES <P>\gtk WeasyPrint chiama os.add_dll_directory su ogni cartella (separatore ;) prima di caricare le DLL. Va impostata prima di importare weasyprint (lo fa report_service all'import).
BROWSER_PATH msedge.exe o chrome.exe Choreographer (sotto Kaleido) la legge da sé e ha la precedenza su ogni ricerca (verificato: un percorso inesistente dà FileNotFoundError). Senza la variabile Kaleido cerca Edge/Chrome da solo (registro e percorsi tipici). Nessun codice in report_service.py per questo.

Fontconfig non richiede variabili: trova etc\fonts accanto alle DLL (verificato). Se manca etc\fonts WeasyPrint stampa "Fontconfig error: Cannot load default config file". I font dei report vengono da C:\Windows\Fonts.

MathJax: la modifica a report_service.py

Kaleido 1.x inserisce nella pagina di render uno <script> MathJax da cdnjs.cloudflare.com anche quando i grafici non hanno formule. Misurato su daitarn con Edge: con rete che scarta i pacchetti ogni grafico aspetta ~19 s (due grafici per report SPC); con rete che rifiuta subito ~2 s. report_service.py imposta pio.defaults.mathjax su un file .js vuoto in %TEMP% (tmf-mathjax-vuoto.js): nessuna richiesta di rete, report SPC in ~7 s anche a rete bloccata. Se un giorno i grafici useranno LaTeX, qui serve un MathJax locale. plotly.min.js invece è già locale (dentro il pacchetto plotly).

Flag Nuitka e dati da includere (per build.ps1, piano C)

⚠️ Stato: ricavati leggendo i sorgenti installati (versioni del uv.lock); non provati con una build Nuitka: la verifica è la prima build di C. I dati e i metadati sono quelli che il codice legge davvero da disco.

Dati dei pacchetti (file non .py letti a runtime):

--include-package-data=weasyprint     # css/html5_ua.css, html5_ph.css, html5_ua_form.css, pdf/sRGB2014.icc
--include-package-data=pyphen         # dictionaries/*.dic (84 file, sillabazione)
--include-package-data=kaleido        # vendor/kaleido_scopes.js (pagina di render)
--include-package-data=plotly         # package_data/plotly.min.js (Kaleido lo cerca come file accanto a plotly/__init__), templates/*.json, validators/_validators.json
--include-package-data=tzdata         # zoneinfo: Windows non ha il database IANA (vedi sotto)
--include-data-dir=src/backend/templates=src/backend/templates   # Jinja2: report_service legge i .html dal disco

choreographer/resources/last_known_good_chrome.json serve solo al comando che scarica Chrome: non serve.

Metadati delle distribuzioni (importlib.metadata.version al momento dell'import): senza, import plotly e Kaleido falliscono con PackageNotFoundError:

--include-distribution-metadata=plotly
--include-distribution-metadata=kaleido

Import dinamici che l'analisi statica di Nuitka può perdere:

--include-package=fontTools            # ttLib/ttFont.py importa le tabelle con __import__("fontTools.ttLib.tables." + tag)
--include-package=plotly               # _plotly_utils/importers.py: importlib.import_module lazy dei moduli graph_objs
--include-package=_plotly_utils
--include-module=_cffi_backend         # WeasyPrint usa cffi in modalita' ABI (dlopen), serve il modulo C di cffi
--include-package=weasyprint
--include-package=kaleido
--include-package=choreographer

Altre note per C:

  • WeasyPrint salta os.add_dll_directory se sys.frozen esiste (weasyprint/text/ffi.py). Nuitka di norma non lo imposta (PyInstaller sì): se in TieMeasureFlow.exe le DLL non si trovassero, chiamare os.add_dll_directory(<P>\gtk) da avvio.py prima dell'import.
  • Le DLL non vanno compilate né incluse da Nuitka: restano file in P\gtk\.
  • Il controllo "nessun .py" di build.ps1 non deve scandire gtk\ (non ci sono .py, ma è meglio saperlo) né licenze\.
  • tzdata manca da pyproject.toml: su Windows ZoneInfo("Europe/Rome") (usato da src/backend/utils/tempo.py, quindi da ogni report) dà ZoneInfoNotFoundError. Va aggiunto tzdata; sys_platform == 'win32' agli extra server e client (o al gruppo windows) e il frontend (src/frontend/flask_app/app.py usa ZoneInfo) ne ha bisogno allo stesso modo.
  • pdfplumber è già nell'extra server (il test lo usa per leggere i PDF).

Licenze delle DLL

Le DLL sono binari MSYS2/MinGW (ucrt64) non modificati, collegati dinamicamente. prendi-gtk.ps1 mette in <cartella>\licenze\ i testi di licenza di ogni pacchetto più un <pacchetto>.txt con nome del file e licenza. L'installer deve includere gtk\licenze\.

Libreria Licenza
Pango, GLib/GObject/GIO, fribidi, libthai, libdatrie, graphite2, libintl (gettext-runtime), libiconv LGPL 2.1 o successive
FreeType FTL oppure GPL 2+ (si sceglie FTL)
HarfBuzz, expat, libffi, brotli, fontconfig MIT / permissiva
libpng, zlib-ng, bzip2, pcre2 permissive (libpng, Zlib, bzip2, BSD-3)
libgcc, libstdc++ GPL 3 con GCC Runtime Library Exception (ridistribuzione libera)
libwinpthread MIT e BSD-3

Per le librerie LGPL, chi riceve il pacchetto può sostituire le DLL (sono file separati in gtk\); i sorgenti sono pubblici su MSYS2 (https://packages.msys2.org/), e l'elenco esatto delle versioni è nello script.

Test

src/backend/tests/test_report_windows.py (saltato fuori da Windows, senza DLL, senza Edge/Chrome o senza tzdata): in un sottoprocesso con WeasyPrint vero (il conftest.py altrimenti lo sostituisce con un mock) genera su SQLite in memoria il report SPC (carta di controllo e istogramma) e quello a tabella, e controlla con pdfplumber byte %PDF-, almeno una pagina, titoli dei grafici e segni vettoriali.

# DLL (una volta) e test
powershell -NoProfile -ExecutionPolicy Bypass -File packaging\windows\pdf\prendi-gtk.ps1 -Destinazione packaging\windows\pdf\gtk
uv run --python 3.13 --extra server --extra dev pytest src/backend/tests/test_report_windows.py -q

packaging/windows/pdf/gtk/ e cache/ sono in .gitignore: le DLL non si committano.

Prova a rete bloccata

HTTP_PROXY/HTTPS_PROXY non bastano: Edge e Chrome usano il proxy di sistema di Windows. Per provare davvero il PC senza Internet: impostare temporaneamente il proxy di sistema (Impostazioni, Rete, Proxy) su un indirizzo morto (127.0.0.1:9 rifiuta subito; 10.255.255.1:8080 scarta i pacchetti, il caso peggiore), generare i report e ripristinare il proxy. Esito del 2026-10-17 con proxy che scarta i pacchetti: report SPC e report a tabella completi in ~7 s.