Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2023 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
package com.itextpdf.layout.renderer;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.canvas.CanvasArtifact;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.borders.Border.Side;
import com.itextpdf.layout.element.MulticolContainer;
import com.itextpdf.layout.exceptions.LayoutExceptionMessageConstant;
import com.itextpdf.layout.layout.LayoutArea;
import com.itextpdf.layout.layout.LayoutContext;
import com.itextpdf.layout.layout.LayoutResult;
import com.itextpdf.layout.properties.ContinuousContainer;
import com.itextpdf.layout.properties.OverflowPropertyValue;
import com.itextpdf.layout.properties.Property;
import com.itextpdf.layout.properties.UnitValue;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
* Represents a renderer for columns.
*/
public class MulticolRenderer extends AbstractRenderer {
private static final float ZERO_DELTA = 0.0001F;
private ColumnHeightCalculator heightCalculator;
private BlockRenderer elementRenderer;
private int columnCount;
private float columnWidth;
private float approximateHeight;
private Float heightFromProperties;
private float columnGap;
private boolean isFirstLayout = true;
/**
* Creates a DivRenderer from its corresponding layout object.
*
* @param modelElement the {@link MulticolContainer} which this object should manage
*/
public MulticolRenderer(MulticolContainer modelElement) {
super(modelElement);
setHeightCalculator(new LayoutInInfiniteHeightCalculator());
}
/**
* Sets the height calculator to be used by this renderer.
*
* @param heightCalculator the height calculator to be used by this renderer.
*/
public final void setHeightCalculator(ColumnHeightCalculator heightCalculator) {
this.heightCalculator = heightCalculator;
}
/**
* {@inheritDoc}
*/
@Override
public LayoutResult layout(LayoutContext layoutContext) {
this.setProperty(Property.TREAT_AS_CONTINUOUS_CONTAINER, Boolean.TRUE);
setOverflowForAllChildren(this);
Rectangle actualBBox = layoutContext.getArea().getBBox().clone();
float originalWidth = actualBBox.getWidth();
applyWidth(actualBBox, originalWidth);
ContinuousContainer.setupContinuousContainerIfNeeded(this);
applyPaddings(actualBBox, false);
applyBorderBox(actualBBox, false);
applyMargins(actualBBox, false);
calculateColumnCountAndWidth(actualBBox.getWidth());
heightFromProperties = determineHeight(actualBBox);
if (this.elementRenderer == null) {
// initialize elementRenderer on first layout when first child represents renderer of element which
// should be layouted in multicol, because on the next layouts this can have multiple children
elementRenderer = getElementsRenderer();
}
//It is necessary to set parent, because during relayout elementRenderer's parent gets cleaned up
elementRenderer.setParent(this);
final MulticolLayoutResult layoutResult = layoutInColumns(layoutContext, actualBBox);
if (layoutResult.getSplitRenderers().isEmpty()) {
return new LayoutResult(LayoutResult.NOTHING, null, null, this, layoutResult.getCauseOfNothing());
} else if (layoutResult.getOverflowRenderer() == null) {
final ContinuousContainer continuousContainer = this.getProperty(
Property.TREAT_AS_CONTINUOUS_CONTAINER_RESULT);
if (continuousContainer != null) {
continuousContainer.reApplyProperties(this);
}
this.childRenderers.clear();
addAllChildRenderers(layoutResult.getSplitRenderers());
this.occupiedArea = calculateContainerOccupiedArea(layoutContext, true);
return new LayoutResult(LayoutResult.FULL, this.occupiedArea, this, null);
} else {
this.occupiedArea = calculateContainerOccupiedArea(layoutContext, false);
return new LayoutResult(LayoutResult.PARTIAL, this.occupiedArea,
createSplitRenderer(layoutResult.getSplitRenderers()),
createOverflowRenderer(layoutResult.getOverflowRenderer()));
}
}
/**
* {@inheritDoc}
*/
@Override
public IRenderer getNextRenderer() {
logWarningIfGetNextRendererNotOverridden(MulticolRenderer.class, this.getClass());
return new MulticolRenderer((MulticolContainer) modelElement);
}
/**
* Performs the drawing operation for the border of this renderer, if
* defined by any of the {@link Property#BORDER} values in either the layout
* element or this {@link IRenderer} itself.
*
* @param drawContext the context (canvas, document, etc) of this drawing operation.
*/
@Override
public void drawBorder(DrawContext drawContext) {
super.drawBorder(drawContext);
Rectangle borderRect = applyMargins(occupiedArea.getBBox().clone(), getMargins(), false);
boolean isAreaClipped = clipBorderArea(drawContext, borderRect);
Border gap = this.getProperty(Property.COLUMN_GAP_BORDER);
if (getChildRenderers().isEmpty() || gap == null || gap.getWidth() <= ZERO_DELTA) {
return;
}
drawTaggedWhenNeeded(drawContext, canvas -> {
for (int i = 0; i < getChildRenderers().size() - 1; ++i) {
Rectangle columnBBox = getChildRenderers().get(i).getOccupiedArea().getBBox();
Rectangle columnSpaceBBox = new Rectangle(columnBBox.getX() + columnBBox.getWidth(), columnBBox.getY(),
columnGap, columnBBox.getHeight());
float x1 = columnSpaceBBox.getX() + columnSpaceBBox.getWidth() / 2 + gap.getWidth() / 2;
float y1 = columnSpaceBBox.getY();
float y2 = columnSpaceBBox.getY() + columnSpaceBBox.getHeight();
gap.draw(canvas, x1, y1, x1, y2, Side.RIGHT, 0, 0);
}
if (isAreaClipped) {
drawContext.getCanvas().restoreState();
}
});
}
protected MulticolLayoutResult layoutInColumns(LayoutContext layoutContext, Rectangle actualBBox) {
LayoutResult inifiniteHeighOneColumnLayoutResult = elementRenderer.layout(
new LayoutContext(new LayoutArea(1, new Rectangle(columnWidth, INF))));
if (inifiniteHeighOneColumnLayoutResult.getStatus() != LayoutResult.FULL) {
final MulticolLayoutResult result = new MulticolLayoutResult();
result.setCauseOfNothing(inifiniteHeighOneColumnLayoutResult.getCauseOfNothing());
return result;
}
approximateHeight = inifiniteHeighOneColumnLayoutResult.getOccupiedArea().getBBox().getHeight() / columnCount;
return balanceContentAndLayoutColumns(layoutContext, actualBBox);
}
/**
* Creates a split renderer.
*
* @param children children of the split renderer
*
* @return a new {@link AbstractRenderer} instance
*/
protected AbstractRenderer createSplitRenderer(List children) {
AbstractRenderer splitRenderer = (AbstractRenderer) getNextRenderer();
splitRenderer.parent = parent;
splitRenderer.modelElement = modelElement;
splitRenderer.occupiedArea = occupiedArea;
splitRenderer.isLastRendererForModelElement = false;
splitRenderer.setChildRenderers(children);
splitRenderer.addAllProperties(getOwnProperties());
ContinuousContainer.setupContinuousContainerIfNeeded(splitRenderer);
return splitRenderer;
}
/**
* Creates an overflow renderer.
*
* @param overflowedContentRenderer an overflowed content renderer
*
* @return a new {@link AbstractRenderer} instance
*/
protected AbstractRenderer createOverflowRenderer(IRenderer overflowedContentRenderer) {
MulticolRenderer overflowRenderer = (MulticolRenderer) getNextRenderer();
overflowRenderer.isFirstLayout = false;
overflowRenderer.parent = parent;
overflowRenderer.modelElement = modelElement;
overflowRenderer.addAllProperties(getOwnProperties());
List children = new ArrayList<>(1);
children.add(overflowedContentRenderer);
overflowRenderer.setChildRenderers(children);
ContinuousContainer.clearPropertiesFromOverFlowRenderer(overflowRenderer);
return overflowRenderer;
}
private void setOverflowForAllChildren(IRenderer renderer) {
if (renderer == null) {
return;
}
renderer.setProperty(Property.OVERFLOW_X, OverflowPropertyValue.VISIBLE);
for (IRenderer child : renderer.getChildRenderers()) {
setOverflowForAllChildren(child);
}
}
private void drawTaggedWhenNeeded(DrawContext drawContext, Consumer action) {
PdfCanvas canvas = drawContext.getCanvas();
if (drawContext.isTaggingEnabled()) {
canvas.openTag(new CanvasArtifact());
}
action.accept(canvas);
if (drawContext.isTaggingEnabled()) {
canvas.closeTag();
}
}
private void applyWidth(Rectangle parentBbox, float originalWidth) {
final Float blockWidth = retrieveWidth(originalWidth);
if (blockWidth != null) {
parentBbox.setWidth((float) blockWidth);
} else {
final Float minWidth = retrieveMinWidth(parentBbox.getWidth());
if (minWidth != null && minWidth > parentBbox.getWidth()) {
parentBbox.setWidth((float) minWidth);
}
}
}
private Float determineHeight(Rectangle parentBBox) {
Float height = retrieveHeight();
final Float minHeight = retrieveMinHeight();
final Float maxHeight = retrieveMaxHeight();
if (height == null || (minHeight != null && height < minHeight)) {
if ((minHeight != null) && parentBBox.getHeight() < minHeight) {
height = minHeight;
}
}
if (height != null && maxHeight != null && height > maxHeight) {
height = maxHeight;
}
return height;
}
private void recalculateHeightWidthAfterLayouting(Rectangle parentBBox, boolean isFull) {
Float height = determineHeight(parentBBox);
if (height != null) {
height = updateOccupiedHeight((float) height, isFull);
float heightDelta = parentBBox.getHeight() - (float) height;
parentBBox.moveUp(heightDelta);
parentBBox.setHeight((float) height);
}
applyWidth(parentBBox, parentBBox.getWidth());
}
private float safelyRetrieveFloatProperty(int property) {
final Object value = this.