From 170f98ea97624a477102be3caaaf867abd68be96 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Mon, 7 Sep 2026 17:14:48 +1000 Subject: [PATCH 01/13] Do not paint a table background while a block is only being measured (mirrors mpdf/mpdf#604) A block with page-break-inside:avoid is laid out twice: once to find out how tall it is, and then again for real once BlockTag::close() has decided where it goes. Nothing paints on the measuring pass - BaseWriter::write() throws the output away and PaintDivBB() returns straight back out - because the unwind afterwards only throws away the pages after the one the block started on. Whatever reached that starting page stays there. _tableWrite() splices the table backgrounds straight into $this->pages[$this->page] without asking, so a table inside such a block had its background painted on the starting page during the measuring pass, and then again in its real place after the block had moved to the next page. The reporter saw a coloured band left behind at the bottom of the previous page. It takes a table before the block to show up, because that is what leaves a placeholder on the starting page for the backgrounds to be spliced into. Skip the print while keep_block_together is set, the way everything else that paints already does. The collected backgrounds are still cleared, so the real pass starts from nothing and draws them where the table ended up. All twenty snapshots are unchanged. Co-authored-by: Claude Opus 5 --- src/Mpdf.php | 35 ++++--- tests/Mpdf/TableBackgroundTest.php | 90 ++++++++++++++++++ .../Snapshots/PageBreakAvoidSnapshotTest.php | 67 +++++++++++++ tests/data/snapshots/page-break-avoid.pdf | Bin 0 -> 29645 bytes 4 files changed, 177 insertions(+), 15 deletions(-) create mode 100644 tests/Mpdf/TableBackgroundTest.php create mode 100644 tests/Snapshots/PageBreakAvoidSnapshotTest.php create mode 100644 tests/data/snapshots/page-break-avoid.pdf diff --git a/src/Mpdf.php b/src/Mpdf.php index 4ab0d0f7d..7a5ab7632 100644 --- a/src/Mpdf.php +++ b/src/Mpdf.php @@ -23470,21 +23470,26 @@ function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $spl } if ($this->tableBackgrounds && $level == 1) { - $s = $this->PrintTableBackgrounds(); - if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) { - $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->tablebuffer); - if ($level == 1) { - $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->tablebuffer); - } - } elseif ($this->bufferoutput) { - $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer); - if ($level == 1) { - $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer); - } - } else { - $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]); - if ($level == 1) { - $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]); + // Nothing else paints while a page-break-inside:avoid block is being measured - see + // PaintDivBB() and BaseWriter::write() - and what is drawn on the page it starts on + // stays there when the block moves + if (!$this->keep_block_together) { + $s = $this->PrintTableBackgrounds(); + if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) { + $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->tablebuffer); + if ($level == 1) { + $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->tablebuffer); + } + } elseif ($this->bufferoutput) { + $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer); + if ($level == 1) { + $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer); + } + } else { + $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]); + if ($level == 1) { + $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]); + } } } $this->tableBackgrounds = []; diff --git a/tests/Mpdf/TableBackgroundTest.php b/tests/Mpdf/TableBackgroundTest.php new file mode 100644 index 000000000..b2c8d8796 --- /dev/null +++ b/tests/Mpdf/TableBackgroundTest.php @@ -0,0 +1,90 @@ + 'c']); + $mpdf->compress = false; + $mpdf->WriteHTML($html); + + return $mpdf->Output('', 'S'); + } + + /** + * The content stream of each page, in order + */ + private function pages($pdf) + { + $matches = []; + preg_match_all('/\d+ 0 obj\s*<<\/Length \d+>>\s*stream\n(.*?)\nendstream/s', $pdf, $matches); + + return $matches[1]; + } + + /** + * The rectangles filled red, without the duplicates that come of the same background being + * spliced in behind more than one placeholder + */ + private function redRectangles($pdf) + { + $matches = []; + preg_match_all('/' . preg_quote(self::RED_FILL, '/') . '\n([\d.\- ]+) re f/', $pdf, $matches); + + return array_values(array_unique($matches[1])); + } + + private function document($before, $after) + { + return str_repeat('

Before

', $before) + . '
Normal table cell
' + . '
' + . '
Red table cell
' + . str_repeat('

After

', $after) + . '
'; + } + + /** + * A page-break-inside:avoid block is laid out once to measure it and then again for real. The + * measuring pass used to paint the table background on the page the block starts on, which is + * the one page the unwind does not throw away, so it was left behind after the table had moved + * to the next page. See mpdf/mpdf#570. + */ + public function testATableBackgroundInsideAnAvoidBlockThatMovesIsDrawnInOnePlace() + { + $this->assertCount(1, $this->redRectangles($this->render($this->document(20, 10)))); + } + + public function testItIsDrawnOnThePageTheTableMovedToAndNotTheOneItLeft() + { + $pages = $this->pages($this->render($this->document(20, 10))); + + $this->assertCount(2, $pages); + $this->assertStringNotContainsString(self::RED_FILL, $pages[0]); + $this->assertStringContainsString(self::RED_FILL, $pages[1]); + $this->assertStringContainsString('Red table cell', $pages[1]); + } + + /** + * The block fits where it is, so there is no move to leave anything behind - the background + * still has to be drawn + */ + public function testATableBackgroundInsideAnAvoidBlockThatStaysPutIsStillDrawn() + { + $this->assertCount(1, $this->redRectangles($this->render($this->document(2, 2)))); + } + + public function testATableBackgroundOutsideAnyAvoidBlockIsUnaffected() + { + $html = str_repeat('

Before

', 20) + . '
Red table cell
'; + + $this->assertCount(1, $this->redRectangles($this->render($html))); + } + +} diff --git a/tests/Snapshots/PageBreakAvoidSnapshotTest.php b/tests/Snapshots/PageBreakAvoidSnapshotTest.php new file mode 100644 index 000000000..401601de1 --- /dev/null +++ b/tests/Snapshots/PageBreakAvoidSnapshotTest.php @@ -0,0 +1,67 @@ + + + +

mPDF

+

page-break-inside: avoid

+ +

The block below is too tall for what is left of this page, so it moves to the + next one whole. Its coloured panels move with it, and nothing of them is left behind here. + The plain table above is what puts the backgrounds on the page in the first place.

+ + + +
A table before the block, with no background of its own
+ + +

Filler line , here to push the block over the page boundary.

+ + +
+ + +
Red panel, inside the block
+ + + +
Blue panel, inside the block
+ + +

The block carries this line too, and all of it stays together.

+ +
+ +

After the block.

+ mpdf = new \Mpdf\Mpdf(); + $this->mpdf->WriteHTML($html); + } +} diff --git a/tests/data/snapshots/page-break-avoid.pdf b/tests/data/snapshots/page-break-avoid.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bd80633ca432f5d2a021e0bf70159ab518ac4fdc GIT binary patch literal 29645 zcmeFY1yEhfx~Pk5aQ8{j;I0#QcXxM};1WEz1qiM|gS)!~hmhdz!QI~^Yn`*#UVFcL z&pGv~ZoR6@tT9L1|M%ZL`fr(xk3vyYoSvD14S`}~b7BU86~F{=Ft$bj0vT039nAoY zibfV@2%v9gGkaG6GbmRXflgklY{@Q4jFt8oWc$h+*ZE97hr+DfFm{LbRhII&Y^b!{g zR+h$HkW9U_P>O*lY}k4XR_}QOr~;e84N;eJ z9M37Y#+3)t4==nWUx}zqVR{_5J-w@*By!|MC=q@j3|Yu?Qu}f$9@QtS8=+xLHwTRA zL&=&DBjb3?I32eEZ+nm_@3~(MAOa_)b)^i%{b-{O^~R{%ngLHZ)u8`7-^A`4m_GVS zbtl5J;SUxn`jwt-7SCuWR3p$A@|#}$CTmT{@(ImUz*SMJ3(3Z*E%0sJO_x7G0nHa-=z7`P$t5M-JI%R9=Pruuzc&0ctQ?_^Led}PYGGxEl;$u3Iw~?W-Fj4Q~dPv<0h%1vMI?ewI<&; zgtrpmoz?DzV1VJ(k*QL5%4?MnebQ&#NBy4FqSqi;_aF^K2Q#focsD=s6Ebpd(JgA+ zGesSfFD;Ly&Nnk6I9I=OnvSfQ#H&jMahgY!8tLZ zhR={=7J&3TfKBw=-WyXLOw;LNF3(tzuhJiE+r*`sFI~ih+&}Vu*1i8OCWP#8rELy) z02K}?W&Ey?g06*WX&=*GQkxT58nA$&UO)1Ip%lhi!jgK`3R4Z5y)oC+#Y)obyHEwi zoEtw{WNGkca`mJ`&f;?I;xQt*}Xq=c82f0V43C~=M`9w`m)Gx#;W)i4ElbizvGis>sb!94< zBomSUq6Cc)xjmbA8CQ~hVi6QXdd^4x2@ipq#W5Q657K&C_`J0y6NgkQR{^2136pnV zvF9}bkthgB!f!tqL!ThdNBtm7dhvsB;$TZ|Biwp;H6fC$#kA3CFJmqjBTVhML$TJ)?d#)g2-9+L{^r8Hp#% z1(&`X`;+t525uSi=S#NYiz`|=vCIMjxo<+7{sp-!r}llY7NN5h-KNiwZg$K+W9}&u zAJtfW<&P^avszh-b-f_U+>i{Bf_Mx%r&VXATBhpW9jAFc#atG65N$(vP})UXNnx45 z06Wagu^N#C<{L9Ot}JyhY=0g#nH8luXx-@8cde+&-FLZ9T#G;2Ys^QmPuLY}Kqy#c zqY>Sro|W-2PH`B$WoMn@$>p>+&wG~(&&D{#K&fhKLAfCDeBGB8(SW8m$%;`yhJVhA zA%`~HF@&R(yU}{i{3E=;$ns9Q(|ykuVq!x?g`=e{-xNR|HoQxqd+sK~OvtAbCzD1S zY(~9Lm!uQZw)pOq@fpJJr+U8rI{;_9 zEw(_UcnWvuUS58VuzN#URbQ2`ZIsXY_VfShM6XNizbwEm2#hjTrY-=T*X3Cm@akyF z0MJ4!;@}2am0A9`EAl_qL(ade7xQqHP;mvVhzN|rasWno2WLAYTLeZEBLFiDJ~6U)eN_Q!GWTm^UpEyFYW7yY z7j5R(U5D~t?0PNoPkr_`yF{c!UsGPq1NGH^(L9ilil>XKnVpoqxdVXt&#lj^o>vo{ ztsGq)oB_;#?p#DQ0PJj_rfUFn0IV#aFBUEi04oPGfQ^|Az{$=AVCQ53aIv$4HX6(T z7B)@*D?9V=i;bP>cmD74Ol*J1F|mRkE`W^-B=uX69TegQu!52~m;f9szXdp0LA9`P zu>iP1J1h>677kEt99&!gP8QG|B*?)6s+pM^z{$l9stI(x%7Uz5=jH-%gUWDnaRNA5 zxj-t}|G0C4WSBWX@mGsL=^&llEbIUdHfE3v8%W=)9+1@_GhXknt$MZo^i);e}}o)L3yBbb~cbbuX3P^jTNK|)P~ny`<=%G zY6~Z*3=^mmKw&n{KYH=C{@31otsm4&uU;S}s_GyiB`Rm+2w?v0A%7VgzX!{ogX7Q5 z9xK~lHhZtqQubD$!K&!&V4`B?3NW{_H+BAVYYH$nv#_#906k_?D-+j0W53f(?2H_L zSN)Gc^15kO29*b`ik<*!A<+KQ>>UE5g0m@T;Dd%awVH?uD6Zn>=xA$Z_qv^C0@(>L zH8Th40og8ZWCz-i{uZPEPmch}{G-J0RTX3kNP_tfTVDMEv@bR>1MQDN`*a{^yUoN8 z0E&bDes6*Qmj9=evH8C#4){a(bw>J+d{FRrGyl^%6F2AAbHMMu_-*s6Emrns|JhxR z4vw!1eqaA)Zu`sR_TTQPS9Pz?@wdzUi?{#7ClMH5ee{pc{9nyKpksuQqokRYg{3RV zXSfjnjGBMO*qL8ruUdqEpJM1axLE=8tRSytW(U>I%*@0M8hEeg2n*180~8W^ow8g( z-ogcn{4s&K0_a&-IY3caGo#;=91|!k zK!ZfX$`o`Od7VmLb3H-jUQa`cM)nRapde_P1NjmF|+=(QMEe z#|&iQ??cdEo!~FWy8mzjCN_}BzaEKRrT+6AbTqN64hIGnFm;mgOUc}n!rN4pCfJrf zX()W}$sjLmFWcpWU}>Q+@Vc(i_vl0rmUnhN#DGiiH(M zTJOvI@pjfXrl4b>@9N9$i$#me1mCxt#+UIEU8Z0(;lN^SG&V9?PL6Ts%P_3T3qIYu z5NzKNh2|5UU`GTXC)T+0&|#f~pRlB4K*yLER{y6;>z6Zp;Vltd1GVx-4FKaUzzGKU zfRmzzlkEHH91oa6I(y|Bq2z#C6uTIpX!;(PL=%DtT)r~$6o{48nxaWqn@vn`hYb7y z;is2Yujqit>L5ZrLNH*^xM!gez6&=Q zs;oXkNUzST(Y`c@&&+7uoM=9W2AX(+`{5*JKcvL%S4L!BWOSdELn zKe_uT2}7QwSHPMBMb61-BWy*zvToMhDAdQt`StB~Sn*BR?6t^g^qrn)#EZCmG3BNQR3v-z*>7*s_s4>iPZP}2wb99r!2J(H_z~w{}1shc+ zRoRgEJ{INUNuuyvin){L%4M zpexgbfOCCJ5w0YbGUCxQdy^}+Rhf7>0+VTxLCK^WW@ei6ITECm0|Gt{?>wJh2@GOl-^U~aJC-6Q zO%*1Qo?4PR!gUbnQ2Z2f*hA(L4GNwnL+Q}a35f}@hza?VZ5_NMZp&Q2DsB)SkFq)B z96Ap4FYF$Peh#2)fFa>CDH8l3rBlVgn<5jH>3O?(vS0V4qVizlw(*0koFa&o6&7V= zG%m~1tE@yS*DHY#+r7t29uZaoqJGFQqXyZ(S-xeX?d_!z&ZrLJonh7`%jhA@#apQ+ zk~xTo(<(?}(NV?<&~8xmo}QQ`)Gv_4 zGl{N{YRI{C(7V_VmajS!dkx0jZZM7alsKm@(ogVnS1lXr;S;HUbl(}I*Clx%7hRo* z1Y!BnM_!mg!PPFI?ghhZRJVMxq1Xyrb`Ze>+#hAd#ko)Vd!nxHyer_bq?N#Ym6 z5r)%riw?hy_7tk!&eRPZem;M9PNOd=R>lq7OWgQjUp3`UGlPR@Z?bL%pD(FSp4+YR zP0~<^vL0tcAsy=SU-|++V4ufV-!%-t#IM16QlUFXVzXi7g74HhK?bT!xGSV+1uUY{ z$Gkc77-+4flp7vOWsBG{mH(U(&+Vi|ptp%uMd+murE^RpNb#>JA+CO$!cW18&+jeDZB8<9%FH79a zK%4hf<8XYJC@kRgkD>)CN zz;PShrN8c8~h&FQvuQmu-W zC=~3H>yhI-!nK~yuXjUeNP80EKUhV8nR(GNa?vwIP3I8lk)IQxBCQcmReXH;qIDVd znr*P%-NvE$F?JfprnhQG=N<*BNLVehTmX%=VXkKSaNZ=arF)upbc5)^Rf=rz9{wSbCfN zGYFOyRNr`|6>qxFq#-Rm#rSP_vTyFMj>@txe#%nM<7?k@4|4kkAv0FDkLt5RE zQC$x@Pk^lJ@%>LvX-R+VeDh0YdvX*uUnzETLNihU_PbBhHh^A+xNgK>LpAJlrp9$B zh*;W)U3bWo-WiL=HY@^Gc?NRrhHPFA+BNnM*KV6T*Wcp4{`%s%+&Yup@f`R(f~&9d zavXAzf{_qHf>-GhD9&{gM!kbV^R@tOS{RuSGDIW>ev?}qwTMo$+PJ%Cn-)3hXG!Pf zSo=-qEuMtoiBhX`lwWel1ff*B&-3Fpr+&+A{OL`#Nx-q)y+IScz{kVW8NJqLW44*j zKwDlx2_XPMUoc7&wQ1R!BlFPRgxRgdE+s}uQh78%!Sk0h)l=gE6N~Q_xuHD_*h1ia z`9~a;7RJHEEN07jVuwGm*eiRqRZJ>UHL5mG{g-clkx01D+BJEu_e5WLEUs)0-tfCW za;2#?+UYERv|D6w^YNZ|Xmsf8+&zjx;a7Qoe!iXPHo@e}t*Xz%>TC2VEaVnk`e%+K zlPr)^)_cM?2*~5}zMH@xFzZ>&VX`&feHZtM7BP!oTA-|=tgJ#$pXwJeFU*6#F9Gb# z_N5a~`i|?-eR}m9i3qfuk@t*&G|dL>5Zr6HCF;gMSP`MT>n8R$>ROV(7cljf0+N!z zop`eFU@T1GDv`z|Nm$7Ff=sG2F6j(CMex2SBntxrX5X-LvB#LDHmPcqqCbI3B0~sf zr7+}36sn$q{W=+vfa^WeoMxA!ggqX^!HK7ANn#L)TICH`-DG22d;2b#t(YCH58^== z6{mXmW+ySUz4OQ}5)VBVk2+fvIiaYR?HVkaFRBbrU+%)LCip=N>h>$_VIR?%5nW?b z9@LjTm?~-Ofa8tJ{Cx)>w=<-}tU>I>kJw?djwxy()yyP(HybzFA0=))U4DEF?RQB| z{aVz@Hdr{gP6zHR!Q6VI_+CHUuCRE2DpP3+&M5bY*B!O{I*6ynbJ_dakb%Y0@V#Be zPc$DkIL~+wluoQ4eT-vv9!K?|OND1;jRM@pw{Pw@O*XGhfj2u_j}U}a-_Fea1fD9> zuV+v!cuLI|k37~`cTb0=lawwUGk|>#c;5sW8mu$Y)3Xm7jaWbDrwhR61cNlFm%f$NcB zRWR@KRwrA-n&61QY~eFyvY9qrfvEiXSm1kf_qfY4M58BZ9a%8C;r0~}1C@^TW5;3= zG2tQ=<6e)#p4>zfqYchci6=@gtZaKxF3aAR1wN)AP*jfE`>J)@&uTEg%g3Ru*;93XD)9#U2YK7 zo3S5b;TQ?gAYureo)5!aX3+*bOKPQ1Wn@lBYqd1{nl;=2Kku3m$}zEzKlJ0oa`|s? znX?+aaY>J6GYCjXHWNXwwEs%umZj{DjX1AWxVeh45tHG;X9T}|rkC6C>@XcSJw2e| z_r%sCrceqVC7;D&=yzd68mt2)xe{(Lf8Zmg*^=3@u3Ip2`Uw?f)q%I(e4TNeYSfI}2?MyG+@x;h{o3#O(*p2cTjI}R1LQR}&X-nnHDLJ|uy1&oJblnU2;5&2^A=d93 zjW_P;p3kNob#c4a&34Q4B64wYb=~>G(3OI8kC*$q+5TQna}|fQ$AWR#xRkyf2=50e zpa-O~EjFAy-VjF)PFd`|yC?K5N)kI1=@L!(_(Axy!VRB{gBBt}a@*KtWAdQ2g*wtI z)tM+uXAh3QZiD$)4T)#@xbH>l$x4Ai2kqna&xRxKWUlt7gJ#BzA8J~ARtz$*4JVRa zwdtOcM|x*WgfJGGC}^7?ZhC9H$_p9(P$!gcVy%nm+YZ8CAo6fI%UM7A@t$Yu2_9a|yYj=9lomwYRp=AB* zzBZpyxM7fFfG=C$PHv7wCOFHrxEfu@Pld?z7cPdsI7M;%65Y^>Ys_^1>SzID-iRk* zRjB4ulc7$`9x3448{@dTbfIBPMdEXer8!7fd255Z=)l`b9PFqCH_)R*db*eUo`Hnt zqk+IVU%N=>W0)xey`6Plw*NEtu{5BP%~vjVlJQK0D`lNSWS!0o+#XQonUsV+%XPm81ENfoUd+L=6h^C`jwfO&vNdAbF8rj@gmb}C8YMkIs6FD+9#FS}agzbt#h z(7#I5lj*o54ulh^!eG>Y+u_3)k-Nmp(Ba__&Xv5`ttE3%uq8)gmVWId)Igr)QK2ZP zARwj!7OGx>(}Q0X<;@qO;856lntL^VLX3gU)fu#8g)^7*Y1;l4dn;EXGd8uEIK30{ zsq@SEXks`lzY90;iu(J+pukb6it#l^n-4fWuQfIgq5=4bANZ}SIIn=Ji!-ue``Up2C->`!dSQ#);9-vaDr%!=(BzmbcBp zc;T}BtK|h&uA?-M(XRb>;NBR3%k4``oSGo*TW*3RS$8%pB^h^)FZFtl`r$utZ#5;! z2TVHHr{1%{Xop!`;ATOb%__-)KU{slJ3BUJ3J$9(>mc0us<-JWMeB1twRF%-L4la{ zQcKCm(T@u7ZVYA$npp#pz5h!_#Jt@G!6bw*11k)>rmmzFh&BqTW8M=ve>$ z#3sWYI4K846rAUXJxuozIFT$!&^k^Z^#$d|Q18X-u$q7~M(gllhE=oC^!Y=7B+jUT z@h9|A`A8!k-`eVU{m=GXh6BmF3_|>^cRRGUBp((Y$%k$N9+LX3VJFzSY2ck%&ZGHG zl<$v|5>?ED=~C-$#g@;f7h-PXd#S2E<(V%DG>|+!94u6JJf0tVvNN<}HQ&ZM?@MzB zF<;)Es@Jo7<_Bh(K`ga75kmcpPDG(gGidd2qK7l&H%yIzW#`xkoIw(HxM**bt!(qW zxc`!Epu8Vf8StWrt+ny>`Fw&ThH^}cj0!9%Am7ci|t24 zQ)WP0rM`~h#V9jsAv{WsMr`Np^f$(;8zDs#R~S5EW6y|u*Y3x$fX{99*3uN2RBP4w zMbZ6!m%AvL2Aphcgg&+&S4<@rKS$FD)RQ>)>^KrLrVFXE4rjH_-tmaiL1eY&TJEfm zlp)ur;sx?u^*JYpecBi%Xk5+qWAhePbn72z!FF56^qMPVaBi>~$!z8GTB1%nd$c*! zW_zEL^i}=JT|Qo}9BjU&ZZ>noNH$qM7Xf8o2j5Y8Rqmcohhs+Q@ci3R?GKlf5k^}7 z3$oqAxTwhxH^!_S6l!mVHH78DMMVKYF@os>qGp?K-tHdRE4^WSOBT0bt;I`Vd()$- zNlB?c3ktm3n~K2lxD%1Pzj4d|;NXh-u1*svz_+o!LKb@E=^^THnr_02Zh?efIgFc_ zxIa+nB7HKpvH+m!;0|ck6nWDm@C(x_wb?4O@l5v73sDWR2Z~n44s2x z81)VmoUvNrE48{-@xU3GosZ2!+pt{{ktD%SRDMj|J&$0vR|#PRwgH=v?;+V4P9VPC zKi6#gs!3?*3R7Yb>#J)t+FC7tq&QLD^*-UvELrtC>*rkcIqPo>L5;*5oO(}P=y2g9 zdC?Embs-T?7wNs#k;Rauv(oJJFy>^|1zF)tRvXN;P*BvRQV6T9Oy(Px$*k$Kb8-8t z%c|t#?M0Ps&a(T95^yC^TIMWMAYAl2Odrf8&)NHP>6NFVUq?j^&WevBxJFCIaCv|q4g@W0~XW#r3OgkXz)9H`?n2d}qCzP9LJ(J~oRkn+# zoy8tCE6Q3f(Gm_#&)9?bc<(-grKU45V%gPjUvj5Z#Z9$VOFb-ZosWZ)yV996pT5Lq zcNooorb>JBWb~5CaJ|KD)HJC74N`5&%dwyeA7x3-;Gk8h^NSxM@jznY&)RqMK6AG1 zu4=f-2TRU=PxBUMcKYdToF6J5ASo^1wYfP3GfofE-CnvA20+fOZz#9mG|{+;!~e)B zmR;}64)Cx;vB_=dK`AYJ`=sl(9+ay3{CGwKoR{s?ynDKl+0Rt=@!6H=gkaqbLBWOb ztlCE&{3RpK(uQn!(Hd|AsN@Bn#~73yDDJshk$z28NO#J{|ID+ye6uq(hFHbf&W}h^ z4^LZ#jjo7A1O;zN&WEy^e)esIyBX~omy-6E-gM4fkF!`y0+s#)-15BiW!;{0Z*Ss5 zObX1KUjmOkPUkT)XgB9Oa`4tS>LXQXUHKPGjM-1Nvk9Thds$wKi6Ofod7&-6cee}yJs z)AKWNsMfidRH7JN5ozVx?}BS|oQCzk8+we2sHLGY8}&CG8mqX64uWNzbZF<^8V{~- zT0AyG(>ug>Y^=wS=Ft0+3P@c>m!^JGqIe^cUOIOy0+*y)wcvQ?Q+rAMy_;;m+-0Du7}Ig6NRYPEC9&ftq~V= zBJ05UXfZpM{<1&78>`EA)WWIG6L$apyaRsM!(r<_&`lp3q2rf8V|C4b<#Xn2=bh!< zJ5-mSt?&T?Z*VlVZXuS*wDt-H2r95R^YuQL4Hdr=l5r&Cd8aE*u%<2Akk7#62fjlE zzimmll#Nb@YL~9WuG_d)8fe3=8-xd=>$1L(yF$ctgVmIP8+!OQI}e|czVuVwSf`{( zoRW6%$HLAoJ~TWrgd3M)O;!ZFkl6M6wr0s`kr#rYJ$53wse=6BX^mpdU}pCeX`^2f zIX4{EmGI(s0eATtXy@^!TeMj=&-%jDuv?afz+PYaFB8wm7Q7iK13B+DzM z;pM=W$qnAs@mSU6^uqK`)w?trDWZPvYvB7U|@kY9# zIWN8N8atFY!977gfn3>b_rE;5!CO<=s$JNPn~3Chxc51@P%zjc*h)XvIrN^rDKf}e zRqXWi3wwM%__4M6&A-XN;zj8N1=0sH0b&#!7UCKIRL0P9?X;_=OE2&XWG9NPEzXR6 z=|LIv2b6=LhU(5%K_N@tmq|}~DimY{lyx^6r7YW4RVCs*; zLJ@dp-3294)~V`=!lhe?R2GGb6Eg}MK(91kUi;zo z3%SjsSmmmLv6>dM$}-u9eG83}jHPpYjw4M>>3zhtq{w5r9df*WZ!~|Gh;}Tl$%@#4 z#599bDERFS++8ucIcAS8Uk>I)8cQ8s#~bSHQ$27O1h5}a7p9sH&@Ep4JCTYt^?3{C zGz?Bd?FioAoSyEjwm(>N4{ufR+KRVRXv@5?w zt1%Knaer={uQ#_CRa2t-wU~~Y6>9{hOw54>7NUZCY}+|i5v?w5XC#n>%xAILR5E0g zeL+^txu$-}kgky+R{*|98|_z_6zDa!7xbMM1WA4a^RN*0uZLV!$PW)ZNe*@Ma&yky z4C1^w>=oG3{*qydcOvSMw=Dgfgry|SxO6q2f}#lxx%-|5!RJUm8n2ruGMj^ozbUWp zS%x7t+VbEy=4B4B31%2VQ=jw^X2lwg)$8+s@!9ISz zz*=c%)9V-#`|Ee*y;^6B7Z?3h(|N*)o+)jhUN^(KiMZNVWBI}i@4~=M%rm9@^v}jd z{fl|r2I}KR+y(}@;)%lD2*t?Xdnk{wKGKDh7U`uTkBc2PP4<=xACqFyv>*nKse5Pu zhp~J{wb6+6(Yxut?1GORyN_#-mQR(PzvI`0)3;iuM&{p`RzRy8Q587e-l-0Okl~K< zhwb~Yu^VL^DX*yj?V}SYO|e@WLI9c4)$LmWCQjrtE4CT?PPD9A6)r=~8B>f(Z3$M| zL*B9YbM+~|LWJ&ZgSvi+qt37!?vWAET!W)`i|sOcr@JQ66Rfjn>vq((r_|1nG?4ly zrCM1<#mla8E87=Go%L%=34%(S_~MU;z2_uKj$$Hc6D8ZF!Qv<%VV=hQ#E$s9GCEBi#_xNDbE`EEgxrTJOmm({j+T=~LE&k6nAra*NFBzfi9!)N_u z>Z0VLXIuIisrqz(#8`feSThZAlQ!Pj@-LUv87!IjK-=Sex35o;N=lb%@YG%%;}r3M zE+fCxj&q}pQwIy)DLvQ{bE{6YOPuN9L^s@5zv!d^T{3c$!}gn_j|aQ6U8X$ES$ z4qARcHuK5cSEy-`8xs^C&o0FJS~hrmEvvCqw=5QA-ND*WBZ!qU()B}P$-*GY@!PUH zbo$AH8yT=oE+X@i<3ScY*1HV0GT9o~M8}LME_XAPiZ-_6#h^jf0>MARP_&!&eNTVg z%0FaRAT|{}Yv0VTu?&dcEb&y`Nj<&H7*;zaU0-)K`{SvO#@ljyCiG=Fwf^GQ{z9!z z-Z++0`3Gv39{>(FF`GVwe~Yd7N*}_KR~QFPtoMBr<9_1K}deIFU=A@uOWc zim6;~HSsTp6(_d4B>92W{=11iM+rj`KRuvg2jC~o!?i4dgUU?K zYw1P`ABaQgFD$Ovuy-sVuyx8rFu0M3a<68;PrVKS9#f85Rd&Gp{O9M@; zOE>j)32T0Huxu7)N0t)NH*c)*8dcg_cSPx^Qkf8xq8A^|ZCwU`9v@Q%f8v)PI-?vB z0Vh3fcjMGCaqHLlXSyAtU&H9+Nh4yJ=~;Y>S}k@wG%3;!Bj=-?vU=W8I7e@-XrSY0 z+Z--U@akfUJKeeJs_gr?GwdnBUd$}MwB{{@m4W-WSih0C(GED zPE8l?hnb7DUbZFDe1{X3vATy^@YGr!nAtW~td)ft!xJM}-<@~P%GZw@U7F>GrR7$Y zpIS;$DBf$7GQQU?HGr&~N!Fe`{wkgH)O+w%w)EEWTh`YM7=thg5^!zWfMXMVfit>ea8T*xSW&P39!dE|Xs zY-*>SWlfH-N&TM4O!F0~dEA1Y#|U3>#ZPNNv%UWTcR_Ks0oja)`g(0CM_bo3alrEa zZPJLrK=n=DujSH*%+jTA_G}~211vUbC%AV49+CK)hl@2_tCln05NaNlD;!n@p3bhG zPCB=f7Zo|cBWrq#IC1H{bV_XLts^%M)9vcCWZj>yG0anN?Zk7`Y-dtbJsMacMRw%IycTyhLf#Li$Tx7*Hh&g4oL; zzm8Eo#_Co>pCagbQ?p{^*QH3(b7<$U^+QU~Jh6LY z+|_urw(25gx$(WYvVhc*oN1#iV4XH9k~$4t85@&)sca0oO(A4zb*iecZHJr^MVI=5 z5kCf%b^~tn>{7>df+Y(6RFrx7cgn?RL)cV12z9ly#W?d(a9e1VIuSJ@DpfV(`bR%- zRuQGb3PL#iJK-R;2AzB}Z?nL8%{@cDk1^l6DAEK&=A45fhK)!L4%1;p?ja88$WjE) z-i`+>!jDuSGX?4Go2i#ro`IRkHl*wc6+`5gvKvBX=&nl3+i>58$Teu%4Lp3L=Pjj; z67vFJ<}-cwn$4>;zr`-=Ry8$_DPnX9I23{uyn=yD(N!XfWNb&eEv1`T}?8(9Cy<^8;9K#ZP(j-wGywH?R7JcB~m?Sf>Z+428H1_m!hcz6GBZT0ERG zR2$yiPxu* z8zy-@e9n_XIR12k&ZhBG_0Ud{N$B)9GrAzbwkAsTJw%zSegJNtar5^~foGRNYcYfb z>?%t-T4UMhx#1G6G*4%Hb?cSjKElk5PqY@hrrTc%_msE7`*b~G!aq?JY)6mdXH)e? zW%P}W-OzsU~);d7%u1234>p# zc82CRU21Bb-2&nvs?w~|O1K5ZQgR_l!Kk@uy_Mw`il;oMs;E(T^0(VE`Y`pxjmLjl z4j5sFXOLaIWA36b9BT1t;Ck(;!C~+!HpvA8L~j|>Tn73lpq%jJ_0{FG(v#t> znp-TnnW^Ygs^dNP}W^hAdw}V{D?Q!t}X?!){{R%>X^*G0_7h%w80yju&(_f(r$Rl`rd3G zvW>uViGT(*S1toNI^ z+3ggGu3$<2Y%CXtY%!>sq}o@?eHZIbyL`Yox#!o(bA9Vu{qVv+1I&Kvbnts|(Jq#2 z@{CXTAu?p3f5`CEFZi4Q=$OmYg#+vw9KFlrjMMN=aG4pRW#1|%;og+c_aRmFVvcD; zm6jij-+kv9lc16Wlo^JXjv*nSAaWod6py!{_G}3SiVaT*I&W2Vx{@amUQ;Oy#EZZ< zSJBRsNL=&T$6YI(Hw6OKNvQd-#jeM%ldr|&`AnolQ0Iw6+`qdeyDQbWNW|6EBF>|U zHaH+AHAuwUF*)b}=05@KLqc>!hUc`a`Qv7(!gcb7b|{_-#*%CoaNpW}d`rLyB}pJM zG%&+O+AXnEwW6_rD#(#BKus#Adm=NJjqyXvrohc&Xd%RJdcm-ULr530f?Z_zK+U4y zds4YJx16?HXn84?#@Fx!b$K#cj6STcA0Mfe8-bdohtdb~oY^v#*eS4yg@Rrsfpq&4 z*zW<62gAYw$~H`r^+|NphzZ!PlovJ-g?CN`lyRf!i}Igwgm)UqTn?n%n9`19O3jCr z)yUG+?&8>FD3ejpVCkG)Dcq%PlSa$o;>?wm(FAGGMQxDYHAW?k$V8ZtzoWfM8TlZo zRiSyHAdMl)8ppx$-jG2j z?Y&E5<`e%cG<=DBPE9dGoyPM?wuChCU0RYbr7}xk!Xv9GkfIqT0YiGAKmJ#_yRtjd zu(bpW21bHp3kjYHzzS_eS(^`!NJ2awV5JHtWvAf!p|apmRyjeWAW1nP1CyL0EUrLQ z;N81$a>`rD2tV@fFmfioosY4ziSfn;R;Dm`%s^Fq=1*@(c4(1MD0(saj5L8b|R`4g8W^72D zWen*X2$rhOl>EibTvS$xi6L%m-QU3vQ{uoEag0GD7E9qP7|x$tpzz_Asp`2Z@bdV( z!a=5_7r06LeSDVVp_Ei?E;&t3A|`vZYpgA!+XO{?6B%;P_Jkth1Db;L%d7YhD2v>XD>W7yum` zpC%+ZVKNT&9a~Z`WjHBm0pF;Ne<)OtSI|574?2qIRQka^OZhHt<@i$BWEBf6D@2SraDDmIPPtT&l&q+CnLNQe{f-82Xtm%HB?*3A3 zw2!~E`?5%K`SLkX;A#KEc5Qy^o&cgi!vyLUP$ORR*jZRXoDS&z$^e1F z>>yj%L97oesL`+Yf6wW#{GH)q{tKD*ABe*L<#hfJaXNni{r=~i&cCJd{@a|+A8!8N zfOR-QW0szkiyOeq0UA@x%xvucfOS|nSeV&fA)Wsqc359YpZ_+o!_2|{-$r&o4)?!8 zc0iv0&r{}a2l*Sf_J5I`|F1Zo|952P_k;N7erIL|F^T_MWQUuB?VpgH?>_K8nEi7O zY8p4DC|Z&q1alHmg#l1GQ99l!t=nzAnq|7Pd$ zy!}Y>A>6T&n#S2_)v?u{+S!Rz#q&$4H)Ue@(nH5h$Ld3S9iQH^+LgdVi3(p399i(A z62aTzwklK64`<6@Bp&B{8D`0DJhd9_H`>9DFiY=MITp5BI~O5ugb{#?R%{yIecLy7 zHBjFg2)>lkWPXnVt_kMh9s8D99}Q^TrDq2}K*J$nmLc)9SJ_R!4bEH0@mFOBo^pEQ zuRp^rp_G2DWzQzyIt6cfK|R%&{Y>LF=l@-u46`EaS>w=k%=}DcT_YG~u)AHivW&I- z@^*{1X+ng0deydOfd?m_7i}b~`+f{N=&qyY%4CgrPbLx-J17Jv9_~nVpEQR_usI=e zh#D$T6<;B`ARbES5yFZma&qEFyBIknIt`2U)no!MFMGR{L)-i&gHH)zZdm+9Z1In0 z>sA3DV?V4ZJx8o{!y80iUObD;%*>QqJ_7o;@K%C0Swo0_a64UZ+ZDkOL_yrnmH2ON zhkp-y7Uv3b)R7Gg3Ci!SBKb$*AVoI(DC0>~%*fC@BWh$Mi8D*67Odq^bj7#$5ebvj zNm2F^`BS)T*vo+fxo4IT(n2NFNQL=t3dGW=5zX=m+0eH0fJw0kilj8ciLd|5DYRsv z5&2E)xbhN>39?Snu@NMb5uGvuz(FsNag9u|69()6c>VQZ_*fmGm*~SL>Wc{**I?vCt zmMB*sM3$h7(tBfpc&S#!kp(6}$MPYndQZv28R`DWlJ)wNKJ1nX7rq)~CIr`eW29>+ zeFj7S?r>kDay3)kVAi_=M1UG(O>Z0G<1PI0)ewW;i&@yO$t5Y7(SEZb$=1!8NGE$U;f*iXj}`yWw~jXjI`MjNcISdy<6l3x;v8iq{bz zM^5oSM@Yi*C?r&&-CDFXi}3`G04X=%q6LW8tKnQj*w!tp4MicPU~1kvX7}Ptkwb_( zId=(>@<{kInN6$Sjn;*)JMr0XgQ463Xn6K4Qes`*vE{HHq-Kz%pHLDkCxVP!XD$4{ z-_4&kE#;M*cQ@AI(Udj-A+wTYLnvE=22XXV6okF zQA4XS20*{bHfiv{Y{K+3Q77yLVFYz&kfM=8j2Ev_yHkptCfYtb^~5rp3CMEuZNK+& z0*YT&)XX?~7yaUiNfSJ)Fe~`{47dBXEtR1~(7#O*XH)D~TX+ydzzfDDZaZLnDp-gR zCCEc;I4}b8RFe=lHc>q0LwXlD7abD~@t`2&))iRMCi8WeyQSu-2QC}ej-g%UcNrf6 z-@9Io!#5H|b646{9nJ@l7jDUw>$h!Io-+qU6Q4^7BJ+>+KXOu%c>kK@M!L@Lv<&T^ z%(jh$_B#1F6PD$#CsG-(I~YOhD?MoT>FWLMgKQdCwGUvcsA*j0^V8ZHe_YVLP?i)_ zg{XCak8uyR__7s@A?I5(38TgbrbZ`^vDDyTW)pOb!8#KDrH+^8-Jvh(yfL<>@t98fewSA`Jobt&ql8{M|cTzq@t){(VN^R#RBQB6_UDkgk`85}J56 z|FxUc{o@ob$_;$J1-9l$qy^#ekr=Egkgu}&u4CehZaLQZGzsliu;B8OKH5d*{y3{*-*Az)C|Xs%Qj+H>k!5qM__IayEzf8388yyqBUb+zp!`Zl@uI zld+I&pMh)R?vb>|vIgrC9xe2#O2XAqwF;A!kV=L;UNn*ZQY?cxx#Ne018ogIL`!wA}{rxhTF6*}WKs?4m}SXIzA6(>kYntTr?Qttc5Zz86_66R~>{ zJZ82OD-5>S3u*j8q%M0Qr9Pau(0&C9Vi+nn^Y^LUiSMzBw79R~wlq^1< z7F*-&^qyqq1}Vtk_*I%5YVJ~;1suFk^BAcbb{RSrcLhu`rCD)&@JEobj{ImnFXf+` z-954$=6eQqe>Zz`=5jkZcrs)A`N9uUKQetyQ)0}Og7Bj0O?LO^IKt=YlS`O-1%^73Eqc0x~xb5l^JBQ!{OB7(AbPk(8P zW!nJ`t-DANZUeHqrnFYj$lk&ZR*e=wT`*-7umz+_%w88M?0{a8lsiNw&Uwi(7{6;P z9Di7WrscHP@*WH?{H9H?Lw8!1cr7ND$yMj0aNAX@(0(->N6O_rHqfp^%=fd+_%3fd zQVTjBv4#(}?kVl2-@!8#iSpF)_(FrZBH zFu&}L0K4VA;%rXKOy(5$Z-}0*Xr2Q7z_t8l3wC#BLmJeNHQ$??8?BGxg@hCsZAp4v zEgI}IdJuDLz@GL(!c4A4v1?RzUTw9W@DIz!q0vA%ArI|E#Njm27SwgYImK-DxCr}8 z$RT=aw0ZEU`>dbb%AD@a(_|Yd(#9C7F>-zZ4y94TzB8wkrxX(EwzvC#3OnntDBiYV zgGhrYAtfaZg20l}sS7OKA|2AO^wKGfl!SyzcXtU$cPt^zf^>JuH~ixL-pA*UcaEK% z*=w$ux#ynQV`q=K&->)E>9)PXy~T|xJr`<%@yKZUw7oyAkBA*RIT;%}$x|_Qb^s@o z$Dd$}USE(?d0%uP%sbF`-$8cyl#LM#(_(@kF}Z5vDMjyF!5s%8bj0ryLNA*!Y{#v3ozvMVFq;CA zvuARApjXQ^?)4at6L#^}24s}7-11-gVjeG7gA|UM@rT=NlW9hZn>90%v!~2W)mf(G zg-P|^r)w%H>F@Jz_K`mz6UQsmYHl=sl3q{kQ181V6j0r0{|bkGeJ}Zn)T=ydy_cI= zRS5GMX_Y>3iSC7k;AE`(6mghbkNed_VK>s&O97E?XIFf@L@vYOZxiw_^4r~2blcJN z^_d>(;-9^fnCA?L<3L#p=n;bcW`xbIpG@}Gg)H+p>pj;1wH&?s8#hjSFy10Gw-~RiGwn5%e*Tc{vlPz(=NOGVmt`96r$%e9RGa&^m2qbvOP?AmkG)^{{D$gj6lU5D z)S^#$ztvMp$xoAvPW>t2!@)f`{3b(Pr@?;tL8`|MfBJNTy;p6J^-7B%!s39s)`yLAgRawDSCTm)Mp z(<`x++qG)=0{Pok>PPsGM;g_>N1y|u|RIQMR;_yUnJv^U-Ws2E^x#UH6a0+V62(c~q zH&9}zCA%>kqtcHX6#DxBcV3a)0GqcSGkp7n^027e)^xdM;y!uAxz{bT_d{}Ocj4Gr z*{N2y_!g-QKg30e@K(!Xu;}p|(SzS;+5`phh9s=tmRr)A3iE*Z{O-sv-N^Qc5`m}@ zNcjq!<_`XQvNyBz7dse6=yQl9+)7cvnKLaowOj^&4m)M;aX{Y?hd~bvn(9f%6e$;? z3LS74h9G8WveijZp8>hY%ozcYc83MIU-`^W{#F==_8YIAq6 zK8L>XYu^kqf6>4_V0(u+)fb7}VUVP9g(c}XX`L*wBg>R_Or?KaZf`I>ap@KbBJO6C zc=#rE`mmV7(l5=0G3Nr;;L3SCd<7SW$-d=z_rd-qi!EQE5an)H0P&s*O;OcI-(oA( z4bhon`^cG#((|-iRQRqrKZV6A$<(*^&EDELO)V+c?Q|bmiGjzvNm{0X9S}>qZo%HN z+Y74QMc121gzjD74)Wj*VVvMtyesoShWBoPxONYd!kS({Y|q(z;mn1%{p2*6aB^J}}^Jt*9DZ z&pxjCUWX}hHSW?d(0S5$aukx_eCT!Vll&EnsH6?ge^#*GJNc(fghE{nLHAj_LV`4+ z4Pp4W)UDlr=1iEni2FQj7 zcF`M*Tv3dn9=HTgK9Lrw=$PxraPMNir1OV33`HKO=%n=687SWT;;XU=9cg%E{0zA< zjihMC&vlMgq0Zaic#!C})JMi;2JVS=4esd`dJSVPEY~EK8={MgXBx;{6jI1^S;l(R zEIr+T9IiDTyf`C`x4cd)z`~0~utCxjXN-!W{0gNbZ1~#vfUiAiGP;;|D-SAxO-A`VEkWwDFy!{k5fD+j9Q2)oZw|bZ4W(-5gOSpWjsARI zvU#C1Jzt^Yxk;egdy*f~&kB>iP#J$){K$Rp3d@yJRG~b_^?2Wo`+RyIEx<() zaXvgkQnLu38|M6k)P;|VQplQ77WfU(&C~32U8fy4f@7>l?U5z%edItG1B7qAEh?%& zZh>&PQQY)L2vN!@Vs;1!lPU0y<6ZnFiyVKoa?JJ*{6ZBLjm z{r2R(piIB22r-NK!RIbyiW`N@Tjn^`o+na?pFvtNW`EYWKSZYS=xZ$k6xYuh7RJ)Y zIGdek*4<1Ww}A?yh;n5}cu{MNz;1lxeYqCbsJHaWfD6qxqG)erzl?gPp}E;8Ft*b- z@A!k@lUuY8y5kweoEL8+`hUS+GT9w;x_`*ck3DXdeXe+ns#}*?r1ZpaO;Hw zz2FjPfX~IMCd#_E_s~m!EUTDFkd9+9OI*fe-PoWxC(*lJ7u5&NfgY}QWK1-bZTG%x z6{;(oZ)q~YpD@OsT>2U(YyUd2SVjC1-WderS5|vD;n$;#p7+e(P%#s^7>Ug>&jq#y zu?v3s^LBdPcI}^N&TS`B<#C+uHa9Au@wm4f&UwyOUVksYN!C5F3-eig#MAZQ;|FD> z34$nscUIhlvs(=QFF4#xB8C+%H*;yDI$0-++;`P}dnL!Zil;jt%6fELxd?(QUV3cR zFBx?4QLuUAc;ZPp40Oj&QCQM|9yJzXJST0;=K#OR%or8z)6eI+sQt-1-8d1XI6kTX ziDi7%Cwi+X^{|-JR?x;(a}FvvUsP~{D$UD`VS>G1Ae;b6Vo8%AwPh&M3ecx&ZoBD0 zuu_yxcdaKZS|DsEUp1cGY)4^#{1W?V%6e8YGycl(foBn_E|mtnVPCb}N?^{a$B-QU z3?;q3{cs|T=%PyPRj)~k+WUgmh3^Td*C`CIbRG)~`R?&suPMnkC()VaeyWlbD4 z-%05g+Ah(J*+r#i)Hy8Tg}bP~d+}Y~Cv9?od&B`-&$?FMG1WIstzYdbW(A~h**qQ9 z!Cd}D8MS(LM&3E~1Mba|#%bhG2GLzEc^3s0%aFpXO7n4fLX>84Z+)D(&NC`)+hOK_ zZ3`J%_jjfpUOz-$yY8`>QF`t=onAFkXwJR;vH}71v!uZ=Rs@FCYkwzDA-Uj*8u0T$ zQFufWAK0F|ChrtJG@}8JwUdn|1N4E8xKYLq2t}H4`tV1|pMJKuRb3sadW|OHr(%9UT zk~whYZkXcw1L3EYUsKZyl0JXf!s;%$zGIOn?bt)6ss~#$h%XqPTphTc%j)fIqO2Nc z#updciY(Q+c6wbDUkv{GEp7B%SfGA&r_u?H@6adt7tA5!#ZHryLKrDu5pkoB^ViAn z7Y%(8-u*TW1rsx_Ho9kf=5hL#9I%>crz_5SIX&I;Q@DjW+RAj!FO%Zf0>$EH*oN(=Pcyf2iH@Ej;xMiQ1yu*eg z*d`#Ap_~+*VHsE*J5ksAX^qVkKHn`{qh|zdzXvpMN`&5eTOtS5j-c>JRPFW+7uWSk z;UNjSysj@pv1~S4Eo$XfHDsUApB7;4>(&-M>sUQLe^hOR5W4C`FHQ5x@YZF+Njjep z{Xx>LMHBrM<1>X!6ikBOKi+ObI(RC2iUB8p$-U=w{X^F8?MKa#b%vwr&sus8?fbt< z+Eh8vXNwLHRoJw%?_dV4aHU}G!9RGTWE37!Rx|A0kWHqM`r=t7|A7A*NGGu88x6wj z_@&tm&mjqNaE%zGTvasJ#N_;>&(7AaUXww_A|WZ#bxBakk}1t3MNt@tWE7P(KFRA&8#mx@TX5*Ob)+f-8T+&yxoF^%@Crhh?Q0W@~Xd#4=u}<++pB-b7BYpyTOyVoRl=+oIBZ`|4^?!$?a9 zUsL5td(m3mR81o-deL;$t>%Dbi;b>RT6o8mIKmFGuYAbN94EhYismCn3QD3V+A&c# zmmmpbEX}FP_Q+Axo9S0YG`bhKjK?!K?!9>XWNEq9x#0|%YdNxFg_BGot7<2eFGrqi zSDc%4Cai=e5pw9g`m@od(qsPcCHF|&-NkYwCNZ91X{-Kz-Kxh7>(egS^L46$4Ijm^Q%#zunVUj9`vH!eZW1r9K0SM_Xep#zNcW|in^-wD2X_sFb~G-F zjKbHEK_E0(><&z__}c!9Y@ID3CGtyOVV=~CwwVX!Gj6D3_^o#W8Wn2}eJ6PEsB27x&tr=ln$BumU^6?pY z#s`Cpay+*)DalSO??EEIFS=vs^Fu$!C^lS{rMbnY=Vo5KTEeo4-u3lCPoZup88All zSTTW5B!jXZc8V`5IY_NDrUVXU{0RNXEaT)&8~ly%@JW^Sbd3OR6IMSA!tRi@GCjMe8ISd!`ovjg zJCBiW;tf&{Nt{zzZI?!enCktq<4BT3K|;UsP8{E9j0pbY?g6X_b5L6 zxqkX>O1m_=xdev2AijXQAlIC6_PZ;$R55rMD??Ib{|a^$buxI?Jarzjuj!3)(|7aq zs(ItQzP0x5@!j(~-n)R`<{s{6Si9E7~o@(Xu3+%j{y5 z9~FE!JRFgguD0*Vn+`Hvb49BoGf<%Q+%nAK>+_v_CdMR*%kd4rYYQnmEIR({;DM=F z{s%=+L_X3S83|*DC&38(P{b~93}V)pom23W0WUuyfxxzzN4cb|--3s?aL{N3-q|Z~ zns5lUv?)`1_{nj19VkiekNzJv|=%B&R5V> z#e3M`sMEfdCoc0IGzc6j+C>$#_am0JQP~8^nv-htf|4E2_hW7noqn{eWXsb#k0j{S zDtTuAonw;evra622HVhwWfa$@R4gE^BAy(W0F8))iR;+Az&m*tH^qw7g7A$4a!jLD zrZCzWwOmIo8*X@jxQ&6?kF-Iw5-zkzU&&G{|VW#rf1Hcbt4!yu#p)5ZoA2B>f4_$fN;GW%XgED29`q^2l zF=}?)V!Py`x_zgv^&{;N{W?Z^qDfTIj{yI7nU>X!r*`bss*YH-W{Iz7VQ%jdr69Ug z&Ua5x&ACb36^b?Kx$BfCjumBSqx8uy@Sj@N!z`W@MYBSYSYLtY-TqWP{@#@TYldjj9R(gETp|5JhenoaKC#LphX z8TTR28x0%%?$URE5kI(ee8L;dc|(%|k2P$uVsjpd|j`aSWp zHE+GQ=R9Vokt`(LS@{|n+H=snD39osMg=nAQ{&rOuKIO; zFFw3+2lDxlfX|L&X?lvyb0OXQ@V#nJy+KGtgY=*n+K~P`KHYVK!hTSi!?+_6XWr$| z$lwErE{kvmm3x3Q=NP@~FsXHuBRtaSn3vw=2v!6l654~%gsv0W6)$$0KXs-p z?z2sCtX=luGb^?fW-E;4&m1BJh@U)XIQ3I!8CNOL`9}SLcD9bZHt}}7tp`B-5Rh({ zW)~59s3zO;bCCrXbm%@$xL5HZmoxdzc)f6PL-_v`VaCQ%U$aS}e z7a*o1oZPrb_p}AE%PrNucK@DHWsaucjXwNluY)z*nCG(2@J5|tis1A zq2rL`&OukT=-tu^NOg@nVLOBdG~l$v6unrU8@H_U5FuM9qIysKpy~$8Zvey(^e^Hk z``D+CCo&h!H#&Fuz#+&zCi7ykjzkXDGgYc3Mm(Or7iRrY}0Iy#exfrLK+so$b z($e$A{|KF&f5mt7h%x8|d&Va#mgs19WMNA~Ltm1qIoNhkGMtelneFvZn+3%<1e zo)?0cd6w!KvTF_7>_PZ65qIp&gxFaH8Jo7Rwky2-Ea#mPi?QnyZ?#!f-CkH;=Be3k z<0n4la=-ain5yV<%#lmMSom9eW2LCkJI!JB*?Gq5Bh-FyVyoB=}6NJ=_Nn zKRkIL*gf$Rbx-`9qWx@|!Gb$}c>im=St2{M*2dDbebq{-yJG#=V<(JF*hT$r2u(rz zciL~Zxdk(4MfDDzfvsLQqACB>>q_=C4qPU?)-9M;b3Z`*kW5qp#7`a+p^Xe0NAY8VX<<$Cw^v>%=2nDts|(6A#59N7?P_5tN3xFlMi1-N35l4;h2Sk z7nLlQIIhSZ7v+3wGg}gJ`urGOqoBxU1f-B+X${XoBj-@n%)~cBu!XT8*R)|FlKCB= zX?}8pyH>?fj+sxo-$!w1n-n7NAZ1$ls;l2nW|2A~S($~^&I|qO95g8q=7k&A@v3XKk+8vuC`Sh6Z{(*Ra|^=D z5uongS7fwzgp{pVqp>ZPhZXQQ@-qcdP!+xik}d&hUpziT z>YWDGH!d=a$|lF6K?IZi=l-wL<2x{=De5~@7mMo6pOejtOVPng##kLQO zG3R&c2;@q!v#`JD?;SDgiOHw8ry^xk>}4KgPt|4V2w9o%AG3fO=A`OyK!ygHdqU!> zRl~!o>FJ?{<*9M{@rjv1<^rgj?r|U}R>dlJlBSXt%lg{tdi;a;7Bm?J5^L|}NVHmN z_=U-BRmY)6P}$th-eiGnR2&e2nCjZMiIuU`lV11yPUz_B%C~lWHUkqRYjFi}Gft(^ zUsmE6NU|?qq-dnZCA=d;kCRP`+G_6HGBJg5f2Mp3cby=<$MnK4c^E|%hTaS+ z4vu|tdSck2=Fl+F#$aNBUDcWI?f?bH@CttbfFFU*pC>9J_yF+JgV@O-ej5Lt5fY5f zF$8{q6I;9n06%IMFVg_v=Oq#V{8$(|+P9 zKj}AbaWp_hYCMs_Ivzq1)n%s6RR3wMl* zVNpx!2fF;P5Bijc7rQ%;T{F$xCU1oo2U{g6M%6&IW>UCW9lzWhtK9=r;_j{lw`~_? zGe7(I%^wR|P2CaYI?`PG9Nk>*hm3qW>J*S&=%FuFg-(9H8O%rpQ|Cs4};?^q{w8tPOd!w`Dc-!)+F0Y%g-%vk>atUXxb5nOZ)jpNV2;XtB*UE9q_(9>+)) zeDsZ!c(E=)aoqtuL*2n(0I}+}ULG^<=*hHqKVfn=d9A)1^;_dAA>b1>Vb# zk)M0eM4@+Vt1YLnO%d5il+aUHg>7+kRSBQ=w&Y|^h?T}Q*a{X0_g-C4Ul?n~G)dsA zXG|^99c3xbGC>{C8kOBUqv=Qcm~CIl?Kf9Vr_~T1OMeUl%lU(6pNr;4#!7peD2LTO zmU)qt<=iHgpAd_R0`9-bRKcb{-UVe}zV5x@9bI_FHe-(O5jOn{k`fE19eAHAN47L} zfL)$qzL;v1g`xL=e$&D$>jXkqj0c7sCaSyz_c6U>?##%l z3MJ_3lqHCYJ+vnkjf+6RM;P@|7+=3}eHc@2?4MQc)sr;3FqTBk_4D&w`kY)ta{^t* z&Z-_=J>=P%v`lsgnD+V&_;XK>q25@J2;C^FuI!u?zR_*gLs{En#@ef^H?@zbY0)}m zX=A~jfwhF9zBtj?@xgY_9XJ@-KmN!L>1gU1X-gj)vB%$jZKV2xhSP8CBZLkuz4Kvg z5}`vGV<=k4My839eIPf}C#g32u-G?95pz<{O%$2iKTB1GRxrk>{_qKZUnEv(R z#dMFQu%0PA5EDd>Kk;wTyAAb0Ido2X#Q!q{IAO7?Mpzw1K9IS-2lG{*alGfN&GXF{AUUV{3-Xa z4B(jnW;m(;qS?Yjt4pKVA-iQ~7uQfG-egXW|T4 zNvN5A-yzu`DIrNA$skc9@guP#fq;<T7 z3NR8;GfBPT;^5-t5a8hB;Nt{waD{w4)s6txHtrWGxtY~iyJUk{1LmS zkN${(I01_3&lnf?f0YIB5B*nJKmq{a^q=c;@_;!0EDI;ky%gw=Jb*{~v#vOKxdHjm zpLyJT|FsWZzW+L2P5{XIb9-F;e1NRzKeq>HjQ)2$J3$Qr-3k=Y{s1JK1;M1& literal 0 HcmV?d00001 From 82729b07ee67ea92b0696c8bb1668708727c0ca2 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Mon, 7 Sep 2026 20:32:41 +1000 Subject: [PATCH 02/13] Stop a table with no background leaving the next one painted twice Every table writes a ___TABLE___BACKGROUNDS placeholder into the page and, when it ends, splices its backgrounds in behind it. The splice only ran when the table actually had backgrounds, so a table with none left its placeholder sitting in the page - and because the splice is a plain preg_replace over the whole page, the next table's backgrounds went behind that stale placeholder as well as behind its own. Five plain tables in front of a coloured one had its background painted six times. That is invisible while the fill is opaque, but a translucent background comes out darker than it was asked to be, a background image is drawn and drawn again, and the page content stream carries a copy of the whole lot each time. Take the placeholder out at the end of every level 1 table, whether or not anything was put behind it. The two preg_replace calls per buffer collapse into one str_replace, which is also no longer at the mercy of a $ or a backslash turning up in the background operators. tests/data/snapshots/table.pdf is regenerated: its fixture has a plain table in front of a coloured one, so three pixels of antialiasing along the table's right edge shift now that the background underneath is painted once. Co-authored-by: Claude Opus 5 --- src/Mpdf.php | 26 +++---- tests/Mpdf/TableBackgroundTest.php | 55 ++++++++++++- .../Snapshots/TableBackgroundSnapshotTest.php | 72 ++++++++++++++++++ tests/data/snapshots/table-background.pdf | Bin 0 -> 28905 bytes tests/data/snapshots/table.pdf | Bin 80763 -> 80763 bytes 5 files changed, 136 insertions(+), 17 deletions(-) create mode 100644 tests/Snapshots/TableBackgroundSnapshotTest.php create mode 100644 tests/data/snapshots/table-background.pdf diff --git a/src/Mpdf.php b/src/Mpdf.php index 7a5ab7632..5abec91b4 100644 --- a/src/Mpdf.php +++ b/src/Mpdf.php @@ -23469,29 +23469,27 @@ function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $spl /* -- END BACKGROUNDS -- */ } - if ($this->tableBackgrounds && $level == 1) { + if ($level == 1) { // Nothing else paints while a page-break-inside:avoid block is being measured - see // PaintDivBB() and BaseWriter::write() - and what is drawn on the page it starts on // stays there when the block moves if (!$this->keep_block_together) { - $s = $this->PrintTableBackgrounds(); + $s = $this->tableBackgrounds ? "\n" . $this->PrintTableBackgrounds() . "\n" : ''; + + // The placeholder this table wrote is spent whether or not anything was put behind + // it. One left in place takes a copy of the next table's backgrounds as well, so a + // table with no background of its own used to leave the one after it painted twice + $placeholder = '___TABLE___BACKGROUNDS' . $this->uniqstr; + if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) { - $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->tablebuffer); - if ($level == 1) { - $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->tablebuffer); - } + $this->tablebuffer = str_replace($placeholder, ' ' . $s, $this->tablebuffer); } elseif ($this->bufferoutput) { - $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer); - if ($level == 1) { - $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer); - } + $this->headerbuffer = str_replace($placeholder, ' ' . $s, $this->headerbuffer); } else { - $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]); - if ($level == 1) { - $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]); - } + $this->pages[$this->page] = str_replace($placeholder, ' ' . $s, $this->pages[$this->page]); } } + $this->tableBackgrounds = []; } diff --git a/tests/Mpdf/TableBackgroundTest.php b/tests/Mpdf/TableBackgroundTest.php index b2c8d8796..1fd82df18 100644 --- a/tests/Mpdf/TableBackgroundTest.php +++ b/tests/Mpdf/TableBackgroundTest.php @@ -28,15 +28,14 @@ private function pages($pdf) } /** - * The rectangles filled red, without the duplicates that come of the same background being - * spliced in behind more than one placeholder + * Every rectangle filled red, in order, including any drawn more than once */ private function redRectangles($pdf) { $matches = []; preg_match_all('/' . preg_quote(self::RED_FILL, '/') . '\n([\d.\- ]+) re f/', $pdf, $matches); - return array_values(array_unique($matches[1])); + return $matches[1]; } private function document($before, $after) @@ -87,4 +86,54 @@ public function testATableBackgroundOutsideAnyAvoidBlockIsUnaffected() $this->assertCount(1, $this->redRectangles($this->render($html))); } + /** + * Each table writes a placeholder for its backgrounds to be spliced in behind. A table with no + * background of its own left the placeholder in the page, and the next table's backgrounds went + * behind that one as well as its own. + */ + public function testATableWithNoBackgroundDoesNotLeaveTheNextOnePaintedTwice() + { + $html = '
Plain table cell
' + . '
Red table cell
'; + + $this->assertCount(1, $this->redRectangles($this->render($html))); + } + + public function testEachTableWithNoBackgroundUsedToAddAnotherCopy() + { + $html = str_repeat('
Plain table cell
', 5) + . '
Red table cell
'; + + $this->assertCount(1, $this->redRectangles($this->render($html))); + } + + /** + * Painting the same rectangle twice is invisible while it is opaque; a translucent one comes out + * darker than it was asked to be + */ + public function testATranslucentBackgroundIsNotDarkenedByBeingPaintedTwice() + { + $table = '
Translucent table cell
'; + + $alone = $this->render($table); + $preceded = $this->render('
Plain table cell
' . $table); + + $this->assertSame( + preg_match_all('/ re f/', $alone), + preg_match_all('/ re f/', $preceded) + ); + } + + /** + * The table with the background comes first here, so its placeholder was always taken out before + * the plain table wrote one - this way round has never been wrong + */ + public function testATableWithNoBackgroundAfterAColouredOneIsStillUnaffected() + { + $html = '
Red table cell
' + . '
Plain table cell
'; + + $this->assertCount(1, $this->redRectangles($this->render($html))); + } + } diff --git a/tests/Snapshots/TableBackgroundSnapshotTest.php b/tests/Snapshots/TableBackgroundSnapshotTest.php new file mode 100644 index 000000000..50b79b6f7 --- /dev/null +++ b/tests/Snapshots/TableBackgroundSnapshotTest.php @@ -0,0 +1,72 @@ + + + +

mPDF

+

Table backgrounds

+ +

Each pair below is the same colour written twice. The second of each pair has a + table with no background of its own in front of it, which used to leave the pair's second + half painted on top of itself and so darker than the first.

+ + + +
Translucent red, with nothing in front of it
+ + + +
A table with no background of its own
+ + + +
Translucent red again, and the same shade as the first
+ + + +
Another table with no background of its own
+ + + +
Translucent blue, one table with no background behind it
+ + + +
A third table with no background of its own
+ + + +
A translucent cell rather than a translucent table
+ mpdf = new \Mpdf\Mpdf(); + $this->mpdf->WriteHTML($html); + } +} diff --git a/tests/data/snapshots/table-background.pdf b/tests/data/snapshots/table-background.pdf new file mode 100644 index 0000000000000000000000000000000000000000..64ca7fac1e87578893a122ff3641d54679163373 GIT binary patch literal 28905 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

