From 4a279e34c6b393319050a5ab75c58f7103f3c1af Mon Sep 17 00:00:00 2001 From: Edgar Asatryan Date: Tue, 10 Nov 2026 21:29:09 +0400 Subject: [PATCH] Relayout positioned renderers on block split --- .../layout/renderer/BlockRenderer.java | 3 ++ .../layout/renderer/BlockRendererTest.java | 34 ++++++++++++++++++ ...AnchoredToFirstFragmentWhenFloatSplits.pdf | Bin 0 -> 2888 bytes 3 files changed, 37 insertions(+) create mode 100644 layout/src/test/resources/com/itextpdf/layout/BlockRendererTest/cmp_absolutePositionedChildIsAnchoredToFirstFragmentWhenFloatSplits.pdf diff --git a/layout/src/main/java/com/itextpdf/layout/renderer/BlockRenderer.java b/layout/src/main/java/com/itextpdf/layout/renderer/BlockRenderer.java index f1a77fd502..8bcfa077fe 100644 --- a/layout/src/main/java/com/itextpdf/layout/renderer/BlockRenderer.java +++ b/layout/src/main/java/com/itextpdf/layout/renderer/BlockRenderer.java @@ -422,6 +422,9 @@ public LayoutResult layout(LayoutContext layoutContext) { splitRenderer = createSplitRenderer(layoutResult); splitRenderer.childRenderers = new ArrayList<>(childRenderers); replaceSplitRendererKidFloats(waitingFloatsSplitRenderers, splitRenderer); + if (overflowRenderer != null) { + handlePositionedRenderers(layoutResult, splitRenderer, overflowRenderer); + } float usedHeight = occupiedArea.getBBox().getHeight(); if (!includeFloatsInOccupiedArea) { diff --git a/layout/src/test/java/com/itextpdf/layout/renderer/BlockRendererTest.java b/layout/src/test/java/com/itextpdf/layout/renderer/BlockRendererTest.java index 6a5ea6d089..8e95bb7253 100644 --- a/layout/src/test/java/com/itextpdf/layout/renderer/BlockRendererTest.java +++ b/layout/src/test/java/com/itextpdf/layout/renderer/BlockRendererTest.java @@ -299,6 +299,40 @@ public void absolutePositionedChildIsNotDroppedWhenParentSplitsTest() throws Exc Assertions.assertNull(new CompareTool().compareByContent(outFile, cmpFileName, DESTINATION_FOLDER)); } + @Test + public void absolutePositionedChildIsAnchoredToFirstFragmentWhenFloatSplitsTest() throws Exception { + String cmpFileName = SOURCE_FOLDER + "cmp_absolutePositionedChildIsAnchoredToFirstFragmentWhenFloatSplits.pdf"; + String outFile = DESTINATION_FOLDER + "absolutePositionedChildIsAnchoredToFirstFragmentWhenFloatSplits.pdf"; + + try (PdfDocument pdfDoc = new PdfDocument(CompareTool.createTestPdfWriter(outFile)); + Document doc = new Document(pdfDoc)) { + Div container = new Div(); + container.setProperty(Property.POSITION, LayoutPosition.RELATIVE); + + // Float that is taller than a page, so it is split and its remainder overflows to the next page + StringBuilder floatText = new StringBuilder(); + for (int i = 0; i < 150; ++i) { + floatText.append("Float line ").append(i).append(". "); + } + Div floatingDiv = new Div().add(new Paragraph(floatText.toString())); + floatingDiv.setProperty(Property.FLOAT, FloatPropertyValue.LEFT); + floatingDiv.setWidth(100); + + Div positioned = new Div().add(new Paragraph("PositionedMarker")); + positioned.setProperty(Property.POSITION, LayoutPosition.ABSOLUTE); + positioned.setProperty(Property.RIGHT, 6f); + positioned.setProperty(Property.TOP, 6f); + + container.add(floatingDiv); + container.add(new Paragraph("Short text next to the float")); + container.add(positioned); + + doc.add(container); + } + + Assertions.assertNull(new CompareTool().compareByContent(outFile, cmpFileName, DESTINATION_FOLDER)); + } + public static class WrongParentTreeDivRenderer extends DivRenderer { public WrongParentTreeDivRenderer(Div modelElement) { super(modelElement); diff --git a/layout/src/test/resources/com/itextpdf/layout/BlockRendererTest/cmp_absolutePositionedChildIsAnchoredToFirstFragmentWhenFloatSplits.pdf b/layout/src/test/resources/com/itextpdf/layout/BlockRendererTest/cmp_absolutePositionedChildIsAnchoredToFirstFragmentWhenFloatSplits.pdf new file mode 100644 index 0000000000000000000000000000000000000000..faf3df3fe42d28a9da5f19ec0789d43547f3f414 GIT binary patch literal 2888 zcmc(hc~}$I7Qk!3hX%zJ5k)$*BAYNXNhTpFCLo(gSPB#rj4~k;j0uTO!YUx@qgwrZ z#-)miY=YEwkUskGXfwoO|xM_x{c~ zWA5wXW((V~sOFD;YrI2600>YBw@@7%KsSj@h2fx^3{_z+SeQbDfqYCJuG$PB5W?ju zRXB!5P-9ZRKUO8b56#*W%}cIRp{|FT(@Pu`Hn*}8cYd}_E!{=O-`%P&z_-NRp1Jp2 z?b5*^$NnQ9ISD6n8}%4ZZ+^5li(Me3d_m{&qj~AAZ^V}#H;TO#=@H1iJ3`YJv8NmhcXgY^c+Bu)8s#Jp4E4>Q;llZl zk!&c_&DmGqW3Hf*ep-W{El z9B!a>p8l#{OW0l?+;;c*!cx3-d8l5&`|syFW!N|z7+mWF9KGh>V`CL>K67qqiCIa3 z%goi0ZMRbjjv8qK{4dy8smwj+3hMJ)TU>Xpo0G^7>ejhhq-lPBohn#nvG>=d^;*6a>Ran+2SQL8U0nIo*cv*Wr9MSKH*Y;(cL$>b&gK_BK_X8&IL)Jl`@+AeCO`@4j4^Ut+ztQycNGw&1%@ z<0KJpTPT%*F21SSn##iqEKh6O;&WOJUmUsI6r}%QZA4~Iz*7C8wOtuey9ba>=YY4G zgQanfLDzKyUv{jzD3vt+;!S)Ic!nHo_3Sy5Z+fdFK<~~>Zm%?x$>v=t7z|cdZsTla03LHig|YYiqi6BtEVJHE`G}vzV5CeP7<++M4!h@9g|cS*=((n)%q#PQOb6 z#wTt!j(GCq+0*3pI(q4|VuQ0&bWSDdcbz-fzOg>~T9tQ}j58HT)&vWmfF$|&OM!$* z=KqvPI>Pt^kq27JwzNAg{QJX=KAE@Wb$68=#ksSuCOCp~3w7WXSJVR*_$rFenYBFq zn%tK9_b=|JrPXs~yd4qTf#BH#!|@%aX%9w6K3=XG9eGoFaGSP2{+{Xk#_j>-(2ogQ zgLTqU4a~gu80D_WPV1{Zr~k}S_jrFKyKG*ag!*jB-c$a$YHxSGx96HK=dpA^vl3~} z6HW5l&0QJIy8I5EYX)A`M<1_=+Dtin)8+NgD?-X#*5EB598Je~jY zfN+o9=GQ$vrB>7ViS~;~wY;mP#_Os@#f7By$|d`keVcU6jycxlSNRp!%!LkkGe8r{ z*{_qud(1+YbZm}VOgY==bP`FWZ<@V`7@qd|8ke9sIVt4 z=@TkrH}}`FGT{Z8)s*A8aV*U_qbgLU2%pGw^a*8Wq7FE8VHl#( z=rkCzffyDLWMKt*DMbHU!53GEBEvA;QWAj0sPX}41&#r1JGLEU>+kL4>%Yb)0N7yZ zr|LHtjfDi{JM3`nGB8HIY#P$%I4Y65t8dC&^F%gD6ora(YN@I)I1VMx< z1Y=AVhGA?NMGzr_h6q_g3=)bN7$g?ag>Jm~fSC*t_y6yK0Q8WH6#yARexRR1p#tEsp}!;!BXA%HF&~QoTQUS} ZCx-x4sX}qp7&