Skip to content

Commit 5daf392

Browse files
committed
Merge branch 'feature/overflow' into develop
2 parents 79e118a + a4ba6c2 commit 5daf392

20 files changed

+517
-93
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ This file is part of the iText (R) project.
4444
package com.itextpdf.layout.element;
4545

4646
import com.itextpdf.kernel.pdf.tagutils.IAccessibleElement;
47+
import com.itextpdf.layout.property.OverflowPropertyValue;
4748
import com.itextpdf.layout.property.Property;
4849
import com.itextpdf.layout.property.VerticalAlignment;
4950

@@ -63,6 +64,18 @@ public abstract class BlockElement<T extends IElement> extends AbstractElement<T
6364
protected BlockElement() {
6465
}
6566

67+
@Override
68+
public <T1> T1 getDefaultProperty(int property) {
69+
switch (property) {
70+
case Property.OVERFLOW:
71+
case Property.OVERFLOW_X:
72+
case Property.OVERFLOW_Y:
73+
return (T1) (Object) OverflowPropertyValue.FIT;
74+
default:
75+
return super.<T1>getDefaultProperty(property);
76+
}
77+
}
78+
6679
/**
6780
* Gets the current left margin width of the element.
6881
*

layout/src/main/java/com/itextpdf/layout/layout/LayoutArea.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ This file is part of the iText (R) project.
4545

4646
import com.itextpdf.io.util.HashCode;
4747
import com.itextpdf.kernel.geom.Rectangle;
48+
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
49+
import com.itextpdf.layout.border.Border;
4850
import com.itextpdf.layout.renderer.AbstractRenderer;
4951

5052
import com.itextpdf.io.util.MessageFormatUtil;
@@ -64,7 +66,9 @@ public class LayoutArea {
6466
protected Rectangle bBox;
6567
/**
6668
* Indicates whether the area already has some placed content or not.
69+
* @deprecated Will be removed in 7.1.0.
6770
*/
71+
@Deprecated
6872
protected boolean emptyArea = true;
6973

7074
/**
@@ -107,14 +111,18 @@ public void setBBox(Rectangle bbox) {
107111
* Indicates whether the area already has some placed content or not.
108112
*
109113
* @return whether the area is empty or not
114+
* @deprecated Will be removed in 7.1.0.
110115
*/
116+
@Deprecated
111117
public boolean isEmptyArea() {
112118
return emptyArea;
113119
}
114120

115121
/**
116122
* Defines whether the area already has some placed content or not.
123+
* @deprecated Will be removed in 7.1.0.
117124
*/
125+
@Deprecated
118126
public void setEmptyArea(boolean emptyArea) {
119127
this.emptyArea = emptyArea;
120128
}

layout/src/main/java/com/itextpdf/layout/layout/LayoutContext.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public class LayoutContext {
6363

6464
protected List<Rectangle> floatRendererAreas = new ArrayList<>();
6565

66+
/**
67+
* Indicates whether the height is clipped or not.
68+
*/
69+
protected boolean clippedHeight = false;
70+
6671
public LayoutContext(LayoutArea area) {
6772
this.area = area;
6873
}
@@ -79,6 +84,19 @@ public LayoutContext(LayoutArea area, MarginsCollapseInfo marginsCollapseInfo, L
7984
}
8085
}
8186

87+
public LayoutContext(LayoutArea area, boolean clippedHeight) {
88+
this(area);
89+
this.clippedHeight = clippedHeight;
90+
}
91+
92+
public LayoutContext(LayoutArea area, MarginsCollapseInfo marginsCollapseInfo, List<Rectangle> floatedRendererAreas, boolean clippedHeight) {
93+
this(area, marginsCollapseInfo);
94+
if (floatedRendererAreas != null) {
95+
this.floatRendererAreas = floatedRendererAreas;
96+
}
97+
this.clippedHeight = clippedHeight;
98+
}
99+
82100
/**
83101
* Gets the {@link LayoutArea area} the content to be placed on.
84102
*
@@ -96,6 +114,23 @@ public List<Rectangle> getFloatRendererAreas() {
96114
return floatRendererAreas;
97115
}
98116

117+
/**
118+
* Indicates whether the layout area's height is clipped or not.
119+
*
120+
* @return whether the layout area's height is clipped or not.
121+
*/
122+
public boolean isClippedHeight() {
123+
return clippedHeight;
124+
}
125+
126+
/**
127+
* Defines whether the layout area's height is clipped or not.
128+
*/
129+
public void setClippedHeight(boolean clippedHeight) {
130+
this.clippedHeight = clippedHeight;
131+
}
132+
133+
99134
/**
100135
* {@inheritDoc}
101136
*/
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.itextpdf.layout.property;
2+
3+
/**
4+
* The possible values for the type of overflow of {@link
5+
* com.itextpdf.layout.element.BlockElement}.
6+
*/
7+
public enum OverflowPropertyValue {
8+
FIT,
9+
VISIBLE,
10+
HIDDEN
11+
}