mYvRMF6xg?if{%U9=bvQJZ_c;n5o~TWMrzP| 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 literal 0 HcmV?d00001 diff --git a/tests/data/snapshots/table.pdf b/tests/data/snapshots/table.pdf index 9a14e7e0751fb96542b25629a019d1ca62f1a863..bd4fb15960d77e0d66de5992a12ad2d02d1b9d06 100644 GIT binary patch delta 16928 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 E0ZN}Df&c&j delta 16930 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 b#mdRdVQOf?X$ZObP0aWIa*5aHg~Iz^zgf^j From 23a4ab78dd90d2281d62cd16d3bf3e0209c9c91e Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 9 Sep 2026 11:42:48 +1000 Subject: [PATCH 03/13] Put the page back as it was after measuring a page-break-inside:avoid block A block with page-break-inside:avoid is laid out once to see where it ends and, if that ran onto another page, the pages it made are thrown away and it is laid out again from where it started. The block itself paints nothing on the measuring pass, but AddPage() switches that discard off while it runs, so on the page the block started from - the one page the unwind keeps - it wrote the footer, the watermark and the page and body backgrounds, and _beginpage() then selected the next page's @page rules, margins, header and footer. The unwind put back y and nothing else. When the block was laid out for real the same page was finished again, under the wrong rules: a watermark drawn twice (mpdf/mpdf#533), page 2's footer and background on page 1 (#1131), the @page background painted over the block backgrounds collected before it (#1805), and the table background of #570 that the previous commit dealt with on its own. Record the page-level state when the block opens and put it back when the block unwinds: the @page selection and everything it sets, the header and footer slots, the arrays keyed by page (which also drops the pages the measuring pass made), the backgrounds collected for the page, the patterns and page-number substitutions added since, the float bookkeeping, and the enclosing blocks, which had painted their borders down to the foot of the page and remembered doing so. Two more things the measuring pass got wrong. A float inside the block cancelled keep-together but left the block flagged, so the float was written by both passes, at a y the unwind had zeroed (#785); a floated block is now never flagged in the first place, and a float inside a kept block is measured with the rest of it. And a block inside a header, footer or fixed-position block has no page to break, and its output is buffered rather than discarded, so it was written twice (the cases in #1131's thread, and the likely shape of #322); the measuring pass is not started while output is buffered. The tests read the page content streams; the helpers for that are now shared with TableBackgroundTest. Co-Authored-By: Claude Fable 5.1 --- src/Tag/BlockTag.php | 99 +++++++---- tests/Mpdf/PageBreakInsideAvoidTest.php | 209 ++++++++++++++++++++++++ tests/Mpdf/PageStreams.php | 38 +++++ tests/Mpdf/TableBackgroundTest.php | 21 +-- 4 files changed, 312 insertions(+), 55 deletions(-) create mode 100644 tests/Mpdf/PageBreakInsideAvoidTest.php create mode 100644 tests/Mpdf/PageStreams.php diff --git a/src/Tag/BlockTag.php b/src/Tag/BlockTag.php index 2d8939ae9..88f72e7a0 100644 --- a/src/Tag/BlockTag.php +++ b/src/Tag/BlockTag.php @@ -478,11 +478,17 @@ public function open($attr, &$ahtml, &$ihtml) $properties = $this->cssManager->MergeCSS('BLOCK', $tag, $attr); // mPDF 6 - moved to after page-break-before // mPDF 6 page-break-inside:avoid + // Not for a floated block, and not while output is buffered (headers, footers, fixed-position blocks): there + // is no page to break there, and buffered output is not discarded while a block is measured, so it would be + // written twice + $float = isset($properties['FLOAT']) ? strtoupper($properties['FLOAT']) : ''; if (isset($properties['PAGE-BREAK-INSIDE']) && strtoupper($properties['PAGE-BREAK-INSIDE']) === 'AVOID' - && !$this->mpdf->ColActive && !$this->mpdf->keep_block_together && !isset($attr['PAGEBREAKAVOIDCHECKED'])) { + && !$this->mpdf->ColActive && !$this->mpdf->keep_block_together && !isset($attr['PAGEBREAKAVOIDCHECKED']) + && $float !== 'LEFT' && $float !== 'RIGHT' && !$this->mpdf->bufferoutput) { // avoid re-iterating using PAGEBREAKAVOIDCHECKED; set in CloseTag $currblk['keep_block_together'] = 1; $currblk['array_i'] = $ihtml; // mPDF 6 + $currblk['kt_state'] = $this->savePageState(); $this->mpdf->kt_y00 = $this->mpdf->y; $this->mpdf->kt_p00 = $this->mpdf->page; $this->mpdf->keep_block_together = 1; @@ -586,11 +592,6 @@ public function open($attr, &$ahtml, &$ihtml) /* -- CSS-FLOAT -- */ if (isset($properties['FLOAT']) && strtoupper($properties['FLOAT']) === 'RIGHT' && !$this->mpdf->ColActive) { - // Cancel Keep-Block-together - $currblk['keep_block_together'] = false; - $this->mpdf->kt_y00 = 0; - $this->mpdf->keep_block_together = 0; - $this->mpdf->blockContext++; $currblk['blockContext'] = $this->mpdf->blockContext; @@ -637,11 +638,6 @@ public function open($attr, &$ahtml, &$ihtml) } } elseif (isset($properties['FLOAT']) && strtoupper($properties['FLOAT']) === 'LEFT' && !$this->mpdf->ColActive) { - // Cancel Keep-Block-together - $currblk['keep_block_together'] = false; - $this->mpdf->kt_y00 = 0; - $this->mpdf->keep_block_together = 0; - $this->mpdf->blockContext++; $currblk['blockContext'] = $this->mpdf->blockContext; @@ -937,10 +933,7 @@ public function open($attr, &$ahtml, &$ihtml) // Save x,y coords in case we need to print borders... $currblk['y0'] = $this->mpdf->y; - $currblk['initial_y0'] = $this->mpdf->y; // mPDF 6 $currblk['x0'] = $this->mpdf->x; - $currblk['initial_x0'] = $this->mpdf->x; // mPDF 6 - $currblk['initial_startpage'] = $this->mpdf->page; $currblk['startpage'] = $this->mpdf->page; // mPDF 6 $this->mpdf->oldy = $this->mpdf->y; @@ -1379,22 +1372,9 @@ public function close(&$ahtml, &$ihtml) if (($this->mpdf->page - $this->mpdf->kt_p00) == 1 && $this->mpdf->y < $this->mpdf->kt_y00) { $movepage = true; } - if (($this->mpdf->page - $this->mpdf->kt_p00) > 0) { - for ($i = $this->mpdf->page; $i > $this->mpdf->kt_p00; $i--) { - unset($this->mpdf->pages[$i]); - if (isset($this->mpdf->blk[$this->mpdf->blklvl]['bb_painted'][$i])) { - unset($this->mpdf->blk[$this->mpdf->blklvl]['bb_painted'][$i]); - } - if (isset($this->mpdf->blk[$this->mpdf->blklvl]['marginCorrected'][$i])) { - unset($this->mpdf->blk[$this->mpdf->blklvl]['marginCorrected'][$i]); - } - if (isset($this->mpdf->pageoutput[$i])) { - unset($this->mpdf->pageoutput[$i]); - } - } - $this->mpdf->page = $this->mpdf->kt_p00; - } + $this->mpdf->page = $this->mpdf->kt_p00; $this->mpdf->keep_block_together = 0; + $this->restorePageState($this->mpdf->blk[$this->mpdf->blklvl]['kt_state']); $this->mpdf->pageoutput[$this->mpdf->page] = []; $this->mpdf->y = $this->mpdf->kt_y00; @@ -1406,12 +1386,6 @@ public function close(&$ahtml, &$ihtml) unset($this->mpdf->blk[$this->mpdf->blklvl]); $this->mpdf->blklvl--; - for ($blklvl = 1; $blklvl <= $this->mpdf->blklvl; $blklvl++) { - $this->mpdf->blk[$blklvl]['y0'] = $this->mpdf->blk[$blklvl]['initial_y0']; - $this->mpdf->blk[$blklvl]['x0'] = $this->mpdf->blk[$blklvl]['initial_x0']; - $this->mpdf->blk[$blklvl]['startpage'] = $this->mpdf->blk[$blklvl]['initial_startpage']; - } - if (isset($this->mpdf->blk[$this->mpdf->blklvl]['x0'])) { $this->mpdf->x = $this->mpdf->blk[$this->mpdf->blklvl]['x0']; } else { @@ -1491,4 +1465,59 @@ public function close(&$ahtml, &$ihtml) } } + /** + * What a page-break-inside:avoid block has to put back after being measured. + * + * The block is laid out once to see where it ends, and if that runs onto another page the pages it made + * are thrown away and it is laid out again from where it started. AddPage() runs in full during that + * measuring pass, so the footer, watermark and backgrounds of the starting page were painted a second + * time when the block was laid out for real, and the @page selection, margins and headers of the page + * after it were carried back onto it. See mpdf/mpdf#533, #1131, #1805. + * + * The first group is what _beginpage() selects for the next page. The second is what AddPage() finishes the + * current page with, written only because it switches the measuring discard off while it runs; the arrays + * keyed by page also carry the pages the measuring pass made, so putting them back discards those too. The + * third is float bookkeeping. + */ + private static $pageState = [ + 'HTMLHeader', 'HTMLHeaderE', 'HTMLFooter', 'HTMLFooterE', + 'firstPageBoxHeader', 'firstPageBoxHeaderEven', 'firstPageBoxFooter', 'firstPageBoxFooterEven', + 'page_box', 'show_marks', 'bodyBackgroundColor', 'bodyBackgroundGradient', 'bodyBackgroundImage', + 'CurOrientation', 'DefOrientation', 'w', 'h', 'wPt', 'hPt', 'fw', 'fh', 'fwPt', 'fhPt', + 'pgwidth', 'PageBreakTrigger', 'tMargin', 'bMargin', 'lMargin', 'rMargin', 'DeflMargin', 'DefrMargin', + 'margin_header', 'margin_footer', 'MarginCorrection', 'y0', + + 'pages', 'pageoutput', 'saveHTMLHeader', 'saveHTMLFooter', 'PageLinks', 'PageAnnots', 'pageDim', + 'OrientationChanges', 'pageBackgrounds', 'patterns', 'PageNumSubstitutions', + + 'blockContext', 'floatmargins', 'floatbuffer', 'floatDivs', + ]; + + private function savePageState() + { + $state = ['props' => [], 'blk' => []]; + + foreach (self::$pageState as $name) { + $state['props'][$name] = $this->mpdf->$name; + } + + // Level by level: open() holds the level below by reference, and a copy of the whole array would share it + for ($i = 1; $i < $this->mpdf->blklvl; $i++) { + $state['blk'][$i] = $this->mpdf->blk[$i]; + } + + return $state; + } + + private function restorePageState($state) + { + foreach ($state['props'] as $name => $value) { + $this->mpdf->$name = $value; + } + + foreach ($state['blk'] as $i => $blk) { + $this->mpdf->blk[$i] = $blk; + } + } + } diff --git a/tests/Mpdf/PageBreakInsideAvoidTest.php b/tests/Mpdf/PageBreakInsideAvoidTest.php new file mode 100644 index 000000000..f61267722 --- /dev/null +++ b/tests/Mpdf/PageBreakInsideAvoidTest.php @@ -0,0 +1,209 @@ +Filler

