@@ -40,11 +40,9 @@ This file is part of the iText (R) project.
4040 */
4141public 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