Skip to content

Commit 91bea8f

Browse files
committed
KeepTogether disable for not fit in keepTogether tree fix
DEVSIX-3468
1 parent 451ef20 commit 91bea8f

9 files changed

+101
-38
lines changed

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

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -168,35 +168,16 @@ public void addChild(IRenderer renderer) {
168168
}
169169
} else {
170170
if (currentArea.isEmptyArea() && result.getAreaBreak() == null) {
171-
if (Boolean.TRUE.equals(result.getOverflowRenderer().getModelElement().<Boolean>getProperty(Property.KEEP_TOGETHER))) {
172-
result.getOverflowRenderer().getModelElement().setProperty(Property.KEEP_TOGETHER, false);
173-
Logger logger = LoggerFactory.getLogger(RootRenderer.class);
174-
logger.warn(MessageFormatUtil.format(LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, "KeepTogether property will be ignored."));
175-
if (!rendererIsFloat) {
176-
rootRendererStateHandler.attemptGoBackToStoredPreviousStateAndStoreNextState(this);
177-
}
178-
} else if (null != result.getCauseOfNothing() && Boolean.TRUE.equals(result.getCauseOfNothing().<Boolean>getProperty(Property.KEEP_TOGETHER))) {
179-
// set KEEP_TOGETHER false on the deepest parent (maybe the element itself) to have KEEP_TOGETHER == true
180-
IRenderer theDeepestKeptTogether = result.getCauseOfNothing();
181-
IRenderer parent;
182-
while (null == theDeepestKeptTogether.getModelElement() || null == theDeepestKeptTogether.getModelElement().<Boolean>getOwnProperty(Property.KEEP_TOGETHER)) {
183-
parent = ((AbstractRenderer) theDeepestKeptTogether).parent;
184-
if (parent == null) {
185-
break;
186-
}
187-
theDeepestKeptTogether = parent;
188-
}
189-
theDeepestKeptTogether.getModelElement().setProperty(Property.KEEP_TOGETHER, false);
190-
Logger logger = LoggerFactory.getLogger(RootRenderer.class);
191-
logger.warn(MessageFormatUtil.format(LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, "KeepTogether property of inner element will be ignored."));
192-
if (!rendererIsFloat) {
193-
rootRendererStateHandler.attemptGoBackToStoredPreviousStateAndStoreNextState(this);
194-
}
195-
} else if (!Boolean.TRUE.equals(renderer.<Boolean>getProperty(Property.FORCED_PLACEMENT))) {
196-
result.getOverflowRenderer().setProperty(Property.FORCED_PLACEMENT, true);
197-
Logger logger = LoggerFactory.getLogger(RootRenderer.class);
198-
logger.warn(MessageFormatUtil.format(LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, ""));
199-
} else {
171+
boolean keepTogetherChanged = tryDisableKeepTogether(result,
172+
rendererIsFloat, rootRendererStateHandler);
173+
174+
boolean areKeepTogetherAndForcedPlacementBothNotChanged = !keepTogetherChanged;
175+
if (areKeepTogetherAndForcedPlacementBothNotChanged) {
176+
areKeepTogetherAndForcedPlacementBothNotChanged =
177+
! updateForcedPlacement(renderer, result.getOverflowRenderer());
178+
}
179+
180+
if (areKeepTogetherAndForcedPlacementBothNotChanged) {
200181
// FORCED_PLACEMENT was already set to the renderer and
201182
// LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA message was logged.
202183
// This else-clause should never be hit, otherwise there is a bug in FORCED_PLACEMENT implementation.
@@ -490,4 +471,51 @@ private void addWaitingNextPageRenderers() {
490471
addChild(renderer);
491472
}
492473
}
474+
475+
private boolean updateForcedPlacement(IRenderer currentRenderer, IRenderer overflowRenderer) {
476+
if (Boolean.TRUE.equals(currentRenderer.<Boolean>getProperty(Property.FORCED_PLACEMENT))) {
477+
return false;
478+
} else {
479+
overflowRenderer.setProperty(Property.FORCED_PLACEMENT, true);
480+
Logger logger = LoggerFactory.getLogger(RootRenderer.class);
481+
if (logger.isWarnEnabled()) {
482+
logger.warn(MessageFormatUtil.format(LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, ""));
483+
}
484+
return true;
485+
}
486+
}
487+
488+
private boolean tryDisableKeepTogether(LayoutResult result,
489+
boolean rendererIsFloat, RootRendererAreaStateHandler rootRendererStateHandler) {
490+
IRenderer toDisableKeepTogether = null;
491+
492+
// looking for the most outer keep together element
493+
IRenderer current = result.getCauseOfNothing();
494+
while (current != null) {
495+
if (Boolean.TRUE.equals(current.<Boolean>getProperty(Property.KEEP_TOGETHER))) {
496+
toDisableKeepTogether = current;
497+
}
498+
current = current.getParent();
499+
}
500+
501+
if (toDisableKeepTogether == null) {
502+
return false;
503+
}
504+
505+
// Ideally the disabling of keep together property should be done on the renderers layer,
506+
// but due to the problem with renderers tree (parent links from causeOfNothing
507+
// may not lead to overflowRenderer) such approach does not work now. So we
508+
// disabling keep together on the models layer.
509+
toDisableKeepTogether.getModelElement().setProperty(Property.KEEP_TOGETHER, false);
510+
Logger logger = LoggerFactory.getLogger(RootRenderer.class);
511+
if (logger.isWarnEnabled()) {
512+
logger.warn(MessageFormatUtil.format(
513+
LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA,
514+
"KeepTogether property will be ignored."));
515+
}
516+
if (!rendererIsFloat) {
517+
rootRendererStateHandler.attemptGoBackToStoredPreviousStateAndStoreNextState(this);
518+
}
519+
return true;
520+
}
493521
}

layout/src/test/java/com/itextpdf/layout/KeepTogetherTest.java

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,10 +1028,10 @@ public void smallFloatInsideKeptTogetherTableTest02() throws IOException, Interr
10281028

10291029
@Test
10301030
@LogMessages(messages = {
1031-
@LogMessage(messageTemplate = LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2)
1031+
@LogMessage(messageTemplate = LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
10321032
})
1033-
public void contentOverlappingInDivWithKeepTogetherTest() throws IOException, InterruptedException {
1034-
String filename = "contentOverlappingInDivWithKeepTogether.pdf";
1033+
public void keepTogetherTreeWithParentNotFitOnDocumentTest() throws IOException, InterruptedException {
1034+
String filename = "keepTogetherTreeWithParentNotFitOnDocument.pdf";
10351035
String outFile = destinationFolder + filename;
10361036
String cmpFileName = sourceFolder + "cmp_" + filename;
10371037

@@ -1060,10 +1060,10 @@ public void contentOverlappingInDivWithKeepTogetherTest() throws IOException, In
10601060

10611061
@Test
10621062
@LogMessages(messages = {
1063-
@LogMessage(messageTemplate = LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2)
1063+
@LogMessage(messageTemplate = LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
10641064
})
1065-
public void missContentAndOverlappingInDivNoKeepTogetherTest() throws IOException, InterruptedException {
1066-
String filename = "missContentAndOverlappingInDivNoKeepTogether.pdf";
1065+
public void keepTogetherSubTreeWithParentNotFitOnDocumentTest() throws IOException, InterruptedException {
1066+
String filename = "keepTogetherSubTreeWithParentNotFitOnDocument.pdf";
10671067
String outFile = destinationFolder + filename;
10681068
String cmpFileName = sourceFolder + "cmp_" + filename;
10691069

@@ -1094,10 +1094,45 @@ public void missContentAndOverlappingInDivNoKeepTogetherTest() throws IOExceptio
10941094

10951095
@Test
10961096
@LogMessages(messages = {
1097-
@LogMessage(messageTemplate = LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, count = 2)
1097+
@LogMessage(messageTemplate = LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
1098+
})
1099+
public void keepTogetherSubTreeWithChildKeepTogetherFalseAndParentNotFitOnDocumentTest()
1100+
throws IOException, InterruptedException {
1101+
String filename = "keepTogetherSubTreeWithChildKeepTogetherFalseAndParentNotFitOnDocument.pdf";
1102+
String outFile = destinationFolder + filename;
1103+
String cmpFileName = sourceFolder + "cmp_" + filename;
1104+
1105+
try (Document doc = new Document(new PdfDocument(new PdfWriter(outFile)))) {
1106+
doc.getPdfDocument().addNewPage(PageSize.A5.rotate());
1107+
1108+
Div main = new Div();
1109+
1110+
Div child1 = createChildDivWithText(main, null).setKeepTogether(true);
1111+
createChildDivWithText(child1, BIG_TEXT);
1112+
1113+
Div div1_2 = createChildDivWithText(child1, null).setKeepTogether(false);
1114+
createChildDivWithText(div1_2, "Section A");
1115+
1116+
createChildDivWithText(div1_2, null).add(new Paragraph(MEDIUM_TEXT).setFirstLineIndent(20));
1117+
1118+
// KEEP_TOGETHER is not set here
1119+
Div child2 = createChildDivWithText(main, null);
1120+
createChildDivWithText(child2, "Section B");
1121+
createChildDivWithText(child2, null);
1122+
createChildDivWithText(child2, "Lorem ipsum dolor sit amet!");
1123+
1124+
doc.add(main);
1125+
}
1126+
1127+
Assert.assertNull(new CompareTool().compareByContent(outFile, cmpFileName, destinationFolder));
1128+
}
1129+
1130+
@Test
1131+
@LogMessages(messages = {
1132+
@LogMessage(messageTemplate = LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
10981133
})
1099-
public void contentOverlappingDivKeepTogetherInRectTest() throws IOException, InterruptedException {
1100-
String filename = "contentOverlappingDivKeepTogetherInRect.pdf";
1134+
public void keepTogetherTreeWithParentNotFitOnPageCanvasTest() throws IOException, InterruptedException {
1135+
String filename = "keepTogetherTreeWithParentNotFitOnPageCanvas.pdf";
11011136
String outFile = destinationFolder + filename;
11021137
String cmpFileName = sourceFolder + "cmp_" + filename;
11031138

0 commit comments

Comments
 (0)