', $paragraphs); + } + + /** + * Thirty paragraphs leave room for a few more on the page, so a block of twelve moves whole to page 2 + */ + private function movingBlock($inner = '', $paragraphs = 12) + { + return $this->filler(30) . '
' . $inner . str_repeat('

Kept

', $paragraphs) . '
'; + } + + private function assertTextCount($expected, $text, $stream, $message = '') + { + $this->assertSame($expected, substr_count($stream, '(' . $text . ')'), $message ?: "'$text' should appear $expected time(s)"); + } + + /** + * mpdf/mpdf#533: the footer is written when the measuring pass reaches the end of the starting page, and + * that page is the one the unwind kept, so the watermark on it was drawn again when the block was laid + * out for real + */ + public function testAWatermarkIsDrawnOncePerPage() + { + $mpdf = $this->mpdf(); + $mpdf->setWatermarkText('TESTING'); + $mpdf->showWatermarkText = true; + $mpdf->WriteHTML('

Outside

' . str_repeat('

Inside

', 100) . '
'); + + $pages = $this->pages($mpdf->Output('', 'S')); + + $this->assertCount(4, $pages); + foreach ($pages as $i => $page) { + $this->assertTextCount(1, 'TESTING', $page, 'Page ' . ($i + 1) . ' should carry one watermark'); + } + } + + /** + * mpdf/mpdf#1131: the measuring pass starts page 2, which selects the @page rules for it, and the + * unwind did not put page 1's back. So page 1 was finished with page 2's footer and background + */ + public function testTheFirstPageKeepsItsOwnPageRulesWhenABlockMovesOffIt() + { + $mpdf = $this->mpdf(); + $mpdf->WriteHTML(''); + $mpdf->WriteHTML('This is Footer 1'); + $mpdf->WriteHTML('This is Footer 2'); + $mpdf->WriteHTML('

