Skip to content

Commit 4bd563b

Browse files
committed
Support Padding, borders, and margin for MulticolRenderer
DEVSIX-7604
1 parent 86b649f commit 4bd563b

17 files changed

+98
-37
lines changed

layout/src/main/java/com/itextpdf/layout/element/MulticolContainer.java

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.layout.element;
2424

25-
import com.itextpdf.layout.renderer.MulticolRenderer;
2625
import com.itextpdf.layout.renderer.IRenderer;
27-
28-
import java.util.Map;
26+
import com.itextpdf.layout.renderer.MulticolRenderer;
2927

3028
/**
3129
* represents a container of the column objects.
@@ -39,17 +37,6 @@ public MulticolContainer() {
3937
super();
4038
}
4139

42-
/**
43-
* Copies all properties of {@link MulticolContainer} to its child elements.
44-
*/
45-
public void copyAllPropertiesToChildren() {
46-
for (final IElement child : this.getChildren()) {
47-
for (final Map.Entry<Integer, Object> entry : this.properties.entrySet()) {
48-
child.setProperty(entry.getKey(), entry.getValue());
49-
}
50-
}
51-
}
52-
5340
@Override
5441
protected IRenderer makeNewRenderer() {
5542
return new MulticolRenderer(this);

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1793,6 +1793,7 @@ protected Rectangle applyMargins(Rectangle rect, UnitValue[] margins, boolean re
17931793

17941794
/**
17951795
* Returns margins of the renderer
1796+
* [0] - top; [1] - right; [2] - bottom; [3] - left
17961797
*
17971798
* @return a {@code float[]} margins of the renderer
17981799
*/
@@ -1802,6 +1803,7 @@ protected UnitValue[] getMargins() {
18021803

18031804
/**
18041805
* Returns paddings of the renderer
1806+
* [0] - top; [1] - right; [2] - bottom; [3] - left
18051807
*
18061808
* @return a {@code float[]} paddings of the renderer
18071809
*/
@@ -1815,7 +1817,7 @@ protected UnitValue[] getPaddings() {
18151817
* @param rect a rectangle paddings will be applied on.
18161818
* @param paddings the paddings to be applied on the given rectangle
18171819
* @param reverse indicates whether paddings will be applied
1818-
* inside (in case of false) or outside (in case of false) the rectangle.
1820+
* inside (in case of false) or outside (in case of true) the rectangle.
18191821
* @return a {@link Rectangle border box} of the renderer
18201822
*/
18211823
protected Rectangle applyPaddings(Rectangle rect, UnitValue[] paddings, boolean reverse) {

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

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ This file is part of the iText (R) project.
2323
package com.itextpdf.layout.renderer;
2424

2525
import com.itextpdf.kernel.geom.Rectangle;
26+
import com.itextpdf.layout.borders.Border;
2627
import com.itextpdf.layout.element.MulticolContainer;
2728
import com.itextpdf.layout.layout.LayoutArea;
2829
import com.itextpdf.layout.layout.LayoutContext;
2930
import com.itextpdf.layout.layout.LayoutResult;
3031
import com.itextpdf.layout.properties.Property;
32+
import com.itextpdf.layout.properties.UnitValue;
3133

3234
import java.util.ArrayList;
3335
import java.util.List;
@@ -38,7 +40,7 @@ This file is part of the iText (R) project.
3840
public class MulticolRenderer extends AbstractRenderer {
3941

4042
private static final int MAX_RELAYOUT_COUNT = 4;
41-
private static final float ZERO_DELTA = 0.0001f;
43+
private static final float ZERO_DELTA = 0.0001F;
4244

4345
private BlockRenderer elementRenderer;
4446
private int columnCount;
@@ -54,22 +56,19 @@ public MulticolRenderer(MulticolContainer modelElement) {
5456
super(modelElement);
5557
}
5658

57-
@Override
58-
public IRenderer getNextRenderer() {
59-
logWarningIfGetNextRendererNotOverridden(MulticolRenderer.class, this.getClass());
60-
return new MulticolRenderer((MulticolContainer) modelElement);
61-
}
62-
6359
/**
6460
* {@inheritDoc}
6561
*/
6662
@Override
6763
public LayoutResult layout(LayoutContext layoutContext) {
68-
((MulticolContainer) this.getModelElement()).copyAllPropertiesToChildren();
6964
this.setProperty(Property.TREAT_AS_CONTINUOUS_CONTAINER, Boolean.TRUE);
70-
final Rectangle initialBBox = layoutContext.getArea().getBBox();
65+
final Rectangle actualBBox = layoutContext.getArea().getBBox().clone();
66+
applyPaddings(actualBBox, false);
67+
applyBorderBox(actualBBox, false);
68+
applyMargins(actualBBox, false);
69+
7170
columnCount = (int) this.<Integer>getProperty(Property.COLUMN_COUNT);
72-
columnWidth = initialBBox.getWidth() / columnCount;
71+
columnWidth = actualBBox.getWidth() / columnCount;
7372
if (this.elementRenderer == null) {
7473
// initialize elementRenderer on first layout when first child represents renderer of element which
7574
// should be layouted in multicol, because on the next layouts this can have multiple children
@@ -85,7 +84,7 @@ public LayoutResult layout(LayoutContext layoutContext) {
8584

8685
approximateHeight = prelayoutResult.getOccupiedArea().getBBox().getHeight() / columnCount;
8786

88-
List<IRenderer> container = balanceContentAndLayoutColumns(layoutContext);
87+
List<IRenderer> container = balanceContentAndLayoutColumns(layoutContext, actualBBox);
8988

9089
this.occupiedArea = calculateContainerOccupiedArea(layoutContext);
9190
this.setChildRenderers(container);
@@ -97,19 +96,38 @@ public LayoutResult layout(LayoutContext layoutContext) {
9796
return result;
9897
}
9998

100-
private List<IRenderer> balanceContentAndLayoutColumns(LayoutContext prelayoutContext) {
99+
@Override
100+
public IRenderer getNextRenderer() {
101+
logWarningIfGetNextRendererNotOverridden(MulticolRenderer.class, this.getClass());
102+
return new MulticolRenderer((MulticolContainer) modelElement);
103+
}
104+
105+
private float safelyRetrieveFloatProperty(int property) {
106+
final Object value = this.<Object>getProperty(property);
107+
if (value instanceof UnitValue) {
108+
return ((UnitValue) value).getValue();
109+
}
110+
if (value instanceof Border) {
111+
return ((Border) value).getWidth();
112+
}
113+
return 0F;
114+
}
115+
116+
private List<IRenderer> balanceContentAndLayoutColumns(LayoutContext prelayoutContext, Rectangle actualBBox) {
101117
Float additionalHeightPerIteration = null;
102-
List<IRenderer> container = new ArrayList<>();
118+
final List<IRenderer> container = new ArrayList<>();
103119
int counter = MAX_RELAYOUT_COUNT;
104120
while (counter-- > 0) {
105-
IRenderer overflowRenderer = layoutColumnsAndReturnOverflowRenderer(prelayoutContext, container);
121+
final IRenderer overflowRenderer = layoutColumnsAndReturnOverflowRenderer(prelayoutContext, container,
122+
actualBBox);
106123
if (overflowRenderer == null) {
107124
return container;
108125
}
109126
if (additionalHeightPerIteration == null) {
110127
LayoutResult overflowResult = overflowRenderer.layout(
111128
new LayoutContext(new LayoutArea(1, new Rectangle(columnWidth, INF))));
112-
additionalHeightPerIteration = overflowResult.getOccupiedArea().getBBox().getHeight() / MAX_RELAYOUT_COUNT;
129+
additionalHeightPerIteration =
130+
overflowResult.getOccupiedArea().getBBox().getHeight() / MAX_RELAYOUT_COUNT;
113131
}
114132
if (Math.abs(additionalHeightPerIteration.floatValue()) <= ZERO_DELTA) {
115133
return container;
@@ -119,10 +137,22 @@ private List<IRenderer> balanceContentAndLayoutColumns(LayoutContext prelayoutCo
119137
return container;
120138
}
121139

140+
122141
private LayoutArea calculateContainerOccupiedArea(LayoutContext layoutContext) {
123142
LayoutArea area = layoutContext.getArea().clone();
124-
area.getBBox().setHeight(approximateHeight);
125-
Rectangle initialBBox = layoutContext.getArea().getBBox();
143+
float totalHeight = approximateHeight;
144+
145+
totalHeight += safelyRetrieveFloatProperty(Property.PADDING_BOTTOM);
146+
totalHeight += safelyRetrieveFloatProperty(Property.PADDING_TOP);
147+
totalHeight += safelyRetrieveFloatProperty(Property.MARGIN_BOTTOM);
148+
totalHeight += safelyRetrieveFloatProperty(Property.MARGIN_TOP);
149+
totalHeight += safelyRetrieveFloatProperty(Property.BORDER_BOTTOM);
150+
totalHeight += safelyRetrieveFloatProperty(Property.BORDER_TOP);
151+
final float TOP_AND_BOTTOM = 2;
152+
totalHeight += safelyRetrieveFloatProperty(Property.BORDER) * TOP_AND_BOTTOM;
153+
154+
area.getBBox().setHeight(totalHeight);
155+
final Rectangle initialBBox = layoutContext.getArea().getBBox();
126156
area.getBBox().setY(initialBBox.getY() + initialBBox.getHeight() - area.getBBox().getHeight());
127157
return area;
128158
}
@@ -134,16 +164,20 @@ private BlockRenderer getElementsRenderer() {
134164
return (BlockRenderer) getChildRenderers().get(0);
135165
}
136166

137-
private IRenderer layoutColumnsAndReturnOverflowRenderer(LayoutContext preLayoutContext, List<IRenderer> container) {
167+
private IRenderer layoutColumnsAndReturnOverflowRenderer(LayoutContext preLayoutContext,
168+
List<IRenderer> container, Rectangle actualBBox) {
138169
container.clear();
139-
Rectangle initialBBox = preLayoutContext.getArea().getBBox();
170+
171+
final Rectangle initialBBox = actualBBox.clone();
140172
IRenderer renderer = elementRenderer;
141173
for (int i = 0; i < columnCount && renderer != null; i++) {
142174
LayoutArea tempArea = preLayoutContext.getArea().clone();
143175
tempArea.getBBox().setWidth(columnWidth);
144176
tempArea.getBBox().setHeight(approximateHeight);
145177
tempArea.getBBox().setX(initialBBox.getX() + columnWidth * i);
146-
tempArea.getBBox().setY(initialBBox.getY() + initialBBox.getHeight() - tempArea.getBBox().getHeight());
178+
tempArea.getBBox().setY(initialBBox.getY() + initialBBox.getHeight() - tempArea.getBBox()
179+
.getHeight());
180+
147181
LayoutContext columnContext = new LayoutContext(tempArea, preLayoutContext.getMarginsCollapseInfo(),
148182
preLayoutContext.getFloatRendererAreas(), preLayoutContext.isClippedHeight());
149183

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

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,13 @@ This file is part of the iText (R) project.
3636
import com.itextpdf.test.ExtendedITextTest;
3737
import com.itextpdf.test.annotations.type.IntegrationTest;
3838

39+
import java.io.IOException;
3940
import java.util.function.Consumer;
4041
import org.junit.Assert;
4142
import org.junit.BeforeClass;
4243
import org.junit.Test;
4344
import org.junit.experimental.categories.Category;
4445

45-
import java.io.IOException;
46-
4746
@Category(IntegrationTest.class)
4847
public class MulticolContainerTest extends ExtendedITextTest {
4948
public static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/layout/MulticolContainerTest/";
@@ -170,6 +169,45 @@ public void continuousColumContainerParagraphAll() throws IOException, Interrupt
170169
});
171170
}
172171

172+
@Test
173+
public void continuousColumContainerParagraphAllChildStart() throws IOException, InterruptedException {
174+
executeTest("continuousColumContainerParagraphAllChildStart", new MulticolContainer(), ctx -> {
175+
ctx.setProperty(Property.COLUMN_COUNT, 3);
176+
ctx.setBackgroundColor(DEFAULT_BACKGROUND_COLOR);
177+
ctx.setBorder(DEFAULT_BORDER);
178+
ctx.setMarginTop(DEFAULT_MARGIN);
179+
ctx.setPaddingTop(DEFAULT_PADDING);
180+
ctx.setMarginBottom(DEFAULT_MARGIN);
181+
ctx.setPaddingBottom(DEFAULT_PADDING);
182+
Paragraph paragraph = new Paragraph(generateLongString(300));
183+
paragraph.setBorder(new SolidBorder(ColorConstants.RED, 2));
184+
paragraph.setMarginTop(200);
185+
paragraph.setPaddingTop(40);
186+
paragraph.setBackgroundColor(ColorConstants.PINK);
187+
ctx.add(paragraph);
188+
});
189+
}
190+
191+
@Test
192+
public void continuousColumContainerParagraphAllChildEnd() throws IOException, InterruptedException {
193+
executeTest("continuousColumContainerParagraphAllChildEnd", new MulticolContainer(), ctx -> {
194+
ctx.setProperty(Property.COLUMN_COUNT, 3);
195+
ctx.setBackgroundColor(DEFAULT_BACKGROUND_COLOR);
196+
ctx.setBorder(DEFAULT_BORDER);
197+
ctx.setMarginTop(DEFAULT_MARGIN);
198+
ctx.setPaddingTop(DEFAULT_PADDING);
199+
ctx.setMarginBottom(DEFAULT_MARGIN);
200+
ctx.setPaddingBottom(DEFAULT_PADDING);
201+
Paragraph paragraph = new Paragraph(generateLongString(300));
202+
paragraph.setBorder(new SolidBorder(ColorConstants.RED, 2));
203+
paragraph.setMarginBottom(200);
204+
paragraph.setPaddingBottom(40);
205+
paragraph.setBackgroundColor(ColorConstants.PINK);
206+
ctx.add(paragraph);
207+
});
208+
}
209+
210+
173211
@Test
174212
public void continuousColumContainerParagraphOverflowShouldShow() throws IOException, InterruptedException {
175213
executeTest("continuousColumContainerParagraphOverflowShouldShow", new MulticolContainer(), ctx -> {

0 commit comments

Comments
 (0)