This commit is contained in:
2026-07-29 11:36:49 +02:00
parent b30b87e74a
commit 2fa76d5073
2 changed files with 46 additions and 146 deletions

View File

@@ -30,31 +30,19 @@
* l'ellissi rende visibile l'accorciamento, mentre un nome che deborda
* corromperebbe la griglia in silenzio.
*
* Le due celle hanno vincoli diversi.
*
* Le larghezze qui sotto sono misurate sul PDF renderizzato, non dedotte
* dalle percentuali: al netto di padding e bordi le celle risultano
* 35.9mm e 22.2mm, e queste costanti tengono un margine su quei valori.
* Se cambiano le percentuali delle colonne o i margini di pagina, vanno
* rimisurate.
* La larghezza qui sotto è misurata sul PDF renderizzato, non dedotta
* dalle percentuali: al netto di padding e bordi la cella risulta 35.9mm,
* e la costante tiene un margine su quel valore. Se cambiano le
* percentuali delle colonne o i margini di pagina, va rimisurata.
*
* "Assegnato a" è nowrap su una riga sola: basta il corpo che ci sta, e
* oltre il minimo si tronca.
*
* Il riporto sta in una colonna stretta alta due righe. Lì il
* nome NON viene mandato a capo da dompdf: lo spezziamo noi in due righe
* bilanciate e le teniamo nowrap. Lasciar decidere al wrapper significa
* non sapere quante righe verranno fuori — e una terza riga non è un
* problema estetico, sfonda l'altezza del blocco e manda l'ultimo
* territorio su una pagina nuova, raddoppiando i fogli. Spezzando a mano
* l'altezza della cella è esatta per costruzione.
* oltre il minimo si tronca. La colonna del riporto ospita solo una data,
* quindi non ha bisogno di adattamenti.
*/
$MM_PER_CARATTERE_PER_PUNTO = 0.20;
$CORPO_MIN_PT = 4.5;
$LARGHEZZA_NOME_MM = 34.0; // cella misurata 35.9mm
$LARGHEZZA_RIPORTO_MM = 20.5; // colonna misurata 22.2mm
$CORPO_RIPORTO_MAX_PT = 6.0;
$LARGHEZZA_NOME_MM = 34.0; // cella misurata 35.9mm
$corpoPer = static function (string $testo, float $larghezzaMm, float $corpoMaxPt) use ($MM_PER_CARATTERE_PER_PUNTO, $CORPO_MIN_PT): string {
$len = mb_strlen($testo);
@@ -74,46 +62,6 @@
return mb_strlen($testo) > $max ? mb_substr($testo, 0, $max - 1) . '…' : $testo;
};
/**
* Spezza il nome in due righe scegliendo, fra tutti i punti fra una parola
* e l'altra, quello che rende più corta la riga più lunga: è la riga più
* lunga a decidere il corpo, quindi bilanciare significa poter scrivere
* più grande.
*
* @return array{righe: array<int, string>, corpo: string}
*/
$adattaRiporto = static function (string $testo) use ($corpoPer, $troncaPer, $LARGHEZZA_RIPORTO_MM, $CORPO_RIPORTO_MAX_PT): array {
$parole = preg_split('/\s+/', trim($testo), -1, PREG_SPLIT_NO_EMPTY);
$righe = [$testo];
if (count($parole) > 1) {
$migliore = null;
for ($i = 1; $i < count($parole); $i++) {
$prima = implode(' ', array_slice($parole, 0, $i));
$dopo = implode(' ', array_slice($parole, $i));
$peggio = max(mb_strlen($prima), mb_strlen($dopo));
if ($migliore === null || $peggio < $migliore['peggio']) {
$migliore = ['righe' => [$prima, $dopo], 'peggio' => $peggio];
}
}
$righe = $migliore['righe'];
}
// Una riga più larga della colonna deborderebbe: si accorcia.
$righe = array_map(fn (string $r) => $troncaPer($r, $LARGHEZZA_RIPORTO_MM), $righe);
$piuLunga = array_reduce($righe, fn (?string $a, string $b) => mb_strlen($b) > mb_strlen((string) $a) ? $b : $a);
return [
'righe' => $righe,
'corpo' => $corpoPer((string) $piuLunga, $LARGHEZZA_RIPORTO_MM, $CORPO_RIPORTO_MAX_PT),
];
};
$ultimaPagina = $pagine->count() - 1;
@endphp
<!DOCTYPE html>
@@ -172,9 +120,9 @@
border: 1.6pt solid #000;
/* Obbligatorio, non cosmetico: in layout automatico le larghezze
del <colgroup> sono solo un suggerimento e una cella con del
testo lungo (la colonna del riporto, quando ospita un nome) si
allarga schiacciando le altre. I corpi dei nomi sono calcolati
su larghezze fisse, quindi a quel punto il testo deborda. */
testo lungo (un nome di proclamatore) si allarga schiacciando le
altre. I corpi dei nomi sono calcolati su larghezze fisse,
quindi a quel punto il testo deborda. */
table-layout: fixed;
}
td, th {
@@ -194,21 +142,18 @@
th.sotto { font-size: 6.4pt; }
/* Corpo */
/* 9.4mm a blocco. Il vincolo non sono i nomi (8pt = 2.8mm) ma la cella
del riporto, che a cavallo delle due righe ospita due righe di nome
a 6pt più la data: ~7.2mm. */
/* 9.4mm a blocco: 21 blocchi devono stare in un foglio come sul modulo
cartaceo. Alzando queste altezze l'ultimo territorio scivola su una
pagina nuova e ogni foglio ne occupa due. */
tr.riga-nome td { height: 5.6mm; }
tr.riga-date td { height: 3.8mm; }
td.numero { font-size: 9.5pt; font-weight: bold; }
td.data { font-size: 7.5pt; }
/* La colonna del riporto è alta due righe: quando ospita
un'assegnazione ci stanno il nome (che qui può andare a capo, c'è
spazio in verticale) e sotto la data di rientro. */
td.riporto { padding: 0 0.5mm; }
.riporto-nome { line-height: 1.05; white-space: nowrap; }
.riporto-data { font-size: 7pt; line-height: 1.1; }
/* La colonna del riporto è alta due righe e ospita solo la data di
rientro con cui si chiudeva il ciclo precedente. */
td.riporto { font-size: 7.5pt; padding: 0 0.5mm; }
td.nome { line-height: 1.1; padding: 0 0.6mm; white-space: nowrap; }
.nota { font-size: 7.4pt; margin-top: 1.6mm; }
@@ -251,17 +196,9 @@
</thead>
<tbody>
@foreach($righe as $riga)
@php($riporto = $adattaRiporto($riga['riporto']['nome']))
<tr class="riga-nome">
<td class="numero" rowspan="2">{{ $riga['numero'] }}</td>
<td class="riporto" rowspan="2">
@if($riga['riporto']['nome'] !== '')
<div class="riporto-nome" style="font-size:{{ $riporto['corpo'] }}">
@foreach($riporto['righe'] as $r)<div>{{ $r }}</div>@endforeach
</div>
@endif
<div class="riporto-data">{{ $riga['riporto']['data'] }}</div>
</td>
<td class="riporto" rowspan="2">{{ $riga['riporto'] }}</td>
@foreach($riga['assegnazioni'] as $a)
@php($nome = $troncaPer($a['nome'], $LARGHEZZA_NOME_MM))
<td class="nome" colspan="2" style="font-size:{{ $corpoPer($nome, $LARGHEZZA_NOME_MM, 8) }}">{{ $nome }}</td>