Hello

' . str_repeat('

' . str_repeat('Lorem ipsum dolor sit amet. ', 12) . '

', 15)); + + $pages = $this->pages($mpdf->Output('', 'S')); + + $this->assertCount(2, $pages); + + $this->assertSame(1, substr_count($pages[0], self::RED)); + $this->assertSame(0, substr_count($pages[0], self::GREEN)); + $this->assertTextCount(1, 'This is Footer 1', $pages[0]); + $this->assertTextCount(0, 'This is Footer 2', $pages[0]); + + $this->assertSame(0, substr_count($pages[1], self::RED)); + $this->assertSame(1, substr_count($pages[1], self::GREEN)); + $this->assertTextCount(0, 'This is Footer 1', $pages[1]); + $this->assertTextCount(1, 'This is Footer 2', $pages[1]); + } + + /** + * mpdf/mpdf#1805: the measuring pass painted the @page background onto the starting page a second time, + * on top of the block backgrounds already collected for it, so every page but the last lost them + */ + public function testAPageBackgroundIsPaintedOnceAndUnderTheBlockBackgrounds() + { + $html = ''; + for ($i = 1; $i <= 14; $i++) { + $html .= '
Card ' . $i . '

' . str_repeat('Body text. ', 60) . '

'; + } + + $pages = $this->pages($this->render($html)); + + $this->assertCount(3, $pages); + foreach ($pages as $i => $page) { + $this->assertSame(1, substr_count($page, self::WHITE), 'Page ' . ($i + 1) . ' should paint its background once'); + $this->assertGreaterThan(strrpos($page, self::WHITE), strpos($page, self::RED), 'Page ' . ($i + 1) . ' should paint the card heads over the page background'); + } + } + + /** + * The backgrounds collected for the starting page before the block are flushed onto it by the measuring + * pass. Putting the page back as it was has to put them back too, or they would never be painted + */ + public function testABackgroundCollectedBeforeTheBlockIsStillPainted() + { + $pages = $this->pages($this->render('
Red box
' . $this->movingBlock())); + + $this->assertCount(2, $pages); + $this->assertSame(1, substr_count($pages[0], self::RED)); + $this->assertTextCount(0, 'Kept', $pages[0]); + $this->assertTextCount(12, 'Kept', $pages[1]); + } + + /** + * The border of a block enclosing the moved one is stroked down to the foot of the starting page during + * the measuring pass, and the block remembers having done so. Putting the page back as it was without + * also putting the block back would leave page 1 with no border at all + */ + public function testAnEnclosingBorderIsPaintedOnTheFirstPageAfterTheBlockMoves() + { + $html = '
' . $this->movingBlock() . '

Tail

'; + + $pages = $this->pages($this->render($html)); + + $this->assertCount(2, $pages); + $this->assertMatchesRegularExpression('/^S$/m', $pages[0], 'Page 1 should carry the border down to its foot'); + $this->assertMatchesRegularExpression('/^S$/m', $pages[1]); + } + + /** + * mpdf/mpdf#785: a float inside the block switched the discard off but left the block flagged, so the + * float was written by the measuring pass and again by the real one, at a y the unwind had zeroed + */ + public function testAFloatInsideTheBlockIsLaidOutOnce() + { + $html = $this->filler(5) + . '

Signed

' + . '
Me
You
' + . '

After

'; + + $pages = $this->pages($this->render($html)); + + $this->assertCount(1, $pages); + foreach (['Filler' => 5, 'Signed' => 1, 'Me' => 1, 'You' => 1, 'After' => 1] as $text => $count) { + $this->assertTextCount($count, $text, $pages[0]); + } + } + + public function testAFloatInsideTheBlockMovesWithIt() + { + $floats = '

Head

Left
Right
'; + + $pages = $this->pages($this->render($this->movingBlock($floats, 10) . '

Tail

')); + + $this->assertCount(2, $pages); + foreach (['Head', 'Left', 'Right', 'Kept'] as $text) { + $this->assertTextCount(0, $text, $pages[0]); + } + foreach (['Head' => 1, 'Left' => 1, 'Right' => 1, 'Kept' => 10, 'Tail' => 1] as $text => $count) { + $this->assertTextCount($count, $text, $pages[1]); + } + } + + /** + * A floated block is never kept together, as before + */ + public function testAFloatedBlockIsNotKeptTogether() + { + $html = $this->filler(30) . '
' . str_repeat('

Kept

', 12) . '

Tail

'; + + $pages = $this->pages($this->render($html)); + + $this->assertCount(2, $pages); + $this->assertTextCount(12, 'Kept', implode('', $pages)); + $this->assertTextCount(1, 'Tail', $pages[1]); + } + + /** + * mpdf/mpdf#1131 (comments): a header, footer or fixed-position block is buffered, not discarded, while + * being measured, so a kept-together paragraph inside one was written twice + */ + public function testAKeptTogetherBlockInsideAFixedPositionBlockIsWrittenOnce() + { + $html = '

