fix(pdf): een tabel over meerdere bladen laat zijn kopregel niet achter (#1790) #1799

Merged
brenno merged 1 commit from fix/wees-tabelkop-1790 into main 2026-08-26 20:57:12 +00:00
Owner

Waarom

Ik had #1790 gesloten met de redenering dat MultiPage geen route biedt waarlangs een spannende widget zich kan terugtrekken, en dat een omhullende widget zou kunnen lussen. Op aandringen opnieuw bekeken — en beide argumenten hielden geen stand.

De eerste opmaakaanroep gebruikt de volle paginabeperking, niet de resterende ruimte. Een tabel die langer is dan een blad valt daardoor altijd in de spanningstak (multi_page.dart regel 376), en dáár wordt hij opnieuw opgemaakt met precies de ruimte die op dit blad over is. Dat is het aangrijpingspunt dat ik miste. En het lusargument gold alleen voor de naïeve variant; een grendel die vooruitgang afdwingt was te ontwerpen in plaats van weg te redeneren.

Hoe het werkt

OrphanSafeTable (subklasse van pw.Table) stelt na de opmaak vast dat er alleen herhaalrijen geplaatst zijn en zet lastLine op nul — paint() valt daar meteen op terug, dus dit blad blijft leeg en de opmaak gaat verder op het volgende. Mogelijk gemaakt doordat saveContext() het lévende TableContext teruggeeft, met publieke firstLine/lastLine.

Twee grendels houden het eindig:

  1. Uitwijken mag alleen wanneer de aangeboden hoogte merkbaar kleiner is dan de grootste die deze tabel ooit kreeg — de bladhoogte, die hij op elk blad bij de eerste aanroep ziet. Op een vers blad wijkt hij dus nooit uit.
  2. Nooit twee keer op dezelfde leesregel. Elke geslaagde opmaak schuift die op, dus vooruitgang is gegarandeerd.

Een valkuil die een ronde kostte

MultiPage kloont de context vóór de eerste aanroep en zet hem vóór de tweede terug. Liet ik de bewaarde leesregel na de eerste aanroep los, dan begon de tabel bij de tweede weer bij rij nul en herhaalde hij alles. Hij wordt nu pas losgelaten wanneer een opmaak werkelijk iets plaatst. Precies daarom eist de regressietoets niet alleen "geen verweesde kop" maar óók dat alle veertig rijen precies één keer voorkomen — die tweede assertie ving deze fout.

Bijvangst: #1798

Bij het toetsen van de grendel bleek package:pdf zélf oneindig rond te lopen wanneer één tabelrij hoger is dan een blad. Vastgesteld met een kale pw.Table, dus het ligt niet aan deze wijziging. De bewaking daarvoor staat upstream in een assert en doet in een release-build niets — de gebruiker krijgt een bevroren venster. Apart vastgelegd als #1798. De grendeltoets hier gebruikt daarom rijen die bijna, maar niet helemaal, een blad hoog zijn.

Toetsplan

  • make check groen (MAKE_CHECK_EXIT=0, nul fouten in het log)
  • 155 PDF-toetsen; de nieuwe weestoets één keer rood geproefd tegen een kale pw.Table
  • make check-secrets en make sast — 0 bevindingen
## Waarom Ik had #1790 gesloten met de redenering dat `MultiPage` geen route biedt waarlangs een spannende widget zich kan terugtrekken, en dat een omhullende widget zou kunnen lussen. Op aandringen opnieuw bekeken — en beide argumenten hielden geen stand. De eerste opmaakaanroep gebruikt de **volle** paginabeperking, niet de resterende ruimte. Een tabel die langer is dan een blad valt daardoor altijd in de spanningstak (`multi_page.dart` regel 376), en dáár wordt hij opnieuw opgemaakt met precies de ruimte die op dit blad over is. Dat is het aangrijpingspunt dat ik miste. En het lusargument gold alleen voor de naïeve variant; een grendel die vooruitgang afdwingt was te ontwerpen in plaats van weg te redeneren. ## Hoe het werkt `OrphanSafeTable` (subklasse van `pw.Table`) stelt na de opmaak vast dat er alleen herhaalrijen geplaatst zijn en zet `lastLine` op nul — `paint()` valt daar meteen op terug, dus dit blad blijft leeg en de opmaak gaat verder op het volgende. Mogelijk gemaakt doordat `saveContext()` het lévende `TableContext` teruggeeft, met publieke `firstLine`/`lastLine`. **Twee grendels houden het eindig:** 1. Uitwijken mag alleen wanneer de aangeboden hoogte merkbaar kleiner is dan de grootste die deze tabel ooit kreeg — de bladhoogte, die hij op elk blad bij de eerste aanroep ziet. Op een vers blad wijkt hij dus nooit uit. 2. Nooit twee keer op dezelfde leesregel. Elke geslaagde opmaak schuift die op, dus vooruitgang is gegarandeerd. ## Een valkuil die een ronde kostte `MultiPage` kloont de context vóór de eerste aanroep en zet hem vóór de tweede terug. Liet ik de bewaarde leesregel na de eerste aanroep los, dan begon de tabel bij de tweede weer bij rij nul en herhaalde hij alles. Hij wordt nu pas losgelaten wanneer een opmaak werkelijk iets plaatst. Precies daarom eist de regressietoets niet alleen "geen verweesde kop" maar óók dat alle veertig rijen precies één keer voorkomen — die tweede assertie ving deze fout. ## Bijvangst: #1798 Bij het toetsen van de grendel bleek `package:pdf` zélf oneindig rond te lopen wanneer één tabelrij hoger is dan een blad. Vastgesteld met een kale `pw.Table`, dus het ligt niet aan deze wijziging. De bewaking daarvoor staat upstream in een `assert` en doet in een release-build niets — de gebruiker krijgt een bevroren venster. Apart vastgelegd als #1798. De grendeltoets hier gebruikt daarom rijen die bijna, maar niet helemaal, een blad hoog zijn. ## Toetsplan - [x] `make check` groen (`MAKE_CHECK_EXIT=0`, nul fouten in het log) - [x] 155 PDF-toetsen; de nieuwe weestoets één keer rood geproefd tegen een kale `pw.Table` - [x] `make check-secrets` en `make sast` — 0 bevindingen
fix(pdf): een tabel over meerdere bladen laat zijn kopregel niet achter (#1790)
All checks were successful
scans / scans (pull_request) Successful in 2m1s
static-gate / static-gate (pull_request) Successful in 4m54s
6dae13b48c
Ik had dit issue gesloten met de redenering dat `MultiPage` geen route biedt
waarlangs een spannende widget zich kan terugtrekken. Dat klopte niet. De
eerste opmaakaanroep gebruikt de vólle paginabeperking, waardoor een tabel die
langer is dan een blad altijd in de spanningstak valt — en dáár wordt hij
opnieuw opgemaakt met precies de ruimte die op dit blad over is. Dat is een
aangrijpingspunt.

`OrphanSafeTable` stelt daar vast dat er alleen herhaalrijen geplaatst zijn en
zet `lastLine` op nul; `paint()` tekent dan niets en de opmaak gaat verder op
het volgende blad. `Table` biedt geen instelling hiervoor, maar het
`SpanningWidget`-contract volstaat: `saveContext()` geeft het lévende
`TableContext` met publieke `firstLine`/`lastLine`.

Twee grendels houden het eindig. Uitwijken mag alleen wanneer de aangeboden
hoogte merkbaar kleiner is dan de bladhoogte — op een vers blad valt er niets
beters te halen — en nooit twee keer op dezelfde leesregel, want elke geslaagde
opmaak schuift die op.

Eén valkuil kostte een ronde: `MultiPage` kloont de context vóór de eerste
aanroep en zet hem vóór de tweede terug. Liet ik de bewaarde leesregel na de
eerste aanroep los, dan begon de tabel bij die tweede aanroep weer bij rij nul
en herhaalde hij alles. Hij wordt nu pas losgelaten wanneer een opmaak
werkelijk iets plaatst.

De regressietoets eist twee dingen tegelijk — de tweede is waar een fout in het
herstellen van de leesregel zich zou tonen: geen blad met alleen de kopregel,
én alle veertig rijen precies één keer.

Bij het toetsen van de grendel bleek `package:pdf` zélf oneindig rond te lopen
wanneer één rij hoger is dan een blad, ook met een kale `pw.Table`. Vastgelegd
als #1798; de grendeltoets gebruikt daarom rijen die bijna, maar niet helemaal,
een blad hoog zijn.
brenno merged commit 4e83b19ac8 into main 2026-08-26 20:57:12 +00:00
Sign in to join this conversation.
No description provided.