fix(pdf): een tabelrij hoger dan een blad laat de export niet vastlopen (#1798) #1800

Merged
brenno merged 1 commit from fix/export-hang-1798 into main 2026-08-26 21:54:12 +00:00
Owner

Waarom

Gevonden bij het toetsen van de weesbescherming uit #1799. Een pw.Table-rij kan niet over een bladovergang heen breken. Past de rij op geen enkel blad, dan plaatst MultiPage niets, begint een nieuw blad, en gebeurt daar precies hetzelfde — de opmaak loopt oneindig rond, op ~140% CPU, zonder melding.

De bewaking die dat zou moeten vangen staat upstream in een assert (multi_page.dart regel 294) en verdwijnt dus in een release-build. De gebruiker kreeg een bevroren venster zonder te weten welke tabel het was. Eén lange alinea in een tabelcel is genoeg, en dat is in een rapport heel gewoon.

De oplossing

Een tabel met zo'n rij gaat als losse blokken de stroom in: per rij elke cel op een eigen regel, met de kolomkop ervoor zodat zichtbaar blijft welk gegeven bij welke kolom hoort. De uitlijning naast elkaar gaat verloren — die was bij een cel van een halve bladzijde toch geen leeshulp meer. De inhoud blijft volledig.

Twee dingen die een ronde kostten

  • De lus is synchroon. Een Timeout in een toets vuurt niet, want de isolate geeft nooit terug. Detectie moest daarom vóór het renderen, en ik heb de grens met een pure functie bepaald in plaats van met een renderproef.
  • Alleen een widget die rechtstreeks in de lijst van MultiPage staat mag breken. Mijn eerste terugvalvorm zat in een pw.Column; die plaatst zijn kinderen heel, waardoor de hang gewoon terugkwam in een andere vorm. De blokken gaan nu plat de stroom in.

Waarschuwing bij het onderhouden

De regressietoets hangt wanneer de bescherming stukgaat, in plaats van te falen — om dezelfde reden dat een Timeout niet vuurt. Dat staat als waarschuwing in de toets zelf. Blijft de PDF-suite staan, kijk dan daar eerst.

Bestandssplitsing

Het tekenbestand kroop met deze wijziging naar 1031 regels. Het tabeltekenwerk is als part afgesplitst; document_pdf_widgets.dart zakt naar 820 en het plafond van 975 naar 850. Een extensie en geen losse functies, want dit werk heeft de stijl, de spanrenderer en de bladmaten van de bouwer nodig — en een extensie in een part deelt de bibliotheekscope.

Toetsplan

  • make check groen (MAKE_CHECK_EXIT=0, nul fouten in het log)
  • 158 PDF-toetsen; het hangende geval voltooit nu in milliseconden
  • Een gewone tabel blijft een tabel — getoetst op de aanwezigheid van tabelranden, zodat de terugvalvorm niet te gretig wordt
  • make check-secrets en make sast — 0 bevindingen
## Waarom Gevonden bij het toetsen van de weesbescherming uit #1799. Een `pw.Table`-rij kan niet over een bladovergang heen breken. Past de rij op geen enkel blad, dan plaatst `MultiPage` niets, begint een nieuw blad, en gebeurt daar precies hetzelfde — de opmaak loopt oneindig rond, op ~140% CPU, zonder melding. De bewaking die dat zou moeten vangen staat upstream in een `assert` (`multi_page.dart` regel 294) en verdwijnt dus in een release-build. De gebruiker kreeg een bevroren venster zonder te weten welke tabel het was. Eén lange alinea in een tabelcel is genoeg, en dat is in een rapport heel gewoon. ## De oplossing Een tabel met zo'n rij gaat als losse blokken de stroom in: per rij elke cel op een eigen regel, met de kolomkop ervoor zodat zichtbaar blijft welk gegeven bij welke kolom hoort. De uitlijning naast elkaar gaat verloren — die was bij een cel van een halve bladzijde toch geen leeshulp meer. De inhoud blijft volledig. ## Twee dingen die een ronde kostten - **De lus is synchroon.** Een `Timeout` in een toets vuurt niet, want de isolate geeft nooit terug. Detectie moest daarom vóór het renderen, en ik heb de grens met een pure functie bepaald in plaats van met een renderproef. - **Alleen een widget die rechtstreeks in de lijst van `MultiPage` staat mag breken.** Mijn eerste terugvalvorm zat in een `pw.Column`; die plaatst zijn kinderen heel, waardoor de hang gewoon terugkwam in een andere vorm. De blokken gaan nu plat de stroom in. ## Waarschuwing bij het onderhouden De regressietoets *hangt* wanneer de bescherming stukgaat, in plaats van te falen — om dezelfde reden dat een `Timeout` niet vuurt. Dat staat als waarschuwing in de toets zelf. Blijft de PDF-suite staan, kijk dan daar eerst. ## Bestandssplitsing Het tekenbestand kroop met deze wijziging naar 1031 regels. Het tabeltekenwerk is als `part` afgesplitst; `document_pdf_widgets.dart` zakt naar 820 en het plafond van 975 naar 850. Een extensie en geen losse functies, want dit werk heeft de stijl, de spanrenderer en de bladmaten van de bouwer nodig — en een extensie in een `part` deelt de bibliotheekscope. ## Toetsplan - [x] `make check` groen (`MAKE_CHECK_EXIT=0`, nul fouten in het log) - [x] 158 PDF-toetsen; het hangende geval voltooit nu in milliseconden - [x] Een gewone tabel blijft een tabel — getoetst op de aanwezigheid van tabelranden, zodat de terugvalvorm niet te gretig wordt - [x] `make check-secrets` en `make sast` — 0 bevindingen
fix(pdf): een tabelrij hoger dan een blad laat de export niet vastlopen (#1798)
All checks were successful
scans / scans (pull_request) Successful in 2m2s
static-gate / static-gate (pull_request) Successful in 4m57s
44f871b541
Een `pw.Table`-rij kan niet over een bladovergang heen breken. Past de rij op
geen enkel blad, dan plaatst `MultiPage` niets, begint een nieuw blad, en
gebeurt daar precies hetzelfde — de opmaak loopt oneindig rond, op volle
kracht, zonder melding. De bewaking die dat upstream zou vangen staat in een
`assert` en verdwijnt in een uitgeleverde app: de gebruiker kreeg een bevroren
venster. Eén lange alinea in een tabelcel is genoeg.

Zo'n tabel gaat nu als losse blokken de stroom in: per rij elke cel op een
eigen regel, met de kolomkop ervoor zodat zichtbaar blijft welk gegeven bij
welke kolom hoort. De uitlijning naast elkaar gaat verloren, maar die was bij
een cel van een halve bladzijde toch geen leeshulp meer. De inhoud blijft
volledig.

Twee dingen die het uitzoeken leerde, en beide kostten een ronde:

- De lus is **synchroon**. Een `Timeout` in een toets vuurt niet, want de
  isolate geeft nooit terug. De detectie moest daarom vóór het renderen, en de
  grens is met een pure functie bepaald in plaats van met een renderproef.
- Alleen een widget die rechtstreeks in de lijst van `MultiPage` staat mag
  breken. Mijn eerste terugvalvorm zat in een `pw.Column`, en die plaatst zijn
  kinderen heel — waardoor de hang gewoon terugkwam in een andere vorm. De
  blokken gaan nu plat de stroom in.

Het tabeltekenwerk is als `part` afgesplitst; het tekenbestand kroop met deze
wijziging naar 1031 regels en zakt nu naar 820, met het plafond mee van 975
naar 850. Een extensie en geen losse functies, want dit werk heeft de stijl,
de spanrenderer en de bladmaten van de bouwer nodig — en een extensie in een
`part` deelt de bibliotheekscope.
brenno merged commit 791c669504 into main 2026-08-26 21:54:12 +00:00
Sign in to join this conversation.
No description provided.