Hello

Body

'; + + $pages = $this->pages($this->render($html)); + + $this->assertCount(1, $pages); + $this->assertTextCount(1, 'Hello', $pages[0]); + } + + public function testAKeptTogetherBlockInsideAFooterIsWrittenOnce() + { + $mpdf = $this->mpdf(); + $mpdf->SetHTMLFooter('

Footer para

'); + $mpdf->WriteHTML('

Body

'); + + $pages = $this->pages($mpdf->Output('', 'S')); + + $this->assertCount(1, $pages); + $this->assertTextCount(1, 'Footer para', $pages[0]); + } +} diff --git a/tests/Mpdf/PageStreams.php b/tests/Mpdf/PageStreams.php new file mode 100644 index 000000000..7ec4de355 --- /dev/null +++ b/tests/Mpdf/PageStreams.php @@ -0,0 +1,38 @@ + 'c']); + $mpdf->compress = false; + + return $mpdf; + } + + private function render($html, $config = []) + { + $mpdf = $this->mpdf($config); + $mpdf->WriteHTML($html); + $pdf = $mpdf->Output('', 'S'); + $mpdf->cleanup(); + + return $pdf; + } + + /** + * The content stream of each page, in order + */ + private function pages($pdf) + { + preg_match_all('/\d+ 0 obj\s*<<\/Length \d+>>\s*stream\n(.*?)\nendstream/s', $pdf, $matches); + + return $matches[1]; + } +} diff --git a/tests/Mpdf/TableBackgroundTest.php b/tests/Mpdf/TableBackgroundTest.php index 1fd82df18..1ae898cca 100644 --- a/tests/Mpdf/TableBackgroundTest.php +++ b/tests/Mpdf/TableBackgroundTest.php @@ -4,29 +4,10 @@ class TableBackgroundTest extends \Yoast\PHPUnitPolyfills\TestCases\TestCase { + use PageStreams; const RED_FILL = '1.000 0.000 0.000 rg'; - private function render($html) - { - $mpdf = new Mpdf(['mode' => 'c']); - $mpdf->compress = false; - $mpdf->WriteHTML($html); - - return $mpdf->Output('', 'S'); - } - - /** - * The content stream of each page, in order - */ - private function pages($pdf) - { - $matches = []; - preg_match_all('/\d+ 0 obj\s*<<\/Length \d+>>\s*stream\n(.*?)\nendstream/s', $pdf, $matches); - - return $matches[1]; - } - /** * Every rectangle filled red, in order, including any drawn more than once */ From a79929c3c59bc162e33dfa72284e31158446a4f9 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 9 Sep 2026 11:42:49 +1000 Subject: [PATCH 04/13] Keep the trimmed text in the token a character substitution splits (mpdf/mpdf#2075) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When useSubstitutions finds a character the current font lacks, it splices a span in the backup font into the token array after the current token and trims the working string down to the text before it. The token itself kept the whole text. That is invisible on a single parse, but a page-break-inside:avoid block is parsed twice, and the second parse read the untrimmed token, printed all of it, and then the span and the tail again: "HÄẞLICH" came out as "HÄ[]LICHẞLICH". SubstituteCharsSIP() already wrote the trim back into the token. The seven other splice sites now do the same. Co-Authored-By: Claude Fable 5.1 --- src/Mpdf.php | 16 +++++++++------- tests/Mpdf/PageBreakInsideAvoidTest.php | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/Mpdf.php b/src/Mpdf.php index 5abec91b4..22467df85 100644 --- a/src/Mpdf.php +++ b/src/Mpdf.php @@ -25897,7 +25897,9 @@ function SubstituteCharsNonCore(&$writehtml_a, &$writehtml_i, &$writehtml_e) } else { if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font $cont = mb_substr($writehtml_e, $start + 1); - $writehtml_e = mb_substr($writehtml_e, 0, $start + 1, 'UTF-8'); + // Write the trim back into the token too, here and at every splice below, as SubstituteCharsSIP() + // does: a page-break-inside:avoid block's second parse otherwise prints the untrimmed text (mpdf/mpdf#2075) + $writehtml_a[$writehtml_i] = $writehtml_e = mb_substr($writehtml_e, 0, $start + 1, 'UTF-8'); array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]); $this->subPos = $writehtml_i + 1; @@ -25911,7 +25913,7 @@ function SubstituteCharsNonCore(&$writehtml_a, &$writehtml_i, &$writehtml_e) if ($l > 0) { $patt = mb_substr($writehtml_e, $start, $l, 'UTF-8'); if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) { - $writehtml_e = $m[1]; + $writehtml_a[$writehtml_i] = $writehtml_e = $m[1]; array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]); $this->subPos = $writehtml_i + 3; @@ -26018,7 +26020,7 @@ function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e) if ($l > 0) { $patt = mb_substr($writehtml_e, $start, $l); if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) { - $writehtml_e = $m[1]; + $writehtml_a[$writehtml_i] = $writehtml_e = $m[1]; array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]); $this->subPos = $writehtml_i + 3; return 4; @@ -26067,7 +26069,7 @@ function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e) if ($l > 0) { $patt = mb_substr($writehtml_e, $start, $l); if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) { - $writehtml_e = $m[1]; + $writehtml_a[$writehtml_i] = $writehtml_e = $m[1]; array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]); $this->subPos = $writehtml_i + 3; return 4; @@ -26121,7 +26123,7 @@ function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e) if ($ftype == 'C') { $patt = mb_substr($writehtml_e, $start, count($repl)); if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) { - $writehtml_e = $m[1]; + $writehtml_a[$writehtml_i] = $writehtml_e = $m[1]; array_splice($writehtml_a, $writehtml_i + 1, 0, [$font, implode('|', $repl), '/' . $font, $m[3]]); // e.g. $this->subPos = $writehtml_i + 3; return 4; @@ -26171,7 +26173,7 @@ function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e) } else { if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font $cont = mb_substr($writehtml_e, $start + 1); - $writehtml_e = mb_substr($writehtml_e, 0, $start + 1); + $writehtml_a[$writehtml_i] = $writehtml_e = mb_substr($writehtml_e, 0, $start + 1); array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]); $this->subPos = $writehtml_i + 1; return 2; @@ -26184,7 +26186,7 @@ function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e) if ($l > 0) { $patt = mb_substr($writehtml_e, $start, $l); if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) { - $writehtml_e = $m[1]; + $writehtml_a[$writehtml_i] = $writehtml_e = $m[1]; array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]); $this->subPos = $writehtml_i + 3; return 4; diff --git a/tests/Mpdf/PageBreakInsideAvoidTest.php b/tests/Mpdf/PageBreakInsideAvoidTest.php index f61267722..f245ae135 100644 --- a/tests/Mpdf/PageBreakInsideAvoidTest.php +++ b/tests/Mpdf/PageBreakInsideAvoidTest.php @@ -206,4 +206,21 @@ public function testAKeptTogetherBlockInsideAFooterIsWrittenOnce() $this->assertCount(1, $pages); $this->assertTextCount(1, 'Footer para', $pages[0]); } + + /** + * mpdf/mpdf#2075: a substituted character is spliced into the token stream as a span, and the text + * before it trimmed from the current token. The trim was lost on the token itself, so the second pass + * printed the whole text and then the span and the rest of it again. Text in a TrueType font is written + * as UTF-16 + */ + public function testASubstitutedCharacterIsNotDuplicatedWhenTheBlockIsParsedAgain() + { + $html = '' . $this->filler(5) . '

HÄẞLICH

'; + + $pdf = $this->render($html, ['mode' => 'utf-8', 'useSubstitutions' => true, 'backupSubsFont' => ['dejavusans']]); + + foreach (['HÄ' => 'before', 'LICH' => 'after'] as $text => $side) { + $this->assertSame(1, substr_count($pdf, mb_convert_encoding($text, 'UTF-16BE', 'UTF-8')), "The letters $side the substituted one should be printed once"); + } + } } From b374cdeefab6d869275fcc0bc6f68249b86b5557 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 9 Sep 2026 11:42:49 +1000 Subject: [PATCH 05/13] Only ask page-break-after:avoid for a line a fresh page could hold (mpdf/mpdf#1801) page-break-after:avoid is approximated by demanding room for one more line as tall as the block after it, once per line in Cell() and again when the block closes. A block taller than half a page can never have that on any page, so each check pushed it on to yet another one, and a wide image arrived after two blank pages. Skip the look-ahead when even a fresh page could not satisfy it. A heading with only its own height left on the page still moves. Co-Authored-By: Claude Fable 5.1 --- src/Mpdf.php | 6 ++++- tests/Mpdf/PageBreakInsideAvoidTest.php | 33 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Mpdf.php b/src/Mpdf.php index 22467df85..ad4813cff 100644 --- a/src/Mpdf.php +++ b/src/Mpdf.php @@ -4882,7 +4882,10 @@ function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = ($this->y + $this->divheight > $this->PageBreakTrigger) || ($this->y + $h > $this->PageBreakTrigger) || ( + // page-break-after:avoid wants room for one more line this tall after this one. When a fresh page + // has no such room either, breaking only adds empty pages (mpdf/mpdf#1801) $this->y + ($h * 2) + $bottom > $this->PageBreakTrigger + && $this->tMargin + ($h * 2) + $bottom <= $this->PageBreakTrigger && (isset($this->blk[$this->blklvl]['page_break_after_avoid']) && $this->blk[$this->blklvl]['page_break_after_avoid']) ) ) @@ -6751,7 +6754,8 @@ function finishFlowingBlock($endofblock = false, $next = '') if ($this->blklvl > 0 && !$is_table) { if ($endofblock && $blockstate > 1) { - if ($this->blk[$this->blklvl]['page_break_after_avoid']) { + // As in Cell(): only ask for the extra line if a fresh page could hold it (mpdf/mpdf#1801) + if ($this->blk[$this->blklvl]['page_break_after_avoid'] && $this->tMargin + $check_h + $stackHeight <= $this->PageBreakTrigger) { $check_h += $stackHeight; } $check_h += ($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']); diff --git a/tests/Mpdf/PageBreakInsideAvoidTest.php b/tests/Mpdf/PageBreakInsideAvoidTest.php index f245ae135..57d5630e6 100644 --- a/tests/Mpdf/PageBreakInsideAvoidTest.php +++ b/tests/Mpdf/PageBreakInsideAvoidTest.php @@ -223,4 +223,37 @@ public function testASubstitutedCharacterIsNotDuplicatedWhenTheBlockIsParsedAgai $this->assertSame(1, substr_count($pdf, mb_convert_encoding($text, 'UTF-16BE', 'UTF-8')), "The letters $side the substituted one should be printed once"); } } + + /** + * mpdf/mpdf#1801: page-break-after:avoid asks for room for one more line as tall as the block after it. + * A tall image could never have that, on any page, and each check pushed it on to yet another one + */ + public function testATallBlockWithPageBreakAfterAvoidDoesNotPushBlankPages() + { + $image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + $html = '

Before

After

'; + + $pages = $this->pages($this->render($html)); + + $this->assertCount(1, $pages); + $this->assertSame(1, preg_match_all('/\/I\d+ Do/', $pages[0])); + $this->assertTextCount(1, 'Before', $pages[0]); + $this->assertTextCount(1, 'After', $pages[0]); + } + + /** + * The look-ahead still applies when a fresh page could satisfy it: a heading with only its own height + * left on the page moves to the next one + */ + public function testAHeadingWithPageBreakAfterAvoidStillMovesToKeepItsNextLine() + { + $html = $this->filler(38) . '

Heading

After

'; + + $pages = $this->pages($this->render($html)); + + $this->assertCount(2, $pages); + $this->assertTextCount(0, 'Heading', $pages[0]); + $this->assertTextCount(1, 'Heading', $pages[1]); + $this->assertTextCount(1, 'After', $pages[1]); + } } From 1a9391a94f08676ac4fafd0654a27ed6d0ff31f9 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 9 Sep 2026 11:42:49 +1000 Subject: [PATCH 06/13] Keep a top caption with its table under use_kwt (mpdf/mpdf#1666) A caption is written as a block above its table, so when a table with page-break-inside:avoid moved to the next page the caption stayed where it was. That is keep-with-next, which mPDF only has for headings and only under use_kwt. With it on, a caption above its table is now marked keep-with-table the same way a heading is, and moves with it. Without use_kwt nothing changes. Co-Authored-By: Claude Fable 5.1 --- src/Mpdf.php | 4 +++- tests/Mpdf/PageBreakInsideAvoidTest.php | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Mpdf.php b/src/Mpdf.php index ad4813cff..99e309689 100644 --- a/src/Mpdf.php +++ b/src/Mpdf.php @@ -27332,7 +27332,9 @@ function AdjustHTML($html, $tabSpaces = 8) $html = str_replace("]*)><\/textarea>/si', ' ', $html); - $html = preg_replace('/(]*>)\s*()(.*?<\/table>)/si', '\\2 position="top"\\3\\1\\4\\2 position="bottom"\\3', $html); // *TABLES* + // With use_kwt, a caption above its table is kept with it the same way as a heading (mpdf/mpdf#1666) + $kwt = $this->use_kwt ? ' keep-with-table="1"' : ''; + $html = preg_replace('/(]*>)\s*()(.*?<\/table>)/si', '\\2 position="top"' . $kwt . '\\3\\1\\4\\2 position="bottom"\\3', $html); // *TABLES* if ($this->use_kwt) { $returnHtml = preg_replace('/<(h[1-6])([^>]*(?[^>]*<\/\\1>\s*', 20) . 'Bottom'; + + foreach ([true, false] as $useKwt) { + $mpdf = $this->mpdf(); + $mpdf->use_kwt = $useKwt; + $mpdf->WriteHTML($this->filler(27) . $table); + + $pages = $this->pages($mpdf->Output('', 'S')); + + $this->assertCount(2, $pages); + $this->assertTextCount(1, 'Cell', $pages[1]); + $this->assertTextCount($useKwt ? 0 : 1, 'Table Caption', $pages[0]); + $this->assertTextCount($useKwt ? 1 : 0, 'Table Caption', $pages[1]); + } + } + /** * mpdf/mpdf#1801: page-break-after:avoid asks for room for one more line as tall as the block after it. * A tall image could never have that, on any page, and each check pushed it on to yet another one From 57d272b1373e107f3dd2f6b20276e5fb1200286f Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 9 Sep 2026 11:42:49 +1000 Subject: [PATCH 07/13] Snapshot what a measured block used to leave behind One four-page document for the page-break-inside:avoid fixes: kept-together cards over a page break under a first-page background, footer and watermark; a framed section whose signature block, with floats, moves whole; and a captioned table kept with its caption under use_kwt. Rendered before the fixes, the signature line is printed twice on page 2. Co-Authored-By: Claude Fable 5.1 --- .../PageBreakAvoidStateSnapshotTest.php | 103 ++++++++++++++++++ .../data/snapshots/page-break-avoid-state.pdf | Bin 0 -> 33731 bytes 2 files changed, 103 insertions(+) create mode 100644 tests/Snapshots/PageBreakAvoidStateSnapshotTest.php create mode 100644 tests/data/snapshots/page-break-avoid-state.pdf diff --git a/tests/Snapshots/PageBreakAvoidStateSnapshotTest.php b/tests/Snapshots/PageBreakAvoidStateSnapshotTest.php new file mode 100644 index 000000000..459431ee6 --- /dev/null +++ b/tests/Snapshots/PageBreakAvoidStateSnapshotTest.php @@ -0,0 +1,103 @@ + + + + First page footer + Page {PAGENO} footer + +

mPDF

+

What a measured block must not leave behind

+ +

Every card below is kept together. The first page has its own background colour and footer, and + one watermark, all of which stay as they are when the cards that do not fit here move on.

+ + +
+
Card
+ +

Card text, line . Enough of it that the last cards have to move to page 2 whole.

+ +
+ + +
+

A framed section. Its border runs to the foot of this page and on from the top of the next, + around a signature block that is kept together and moves as one.

+ +

Framed text, line .

+ +
+

Signed on behalf of both parties:

+
Me
+
You
+
+ +

Signature block text, line .

+ +
+

After the signature block.

+
+ + +

Filler before the table, line .

+ + + + + + + + +
A caption kept with its table
The table does not fit on this page, so it moves. With keep-with-table on, the caption moves with it.
Row
+ +

After the table.

