Skip to content

Commit 9e78dab

Browse files
committed
Make MulticolRenderer heightAlgo customizable
DEVSIX-7624
1 parent 3938797 commit 9e78dab

File tree

1 file changed

+69
-35
lines changed

1 file changed

+69
-35
lines changed

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

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,9 @@ This file is part of the iText (R) project.
4040
*/
4141
public class MulticolRenderer extends AbstractRenderer {
4242

43-
private static final int MAX_RELAYOUT_COUNT = 4;
4443
private static final float ZERO_DELTA = 0.0001F;
45-
44+
private ColumnHeightCalculator heightCalculator;
4645
private BlockRenderer elementRenderer;
47-
private final HeightEnhancer heightCalculator = new HeightEnhancer();
4846
private int columnCount;
4947
private float columnWidth;
5048
private float approximateHeight;
@@ -56,6 +54,16 @@ public class MulticolRenderer extends AbstractRenderer {
5654
*/
5755
public MulticolRenderer(MulticolContainer modelElement) {
5856
super(modelElement);
57+
setHeightCalculator(new LayoutInInfiniteHeightCalculator());
58+
}
59+
60+
/**
61+
* Sets the height calculator to be used by this renderer.
62+
*
63+
* @param heightCalculator the height calculator to be used by this renderer.
64+
*/
65+
public final void setHeightCalculator(ColumnHeightCalculator heightCalculator) {
66+
this.heightCalculator = heightCalculator;
5967
}
6068

6169
/**
@@ -78,15 +86,8 @@ public LayoutResult layout(LayoutContext layoutContext) {
7886
}
7987
//It is necessary to set parent, because during relayout elementRenderer's parent gets cleaned up
8088
elementRenderer.setParent(this);
81-
LayoutResult prelayoutResult = elementRenderer.layout(
82-
new LayoutContext(new LayoutArea(1, new Rectangle(columnWidth, INF))));
83-
if (prelayoutResult.getStatus() != LayoutResult.FULL) {
84-
return new LayoutResult(LayoutResult.NOTHING, null, null, this, prelayoutResult.getCauseOfNothing());
85-
}
86-
87-
approximateHeight = prelayoutResult.getOccupiedArea().getBBox().getHeight() / columnCount;
8889

89-
MulticolLayoutResult layoutResult = balanceContentAndLayoutColumns(layoutContext, actualBBox);
90+
final MulticolLayoutResult layoutResult = layoutInColumns(layoutContext, actualBBox);
9091

9192
if (layoutResult.getSplitRenderers().isEmpty()) {
9293
return new LayoutResult(LayoutResult.NOTHING, null, null, this, layoutResult.getCauseOfNothing());
@@ -112,6 +113,18 @@ public IRenderer getNextRenderer() {
112113
return new MulticolRenderer((MulticolContainer) modelElement);
113114
}
114115

116+
protected MulticolLayoutResult layoutInColumns(LayoutContext layoutContext, Rectangle actualBBox) {
117+
LayoutResult inifiniteHeighOneColumnLayoutResult = elementRenderer.layout(
118+
new LayoutContext(new LayoutArea(1, new Rectangle(columnWidth, INF))));
119+
if (inifiniteHeighOneColumnLayoutResult.getStatus() != LayoutResult.FULL) {
120+
final MulticolLayoutResult result = new MulticolLayoutResult();
121+
result.setCauseOfNothing(inifiniteHeighOneColumnLayoutResult.getCauseOfNothing());
122+
return result;
123+
}
124+
125+
approximateHeight = inifiniteHeighOneColumnLayoutResult.getOccupiedArea().getBBox().getHeight() / columnCount;
126+
return balanceContentAndLayoutColumns(layoutContext, actualBBox);
127+
}
115128

116129
/**
117130
* Creates a split renderer.
@@ -162,10 +175,11 @@ private float safelyRetrieveFloatProperty(int property) {
162175
return 0F;
163176
}
164177

165-
private MulticolLayoutResult balanceContentAndLayoutColumns(LayoutContext prelayoutContext, Rectangle actualBbox) {
178+
private MulticolLayoutResult balanceContentAndLayoutColumns(LayoutContext prelayoutContext,
179+
Rectangle actualBbox) {
166180
float additionalHeightPerIteration;
167181
MulticolLayoutResult result = new MulticolLayoutResult();
168-
int counter = MAX_RELAYOUT_COUNT + 1;
182+
int counter = heightCalculator.maxAmountOfRelayouts() + 1;
169183
float maxHeight = actualBbox.getHeight();
170184
boolean isLastLayout = false;
171185
while (counter-- > 0) {
@@ -177,7 +191,7 @@ private MulticolLayoutResult balanceContentAndLayoutColumns(LayoutContext prelay
177191
if (result.getOverflowRenderer() == null || isLastLayout) {
178192
return result;
179193
}
180-
additionalHeightPerIteration = heightCalculator.apply(this, result).floatValue();
194+
additionalHeightPerIteration = heightCalculator.getAdditionalHeightOfEachColumn(this, result).floatValue();
181195
if (Math.abs(additionalHeightPerIteration) <= ZERO_DELTA) {
182196
return result;
183197
}
@@ -217,7 +231,8 @@ private BlockRenderer getElementsRenderer() {
217231
return (BlockRenderer) getChildRenderers().get(0);
218232
}
219233

220-
private MulticolLayoutResult layoutColumnsAndReturnOverflowRenderer(LayoutContext preLayoutContext, Rectangle actualBBox) {
234+
private MulticolLayoutResult layoutColumnsAndReturnOverflowRenderer(LayoutContext preLayoutContext,
235+
Rectangle actualBBox) {
221236
MulticolLayoutResult result = new MulticolLayoutResult();
222237
IRenderer renderer = elementRenderer;
223238
for (int i = 0; i < columnCount && renderer != null; i++) {
@@ -244,17 +259,37 @@ private MulticolLayoutResult layoutColumnsAndReturnOverflowRenderer(LayoutContex
244259
}
245260
renderer = tempResultColumn.getOverflowRenderer();
246261
}
247-
result.setOverflowRenderer((AbstractRenderer)renderer);
262+
result.setOverflowRenderer((AbstractRenderer) renderer);
248263
return result;
249264
}
250265

251266

267+
/**
268+
* Interface which used for additional height calculation
269+
*/
270+
public interface ColumnHeightCalculator {
271+
272+
273+
/**
274+
* Calculate height, by which current height of given {@code MulticolRenderer} should be increased so
275+
* {@code MulticolLayoutResult#getOverflowRenderer} could be lauded
276+
*
277+
* @param renderer multicol renderer for which height needs to be increased
278+
* @param result result of one iteration of {@code MulticolRenderer} layouting
279+
*
280+
* @return height by which current height of given multicol renderer should be increased
281+
*/
282+
Float getAdditionalHeightOfEachColumn(MulticolRenderer renderer, MulticolLayoutResult result);
283+
284+
int maxAmountOfRelayouts();
285+
}
286+
252287
/**
253288
* Represents result of one iteration of MulticolRenderer layouting
254289
* It contains split renderers which were lauded on a given height and overflow renderer
255290
* for which height should be increased, so it can be lauded.
256291
*/
257-
private static class MulticolLayoutResult {
292+
public static class MulticolLayoutResult {
258293
private List<IRenderer> splitRenderers = new ArrayList<>();
259294
private AbstractRenderer overflowRenderer;
260295
private IRenderer causeOfNothing;
@@ -267,34 +302,25 @@ public AbstractRenderer getOverflowRenderer() {
267302
return overflowRenderer;
268303
}
269304

270-
public IRenderer getCauseOfNothing() {
271-
return causeOfNothing;
272-
}
273-
274305
public void setOverflowRenderer(AbstractRenderer overflowRenderer) {
275306
this.overflowRenderer = overflowRenderer;
276307
}
277308

309+
public IRenderer getCauseOfNothing() {
310+
return causeOfNothing;
311+
}
312+
278313
public void setCauseOfNothing(IRenderer causeOfNothing) {
279314
this.causeOfNothing = causeOfNothing;
280315
}
281316
}
282317

283-
/**
284-
* Class which used for additional height calculation
285-
*/
286-
private static class HeightEnhancer {
318+
public static class LayoutInInfiniteHeightCalculator implements ColumnHeightCalculator {
319+
320+
protected int maxRelayoutCount = 4;
287321
private Float height = null;
288322

289-
/**
290-
* Calculate height, by which current height of given {@code MulticolRenderer} should be increased so
291-
* {@code MulticolLayoutResult#getOverflowRenderer} could be lauded
292-
*
293-
* @param renderer multicol renderer for which height needs to be increased
294-
* @param result result of one iteration of {@code MulticolRenderer} layouting
295-
* @return height by which current height of given multicol renderer should be increased
296-
*/
297-
public Float apply(MulticolRenderer renderer, MulticolLayoutResult result) {
323+
public Float getAdditionalHeightOfEachColumn(MulticolRenderer renderer, MulticolLayoutResult result) {
298324
if (height != null) {
299325
return height;
300326
}
@@ -303,8 +329,16 @@ public Float apply(MulticolRenderer renderer, MulticolLayoutResult result) {
303329
}
304330
LayoutResult overflowResult = result.getOverflowRenderer().layout(
305331
new LayoutContext(new LayoutArea(1, new Rectangle(renderer.columnWidth, INF))));
306-
height = overflowResult.getOccupiedArea().getBBox().getHeight() / MAX_RELAYOUT_COUNT;
332+
height = overflowResult.getOccupiedArea().getBBox().getHeight() / maxRelayoutCount;
307333
return height;
308334
}
335+
336+
/**
337+
* @return maximum amount of relayouts which can be done by this height enhancer
338+
*/
339+
@Override
340+
public int maxAmountOfRelayouts() {
341+
return maxRelayoutCount;
342+
}
309343
}
310344
}

0 commit comments

Comments
 (0)