PDF-export: een tabelkopregel kan verweesd onderaan een pagina staan #1790
Labels
No labels
accepted
bug
declined
docs
duplicate
enhancement
good first issue
in-progress
needs-info
privacy
security
triage
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
LibreKAT/Ocideck#1790
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Wat is het probleem?
Een tabelkopregel kan onderaan een pagina worden geplaatst zonder dat er ook maar één inhoudsrij achteraan past. De kopregel herhaalt vervolgens bovenaan de volgende pagina. Het resultaat is een losse gele balk onder aan het blad die niets aankondigt.
#1758 loste dit op voor sub-hoofdstukken (
##/###/####) met keep-with-next. Een tabelkop heeft dezelfde behoefte en kreeg die niet.Hoe te reproduceren
Verwacht gedrag
Een tabelkopregel houdt ten minste één inhoudsrij bij zich, net als een sub-hoofdstuk sinds #1758. Past dat niet meer op het blad, dan schuift de kop mee naar de volgende pagina.
Werkelijk gedrag
Waargenomen in OciDeck 0.4.10, in alle drie de RWM-rapporten:
Incidentrapport-RWM-aanval-op-de-externe-werkomgeving-RDWebRD-Gateway-volledig.pdf: pagina 1 (kernbeoordelingstabel), pagina 20 (§7.1 accountvergrendelingen), pagina 21 (bron-IP-tabel) — telkens alleen de kopregel onderaan;Beoordeling-van-de-beheersing-van-de-gehoste-technische-omgeving-van-RWM-volledig.pdf: pagina 12, §9 Bronnen — de kop staat onderaan, de tabel begint op 13;Incidentrapport-RWM-onjuiste-toegangsrechten-op-de-Q-schijf-volledig.pdf: pagina 11 — kop plus één rij (R-01), de rest op 12.Dat laatste geval laat een tweede kant zien: één enkele rij achterblijven is óók lelijk. Twee rijen bij de kop houden zou hier beter zijn.
Richting voor een oplossing
Dezelfde keep-with-next-machinerie als #1758 toepassen op de kopregel van een tabel, met een drempel van ten minste één en bij voorkeur twee inhoudsrijen. Zie
lib/services/pdf/document_pdf_widgets.darten de bestaande toets intest/pdf/document_pdf_keep_with_next_test.dart.Opgepakt. Tak
fix/pdf-tabelkop-1790. Verwacht te raken: hetzelfde keep-with-next-pad dat #1758 kreeg, nu voor de kopregel van een tabel;lib/services/pdf/document_pdf_widgets.dartentest/pdf/document_pdf_keep_with_next_test.dart.Deels opgelost in PR #1795.
Wat weg is. Een tabel die op één blad past blijft nu heel, zodat zijn kopregel niet meer alleen onderaan kan achterblijven. Dat dekt de korte tabellen die een rapport vullen — in het RDWeb-rapport §7.1 en de bron-IP-tabel, in het Q-schijf-rapport het aanbevelingenregister.
Wat blijft. Een tabel die over meerdere bladen loopt houdt het euvel: §9 Bronnen van de CBW-notitie en de kernbeoordelingstabel op pagina 1 van het RDWeb-rapport.
Waarom niet verder.
pw.Tablekent geen instelling die voorkomt dat er alleen herhaalrijen op een blad landen —Table.layoutplaatst rijen tot er één niet meer past en zetlastLinedaarop, ook als dat alleen de kopregel is.MultiPagekan een spannende widget evenmin vragen zich te verplaatsen.Een volledige reparatie vraagt een eigen
SpanningWidgetdie depw.Tableomhult, na de opmaak vaststelt dat de geplaatste hoogte niet boven die van de kopregel uitkomt, en zich dan metapplyContextterugtrekt zodatMultiPageeen nieuw blad begint. Dat is te bouwen —saveContext/applyContextzijn publiek — maar het draagt een reëel risico op een oneindige lus: trekt de widget zich óók terug op een vers blad waar de tabel sowieso niet past, dan blijftMultiPagebladen openen. Een betrouwbare "sta ik al bovenaan een leeg blad"-toets is er niet.Dat risico weegt niet op tegen de winst zolang de korte tabellen gedekt zijn. Wie dit oppakt: begin bij de lus in
Table.layoutrond regel 470 vanpackage:pdf3.13.0 — een upstream-patch die weigert te breken op alleenrepeat-rijen is waarschijnlijk de eerlijkere plek.Opnieuw opgepakt en nu tot de bodem uitgezocht. De uitkomst: het resterende deel is niet in OciDeck te repareren. Hieronder waarom, zodat niemand deze weg nog eens hoeft af te lopen.
Het mechanisme, exact
package:pdf3.13.0,lib/src/widgets/table.dart, de plaatsingslus rond regel 470:Rijen vóór
_context.firstLineworden overgeslagen tenzijrow.repeat. Past onderaan een blad de kopregel nog wel en de eerste inhoudsrij niet, dan wordtlastLine = 1en tekent hij de kop daar alleen. Op het volgende blad staatfirstLine = 1en herhaalt de kop zich boven de inhoud. Er is geen instelling die dat voorkomt:Tablekent alleenchildren,border,defaultVerticalAlignment,columnWidths,defaultColumnWidthentableWidth.Waarom de voor de hand liggende routes doodlopen
SpanningWidgeteromheen die vaststelt dat er alleen herhaalrijen geplaatst zijn en zich terugtrekt.saveContext/applyContextzijn publiek, dus dat is bouwbaar — maarMultiPagebiedt geen "ik plaatste niets, ga door naar het volgende blad"-route. In de lus (multi_page.dartregel 376) is de spanningstak alleen bereikbaar als de widget te hoog is; een widget die nul hoogte meldt valt in de normale tak, wordt aan het blad toegevoegd en krijgt_index++— de resterende rijen zijn dan weg. Om in de spanningstak te komen moet je een hoogte melden die niet klopt, en dat is precies het soort truc dat bij een volgende upstream-versie stilletjes breekt.sameCount++ > maxPagesstaat in eenassert(regel 294) en bestaat dus alleen in debug. Rekent zo'n proxy zich mis in een release-build, dan blijftMultiPagebladen openen — een vastloper bij de gebruiker, voor een gele balk.Inseparabledie hoger is dan een blad slaat de export stuk. Rafelige bladranden bij élke lange tabel is een slechtere ruil dan een enkele verweesde kop.Wat er wél gebeurd is
Een tabel die op één blad past blijft sinds
aa6d43bf2heel en kan zijn kopregel dus niet meer alleen achterlaten. Dat dekt de korte tabellen die een rapport vullen. Een tabel die over meerdere bladen loopt houdt het euvel.Waar het thuishoort
Upstream. De eerlijke reparatie is dat
Table.layoutweigert te breken op een positie waar alleenrepeat-rijen geplaatst zijn — vier regels in de lus hierboven. Wie dat oppakt:DavBfr/dart_pdf,lib/src/widgets/table.dart.Ik sluit dit issue omdat OciDeck's aandeel af is en het restant een afhankelijkheid betreft die we niet vendoren voor een cosmetisch defect. Komt er een upstream-versie met een oplossing, dan is er niets meer te doen dan bumpen.
Heropend. Mijn afsluiting hierboven was te vroeg en de redenering had een gat.
Ik schreef dat
MultiPagegeen route heeft waarlangs een spannende widget zich kan terugtrekken. Dat klopt niet. De eerstelayout-aanroep in de lus gebruikt de volle paginabeperking, niet de resterende ruimte — een tabel die langer is dan een blad valt daardoor altijd in de spanningstak (multi_page.dartregel 376). Pas dáár wordt hij opnieuw opgemaakt metlocalConstraints, precies de ruimte die over is. Dat is een aangrijpingspunt.Mijn tweede argument — kans op een oneindige lus, en de bewaking zit in een
assert— geldt alleen voor de naïeve variant die zich altijd terugtrekt. Een omhullende widget die hoogstens één keer achtereen uitwijkt en daarna accepteert wat er past, kan per constructie niet lussen. Die bewaking heb ik weggeredeneerd in plaats van hem te ontwerpen.Ik pak hem alsnog op.
Opgelost in
4e83b19ac(PR #1799).OrphanSafeTablestelt in de spanningstak vast dat er alleen herhaalrijen geplaatst zijn en zetlastLineop nul —paint()valt daar meteen op terug, dus dat blad blijft leeg en de opmaak gaat verder op het volgende. Twee grendels houden het eindig: uitwijken mag alleen wanneer de aangeboden hoogte merkbaar kleiner is dan de bladhoogte, en nooit twee keer op dezelfde leesregel.De valkuil die een ronde kostte:
MultiPagekloont de context vóór de eerste opmaakaanroep en zet hem vóór de tweede terug. De bewaarde leesregel mag dus pas losgelaten worden wanneer een opmaak werkelijk iets plaatst — anders begint de tabel bij die tweede aanroep weer bij rij nul en herhaalt hij alles. Dat is precies waarom de regressietoets niet alleen op de verweesde kop let maar óók eist dat alle veertig rijen exact één keer voorkomen; die tweede assertie ving het.Mijn eerdere afsluiting was fout op twee punten, en beide waren te voorkomen geweest: ik nam aan dat de eerste opmaakaanroep de resterende ruimte gebruikt (het is de volle bladhoogte), en ik redeneerde het lusrisico weg in plaats van er een grendel voor te ontwerpen.