layout/src/main/java/com/itextpdf/layout/property/Property.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ private Property() {
139139
public static final int MIN_HEIGHT = 85;
140140

141141
public static final int OPACITY = 92;
142+
public static final int OVERFLOW = 102;
143+
public static final int OVERFLOW_X = 103;
144+
public static final int OVERFLOW_Y = 104;
142145
public static final int PADDING_BOTTOM = 47;
143146
public static final int PADDING_LEFT = 48;
144147
public static final int PADDING_RIGHT = 49;

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,18 +1327,20 @@ protected void alignChildHorizontally(IRenderer childRenderer, Rectangle current
13271327
HorizontalAlignment horizontalAlignment = childRenderer.<HorizontalAlignment>getProperty(Property.HORIZONTAL_ALIGNMENT);
13281328
if (horizontalAlignment != null && horizontalAlignment != HorizontalAlignment.LEFT) {
13291329
float freeSpace = availableWidth - childRenderer.getOccupiedArea().getBBox().getWidth();
1330-
try {
1331-
switch (horizontalAlignment) {
1332-
case RIGHT:
1333-
childRenderer.move(freeSpace, 0);
1334-
break;
1335-
case CENTER:
1336-
childRenderer.move(freeSpace / 2, 0);
1337-
break;
1330+
if (freeSpace > 0) {
1331+
try {
1332+
switch (horizontalAlignment) {
1333+
case RIGHT:
1334+
childRenderer.move(freeSpace, 0);
1335+
break;
1336+
case CENTER:
1337+
childRenderer.move(freeSpace / 2, 0);
1338+
break;
1339+
}
1340+
} catch (NullPointerException npe) {
1341+
Logger logger = LoggerFactory.getLogger(AbstractRenderer.class);
1342+
logger.error(MessageFormatUtil.format(LogMessageConstant.OCCUPIED_AREA_HAS_NOT_BEEN_INITIALIZED, "Some of the children might not end up aligned horizontally."));
13381343
}
1339-
} catch (Exception npe) {
1340-
Logger logger = LoggerFactory.getLogger(AbstractRenderer.class);
1341-
logger.error(MessageFormatUtil.format(LogMessageConstant.OCCUPIED_AREA_HAS_NOT_BEEN_INITIALIZED, "Some of the children might not end up aligned horizontally."));
13421344
}
13431345
}
13441346
}

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

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ This file is part of the iText (R) project.
6868
import com.itextpdf.layout.minmaxwidth.MinMaxWidthUtils;
6969
import com.itextpdf.layout.property.AreaBreakType;
7070
import com.itextpdf.layout.property.FloatPropertyValue;
71+
import com.itextpdf.layout.property.OverflowPropertyValue;
7172
import com.itextpdf.layout.property.Property;
7273
import com.itextpdf.layout.property.UnitValue;
7374
import com.itextpdf.layout.property.VerticalAlignment;
@@ -89,6 +90,7 @@ protected BlockRenderer(IElement modelElement) {
8990
public LayoutResult layout(LayoutContext layoutContext) {
9091
overrideHeightProperties();
9192
boolean wasHeightClipped = false;
93+
boolean wasParentsHeightClipped = layoutContext.isClippedHeight();
9294
int pageNumber = layoutContext.getArea().getPageNumber();
9395

9496
boolean isPositioned = isPositioned();
@@ -130,7 +132,11 @@ public LayoutResult layout(LayoutContext layoutContext) {
130132
float[] paddings = getPaddings();
131133
applyBordersPaddingsMargins(parentBBox, borders, paddings);
132134

133-
if (blockWidth != null && (blockWidth < parentBBox.getWidth() || isPositioned || rotation != null)) {
135+
OverflowPropertyValue overflowX = this.<OverflowPropertyValue>getProperty(Property.OVERFLOW_X);
136+
Float blockMaxHeight = retrieveMaxHeight();
137+
OverflowPropertyValue overflowY = (null == blockMaxHeight || blockMaxHeight > parentBBox.getHeight()) && !wasParentsHeightClipped ? OverflowPropertyValue.FIT : this.<OverflowPropertyValue>getProperty(Property.OVERFLOW_Y);
138+
139+
if (blockWidth != null && (blockWidth < parentBBox.getWidth() || isPositioned || rotation != null || (null != overflowX && !OverflowPropertyValue.FIT.equals(overflowX)))) {
134140
// TODO DEVSIX-1174
135141
UnitValue widthVal = this.<UnitValue>getProperty(Property.WIDTH);
136142
if (widthVal != null && widthVal.isPercentValue() && widthVal.getValue() == 100) {
@@ -139,15 +145,16 @@ public LayoutResult layout(LayoutContext layoutContext) {
139145
}
140146
}
141147

142-
Float blockMaxHeight = retrieveMaxHeight();
143-
if (!isFixedLayout() && null != blockMaxHeight && blockMaxHeight < parentBBox.getHeight()
148+
if (!isFixedLayout() && null != blockMaxHeight && (blockMaxHeight < parentBBox.getHeight() || (null != overflowY && !OverflowPropertyValue.FIT.equals(overflowY)))
144149
&& !Boolean.TRUE.equals(getPropertyAsBoolean(Property.FORCED_PLACEMENT))) {
150+
if (blockMaxHeight < parentBBox.getHeight()) {
151+
wasHeightClipped = true;
152+
}
145153
float heightDelta = parentBBox.getHeight() - (float) blockMaxHeight;
146154
if (marginsCollapsingEnabled && !isCellRenderer) {
147155
marginsCollapseHandler.processFixedHeightAdjustment(heightDelta);
148156
}
149157
parentBBox.moveUp(heightDelta).setHeight((float) blockMaxHeight);
150-
wasHeightClipped = true;
151158
}
152159

153160
List<Rectangle> areas;
@@ -174,7 +181,7 @@ public LayoutResult layout(LayoutContext layoutContext) {
174181
if (marginsCollapsingEnabled) {
175182
childMarginsInfo = marginsCollapseHandler.startChildMarginsHandling(childRenderer, layoutBox);
176183
}
177-
while ((result = childRenderer.setParent(this).layout(new LayoutContext(new LayoutArea(pageNumber, layoutBox), childMarginsInfo, floatRendererAreas)))
184+
while ((result = childRenderer.setParent(this).layout(new LayoutContext(new LayoutArea(pageNumber, layoutBox), childMarginsInfo, floatRendererAreas, wasHeightClipped || wasParentsHeightClipped)))
178185
.getStatus() != LayoutResult.FULL) {
179186
if (marginsCollapsingEnabled) {
180187
if (result.getStatus() != LayoutResult.NOTHING) {
@@ -189,6 +196,9 @@ public LayoutResult layout(LayoutContext layoutContext) {
189196
occupiedArea.setBBox(Rectangle.getCommonRectangle(occupiedArea.getBBox(), layoutBox));
190197
} else if (result.getOccupiedArea() != null && result.getStatus() != LayoutResult.NOTHING) {
191198
occupiedArea.setBBox(Rectangle.getCommonRectangle(occupiedArea.getBBox(), result.getOccupiedArea().getBBox()));
199+
if (occupiedArea.getBBox().getWidth() > layoutBox.getWidth() && !(null == overflowX || OverflowPropertyValue.FIT.equals(overflowX))) {
200+
occupiedArea.getBBox().setWidth(layoutBox.getWidth());
201+
}
192202
}
193203

194204
if (FloatingHelper.isRendererFloating(this) || isCellRenderer) {
@@ -350,6 +360,9 @@ public LayoutResult layout(LayoutContext layoutContext) {
350360
if (result.getOccupiedArea() != null) {
351361
if (!FloatingHelper.isRendererFloating(childRenderer)) { // this check is needed only if margins collapsing is enabled
352362
occupiedArea.setBBox(Rectangle.getCommonRectangle(occupiedArea.getBBox(), result.getOccupiedArea().getBBox()));
363+
if (occupiedArea.getBBox().getWidth() > layoutBox.getWidth() && !(null == overflowX || OverflowPropertyValue.FIT.equals(overflowX))) {
364+
occupiedArea.getBBox().setWidth(layoutBox.getWidth());
365+
}
353366
}
354367
}
355368
if (marginsCollapsingEnabled) {
@@ -370,6 +383,7 @@ public LayoutResult layout(LayoutContext layoutContext) {
370383
causeOfNothing = result.getCauseOfNothing();
371384
}
372385
}
386+
float overflowPartHeight = getOverflowPartHeight(overflowY, layoutBox);
373387
if (marginsCollapsingEnabled && !isCellRenderer) {
374388
marginsCollapseHandler.endMarginsCollapse(layoutBox);
375389
}
@@ -388,7 +402,10 @@ public LayoutResult layout(LayoutContext layoutContext) {
388402
if (isFixedLayout()) {
389403
occupiedArea.getBBox().moveDown((float) blockMinHeight - occupiedArea.getBBox().getHeight()).setHeight((float) blockMinHeight);
390404
} else {
391-
float blockBottom = Math.max(occupiedArea.getBBox().getBottom() - ((float) blockMinHeight - occupiedArea.getBBox().getHeight()), layoutBox.getBottom());
405+
float blockBottom = occupiedArea.getBBox().getBottom() - ((float) blockMinHeight - occupiedArea.getBBox().getHeight());
406+
if ((null == overflowY || OverflowPropertyValue.FIT.equals(overflowY)) && blockBottom < layoutBox.getBottom()) {
407+
blockBottom = layoutBox.getBottom();
408+
}
392409
occupiedArea.getBBox()
393410
.increaseHeight(occupiedArea.getBBox().getBottom() - blockBottom)
394411
.setY(blockBottom);
@@ -444,6 +461,10 @@ public LayoutResult layout(LayoutContext layoutContext) {
444461
}
445462
applyVerticalAlignment();
446463

464+
if (wasHeightClipped) {
465+
occupiedArea.getBBox().moveUp(overflowPartHeight).decreaseHeight(overflowPartHeight);
466+
}
467+
447468
FloatingHelper.removeFloatsAboveRendererBottom(floatRendererAreas, this);
448469
LayoutArea editedArea = FloatingHelper.adjustResultOccupiedAreaForFloatAndClear(this, layoutContext.getFloatRendererAreas(), layoutContext.getArea().getBBox(), clearHeightCorrection, marginsCollapsingEnabled);
449470

@@ -535,8 +556,29 @@ public void draw(DrawContext drawContext) {
535556

536557
drawBackground(drawContext);
537558
drawBorder(drawContext);
559+
560+
OverflowPropertyValue overflowX = this.<OverflowPropertyValue>getProperty(Property.OVERFLOW_X);
561+
OverflowPropertyValue overflowY = this.<OverflowPropertyValue>getProperty(Property.OVERFLOW_Y);
562+
boolean processOverflow = OverflowPropertyValue.HIDDEN.equals(overflowX) || OverflowPropertyValue.HIDDEN.equals(overflowY);
563+
564+
if (processOverflow) {
565+
drawContext.getCanvas().saveState();
566+
Rectangle clippedArea = drawContext.getDocument().getPage(occupiedArea.getPageNumber()).getPageSize();
567+
Rectangle area = getBorderAreaBBox();
568+
if (OverflowPropertyValue.HIDDEN.equals(overflowX)) {
569+
clippedArea.setX(area.getX()).setWidth(area.getWidth());
570+
}
571+
if (OverflowPropertyValue.HIDDEN.equals(overflowY)) {
572+
clippedArea.setY(area.getY()).setHeight(area.getHeight());
573+
}
574+
drawContext.getCanvas().rectangle(clippedArea).clip().newPath();
575+
}
576+
538577
drawChildren(drawContext);
539578
drawPositionedChildren(drawContext);
579+
if (processOverflow) {
580+
drawContext.getCanvas().restoreState();
581+
}
540582

541583
endRotationIfApplied(drawContext.getCanvas());
542584
endElementOpacityApplying(drawContext);
@@ -735,6 +777,16 @@ protected void correctPositionedLayout(Rectangle layoutBox) {
735777
}
736778
}
737779

780+
float getOverflowPartHeight(OverflowPropertyValue overflowY, Rectangle parentBox) {
781+
float difference = 0;
782+
if (null != overflowY && OverflowPropertyValue.FIT != overflowY) {
783+
if (occupiedArea.getBBox().getBottom() < parentBox.getBottom()) {
784+
difference = parentBox.getBottom() - occupiedArea.getBBox().getBottom();
785+
}
786+
}
787+
return difference;
788+
}
789+
738790
protected float applyBordersPaddingsMargins(Rectangle parentBBox, Border[] borders, float[] paddings) {
739791
float parentWidth = parentBBox.getWidth();
740792

@@ -777,7 +829,8 @@ protected MinMaxWidth getMinMaxWidth(float availableWidth) {
777829

778830
MinMaxWidth correctMinMaxWidth(MinMaxWidth minMaxWidth) {
779831
Float width = retrieveWidth(-1);
780-
if (width != null && width >= 0 && width >= minMaxWidth.getChildrenMinWidth()) {
832+
OverflowPropertyValue overflowX = this.<OverflowPropertyValue>getProperty(Property.OVERFLOW_X);
833+
if (width != null && width >= 0 && (width >= minMaxWidth.getChildrenMinWidth() || !OverflowPropertyValue.FIT.equals(overflowX))) {
781834
minMaxWidth.setChildrenMaxWidth((float) width);
782835
minMaxWidth.setChildrenMinWidth((float) width);
783836
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ This file is part of the iText (R) project.
6565
import com.itextpdf.layout.layout.MinMaxWidthLayoutResult;
6666
import com.itextpdf.layout.minmaxwidth.MinMaxWidth;
6767
import com.itextpdf.layout.property.FloatPropertyValue;
68+
import com.itextpdf.layout.property.OverflowPropertyValue;
6869
import com.itextpdf.layout.property.Property;
6970
import com.itextpdf.layout.property.UnitValue;
7071
import org.slf4j.Logger;
@@ -121,6 +122,13 @@ public LayoutResult layout(LayoutContext layoutContext) {
121122
Border[] borders = getBorders();
122123
applyBorderBox(layoutBox, borders, false);
123124

125+
OverflowPropertyValue overflowX = this.parent.<OverflowPropertyValue>getProperty(Property.OVERFLOW_X);
126+
OverflowPropertyValue overflowY = (null == retrieveMaxHeight() || retrieveMaxHeight() > layoutBox.getHeight()) && !layoutContext.isClippedHeight() ? OverflowPropertyValue.FIT : this.parent.<OverflowPropertyValue>getProperty(Property.OVERFLOW_Y);
127+
boolean processOverflowX = (null != overflowX && !OverflowPropertyValue.FIT.equals(overflowX));
128+
boolean processOverflowY = (null != overflowY && !OverflowPropertyValue.FIT.equals(overflowY));
129+
if (isAbsolutePosition()) {
130+
applyAbsolutePosition(layoutBox);
131+
}
124132
occupiedArea = new LayoutArea(area.getPageNumber(), new Rectangle(layoutBox.getX(), layoutBox.getY() + layoutBox.getHeight(), 0, 0));
125133

126134
Float angle = this.getPropertyAsFloat(Property.ROTATION_ANGLE);
@@ -206,7 +214,7 @@ public LayoutResult layout(LayoutContext layoutContext) {
206214
// indicates whether the placement is forced
207215
boolean isPlacingForced = false;
208216
if (width > layoutBox.getWidth() || height > layoutBox.getHeight()) {
209-
if (Boolean.TRUE.equals(getPropertyAsBoolean(Property.FORCED_PLACEMENT))) {
217+
if (Boolean.TRUE.equals(getPropertyAsBoolean(Property.FORCED_PLACEMENT)) || (width > layoutBox.getWidth() && processOverflowX) || (height > layoutBox.getHeight() && processOverflowY)) {
210218
isPlacingForced = true;
211219
} else {
212220
return new MinMaxWidthLayoutResult(LayoutResult.NOTHING, occupiedArea, null, this, this);

0 commit comments

Comments
 (0)