+ mpdf = new \Mpdf\Mpdf(); + $this->mpdf->use_kwt = true; + $this->mpdf->setWatermarkText('mPDF'); + $this->mpdf->showWatermarkText = true; + $this->mpdf->WriteHTML($html); + } +} diff --git a/tests/data/snapshots/page-break-avoid-state.pdf b/tests/data/snapshots/page-break-avoid-state.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e9d5665d550b29016c601003f665c138b8a98990 GIT binary patch literal 33731 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 Date: Wed, 9 Sep 2026 12:02:26 +1000 Subject: [PATCH 08/13] Spend the table-background placeholder from one place, and one snapshot for the avoid block A review pass over the whole PR. _tableWrite() spliced table backgrounds behind their placeholder at two sites: at the end of a level 1 table, in the new one-shot form, and when a table breaks across pages, still in the old two-preg_replace form that only ran when there were backgrounds and so left the placeholder in place when there were none. Both now go through spendTableBackgrounds(), which takes the buffer the placeholder was written to. The guard for a block that is only being measured narrows to the one thing that has a side effect, PrintTableBackgrounds() registering patterns and gradients; the placeholder is spent either way, since BlockTag puts the page back afterwards. BlockTag reuses the float it already worked out for the page-break-inside gate in the two float branches, and restores the saved properties through the existing restoreStateSnapshot(). The state list explains its three groups inline rather than in a docblock that had to be read against blank lines. The two snapshot documents for a moved avoid block become one: the plain table and the coloured panels from page-break-avoid.pdf now sit in front of and inside the cards of page-break-avoid-state.pdf, which is regenerated. Two pairs of TableBackgroundTest cases that rendered the same document are merged, and the test trait cleans up after every document it outputs. Co-Authored-By: Claude Fable 5.1 --- src/Mpdf.php | 58 +++++++-------- src/Tag/BlockTag.php | 34 ++++----- tests/Mpdf/PageBreakInsideAvoidTest.php | 8 +-- tests/Mpdf/PageStreams.php | 6 ++ tests/Mpdf/TableBackgroundTest.php | 36 +++------- .../Snapshots/PageBreakAvoidSnapshotTest.php | 67 ------------------ .../PageBreakAvoidStateSnapshotTest.php | 22 +++++- .../Snapshots/TableBackgroundSnapshotTest.php | 11 ++- .../data/snapshots/page-break-avoid-state.pdf | Bin 33731 -> 34144 bytes tests/data/snapshots/page-break-avoid.pdf | Bin 29645 -> 0 bytes 10 files changed, 84 insertions(+), 158 deletions(-) delete mode 100644 tests/Snapshots/PageBreakAvoidSnapshotTest.php delete mode 100644 tests/data/snapshots/page-break-avoid.pdf diff --git a/src/Mpdf.php b/src/Mpdf.php index 99e309689..f82e3c5a0 100644 --- a/src/Mpdf.php +++ b/src/Mpdf.php @@ -22070,6 +22070,27 @@ function _reverseTableDir(&$table) } } + /** + * Splice the backgrounds collected for a table in behind the placeholder it wrote to $buffer, and spend the + * placeholder whether or not anything was put behind it: one left in place takes a copy of the next table's + * backgrounds as well, so a table with no background of its own left the one after it painted twice + */ + function spendTableBackgrounds($buffer) + { + // PrintTableBackgrounds() registers patterns and gradients as it goes, so like Td and BlockTag it is skipped + // while a page-break-inside:avoid block is only being measured and its output thrown away + $s = $this->tableBackgrounds && !$this->keep_block_together ? "\n" . $this->PrintTableBackgrounds() . "\n" : ''; + $placeholder = '___TABLE___BACKGROUNDS' . $this->uniqstr; + + if ($buffer === 'pages') { + $this->pages[$this->page] = str_replace($placeholder, ' ' . $s, $this->pages[$this->page]); + } else { + $this->$buffer = str_replace($placeholder, ' ' . $s, $this->$buffer); + } + + $this->tableBackgrounds = []; + } + function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $splitpg = 0, $rety = 0) { $level = $table['level']; @@ -22555,17 +22576,7 @@ function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $spl } // $this->AcceptPageBreak() has moved tablebuffer to $this->pages content - if ($this->tableBackgrounds) { - $s = $this->PrintTableBackgrounds(); - if ($this->bufferoutput) { - $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer); - $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer); - } else { - $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]); - $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]); - } - $this->tableBackgrounds = []; - } + $this->spendTableBackgrounds($this->bufferoutput ? 'headerbuffer' : 'pages'); if ($split) { if ($i == 0 && $j == 0) { @@ -23474,27 +23485,12 @@ function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $spl } if ($level == 1) { - // Nothing else paints while a page-break-inside:avoid block is being measured - see - // PaintDivBB() and BaseWriter::write() - and what is drawn on the page it starts on - // stays there when the block moves - if (!$this->keep_block_together) { - $s = $this->tableBackgrounds ? "\n" . $this->PrintTableBackgrounds() . "\n" : ''; - - // The placeholder this table wrote is spent whether or not anything was put behind - // it. One left in place takes a copy of the next table's backgrounds as well, so a - // table with no background of its own used to leave the one after it painted twice - $placeholder = '___TABLE___BACKGROUNDS' . $this->uniqstr; - - if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) { - $this->tablebuffer = str_replace($placeholder, ' ' . $s, $this->tablebuffer); - } elseif ($this->bufferoutput) { - $this->headerbuffer = str_replace($placeholder, ' ' . $s, $this->headerbuffer); - } else { - $this->pages[$this->page] = str_replace($placeholder, ' ' . $s, $this->pages[$this->page]); - } + // The same routing as BaseWriter::write() gave the placeholder + if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) { + $this->spendTableBackgrounds('tablebuffer'); + } else { + $this->spendTableBackgrounds($this->bufferoutput ? 'headerbuffer' : 'pages'); } - - $this->tableBackgrounds = []; } diff --git a/src/Tag/BlockTag.php b/src/Tag/BlockTag.php index 88f72e7a0..8b0bf18e9 100644 --- a/src/Tag/BlockTag.php +++ b/src/Tag/BlockTag.php @@ -478,9 +478,8 @@ public function open($attr, &$ahtml, &$ihtml) $properties = $this->cssManager->MergeCSS('BLOCK', $tag, $attr); // mPDF 6 - moved to after page-break-before // mPDF 6 page-break-inside:avoid - // Not for a floated block, and not while output is buffered (headers, footers, fixed-position blocks): there - // is no page to break there, and buffered output is not discarded while a block is measured, so it would be - // written twice + // Never for a floated block (the float branches used to cancel it), nor while output is buffered (headers, + // footers, fixed-position blocks): that is not discarded while the block is measured, so it would be written twice $float = isset($properties['FLOAT']) ? strtoupper($properties['FLOAT']) : ''; if (isset($properties['PAGE-BREAK-INSIDE']) && strtoupper($properties['PAGE-BREAK-INSIDE']) === 'AVOID' && !$this->mpdf->ColActive && !$this->mpdf->keep_block_together && !isset($attr['PAGEBREAKAVOIDCHECKED']) @@ -590,7 +589,7 @@ public function open($attr, &$ahtml, &$ihtml) } /* -- CSS-FLOAT -- */ - if (isset($properties['FLOAT']) && strtoupper($properties['FLOAT']) === 'RIGHT' && !$this->mpdf->ColActive) { + if ($float === 'RIGHT' && !$this->mpdf->ColActive) { $this->mpdf->blockContext++; $currblk['blockContext'] = $this->mpdf->blockContext; @@ -637,7 +636,7 @@ public function open($attr, &$ahtml, &$ihtml) $currblk['float_width'] = ($currblk['css_set_width'] + $bdl + $pdl + $bdr + $pdr + $currblk['margin_right']); } - } elseif (isset($properties['FLOAT']) && strtoupper($properties['FLOAT']) === 'LEFT' && !$this->mpdf->ColActive) { + } elseif ($float === 'LEFT' && !$this->mpdf->ColActive) { $this->mpdf->blockContext++; $currblk['blockContext'] = $this->mpdf->blockContext; @@ -1466,30 +1465,23 @@ public function close(&$ahtml, &$ihtml) } /** - * What a page-break-inside:avoid block has to put back after being measured. - * - * The block is laid out once to see where it ends, and if that runs onto another page the pages it made - * are thrown away and it is laid out again from where it started. AddPage() runs in full during that - * measuring pass, so the footer, watermark and backgrounds of the starting page were painted a second - * time when the block was laid out for real, and the @page selection, margins and headers of the page - * after it were carried back onto it. See mpdf/mpdf#533, #1131, #1805. - * - * The first group is what _beginpage() selects for the next page. The second is what AddPage() finishes the - * current page with, written only because it switches the measuring discard off while it runs; the arrays - * keyed by page also carry the pages the measuring pass made, so putting them back discards those too. The - * third is float bookkeeping. + * What the measuring pass of a page-break-inside:avoid block has to put back when it is unwound; see + * mpdf/mpdf#533, #1131, #1805. A curated list rather than Mpdf::getStateSnapshot(), which would also roll back + * the parser position, the font and the block stack */ private static $pageState = [ + // What _beginpage() selects for the next page 'HTMLHeader', 'HTMLHeaderE', 'HTMLFooter', 'HTMLFooterE', 'firstPageBoxHeader', 'firstPageBoxHeaderEven', 'firstPageBoxFooter', 'firstPageBoxFooterEven', 'page_box', 'show_marks', 'bodyBackgroundColor', 'bodyBackgroundGradient', 'bodyBackgroundImage', 'CurOrientation', 'DefOrientation', 'w', 'h', 'wPt', 'hPt', 'fw', 'fh', 'fwPt', 'fhPt', 'pgwidth', 'PageBreakTrigger', 'tMargin', 'bMargin', 'lMargin', 'rMargin', 'DeflMargin', 'DefrMargin', 'margin_header', 'margin_footer', 'MarginCorrection', 'y0', - + // What AddPage() finishes the current page with (it switches the measuring discard off while it runs); the + // page-keyed arrays also carry the pages the measuring pass made, so restoring them discards those too 'pages', 'pageoutput', 'saveHTMLHeader', 'saveHTMLFooter', 'PageLinks', 'PageAnnots', 'pageDim', 'OrientationChanges', 'pageBackgrounds', 'patterns', 'PageNumSubstitutions', - + // Float bookkeeping 'blockContext', 'floatmargins', 'floatbuffer', 'floatDivs', ]; @@ -1511,9 +1503,7 @@ private function savePageState() private function restorePageState($state) { - foreach ($state['props'] as $name => $value) { - $this->mpdf->$name = $value; - } + $this->mpdf->restoreStateSnapshot($state['props']); foreach ($state['blk'] as $i => $blk) { $this->mpdf->blk[$i] = $blk; diff --git a/tests/Mpdf/PageBreakInsideAvoidTest.php b/tests/Mpdf/PageBreakInsideAvoidTest.php index 0286126ef..94a9d0c78 100644 --- a/tests/Mpdf/PageBreakInsideAvoidTest.php +++ b/tests/Mpdf/PageBreakInsideAvoidTest.php @@ -44,7 +44,7 @@ public function testAWatermarkIsDrawnOncePerPage() $mpdf->showWatermarkText = true; $mpdf->WriteHTML('

Outside

' . str_repeat('

Inside

', 100) . '
'); - $pages = $this->pages($mpdf->Output('', 'S')); + $pages = $this->pages($this->output($mpdf)); $this->assertCount(4, $pages); foreach ($pages as $i => $page) { @@ -68,7 +68,7 @@ public function testTheFirstPageKeepsItsOwnPageRulesWhenABlockMovesOffIt() $mpdf->WriteHTML('This is Footer 2'); $mpdf->WriteHTML('

Hello

' . str_repeat('

' . str_repeat('Lorem ipsum dolor sit amet. ', 12) . '

', 15)); - $pages = $this->pages($mpdf->Output('', 'S')); + $pages = $this->pages($this->output($mpdf)); $this->assertCount(2, $pages); @@ -201,7 +201,7 @@ public function testAKeptTogetherBlockInsideAFooterIsWrittenOnce() $mpdf->SetHTMLFooter('

Footer para

'); $mpdf->WriteHTML('

Body

'); - $pages = $this->pages($mpdf->Output('', 'S')); + $pages = $this->pages($this->output($mpdf)); $this->assertCount(1, $pages); $this->assertTextCount(1, 'Footer para', $pages[0]); @@ -238,7 +238,7 @@ public function testACaptionMovesWithItsKeptTogetherTableWhenKeepWithTableIsOn() $mpdf->use_kwt = $useKwt; $mpdf->WriteHTML($this->filler(27) . $table); - $pages = $this->pages($mpdf->Output('', 'S')); + $pages = $this->pages($this->output($mpdf)); $this->assertCount(2, $pages); $this->assertTextCount(1, 'Cell', $pages[1]); diff --git a/tests/Mpdf/PageStreams.php b/tests/Mpdf/PageStreams.php index 7ec4de355..b513ca1fd 100644 --- a/tests/Mpdf/PageStreams.php +++ b/tests/Mpdf/PageStreams.php @@ -20,6 +20,12 @@ private function render($html, $config = []) { $mpdf = $this->mpdf($config); $mpdf->WriteHTML($html); + + return $this->output($mpdf); + } + + private function output(Mpdf $mpdf) + { $pdf = $mpdf->Output('', 'S'); $mpdf->cleanup(); diff --git a/tests/Mpdf/TableBackgroundTest.php b/tests/Mpdf/TableBackgroundTest.php index 1ae898cca..536902906 100644 --- a/tests/Mpdf/TableBackgroundTest.php +++ b/tests/Mpdf/TableBackgroundTest.php @@ -6,15 +6,14 @@ class TableBackgroundTest extends \Yoast\PHPUnitPolyfills\TestCases\TestCase { use PageStreams; - const RED_FILL = '1.000 0.000 0.000 rg'; + const RED = '1.000 0.000 0.000 rg'; /** * Every rectangle filled red, in order, including any drawn more than once */ private function redRectangles($pdf) { - $matches = []; - preg_match_all('/' . preg_quote(self::RED_FILL, '/') . '\n([\d.\- ]+) re f/', $pdf, $matches); + preg_match_all('/' . preg_quote(self::RED, '/') . '\n([\d.\- ]+) re f/', $pdf, $matches); return $matches[1]; } @@ -30,23 +29,18 @@ private function document($before, $after) } /** - * A page-break-inside:avoid block is laid out once to measure it and then again for real. The - * measuring pass used to paint the table background on the page the block starts on, which is - * the one page the unwind does not throw away, so it was left behind after the table had moved - * to the next page. See mpdf/mpdf#570. + * The measuring pass of a page-break-inside:avoid block used to paint the table background on the page + * the block started on, and leave it there when the block moved. See mpdf/mpdf#570. */ - public function testATableBackgroundInsideAnAvoidBlockThatMovesIsDrawnInOnePlace() + public function testATableBackgroundInsideAnAvoidBlockThatMovesIsDrawnOnceOnThePageItMovedTo() { - $this->assertCount(1, $this->redRectangles($this->render($this->document(20, 10)))); - } - - public function testItIsDrawnOnThePageTheTableMovedToAndNotTheOneItLeft() - { - $pages = $this->pages($this->render($this->document(20, 10))); + $pdf = $this->render($this->document(20, 10)); + $pages = $this->pages($pdf); + $this->assertCount(1, $this->redRectangles($pdf)); $this->assertCount(2, $pages); - $this->assertStringNotContainsString(self::RED_FILL, $pages[0]); - $this->assertStringContainsString(self::RED_FILL, $pages[1]); + $this->assertStringNotContainsString(self::RED, $pages[0]); + $this->assertStringContainsString(self::RED, $pages[1]); $this->assertStringContainsString('Red table cell', $pages[1]); } @@ -72,15 +66,7 @@ public function testATableBackgroundOutsideAnyAvoidBlockIsUnaffected() * background of its own left the placeholder in the page, and the next table's backgrounds went * behind that one as well as its own. */ - public function testATableWithNoBackgroundDoesNotLeaveTheNextOnePaintedTwice() - { - $html = '
Plain table cell
' - . '
Red table cell
'; - - $this->assertCount(1, $this->redRectangles($this->render($html))); - } - - public function testEachTableWithNoBackgroundUsedToAddAnotherCopy() + public function testTablesWithNoBackgroundDoNotLeaveTheNextOnePaintedAgainForEachOfThem() { $html = str_repeat('
Plain table cell
', 5) . '
Red table cell
'; diff --git a/tests/Snapshots/PageBreakAvoidSnapshotTest.php b/tests/Snapshots/PageBreakAvoidSnapshotTest.php deleted file mode 100644 index 401601de1..000000000 --- a/tests/Snapshots/PageBreakAvoidSnapshotTest.php +++ /dev/null @@ -1,67 +0,0 @@ - - - -

mPDF

-

page-break-inside: avoid

- -

The block below is too tall for what is left of this page, so it moves to the - next one whole. Its coloured panels move with it, and nothing of them is left behind here. - The plain table above is what puts the backgrounds on the page in the first place.

- - - -
A table before the block, with no background of its own
- - -

Filler line , here to push the block over the page boundary.

- - -
- - -
Red panel, inside the block
- - - -
Blue panel, inside the block
- - -

The block carries this line too, and all of it stays together.

- -
- -

After the block.

- mpdf = new \Mpdf\Mpdf(); - $this->mpdf->WriteHTML($html); - } -} diff --git a/tests/Snapshots/PageBreakAvoidStateSnapshotTest.php b/tests/Snapshots/PageBreakAvoidStateSnapshotTest.php index 459431ee6..2b0d37ddf 100644 --- a/tests/Snapshots/PageBreakAvoidStateSnapshotTest.php +++ b/tests/Snapshots/PageBreakAvoidStateSnapshotTest.php @@ -5,8 +5,8 @@ /** * Everything the measuring pass of a page-break-inside:avoid block used to leave behind, on one document: * the watermark and footer of the page a block moves off, the @page rules of that page, the backgrounds of - * blocks that stay on it, a float inside a moved block, an enclosing border, and a table caption kept with - * its table. + * blocks that stay on it and of tables inside the moved block, a float inside a moved block, an enclosing + * border, and a table caption kept with its table. * * @group snapshot */ @@ -30,6 +30,12 @@ public function generatePdf() div.card { page-break-inside: avoid; margin-bottom: 4mm; border: 0.2mm solid #808080; } div.card div.head { background-color: #c8c8c8; padding: 2mm; font-weight: bold; } div.card p { padding: 0 2mm; } + div.card table.panel { margin: 0 2mm 2mm; } + + table.panel { border-collapse: collapse; width: 100%; margin-bottom: 4mm; } + table.panel td { padding: 2mm; border: 0.2mm solid #404040; } + td.red { background-color: #d02020; color: #ffffff; } + td.blue { background-color: #2040c0; color: #ffffff; } div.frame { border: 0.8mm solid #202020; padding: 3mm; margin-bottom: 4mm; } div.signatures { page-break-inside: avoid; } @@ -49,11 +55,21 @@ public function generatePdf()

What a measured block must not leave behind

Every card below is kept together. The first page has its own background colour and footer, and - one watermark, all of which stay as they are when the cards that do not fit here move on.

+ one watermark, all of which stay as they are when the cards that do not fit here move on. The + coloured panels in the last card move with it, and the plain table in front of the cards, which is + what leaves a place on this page for table backgrounds to land, gets nothing behind it.

+ + + +
A table before the cards, with no background of its own
Card
+ +
Red panel, inside the last card
+
Blue panel, inside the last card
+

Card text, line . Enough of it that the last cards have to move to page 2 whole.

diff --git a/tests/Snapshots/TableBackgroundSnapshotTest.php b/tests/Snapshots/TableBackgroundSnapshotTest.php index 50b79b6f7..3b72e212b 100644 --- a/tests/Snapshots/TableBackgroundSnapshotTest.php +++ b/tests/Snapshots/TableBackgroundSnapshotTest.php @@ -17,12 +17,11 @@ public function generatePdf() ob_start(); ?> + +

mPDF

+

Floats inside kept-together blocks

+ +

Each dashed block below is kept together. The first has a floated column of text beside wrapped + text and does not fit on this page, so it moves whole; the wrapped text keeps its indent on the + next page.

+ + +

Filler line , here to push the first block over the page boundary.

+ + +
+
+ +

Floated column, line .

+ +
+ +

Wrapped text beside the floated column, line . It starts to the right of the + column and returns to the margin once the column ends.

+ +
+

After the column has been cleared, inside the same block.

+
+ + +

Filler line , before the block with the pictures.

+ + +
+ + + +

Text between two floated pictures, line . The block moves whole to the next + page and both pictures move with it, drawn once each.

+ +
+
+ +
+

A column floated before the next block opens.

+

It used to vanish with the measuring pass.

+
+ +
+ +

A block that opens while the column above is still floating, line .

+ +
+
+ +
+
+ +

A column taller than a page, line .

+ +
+

This block cannot be kept together because its floated column is taller than a page, so it + breaks like any other block. Nothing in it is written twice.

+
+

The end of the tall block.

+
+ +

After every block.

+ mpdf = new \Mpdf\Mpdf(); + $this->mpdf->SetBasePath(__DIR__ . '/../data'); + $this->mpdf->WriteHTML($html); + } +} diff --git a/tests/data/snapshots/page-break-avoid-floats.pdf b/tests/data/snapshots/page-break-avoid-floats.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3c0c542f17aa3f899f839bf02c21ecb70d010482 GIT binary patch literal 93121 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 IX=R!J17*6RN&o-= literal 0 HcmV?d00001 From e9312714ec102aaea4fee74ae48c3c5d4a234627 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 9 Sep 2026 12:46:58 +1000 Subject: [PATCH 10/13] Make the tests that move a block actually move it A page takes about twenty-nine of the test filler paragraphs, so the thirty used to leave "room for a few more" spilled onto page 2 by themselves and the block that was meant to move simply started there. Twenty-two leave a few lines free and not enough for the block. Run against gravitypdf, eight of the eleven cases in PageBreakInsideAvoidTest and six of the ten float cases now fail; the rest are controls, or guard the rollback against dropping what it restores. Co-Authored-By: Claude Fable 5.1 --- tests/Mpdf/PageBreakInsideAvoidFloatTest.php | 17 ++++++++++------- tests/Mpdf/PageBreakInsideAvoidTest.php | 5 +++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/Mpdf/PageBreakInsideAvoidFloatTest.php b/tests/Mpdf/PageBreakInsideAvoidFloatTest.php index b0162e5c3..8d4909328 100644 --- a/tests/Mpdf/PageBreakInsideAvoidFloatTest.php +++ b/tests/Mpdf/PageBreakInsideAvoidFloatTest.php @@ -13,6 +13,9 @@ class PageBreakInsideAvoidFloatTest extends \Yoast\PHPUnitPolyfills\TestCases\Te const IMAGE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + /** + * A page takes about twenty-nine of these, so twenty-two leave room for a few more but not for a block + */ private function filler($paragraphs) { return str_repeat('

Filler

', $paragraphs); @@ -57,7 +60,7 @@ public function testAFloatInsideTheBlockIsLaidOutOnce() public function testAFloatInsideTheBlockMovesWithIt() { - $html = $this->filler(30) . $this->kept('

Head

' . $this->floatingText('left', 1) . $this->floatingText('right', 1) . str_repeat('

Kept

', 10)); + $html = $this->filler(22) . $this->kept('

Head

' . $this->floatingText('left', 1) . $this->floatingText('right', 1) . str_repeat('

Kept

', 10)); $pages = $this->pages($this->render($html)); @@ -76,7 +79,7 @@ public function testAFloatInsideTheBlockMovesWithIt() */ public function testTextStillWrapsBesideTheFloatAfterTheMove() { - $html = $this->filler(30) . $this->kept($this->floatingText('left', 3) . str_repeat('

Beside

', 3) . '
' . str_repeat('

Kept

', 8)); + $html = $this->filler(22) . $this->kept($this->floatingText('left', 3) . str_repeat('

Beside

', 3) . '
' . str_repeat('

Kept

', 8)); $pages = $this->pages($this->render($html)); @@ -91,7 +94,7 @@ public function testTextStillWrapsBesideTheFloatAfterTheMove() */ public function testAFloatTallerThanTheSpaceLeftMovesWithTheBlock() { - $html = $this->filler(30) . $this->kept($this->floatingText('left', 20) . str_repeat('

Beside

', 5) . '

Kept

'); + $html = $this->filler(22) . $this->kept($this->floatingText('left', 20) . str_repeat('

Beside

', 5) . '

Kept

'); $pages = $this->pages($this->render($html)); @@ -127,7 +130,7 @@ public function testABlockAFloatMakesTallerThanAPageIsLaidOutOnce() */ public function testFloatedImagesMoveWithTheBlock() { - $html = $this->filler(30) . $this->kept($this->floatingImage('left') . $this->floatingImage('right') . str_repeat('

Beside

', 8) . '
' . str_repeat('

Kept

', 4)); + $html = $this->filler(22) . $this->kept($this->floatingImage('left') . $this->floatingImage('right') . str_repeat('

Beside

', 8) . '
' . str_repeat('

Kept

', 4)); $pages = $this->pages($this->render($html)); @@ -169,7 +172,7 @@ public function testAFloatOpenedBeforeTheBlockIsNotLost() */ public function testAFloatedBlockIsNotKeptTogether() { - $html = $this->filler(30) . '
' . str_repeat('

Kept

', 12) . '

Tail

'; + $html = $this->filler(22) . '
' . str_repeat('

Kept

', 12) . '

Tail

'; $pages = $this->pages($this->render($html)); @@ -183,12 +186,12 @@ public function testAFloatedBlockIsNotKeptTogether() */ public function testAFloatInsideTheBlockInsideColumnsIsLaidOutOnce() { - $html = '' . $this->filler(30) . $this->kept($this->floatingText('left', 3) . str_repeat('

Beside

', 3) . '

Kept

'); + $html = '' . $this->filler(22) . $this->kept($this->floatingText('left', 3) . str_repeat('

Beside

', 3) . '

Kept

'); $pages = $this->pages($this->render($html)); $this->assertCount(1, $pages); - foreach (['Filler' => 30, 'Float' => 3, 'Beside' => 3, 'Kept' => 1, 'Tail' => 1] as $text => $count) { + foreach (['Filler' => 22, 'Float' => 3, 'Beside' => 3, 'Kept' => 1, 'Tail' => 1] as $text => $count) { $this->assertTextCount($count, $text, $pages[0]); } } diff --git a/tests/Mpdf/PageBreakInsideAvoidTest.php b/tests/Mpdf/PageBreakInsideAvoidTest.php index befb41d00..2778015cb 100644 --- a/tests/Mpdf/PageBreakInsideAvoidTest.php +++ b/tests/Mpdf/PageBreakInsideAvoidTest.php @@ -20,11 +20,12 @@ private function filler($paragraphs) } /** - * Thirty paragraphs leave room for a few more on the page, so a block of twelve moves whole to page 2 + * A page takes about twenty-nine of these paragraphs, so twenty-two leave room for a few more but not + * for a block of twelve, which moves whole to page 2 */ private function movingBlock($inner = '', $paragraphs = 12) { - return $this->filler(30) . '
' . $inner . str_repeat('

Kept

', $paragraphs) . '
'; + return $this->filler(22) . '
' . $inner . str_repeat('

Kept

', $paragraphs) . '
'; } private function assertTextCount($expected, $text, $stream, $message = '') From 7e0de7bd79e7ee4bd0d51a332c2159c602b9c9ac Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 9 Sep 2026 13:00:47 +1000 Subject: [PATCH 11/13] Keep a floated block together when it asks to be page-break-inside:avoid on a floated block was ignored: the gate excluded floats, which only preserved what mPDF always did, and lifting the exclusion changed nothing, because a float's close puts the position back to where the float started before the keep-together check looks at it. The block was never seen to have left its page, so it split at the foot of it like any float. Record where the float ended before its close resets the position, and decide the move from that. A floated block that does not fit now moves whole to the next page when it would fit there, with the text that wraps beside it; one taller than a page still breaks, as any block does. Plain floats are unchanged. Nine more float cases cover a right float, left and right floats moving together, one that fits staying while one that does not moves, a border and background painted only where the block ends up, a float inside the block, an image and a table inside it, and a block inside a bordered container that continues across the page. The float snapshot gains a kept floated block and a pair of them with a border, a background, a picture and a float of their own. Co-Authored-By: Claude Fable 5.1 --- src/Tag/BlockTag.php | 16 ++- tests/Mpdf/PageBreakInsideAvoidFloatTest.php | 136 +++++++++++++++++- .../PageBreakAvoidFloatsSnapshotTest.php | 51 ++++++- .../snapshots/page-break-avoid-floats.pdf | Bin 93121 -> 96475 bytes 4 files changed, 189 insertions(+), 14 deletions(-) diff --git a/src/Tag/BlockTag.php b/src/Tag/BlockTag.php index 8b0bf18e9..1a4c0d55c 100644 --- a/src/Tag/BlockTag.php +++ b/src/Tag/BlockTag.php @@ -478,12 +478,11 @@ public function open($attr, &$ahtml, &$ihtml) $properties = $this->cssManager->MergeCSS('BLOCK', $tag, $attr); // mPDF 6 - moved to after page-break-before // mPDF 6 page-break-inside:avoid - // Never for a floated block (the float branches used to cancel it), nor while output is buffered (headers, - // footers, fixed-position blocks): that is not discarded while the block is measured, so it would be written twice - $float = isset($properties['FLOAT']) ? strtoupper($properties['FLOAT']) : ''; + // Not while output is buffered (headers, footers, fixed-position blocks): that is not discarded while the + // block is measured, so it would be written twice if (isset($properties['PAGE-BREAK-INSIDE']) && strtoupper($properties['PAGE-BREAK-INSIDE']) === 'AVOID' && !$this->mpdf->ColActive && !$this->mpdf->keep_block_together && !isset($attr['PAGEBREAKAVOIDCHECKED']) - && $float !== 'LEFT' && $float !== 'RIGHT' && !$this->mpdf->bufferoutput) { + && !$this->mpdf->bufferoutput) { // avoid re-iterating using PAGEBREAKAVOIDCHECKED; set in CloseTag $currblk['keep_block_together'] = 1; $currblk['array_i'] = $ihtml; // mPDF 6 @@ -589,6 +588,7 @@ public function open($attr, &$ahtml, &$ihtml) } /* -- CSS-FLOAT -- */ + $float = isset($properties['FLOAT']) ? strtoupper($properties['FLOAT']) : ''; if ($float === 'RIGHT' && !$this->mpdf->ColActive) { $this->mpdf->blockContext++; @@ -1276,6 +1276,9 @@ public function close(&$ahtml, &$ihtml) } /* -- CSS-FLOAT -- */ + // A float's close puts the position back to where the float started, so where it ended is kept for the + // page-break-inside:avoid check below + $floatEnd = null; if ($this->mpdf->blk[$this->mpdf->blklvl]['float'] === 'R') { // If width not set, here would need to adjust and output buffer $s = $this->mpdf->PrintPageBackgrounds(); @@ -1306,6 +1309,7 @@ public function close(&$ahtml, &$ihtml) 'blockContext' => $this->mpdf->blk[$this->mpdf->blklvl - 1]['blockContext'] ]); + $floatEnd = ['page' => $this->mpdf->page, 'y' => $this->mpdf->y]; $this->mpdf->y = $this->mpdf->blk[$this->mpdf->blklvl]['float_start_y']; $this->mpdf->page = $this->mpdf->blk[$this->mpdf->blklvl]['startpage']; $this->mpdf->ResetMargins(); @@ -1341,6 +1345,7 @@ public function close(&$ahtml, &$ihtml) 'blockContext' => $this->mpdf->blk[$this->mpdf->blklvl - 1]['blockContext'] ]); + $floatEnd = ['page' => $this->mpdf->page, 'y' => $this->mpdf->y]; $this->mpdf->y = $this->mpdf->blk[$this->mpdf->blklvl]['float_start_y']; $this->mpdf->page = $this->mpdf->blk[$this->mpdf->blklvl]['startpage']; $this->mpdf->ResetMargins(); @@ -1366,9 +1371,10 @@ public function close(&$ahtml, &$ihtml) // mPDF 6 page-break-inside:avoid if ($this->mpdf->blk[$this->mpdf->blklvl]['keep_block_together']) { + $end = $floatEnd ?: ['page' => $this->mpdf->page, 'y' => $this->mpdf->y]; $movepage = false; // If page-break-inside:avoid section has broken to new page but fits on one side - then move: - if (($this->mpdf->page - $this->mpdf->kt_p00) == 1 && $this->mpdf->y < $this->mpdf->kt_y00) { + if (($end['page'] - $this->mpdf->kt_p00) == 1 && $end['y'] < $this->mpdf->kt_y00) { $movepage = true; } $this->mpdf->page = $this->mpdf->kt_p00; diff --git a/tests/Mpdf/PageBreakInsideAvoidFloatTest.php b/tests/Mpdf/PageBreakInsideAvoidFloatTest.php index 8d4909328..b7b80a07a 100644 --- a/tests/Mpdf/PageBreakInsideAvoidFloatTest.php +++ b/tests/Mpdf/PageBreakInsideAvoidFloatTest.php @@ -11,6 +11,7 @@ class PageBreakInsideAvoidFloatTest extends \Yoast\PHPUnitPolyfills\TestCases\Te { use PageStreams; + const RED = '1.000 0.000 0.000 rg'; const IMAGE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; /** @@ -167,20 +168,145 @@ public function testAFloatOpenedBeforeTheBlockIsNotLost() $this->assertTextCount(10, 'Kept', $pages[1]); } + private function floatedKept($lines, $side = 'left', $style = '', $inner = '') + { + return '
' . $inner . str_repeat('

Kept

', $lines) . '
'; + } + + private function besideAndTail() + { + return str_repeat('

Beside

', 2) . '

Tail

'; + } + + private function assertMovedWhole(array $pages, array $onPage2) + { + $this->assertCount(2, $pages); + foreach ($onPage2 as $text => $count) { + $this->assertTextCount(0, $text, $pages[0]); + $this->assertTextCount($count, $text, $pages[1]); + } + } + /** - * A floated block is never kept together, as before + * A floated block used to be excluded from keep-together and split at the foot of the page like any float. + * Its close puts the position back to where it started, so the move is decided from where it ended */ - public function testAFloatedBlockIsNotKeptTogether() + public function testAFloatedBlockIsKeptTogether() { - $html = $this->filler(22) . '
' . str_repeat('

Kept

', 12) . '

Tail

'; + $pages = $this->pages($this->render($this->filler(26) . $this->floatedKept(6) . $this->besideAndTail())); - $pages = $this->pages($this->render($html)); + $this->assertCount(2, $pages); + $this->assertTextCount(26, 'Filler', $pages[0]); + $this->assertTextCount(0, 'Kept', $pages[0]); + foreach (['Kept' => 6, 'Beside' => 2, 'Tail' => 1] as $text => $count) { + $this->assertTextCount($count, $text, $pages[1]); + } + } + + public function testAFloatedBlockThatFitsStaysPut() + { + $pages = $this->pages($this->render($this->filler(5) . $this->floatedKept(6) . $this->besideAndTail())); + + $this->assertCount(1, $pages); + foreach (['Kept' => 6, 'Beside' => 2, 'Tail' => 1] as $text => $count) { + $this->assertTextCount($count, $text, $pages[0]); + } + } + + public function testAFloatedBlockTallerThanAPageStillBreaks() + { + $pages = $this->pages($this->render($this->filler(26) . $this->floatedKept(40) . $this->besideAndTail())); + + $this->assertCount(3, $pages); + $this->assertGreaterThan(0, substr_count($pages[0], '(Kept)'), 'A block that fits on no page starts where it is'); + $this->assertTextCount(40, 'Kept', implode('', $pages)); + $this->assertTextCount(1, 'Tail', $pages[2]); + } + + public function testARightFloatedBlockIsKeptTogether() + { + $pages = $this->pages($this->render($this->filler(26) . $this->floatedKept(6, 'right') . $this->besideAndTail())); + + $this->assertMovedWhole($pages, ['Kept' => 6, 'Beside' => 2, 'Tail' => 1]); + } + + public function testLeftAndRightFloatedBlocksBothMove() + { + $right = str_replace('

Kept

', '

Right

', $this->floatedKept(6, 'right')); + + $pages = $this->pages($this->render($this->filler(26) . $this->floatedKept(6) . $right . $this->besideAndTail())); + + $this->assertMovedWhole($pages, ['Kept' => 6, 'Right' => 6, 'Beside' => 2, 'Tail' => 1]); + } + + /** + * Of two floated blocks, only the one that does not fit moves + */ + public function testAFloatedBlockThatFitsStaysWhileOneThatDoesNotMoves() + { + $right = str_replace('

Kept

', '

Right

', $this->floatedKept(12, 'right')); + + $pages = $this->pages($this->render($this->filler(22) . $this->floatedKept(3) . $right . $this->besideAndTail())); $this->assertCount(2, $pages); - $this->assertTextCount(12, 'Kept', implode('', $pages)); + $this->assertTextCount(3, 'Kept', $pages[0]); + $this->assertTextCount(0, 'Right', $pages[0]); + $this->assertTextCount(12, 'Right', $pages[1]); $this->assertTextCount(1, 'Tail', $pages[1]); } + /** + * The border and background are painted where the block ends up, and only there + */ + public function testAFloatedBlockWithABorderAndBackgroundMovesWhole() + { + $html = $this->filler(26) . $this->floatedKept(6, 'left', ' border: 1mm solid #000; background-color: red; padding: 2mm') . $this->besideAndTail(); + + $pages = $this->pages($this->render($html)); + + $this->assertMovedWhole($pages, ['Kept' => 6, 'Tail' => 1]); + $this->assertSame(0, substr_count($pages[0], self::RED)); + $this->assertSame(1, substr_count($pages[1], self::RED)); + $this->assertDoesNotMatchRegularExpression('/^S$/m', $pages[0]); + $this->assertMatchesRegularExpression('/^S$/m', $pages[1]); + } + + public function testAFloatInsideAFloatedBlockMovesWithIt() + { + $inner = '
' . str_repeat('

Inner

', 3) . '
'; + + $pages = $this->pages($this->render($this->filler(26) . $this->floatedKept(6, 'left', '', $inner) . $this->besideAndTail())); + + $this->assertMovedWhole($pages, ['Inner' => 3, 'Kept' => 6, 'Beside' => 2, 'Tail' => 1]); + } + + public function testAnImageAndATableInsideAFloatedBlockMoveWithIt() + { + $inner = '
Cell
'; + + $pages = $this->pages($this->render($this->filler(26) . $this->floatedKept(4, 'left', '', $inner) . $this->besideAndTail())); + + $this->assertMovedWhole($pages, ['Cell' => 1, 'Kept' => 4, 'Tail' => 1]); + $this->assertSame(0, $this->images($pages[0])); + $this->assertSame(1, $this->images($pages[1])); + $this->assertSame(0, substr_count($pages[0], self::RED)); + $this->assertSame(1, substr_count($pages[1], self::RED)); + } + + /** + * The container's border runs to the foot of page 1 and continues on page 2 around the moved block + */ + public function testAFloatedBlockInsideABorderedContainerMovesWhole() + { + $html = '
' . $this->filler(25) . $this->floatedKept(6) . $this->besideAndTail() . '
'; + + $pages = $this->pages($this->render($html)); + + $this->assertMovedWhole($pages, ['Kept' => 6, 'Beside' => 2, 'Tail' => 1]); + $this->assertMatchesRegularExpression('/^S$/m', $pages[0]); + $this->assertMatchesRegularExpression('/^S$/m', $pages[1]); + } + /** * Keep-together is off inside columns, and a float there is unaffected */ diff --git a/tests/Snapshots/PageBreakAvoidFloatsSnapshotTest.php b/tests/Snapshots/PageBreakAvoidFloatsSnapshotTest.php index d1782df5f..d2912708d 100644 --- a/tests/Snapshots/PageBreakAvoidFloatsSnapshotTest.php +++ b/tests/Snapshots/PageBreakAvoidFloatsSnapshotTest.php @@ -3,10 +3,12 @@ namespace Snapshots; /** - * Floats inside page-break-inside:avoid blocks: a text column floated beside wrapped text in a block that - * moves whole; floated images in a block that moves; a float still open when a block starts; and a block a - * float makes taller than a page, which breaks like any other. Before the fix every float inside a moved - * block was written twice, and the float opened before one was lost. + * Floats and page-break-inside:avoid: a text column floated beside wrapped text in a block that moves whole; + * floated images in a block that moves; a float still open when a block starts; a block a float makes taller + * than a page, which breaks like any other; a floated block that is itself kept together; and a pair of + * kept floated blocks with a border, a background, a picture and a float of their own. Before the fix + * every float inside a moved block was written twice, the float opened before one was lost, and a floated + * block split at the foot of the page whatever it asked for. * * @group snapshot */ @@ -27,6 +29,10 @@ public function generatePdf() div.together { page-break-inside: avoid; border: 0.3mm dashed #808080; padding: 3mm; margin-bottom: 4mm; } div.aside { float: left; width: 38%; background-color: #e8e8e8; padding: 2mm; margin: 0 4mm 2mm 0; } div.aside.right { float: right; margin: 0 0 2mm 4mm; } + div.aside.kept { page-break-inside: avoid; border: 0.3mm dashed #808080; } + div.aside.boxed { border: 0.6mm solid #204080; background-color: #dfe8f5; } + div.aside.boxed img { float: left; width: 22mm; margin: 0 3mm 2mm 0; } + div.aside.boxed div.inner { float: right; width: 40%; background-color: #ffffff; padding: 1mm; margin: 0 0 2mm 3mm; } div.clear { clear: both; } img.left { float: left; width: 45mm; margin: 0 4mm 2mm 0; } @@ -96,6 +102,43 @@ public function generatePdf()

The end of the tall block.

+ +

Filler line , before the floated block that is itself kept together.

+ + +
+ +

A floated block kept together, line .

+ +
+

Text beside a floated block that does not fit on the page it starts on, so the block moves whole and + this text moves with it.

+
+ + +

Filler line , before the pair of kept floated blocks.

+ + +
+ +

A kept floated block with a border, a background and a picture, line 1.

+

Line 2.

+

Line 3.

+
+

Line 4, after the picture.

+
+
+

A float inside.

+

A kept floated block on the right, with a float of its own, line 1.

+

Line 2.

+

Line 3.

+
+

Line 4, after the inner float.

+
+

Text between two kept floated blocks that did not fit on the page they started on; both moved whole, + with everything in them, and this text moved with them.

+
+

After every block.

3`9 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 G|Nj838!#mR delta 1725 zcmah|Yfuwc6lSSfxKmX*K}?zNUR^XqvDayHWL1d8&W z)tfimIa%TS>PoMhVDCWK;P|t@^|Z$K*X(WnE2?Y7w0(HOtaS^JLC?w+k3M;4;G*;N z`4{FTk1{Iaoz49d=(0nW$BzE?rzLBx-49Y9pY7_u({lgWGOa4>S;*jg@59A)Z4P6V z<7!L8;q4-2*_!=U*%arMhtt~rXcu%&3rvOIG!%6X&0c;X_PYZDcUq{ppeVFqTRe7u zoC@Y77kM1SCrn-FYZfI6qjS!L&KIp~+MLtdrwR5I3RVLJb}!ghR^OO!^SSBMpy=GO zz*i?qw9Sy$2_;SYiogE0#U; zbKS$JR-*oqBSyO>YR&P(zI+HwNxP)CbseZyq4SP8t1GUpGA}EOD1f@|KIPoPJ$Y8y zwvJr`TT;}ewU<9sDR(yOGVkBpqP-Q4|2WU{mh#-~mEUI#BveHItehFwrTC$xWsjwC zU8H((d&cvK$T&+{`lUY4ni;=$oVgM_DbCBgYZp6kx4d?}huyid_VK;Dkt$_Y*wupt zS3|2`BUzW(IswUgwmSwe}o|AIiAUT7NUS zh9d=w&`eaEWU?4pjN}&k3%vMEH5hYDx_lH6T$e!R4kX=d#;53ukn5Q@8 z7%)H$yHL5D!va6x!h{pKymW#a6#4`KdL$yAYcjG5Ly6v;SInBs7?3(dqK}Yw0W%Wj zo&*Ls$~2V-R}kXGX(JeSAm!v4bGY6Jryu@b?N@9V$69SE;tp{~V&uwbu2G0_F~VrB zUo8%Y!`Ly@AmNe@AwR`bo?vnil#Ik&$wAW5k-im~1Yb_|vNI$cMH(C5S;DCz6x_w> zo{r=UArBlK^0i=)8llJ?RC_swmK}}elGFVht(!J%cZUr3*(UKRixA0Ed4rezAqIi{ zK?xEP`--htvq+b3(6g9KhKf@F2Bo81Q+VvCXnecZ9SPi?z#_mKcQd%<@`rXK-K2Ia zi(p~`N~kCtpU!`gsCc0%N0H4Muo;S2h(iiz_!qtj;Xp=!{TCJ~K%jlG1qlu$37i*V z_`!@AI$|(D%8>Rd3({wmNO{f21aXogToZ5#(7Z@vaTH1OM>DPj0VAbc2Z1<{zS(2xaAnAk>{^iEk*+=( z;Iu0l2J+gtkYOMMq&>bI39&*{oN6+$7{O!s8zxRM=9(~K^h$_R6qr_KkWe(l35u3N w3Q)983iKqQr}PZagKUxka_s*hnZuh@!e*P Date: Wed, 9 Sep 2026 20:11:03 +1000 Subject: [PATCH 12/13] Skip a measured block's own float close, and keep its start in the snapshot A review pass over the whole PR after the floated-block commit. The float close of a block being measured for page-break-inside:avoid did work the unwind then threw away (page backgrounds, float_endpos on the enclosing blocks, the float div, the cursor reset), and the previous commit added bookkeeping to undo one side effect of it. Skipping the close for the block being measured is the general form: every other side-effecting site already stays out of the measuring pass, and leaving the cursor where the float ended is what the check needs. The right and left float branches were identical apart from the side, so they are one branch. Where the block started rides in the same snapshot as everything else the unwind puts back, so kt_y00 and kt_p00 on Mpdf are no longer written; their declarations stay. Mpdf::getStateSnapshot() was tried in place of the curated property list. It passes every keep-together test and changes no snapshot document, but numbers every kept
  • as zero (Issue339Test): the list item's own open() has already advanced the counter when the snapshot is taken. The list stays curated, and the docblock now says so. The test helpers the two page-break classes had in common (filler, the PNG, the image count, assertTextCount) move into the PageStreams trait; the float tests that spelt out "nothing on page 1, all of it on page 2" use assertMovedWhole, and the beside-then-clear fragment is one helper. All 23 snapshot documents are byte-identical to the previous commit's. Co-Authored-By: Claude Fable 5.1 --- src/Tag/BlockTag.php | 60 ++---------- tests/Mpdf/PageBreakInsideAvoidFloatTest.php | 99 +++++++------------- tests/Mpdf/PageBreakInsideAvoidTest.php | 22 +---- tests/Mpdf/PageStreams.php | 29 +++++- 4 files changed, 75 insertions(+), 135 deletions(-) diff --git a/src/Tag/BlockTag.php b/src/Tag/BlockTag.php index 1a4c0d55c..febe3faa0 100644 --- a/src/Tag/BlockTag.php +++ b/src/Tag/BlockTag.php @@ -487,8 +487,6 @@ public function open($attr, &$ahtml, &$ihtml) $currblk['keep_block_together'] = 1; $currblk['array_i'] = $ihtml; // mPDF 6 $currblk['kt_state'] = $this->savePageState(); - $this->mpdf->kt_y00 = $this->mpdf->y; - $this->mpdf->kt_p00 = $this->mpdf->page; $this->mpdf->keep_block_together = 1; } if ($lastbottommargin && !empty($properties['MARGIN-TOP']) && empty($properties['FLOAT'])) { @@ -1276,10 +1274,10 @@ public function close(&$ahtml, &$ihtml) } /* -- CSS-FLOAT -- */ - // A float's close puts the position back to where the float started, so where it ended is kept for the - // page-break-inside:avoid check below - $floatEnd = null; - if ($this->mpdf->blk[$this->mpdf->blklvl]['float'] === 'R') { + // A block measured for page-break-inside:avoid leaves nothing behind, so its own float close is skipped; + // that also leaves the position where the float ended for the check below + $float = $this->mpdf->blk[$this->mpdf->blklvl]['float']; + if (($float === 'R' || $float === 'L') && !$this->mpdf->blk[$this->mpdf->blklvl]['keep_block_together']) { // If width not set, here would need to adjust and output buffer $s = $this->mpdf->PrintPageBackgrounds(); // Writes after the marker so not overwritten later by page background etc. @@ -1297,7 +1295,7 @@ public function close(&$ahtml, &$ihtml) } $this->mpdf->addFloatDiv([ - 'side' => 'R', + 'side' => $float, 'startpage' => $this->mpdf->blk[$this->mpdf->blklvl]['startpage'], 'y0' => $this->mpdf->blk[$this->mpdf->blklvl]['float_start_y'], 'startpos' => $this->mpdf->blk[$this->mpdf->blklvl]['startpage'] * 1000 + $this->mpdf->blk[$this->mpdf->blklvl]['float_start_y'], @@ -1309,43 +1307,6 @@ public function close(&$ahtml, &$ihtml) 'blockContext' => $this->mpdf->blk[$this->mpdf->blklvl - 1]['blockContext'] ]); - $floatEnd = ['page' => $this->mpdf->page, 'y' => $this->mpdf->y]; - $this->mpdf->y = $this->mpdf->blk[$this->mpdf->blklvl]['float_start_y']; - $this->mpdf->page = $this->mpdf->blk[$this->mpdf->blklvl]['startpage']; - $this->mpdf->ResetMargins(); - $this->mpdf->pageoutput[$this->mpdf->page] = []; - } - if ($this->mpdf->blk[$this->mpdf->blklvl]['float'] === 'L') { - // If width not set, here would need to adjust and output buffer - $s = $this->mpdf->PrintPageBackgrounds(); - // Writes after the marker so not overwritten later by page background etc. - $this->mpdf->pages[$this->mpdf->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->mpdf->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->mpdf->pages[$this->mpdf->page]); - $this->mpdf->pageBackgrounds = []; - $this->mpdf->Reset(); - $this->mpdf->pageoutput[$this->mpdf->page] = []; - - for ($i = ($this->mpdf->blklvl - 1); $i >= 0; $i--) { - if (isset($this->mpdf->blk[$i]['float_endpos'])) { - $this->mpdf->blk[$i]['float_endpos'] = max($this->mpdf->blk[$i]['float_endpos'], $this->mpdf->page * 1000 + $this->mpdf->y); - } else { - $this->mpdf->blk[$i]['float_endpos'] = $this->mpdf->page * 1000 + $this->mpdf->y; - } - } - - $this->mpdf->addFloatDiv([ - 'side' => 'L', - 'startpage' => $this->mpdf->blk[$this->mpdf->blklvl]['startpage'], - 'y0' => $this->mpdf->blk[$this->mpdf->blklvl]['float_start_y'], - 'startpos' => $this->mpdf->blk[$this->mpdf->blklvl]['startpage'] * 1000 + $this->mpdf->blk[$this->mpdf->blklvl]['float_start_y'], - 'endpage' => $this->mpdf->page, - 'y1' => $this->mpdf->y, - 'endpos' => $this->mpdf->page * 1000 + $this->mpdf->y, - 'w' => $this->mpdf->blk[$this->mpdf->blklvl]['float_width'], - 'blklvl' => $this->mpdf->blklvl, - 'blockContext' => $this->mpdf->blk[$this->mpdf->blklvl - 1]['blockContext'] - ]); - - $floatEnd = ['page' => $this->mpdf->page, 'y' => $this->mpdf->y]; $this->mpdf->y = $this->mpdf->blk[$this->mpdf->blklvl]['float_start_y']; $this->mpdf->page = $this->mpdf->blk[$this->mpdf->blklvl]['startpage']; $this->mpdf->ResetMargins(); @@ -1371,19 +1332,16 @@ public function close(&$ahtml, &$ihtml) // mPDF 6 page-break-inside:avoid if ($this->mpdf->blk[$this->mpdf->blklvl]['keep_block_together']) { - $end = $floatEnd ?: ['page' => $this->mpdf->page, 'y' => $this->mpdf->y]; + $start = $this->mpdf->blk[$this->mpdf->blklvl]['kt_state']['props']; $movepage = false; // If page-break-inside:avoid section has broken to new page but fits on one side - then move: - if (($end['page'] - $this->mpdf->kt_p00) == 1 && $end['y'] < $this->mpdf->kt_y00) { + if (($this->mpdf->page - $start['page']) == 1 && $this->mpdf->y < $start['y']) { $movepage = true; } - $this->mpdf->page = $this->mpdf->kt_p00; $this->mpdf->keep_block_together = 0; $this->restorePageState($this->mpdf->blk[$this->mpdf->blklvl]['kt_state']); $this->mpdf->pageoutput[$this->mpdf->page] = []; - $this->mpdf->y = $this->mpdf->kt_y00; - $ihtml = $this->mpdf->blk[$this->mpdf->blklvl]['array_i'] - 1; $ahtml[$ihtml + 1] .= ' pagebreakavoidchecked="true";'; // avoid re-iterating; read in OpenTag() @@ -1473,9 +1431,11 @@ public function close(&$ahtml, &$ihtml) /** * What the measuring pass of a page-break-inside:avoid block has to put back when it is unwound; see * mpdf/mpdf#533, #1131, #1805. A curated list rather than Mpdf::getStateSnapshot(), which would also roll back - * the parser position, the font and the block stack + * the list counter the block's own open() has advanced (tests/Issues/Issue339Test), the font and the block stack */ private static $pageState = [ + // Where the block started + 'page', 'y', // What _beginpage() selects for the next page 'HTMLHeader', 'HTMLHeaderE', 'HTMLFooter', 'HTMLFooterE', 'firstPageBoxHeader', 'firstPageBoxHeaderEven', 'firstPageBoxFooter', 'firstPageBoxFooterEven', diff --git a/tests/Mpdf/PageBreakInsideAvoidFloatTest.php b/tests/Mpdf/PageBreakInsideAvoidFloatTest.php index b7b80a07a..f4e1aad2f 100644 --- a/tests/Mpdf/PageBreakInsideAvoidFloatTest.php +++ b/tests/Mpdf/PageBreakInsideAvoidFloatTest.php @@ -12,15 +12,6 @@ class PageBreakInsideAvoidFloatTest extends \Yoast\PHPUnitPolyfills\TestCases\Te use PageStreams; const RED = '1.000 0.000 0.000 rg'; - const IMAGE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; - - /** - * A page takes about twenty-nine of these, so twenty-two leave room for a few more but not for a block - */ - private function filler($paragraphs) - { - return str_repeat('

    Filler

    ', $paragraphs); - } private function kept($inner) { @@ -34,17 +25,31 @@ private function floatingText($side, $lines) private function floatingImage($side) { - return ''; + return ''; } - private function assertTextCount($expected, $text, $stream, $message = '') + private function floatedKept($lines, $side = 'left', $style = '', $inner = '') { - $this->assertSame($expected, substr_count($stream, '(' . $text . ')'), $message ?: "'$text' should appear $expected time(s)"); + return '
    ' . $inner . str_repeat('

    Kept

    ', $lines) . '
    '; } - private function images($stream) + private function beside($lines) { - return preg_match_all('/\/I\d+ Do/', $stream); + return str_repeat('

    Beside

    ', $lines) . '
    '; + } + + private function besideAndTail() + { + return $this->beside(2) . '

    Tail

    '; + } + + private function assertMovedWhole(array $pages, array $onPage2) + { + $this->assertCount(2, $pages); + foreach ($onPage2 as $text => $count) { + $this->assertTextCount(0, $text, $pages[0]); + $this->assertTextCount($count, $text, $pages[1]); + } } public function testAFloatInsideTheBlockIsLaidOutOnce() @@ -63,15 +68,7 @@ public function testAFloatInsideTheBlockMovesWithIt() { $html = $this->filler(22) . $this->kept('

    Head

    ' . $this->floatingText('left', 1) . $this->floatingText('right', 1) . str_repeat('

    Kept

    ', 10)); - $pages = $this->pages($this->render($html)); - - $this->assertCount(2, $pages); - foreach (['Head', 'Float', 'Kept'] as $text) { - $this->assertTextCount(0, $text, $pages[0]); - } - foreach (['Head' => 1, 'Float' => 2, 'Kept' => 10, 'Tail' => 1] as $text => $count) { - $this->assertTextCount($count, $text, $pages[1]); - } + $this->assertMovedWhole($this->pages($this->render($html)), ['Head' => 1, 'Float' => 2, 'Kept' => 10, 'Tail' => 1]); } /** @@ -80,7 +77,7 @@ public function testAFloatInsideTheBlockMovesWithIt() */ public function testTextStillWrapsBesideTheFloatAfterTheMove() { - $html = $this->filler(22) . $this->kept($this->floatingText('left', 3) . str_repeat('

    Beside

    ', 3) . '
    ' . str_repeat('

    Kept

    ', 8)); + $html = $this->filler(22) . $this->kept($this->floatingText('left', 3) . $this->beside(3) . str_repeat('

    Kept

    ', 8)); $pages = $this->pages($this->render($html)); @@ -95,17 +92,9 @@ public function testTextStillWrapsBesideTheFloatAfterTheMove() */ public function testAFloatTallerThanTheSpaceLeftMovesWithTheBlock() { - $html = $this->filler(22) . $this->kept($this->floatingText('left', 20) . str_repeat('

    Beside

    ', 5) . '

    Kept

    '); - - $pages = $this->pages($this->render($html)); + $html = $this->filler(22) . $this->kept($this->floatingText('left', 20) . $this->beside(5) . '

    Kept

    '); - $this->assertCount(2, $pages); - foreach (['Float', 'Beside', 'Kept'] as $text) { - $this->assertTextCount(0, $text, $pages[0]); - } - foreach (['Float' => 20, 'Beside' => 5, 'Kept' => 1, 'Tail' => 1] as $text => $count) { - $this->assertTextCount($count, $text, $pages[1]); - } + $this->assertMovedWhole($this->pages($this->render($html)), ['Float' => 20, 'Beside' => 5, 'Kept' => 1, 'Tail' => 1]); } /** @@ -114,7 +103,7 @@ public function testAFloatTallerThanTheSpaceLeftMovesWithTheBlock() */ public function testABlockAFloatMakesTallerThanAPageIsLaidOutOnce() { - $html = $this->filler(5) . $this->kept($this->floatingText('left', 60) . str_repeat('

    Beside

    ', 5) . '

    Kept

    '); + $html = $this->filler(5) . $this->kept($this->floatingText('left', 60) . $this->beside(5) . '

    Kept

    '); $pages = $this->pages($this->render($html)); $all = implode('', $pages); @@ -131,20 +120,18 @@ public function testABlockAFloatMakesTallerThanAPageIsLaidOutOnce() */ public function testFloatedImagesMoveWithTheBlock() { - $html = $this->filler(22) . $this->kept($this->floatingImage('left') . $this->floatingImage('right') . str_repeat('

    Beside

    ', 8) . '
    ' . str_repeat('

    Kept

    ', 4)); + $html = $this->filler(22) . $this->kept($this->floatingImage('left') . $this->floatingImage('right') . $this->beside(8) . str_repeat('

    Kept

    ', 4)); $pages = $this->pages($this->render($html)); - $this->assertCount(2, $pages); + $this->assertMovedWhole($pages, ['Beside' => 8, 'Kept' => 4]); $this->assertSame(0, $this->images($pages[0])); $this->assertSame(2, $this->images($pages[1])); - $this->assertTextCount(8, 'Beside', $pages[1]); - $this->assertTextCount(4, 'Kept', $pages[1]); } public function testAFloatedImageInABlockThatStaysPutIsDrawnOnce() { - $html = $this->filler(5) . $this->kept($this->floatingImage('left') . str_repeat('

    Beside

    ', 8) . '

    Kept

    '); + $html = $this->filler(5) . $this->kept($this->floatingImage('left') . $this->beside(8) . '

    Kept

    '); $pages = $this->pages($this->render($html)); @@ -168,39 +155,17 @@ public function testAFloatOpenedBeforeTheBlockIsNotLost() $this->assertTextCount(10, 'Kept', $pages[1]); } - private function floatedKept($lines, $side = 'left', $style = '', $inner = '') - { - return '
    ' . $inner . str_repeat('

    Kept

    ', $lines) . '
    '; - } - - private function besideAndTail() - { - return str_repeat('

    Beside

    ', 2) . '

    Tail

    '; - } - - private function assertMovedWhole(array $pages, array $onPage2) - { - $this->assertCount(2, $pages); - foreach ($onPage2 as $text => $count) { - $this->assertTextCount(0, $text, $pages[0]); - $this->assertTextCount($count, $text, $pages[1]); - } - } - /** * A floated block used to be excluded from keep-together and split at the foot of the page like any float. - * Its close puts the position back to where it started, so the move is decided from where it ended + * A float's close puts the position back to where it started, so a block being measured skips its own and + * the move is decided from where the float ended */ public function testAFloatedBlockIsKeptTogether() { $pages = $this->pages($this->render($this->filler(26) . $this->floatedKept(6) . $this->besideAndTail())); - $this->assertCount(2, $pages); + $this->assertMovedWhole($pages, ['Kept' => 6, 'Beside' => 2, 'Tail' => 1]); $this->assertTextCount(26, 'Filler', $pages[0]); - $this->assertTextCount(0, 'Kept', $pages[0]); - foreach (['Kept' => 6, 'Beside' => 2, 'Tail' => 1] as $text => $count) { - $this->assertTextCount($count, $text, $pages[1]); - } } public function testAFloatedBlockThatFitsStaysPut() @@ -282,7 +247,7 @@ public function testAFloatInsideAFloatedBlockMovesWithIt() public function testAnImageAndATableInsideAFloatedBlockMoveWithIt() { - $inner = '
    Cell
    '; + $inner = '
    Cell
    '; $pages = $this->pages($this->render($this->filler(26) . $this->floatedKept(4, 'left', '', $inner) . $this->besideAndTail())); @@ -312,7 +277,7 @@ public function testAFloatedBlockInsideABorderedContainerMovesWhole() */ public function testAFloatInsideTheBlockInsideColumnsIsLaidOutOnce() { - $html = '' . $this->filler(22) . $this->kept($this->floatingText('left', 3) . str_repeat('

    Beside

    ', 3) . '

    Kept

    '); + $html = '' . $this->filler(22) . $this->kept($this->floatingText('left', 3) . $this->beside(3) . '

    Kept

    '); $pages = $this->pages($this->render($html)); diff --git a/tests/Mpdf/PageBreakInsideAvoidTest.php b/tests/Mpdf/PageBreakInsideAvoidTest.php index 2778015cb..625cab066 100644 --- a/tests/Mpdf/PageBreakInsideAvoidTest.php +++ b/tests/Mpdf/PageBreakInsideAvoidTest.php @@ -14,23 +14,12 @@ class PageBreakInsideAvoidTest extends \Yoast\PHPUnitPolyfills\TestCases\TestCas const GREEN = '0.000 0.502 0.000 rg'; const WHITE = '1.000 1.000 1.000 rg'; - private function filler($paragraphs) - { - return str_repeat('

    Filler

    ', $paragraphs); - } - /** - * A page takes about twenty-nine of these paragraphs, so twenty-two leave room for a few more but not - * for a block of twelve, which moves whole to page 2 + * Twenty-two filler paragraphs leave room for a few more but not for the block, which moves whole to page 2 */ - private function movingBlock($inner = '', $paragraphs = 12) - { - return $this->filler(22) . '
    ' . $inner . str_repeat('

    Kept

    ', $paragraphs) . '
    '; - } - - private function assertTextCount($expected, $text, $stream, $message = '') + private function movingBlock() { - $this->assertSame($expected, substr_count($stream, '(' . $text . ')'), $message ?: "'$text' should appear $expected time(s)"); + return $this->filler(22) . '
    ' . str_repeat('

    Kept

    ', 12) . '
    '; } /** @@ -206,13 +195,12 @@ public function testACaptionMovesWithItsKeptTogetherTableWhenKeepWithTableIsOn() */ public function testATallBlockWithPageBreakAfterAvoidDoesNotPushBlankPages() { - $image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; - $html = '

    Before

    After

    '; + $html = '

    Before

    After

    '; $pages = $this->pages($this->render($html)); $this->assertCount(1, $pages); - $this->assertSame(1, preg_match_all('/\/I\d+ Do/', $pages[0])); + $this->assertSame(1, $this->images($pages[0])); $this->assertTextCount(1, 'Before', $pages[0]); $this->assertTextCount(1, 'After', $pages[0]); } diff --git a/tests/Mpdf/PageStreams.php b/tests/Mpdf/PageStreams.php index b513ca1fd..2923e8bac 100644 --- a/tests/Mpdf/PageStreams.php +++ b/tests/Mpdf/PageStreams.php @@ -3,7 +3,8 @@ namespace Mpdf; /** - * Renders a document uncompressed and hands back its page content streams, for asserting on what was drawn + * Renders a document uncompressed and hands back its page content streams, with the fixtures and assertions the + * page-break tests share */ trait PageStreams { @@ -41,4 +42,30 @@ private function pages($pdf) return $matches[1]; } + + /** + * A page takes about twenty-nine of these, so twenty-two leave room for a few more but not for a block + */ + private function filler($paragraphs) + { + return str_repeat('

    Filler

    ', $paragraphs); + } + + /** + * A 5x5 PNG, for an image whose size comes from its style + */ + private function pngImage() + { + return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + } + + private function images($stream) + { + return preg_match_all('/\/I\d+ Do/', $stream); + } + + private function assertTextCount($expected, $text, $stream, $message = '') + { + $this->assertSame($expected, substr_count($stream, '(' . $text . ')'), $message ?: "'$text' should appear $expected time(s)"); + } } From 6ded152060acfd5493cffc4efefb63bcc454d6f0 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 9 Sep 2026 21:27:22 +1000 Subject: [PATCH 13/13] Correct the note on why the list counter stays out of the snapshot The docblock said a whole-object snapshot would roll back a counter the block's own open() had already advanced. The increment sits after the snapshot; what breaks is that open() skips it on the second pass, on the assumption that the unwind leaves the counter as the measuring pass left it. A whole-object restore would need that guard dropped, not the counter kept. Co-Authored-By: Claude Fable 5.1 --- src/Tag/BlockTag.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Tag/BlockTag.php b/src/Tag/BlockTag.php index febe3faa0..66861edcf 100644 --- a/src/Tag/BlockTag.php +++ b/src/Tag/BlockTag.php @@ -1430,8 +1430,9 @@ public function close(&$ahtml, &$ihtml) /** * What the measuring pass of a page-break-inside:avoid block has to put back when it is unwound; see - * mpdf/mpdf#533, #1131, #1805. A curated list rather than Mpdf::getStateSnapshot(), which would also roll back - * the list counter the block's own open() has advanced (tests/Issues/Issue339Test), the font and the block stack + * mpdf/mpdf#533, #1131, #1805. A curated list rather than Mpdf::getStateSnapshot(): the second parse builds on + * what the measuring pass left behind, such as the list counter, which open() advances only on the first pass + * (tests/Issues/Issue339Test) */ private static $pageState = [ // Where the block started