Skip to content

Commit 1bae5b7

Browse files
author
Eugene Bochilo
committed
Support collapsing margins for flex container
DEVSIX-5137
1 parent 48b3b3d commit 1bae5b7

File tree

12 files changed

+137
-5
lines changed

12 files changed

+137
-5
lines changed

io/src/main/java/com/itextpdf/io/LogMessageConstant.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ public final class LogMessageConstant {
167167
public static final String ORPHANS_CONSTRAINT_VIOLATED = "Orphans constraint violated for paragraph split at page {0}. Min number of orphans: {1}; actual: {2}. \nComment: {3}";
168168
public static final String OUTLINE_DESTINATION_PAGE_NUMBER_IS_OUT_OF_BOUNDS = "Outline destination page number {0} is out of bounds";
169169
public static final String PAGE_TREE_IS_BROKEN_FAILED_TO_RETRIEVE_PAGE = "Page tree is broken. Failed to retrieve page number {0}. Null will be returned.";
170+
public static final String PAGE_WAS_FLUSHED_ACTION_WILL_NOT_BE_PERFORMED =
171+
"Page was flushed. {0} will not be performed.";
170172
public static final String PASSED_PAGE_SHALL_BE_ON_WHICH_CANVAS_WILL_BE_RENDERED = "The page passed to Canvas#enableAutoTagging(PdfPage) method shall be the one on which this canvas will be rendered. However the actual passed PdfPage instance sets not such page. This might lead to creation of malformed PDF document.";
171173
public static final String PATH_KEY_IS_PRESENT_VERTICES_WILL_BE_IGNORED = "Path key is present. Vertices will be ignored";
172174
public static final String PDF_OBJECT_FLUSHING_NOT_PERFORMED = "PdfObject flushing is not performed: PdfDocument is opened in append mode and the object is not marked as modified ( see PdfObject#setModified() ).";

layout/src/main/java/com/itextpdf/layout/renderer/AbstractRenderer.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,11 +1900,11 @@ protected void applyAction(PdfDocument document) {
19001900
}
19011901

19021902
protected void applyLinkAnnotation(PdfDocument document) {
1903+
Logger logger = LoggerFactory.getLogger(AbstractRenderer.class);
19031904
PdfLinkAnnotation linkAnnotation = this.<PdfLinkAnnotation>getProperty(Property.LINK_ANNOTATION);
19041905
if (linkAnnotation != null) {
19051906
int pageNumber = occupiedArea.getPageNumber();
19061907
if (pageNumber < 1 || pageNumber > document.getNumberOfPages()) {
1907-
Logger logger = LoggerFactory.getLogger(AbstractRenderer.class);
19081908
String logMessageArg = "Property.LINK_ANNOTATION, which specifies a link associated with this element content area, see com.itextpdf.layout.element.Link.";
19091909
logger.warn(MessageFormatUtil.format(LogMessageConstant.UNABLE_TO_APPLY_PAGE_DEPENDENT_PROP_UNKNOWN_PAGE_ON_WHICH_ELEMENT_IS_DRAWN, logMessageArg));
19101910
return;
@@ -1918,7 +1918,14 @@ protected void applyLinkAnnotation(PdfDocument document) {
19181918
linkAnnotation.setRectangle(new PdfArray(pdfBBox));
19191919

19201920
PdfPage page = document.getPage(pageNumber);
1921-
page.addAnnotation(linkAnnotation);
1921+
// TODO DEVSIX-1655 This check is necessary because, in some cases, our renderer's hierarchy may contain
1922+
// a renderer from the different page that was already flushed
1923+
if (page.isFlushed()) {
1924+
logger.error(MessageFormatUtil.format(
1925+
LogMessageConstant.PAGE_WAS_FLUSHED_ACTION_WILL_NOT_BE_PERFORMED, "link annotation applying"));
1926+
} else {
1927+
page.addAnnotation(linkAnnotation);
1928+
}
19221929
}
19231930
}
19241931

layout/src/main/java/com/itextpdf/layout/renderer/BlockFormattingContextUtil.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public static boolean isRendererCreateBfc(IRenderer renderer) {
6868
return (renderer instanceof RootRenderer)
6969
|| (renderer instanceof CellRenderer)
7070
|| isInlineBlock(renderer)
71+
|| renderer.getParent() instanceof FlexContainerRenderer
7172
|| FloatingHelper.isRendererFloating(renderer)
7273
|| isAbsolutePosition(renderer)
7374
|| isFixedPosition(renderer)

layout/src/main/java/com/itextpdf/layout/renderer/BlockRenderer.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ This file is part of the iText (R) project.
4848
import com.itextpdf.kernel.geom.AffineTransform;
4949
import com.itextpdf.kernel.geom.Point;
5050
import com.itextpdf.kernel.geom.Rectangle;
51+
import com.itextpdf.kernel.pdf.PdfPage;
5152
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
5253
import com.itextpdf.kernel.pdf.tagutils.TagTreePointer;
5354
import com.itextpdf.layout.borders.Border;
@@ -229,7 +230,7 @@ public LayoutResult layout(LayoutContext layoutContext) {
229230
}
230231

231232
if (marginsCollapsingEnabled) {
232-
childMarginsInfo = marginsCollapseHandler.startChildMarginsHandling(childRenderer, layoutBox);
233+
childMarginsInfo = startChildMarginsHandling(childRenderer, layoutBox, marginsCollapseHandler);
233234
}
234235
Rectangle changedLayoutBox =
235236
recalculateLayoutBoxBeforeChildLayout(layoutBox, childRenderer, areas.get(0).clone());
@@ -465,8 +466,8 @@ public LayoutResult layout(LayoutContext layoutContext) {
465466

466467
@Override
467468
public void draw(DrawContext drawContext) {
469+
Logger logger = LoggerFactory.getLogger(BlockRenderer.class);
468470
if (occupiedArea == null) {
469-
Logger logger = LoggerFactory.getLogger(BlockRenderer.class);
470471
logger.error(MessageFormatUtil.format(LogMessageConstant.OCCUPIED_AREA_HAS_NOT_BEEN_INITIALIZED, "Drawing won't be performed."));
471472
return;
472473
}
@@ -513,7 +514,16 @@ public void draw(DrawContext drawContext) {
513514
if (pageNumber < 1 || pageNumber > drawContext.getDocument().getNumberOfPages()) {
514515
clippedArea = new Rectangle(-INF / 2 , -INF / 2, INF, INF);
515516
} else {
516-
clippedArea = drawContext.getDocument().getPage(pageNumber).getPageSize();
517+
PdfPage page = drawContext.getDocument().getPage(pageNumber);
518+
// TODO DEVSIX-1655 This check is necessary because, in some cases, our renderer's hierarchy may contain
519+
// a renderer from the different page that was already flushed
520+
if (page.isFlushed()) {
521+
logger.error(MessageFormatUtil.format(
522+
LogMessageConstant.PAGE_WAS_FLUSHED_ACTION_WILL_NOT_BE_PERFORMED, "area clipping"));
523+
clippedArea = new Rectangle(-INF / 2 , -INF / 2, INF, INF);
524+
} else {
525+
clippedArea = page.getPageSize();
526+
}
517527
}
518528
Rectangle area = getBorderAreaBBox();
519529
if (overflowXHidden) {
@@ -587,6 +597,11 @@ protected AbstractRenderer createOverflowRenderer(int layoutResult) {
587597
void recalculateOccupiedAreaAfterChildLayout(Rectangle resultBBox, Float blockMaxHeight) {
588598
occupiedArea.setBBox(Rectangle.getCommonRectangle(occupiedArea.getBBox(), resultBBox));
589599
}
600+
601+
MarginsCollapseInfo startChildMarginsHandling(IRenderer childRenderer,
602+
Rectangle layoutBox, MarginsCollapseHandler marginsCollapseHandler) {
603+
return marginsCollapseHandler.startChildMarginsHandling(childRenderer, layoutBox);
604+
}
590605

591606
Rectangle recalculateLayoutBoxBeforeChildLayout(Rectangle layoutBox,
592607
IRenderer childRenderer, Rectangle initialLayoutBox) {

layout/src/main/java/com/itextpdf/layout/renderer/FlexContainerRenderer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ This file is part of the iText (R) project.
4848
import com.itextpdf.layout.element.Div;
4949
import com.itextpdf.layout.layout.LayoutContext;
5050
import com.itextpdf.layout.layout.LayoutResult;
51+
import com.itextpdf.layout.margincollapse.MarginsCollapseHandler;
52+
import com.itextpdf.layout.margincollapse.MarginsCollapseInfo;
5153
import com.itextpdf.layout.minmaxwidth.MinMaxWidth;
5254
import com.itextpdf.layout.minmaxwidth.MinMaxWidthUtils;
5355
import com.itextpdf.layout.property.OverflowPropertyValue;
@@ -268,6 +270,12 @@ void recalculateOccupiedAreaAfterChildLayout(Rectangle resultBBox, Float blockMa
268270
}
269271
}
270272

273+
@Override
274+
MarginsCollapseInfo startChildMarginsHandling(IRenderer childRenderer,
275+
Rectangle layoutBox, MarginsCollapseHandler marginsCollapseHandler) {
276+
return marginsCollapseHandler.startChildMarginsHandling(null, layoutBox);
277+
}
278+
271279
@Override
272280
void decreaseLayoutBoxAfterChildPlacement(Rectangle layoutBox, LayoutResult result, IRenderer childRenderer) {
273281
// TODO DEVSIX-5086 When flex-wrap will be fully supported

layout/src/test/java/com/itextpdf/layout/element/FlexContainerTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,40 @@ public void linesMaxHeightShouldBeRespectedTest() throws IOException, Interrupte
927927
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, "diff"));
928928
}
929929

930+
@Test
931+
public void collapsingMarginsFlexContainerTest() throws IOException, InterruptedException {
932+
String outFileName = destinationFolder + "collapsingMarginsFlexContainerTest" + testNumber + ".pdf";
933+
String cmpFileName = sourceFolder + "cmp_collapsingMarginsFlexContainerTest" + testNumber + ".pdf";
934+
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(outFileName));
935+
936+
Document document = new Document(pdfDocument);
937+
document.setProperty(Property.COLLAPSING_MARGINS, true);
938+
939+
Div flexContainer = createFlexContainer();
940+
flexContainer.setProperty(Property.MARGIN_TOP, UnitValue.createPointValue(50));
941+
flexContainer.setProperty(Property.BORDER, new SolidBorder(2));
942+
flexContainer.setProperty(Property.BACKGROUND, new Background(ColorConstants.LIGHT_GRAY));
943+
944+
Div child1 = createNewDiv();
945+
child1.setBackgroundColor(ColorConstants.CYAN);
946+
child1.setMargin(50);
947+
948+
Div child2 = createNewDiv();
949+
child2.setBackgroundColor(ColorConstants.CYAN);
950+
child2.setMargin(50);
951+
952+
flexContainer.add(child1).add(child2);
953+
954+
Div flexContainersSibling = createNewDiv();
955+
flexContainersSibling.setMarginBottom(40);
956+
957+
document.add(flexContainersSibling).add(flexContainer);
958+
959+
document.close();
960+
961+
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, "diff"));
962+
}
963+
930964
private Div getFlexContainer(OverflowPropertyValue overflowX, Style style) {
931965
FlexContainer flexContainer = createFlexContainer();
932966
flexContainer

layout/src/test/java/com/itextpdf/layout/renderer/AbstractRendererUnitTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ This file is part of the iText (R) project.
4242
*/
4343
package com.itextpdf.layout.renderer;
4444

45+
import com.itextpdf.io.LogMessageConstant;
4546
import com.itextpdf.io.image.ImageDataFactory;
4647
import com.itextpdf.io.source.ByteArrayOutputStream;
4748
import com.itextpdf.kernel.colors.ColorConstants;
@@ -56,6 +57,7 @@ This file is part of the iText (R) project.
5657
import com.itextpdf.kernel.pdf.PdfDocument;
5758
import com.itextpdf.kernel.pdf.PdfName;
5859
import com.itextpdf.kernel.pdf.PdfWriter;
60+
import com.itextpdf.kernel.pdf.annot.PdfLinkAnnotation;
5961
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
6062
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
6163
import com.itextpdf.kernel.pdf.xobject.PdfImageXObject;
@@ -73,6 +75,8 @@ This file is part of the iText (R) project.
7375
import com.itextpdf.layout.property.Property;
7476
import com.itextpdf.layout.property.UnitValue;
7577
import com.itextpdf.test.ExtendedITextTest;
78+
import com.itextpdf.test.annotations.LogMessage;
79+
import com.itextpdf.test.annotations.LogMessages;
7680
import com.itextpdf.test.annotations.type.UnitTest;
7781

7882
import java.util.Arrays;
@@ -621,4 +625,22 @@ public PdfCanvas addXObjectFittedIntoRectangle(PdfXObject xObject, Rectangle rec
621625
renderer.setProperty(Property.BACKGROUND_IMAGE, backgroundImage);
622626
renderer.drawBackground(drawContext);
623627
}
628+
629+
@Test
630+
@LogMessages(messages = @LogMessage(messageTemplate = LogMessageConstant.PAGE_WAS_FLUSHED_ACTION_WILL_NOT_BE_PERFORMED))
631+
public void applyLinkAnnotationFlushedPageTest() {
632+
AbstractRenderer abstractRenderer = new DivRenderer(new Div());
633+
abstractRenderer.occupiedArea = new LayoutArea(1, new Rectangle(100, 100));
634+
635+
abstractRenderer.setProperty(Property.LINK_ANNOTATION, new PdfLinkAnnotation(new Rectangle(0, 0, 0, 0)));
636+
637+
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
638+
pdfDocument.addNewPage();
639+
pdfDocument.getPage(1).flush();
640+
641+
abstractRenderer.applyLinkAnnotation(pdfDocument);
642+
643+
// This test checks that there is log message and there is no NPE so assertions are not required
644+
Assert.assertTrue(true);
645+
}
624646
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.itextpdf.layout.renderer;
2+
3+
import com.itextpdf.io.LogMessageConstant;
4+
import com.itextpdf.io.source.ByteArrayOutputStream;
5+
import com.itextpdf.kernel.geom.Rectangle;
6+
import com.itextpdf.kernel.pdf.PdfDocument;
7+
import com.itextpdf.kernel.pdf.PdfWriter;
8+
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
9+
import com.itextpdf.layout.element.Div;
10+
import com.itextpdf.layout.layout.LayoutArea;
11+
import com.itextpdf.layout.property.OverflowPropertyValue;
12+
import com.itextpdf.layout.property.Property;
13+
import com.itextpdf.test.ExtendedITextTest;
14+
import com.itextpdf.test.annotations.LogMessage;
15+
import com.itextpdf.test.annotations.LogMessages;
16+
import com.itextpdf.test.annotations.type.UnitTest;
17+
import org.junit.Assert;
18+
import org.junit.Test;
19+
import org.junit.experimental.categories.Category;
20+
21+
@Category(UnitTest.class)
22+
public class BlockRendererUnitTest extends ExtendedITextTest {
23+
24+
@Test
25+
@LogMessages(messages = @LogMessage(messageTemplate = LogMessageConstant.PAGE_WAS_FLUSHED_ACTION_WILL_NOT_BE_PERFORMED))
26+
public void clippedAreaFlushedPageTest() {
27+
BlockRenderer blockRenderer = new DivRenderer(new Div());
28+
blockRenderer.setProperty(Property.OVERFLOW_X, OverflowPropertyValue.HIDDEN);
29+
blockRenderer.occupiedArea = new LayoutArea(1, new Rectangle(100, 100));
30+
31+
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
32+
pdfDocument.addNewPage();
33+
PdfCanvas pdfCanvas = new PdfCanvas(pdfDocument.addNewPage());
34+
DrawContext context = new DrawContext(pdfDocument, pdfCanvas);
35+
36+
pdfDocument.getPage(1).flush();
37+
38+
blockRenderer.draw(context);
39+
40+
// This test checks that there is log message and there is no NPE so assertions are not required
41+
Assert.assertTrue(true);
42+
}
43+
}

0 commit comments

Comments
 (0)