Skip to content

Commit 7e469e8

Browse files
committed
Improve border collapse of the current cell's bottom border
The idea is explained in the comments in the code DEVSIX-3356
1 parent 45f3d61 commit 7e469e8

13 files changed

+169
-51
lines changed

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

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -493,11 +493,14 @@ public LayoutResult layout(LayoutContext layoutContext) {
493493
// the element which was the first to cause Layout.Nothing
494494
IRenderer firstCauseOfNothing = null;
495495

496-
// the width of the widest bottom border of the row
496+
// In the next lines we pretend as if the current row will be the last on the current area:
497+
// in this case it will be collapsed with the table's bottom border / the footer's top border
497498
bordersHandler.setFinishRow(rowRange.getStartRow() + row);
498-
Border widestRowBottomBorder = bordersHandler.getWidestHorizontalBorder(rowRange.getStartRow() + row + 1);
499-
bordersHandler.setFinishRow(rowRange.getFinishRow());
499+
final List<Border> rowBottomBorderIfLastOnPage =
500+
bordersHandler.getHorizontalBorder(rowRange.getStartRow() + row + 1);
501+
Border widestRowBottomBorder = TableBorderUtil.getWidestBorder(rowBottomBorderIfLastOnPage);
500502
float widestRowBottomBorderWidth = null == widestRowBottomBorder ? 0 : widestRowBottomBorder.getWidth();
503+
bordersHandler.setFinishRow(rowRange.getFinishRow());
501504

502505
// if cell is in the last row on the page, its borders shouldn't collapse with the next row borders
503506
while (cellProcessingQueue.size() > 0) {
@@ -540,8 +543,41 @@ public LayoutResult layout(LayoutContext layoutContext) {
540543
float[] cellIndents = bordersHandler.getCellBorderIndents(currentCellInfo.finishRowInd, col,
541544
rowspan, colspan);
542545
if (!(bordersHandler instanceof SeparatedTableBorders)) {
546+
// Bottom indent to be applied consists of two parts which should be summed up:
547+
// a) half of the border of the current row (in case it is the last row on the area)
548+
// b) half of the widest possible bottom border (in case it is the last row on the area)
549+
//
550+
// The following "image" demonstrates the idea: C represents some content,
551+
// 1 represents border, 0 represents not occupied space, - represents
552+
// the middle of a horizontal border, | represents vertical border
553+
// (the latter could be of customized width as well, however, for the reasons
554+
// of this comment it could omitted)
555+
// CCCCC|CCCCC
556+
// CCCCC|11111
557+
// CCCCC|11111
558+
// 11111|11111
559+
// -----|-----
560+
// 11111|11111
561+
// 00000|11111
562+
// 00000|11111
563+
//
564+
// The question arises, however: what if the top border of the cell below is wider than the
565+
// bottom border of the table. This is already considered: when considering rowHeight
566+
// the width of the real collapsed border will be added to it.
567+
// It is quite important to understand that in case it is not possible
568+
// to add any other row, the current row should be collapsed with the table's bottom
569+
// footer's top borders rather than with the next row. If it is the case, iText
570+
// will revert collapsing to the one considered in the next calculations.
571+
572+
// Be aware that if the col-th border of rowBottomBorderIfLastOnPage is null,
573+
// cellIndents[2] might not be null: imagine a table without borders,
574+
// a cell with no border (the current cell) and a cell below with some top border.
575+
// Nevertheless, a stated above we do not need to consider cellIndents[2] here.
576+
final float potentialWideCellBorder = null == rowBottomBorderIfLastOnPage.get(col)
577+
? 0
578+
: rowBottomBorderIfLastOnPage.get(col).getWidth();
543579
bordersHandler.applyCellIndents(cellArea.getBBox(), cellIndents[0], cellIndents[1],
544-
cellIndents[2] + widestRowBottomBorderWidth, cellIndents[3], false);
580+
potentialWideCellBorder + widestRowBottomBorderWidth, cellIndents[3], false);
545581
}
546582
// update cell width
547583
cellWidth = cellArea.getBBox().getWidth();
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.itextpdf.layout;
2+
3+
import com.itextpdf.kernel.colors.ColorConstants;
4+
import com.itextpdf.layout.borders.SolidBorder;
5+
import com.itextpdf.layout.element.Table;
6+
import com.itextpdf.layout.property.UnitValue;
7+
import com.itextpdf.test.ExtendedITextTest;
8+
import com.itextpdf.test.annotations.type.IntegrationTest;
9+
10+
import org.junit.experimental.categories.Category;
11+
12+
@Category(IntegrationTest.class)
13+
public class AbstractTableTest extends ExtendedITextTest {
14+
static Document addTableBelowToCheckThatOccupiedAreaIsCorrect(Document doc) {
15+
doc.add(new Table(UnitValue.createPercentArray(1))
16+
.useAllAvailableWidth()
17+
.setBorder(new SolidBorder(ColorConstants.ORANGE, 2))
18+
.addCell("Is my occupied area correct?"));
19+
return doc;
20+
}
21+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,8 @@ public void keepTogetherOnInnerElementMargin02EmptyPageTest() throws IOException
10091009
@LogMessages(messages = {
10101010
@LogMessage(messageTemplate = LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA)
10111011
})
1012+
// TODO DEVSIX-1092 There should be no path of the 15th row on the first page,
1013+
// since the layout box is only of 1 px height
10121014
public void smallFloatInsideKeptTogetherTableTest01() throws IOException, InterruptedException {
10131015
String cmpFileName = sourceFolder + "cmp_smallFloatInsideKeptTogetherTableTest01.pdf";
10141016
String outFile = destinationFolder + "smallFloatInsideKeptTogetherTableTest01.pdf";

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

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ This file is part of the iText (R) project.
7474
import java.io.IOException;
7575

7676
@Category(IntegrationTest.class)
77-
public class TableBorderTest extends ExtendedITextTest {
77+
public class TableBorderTest extends AbstractTableTest {
7878
public static final String sourceFolder = "./src/test/resources/com/itextpdf/layout/TableBorderTest/";
7979
public static final String destinationFolder = "./target/test/com/itextpdf/layout/TableBorderTest/";
8080
public static final String cmpPrefix = "cmp_";
@@ -1388,14 +1388,14 @@ public void tableWithHeaderFooterTest06() throws IOException, InterruptedExcepti
13881388
table.addCell(new Cell().setBorderLeft(new SolidBorder(ColorConstants.BLUE, 0.5f)).setBorderRight(new SolidBorder(ColorConstants.BLUE, 0.5f)).setHeight(30).setBorderBottom(new SolidBorder(ColorConstants.BLUE, 2 * i + 1 > 50 ? 50 : 2 * i + 1)).setBorderTop(new SolidBorder(ColorConstants.GREEN, (50 - 2 * i + 1 >= 0) ? 50 - 2 * i + 1 : 0)).add(new Paragraph(String.valueOf(i + 1))));
13891389
}
13901390
doc.add(table);
1391-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1391+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
13921392

13931393
doc.add(new AreaBreak());
13941394
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
13951395
table.setHorizontalBorderSpacing(20);
13961396
table.setVerticalBorderSpacing(20);
13971397
doc.add(table);
1398-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1398+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
13991399

14001400
closeDocumentAndCompareOutputs(doc);
14011401
}
@@ -1419,14 +1419,14 @@ public void tableWithHeaderFooterTest06A() throws IOException, InterruptedExcept
14191419
table.addCell(new Cell().setBorderLeft(new SolidBorder(ColorConstants.BLUE, 0.5f)).setBorderRight(new SolidBorder(ColorConstants.BLUE, 0.5f)).setHeight(30).setBorderBottom(new SolidBorder(ColorConstants.BLUE, 2 * i + 1 > 50 ? 50 : 2 * i + 1)).setBorderTop(new SolidBorder(ColorConstants.GREEN, (50 - 2 * i + 1 >= 0) ? 50 - 2 * i + 1 : 0)).add(new Paragraph(String.valueOf(i + 1))));
14201420
}
14211421
doc.add(table);
1422-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1422+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
14231423

14241424
doc.add(new AreaBreak());
14251425
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
14261426
table.setHorizontalBorderSpacing(20);
14271427
table.setVerticalBorderSpacing(20);
14281428
doc.add(table);
1429-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1429+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
14301430

14311431
closeDocumentAndCompareOutputs(doc);
14321432
}
@@ -1450,14 +1450,14 @@ public void tableWithHeaderFooterTest06B() throws IOException, InterruptedExcept
14501450
table.addCell(new Cell().setBorderLeft(new SolidBorder(ColorConstants.BLUE, 0.5f)).setBorderRight(new SolidBorder(ColorConstants.BLUE, 0.5f)).setHeight(30).setBorderTop(new SolidBorder(ColorConstants.BLUE, 2 * i + 1 > 50 ? 50 : 2 * i + 1)).setBorderBottom(new SolidBorder(ColorConstants.GREEN, (50 - 2 * i + 1 >= 0) ? 50 - 2 * i + 1 : 0)).add(new Paragraph(String.valueOf(i + 1))));
14511451
}
14521452
doc.add(table);
1453-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1453+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
14541454

14551455
doc.add(new AreaBreak());
14561456
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
14571457
table.setHorizontalBorderSpacing(20);
14581458
table.setVerticalBorderSpacing(20);
14591459
doc.add(table);
1460-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1460+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
14611461

14621462

14631463
closeDocumentAndCompareOutputs(doc);
@@ -1507,14 +1507,14 @@ public void tableWithHeaderFooterTest08() throws IOException, InterruptedExcepti
15071507
table.setBorderBottom(new SolidBorder(ColorConstants.RED, 30));
15081508

15091509
doc.add(table);
1510-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1510+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
15111511

15121512
doc.add(new AreaBreak());
15131513
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
15141514
table.setHorizontalBorderSpacing(20);
15151515
table.setVerticalBorderSpacing(20);
15161516
doc.add(table);
1517-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1517+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
15181518

15191519
closeDocumentAndCompareOutputs(doc);
15201520
}
@@ -1624,15 +1624,15 @@ public void tableWithHeaderFooterTest10() throws IOException, InterruptedExcepti
16241624
table.setSkipLastFooter(true);
16251625
table.setBorder(new SolidBorder(ColorConstants.GREEN, 1));
16261626
doc.add(table);
1627-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1627+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
16281628

16291629
pdfDoc.setDefaultPageSize(new PageSize(480, 350));
16301630
doc.add(new AreaBreak());
16311631
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
16321632
table.setHorizontalBorderSpacing(20);
16331633
table.setVerticalBorderSpacing(20);
16341634
doc.add(table);
1635-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1635+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
16361636

16371637
closeDocumentAndCompareOutputs(doc);
16381638
}
@@ -1656,15 +1656,15 @@ public void tableWithHeaderFooterTest11() throws IOException, InterruptedExcepti
16561656
table.setSkipLastFooter(true);
16571657

16581658
doc.add(table);
1659-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1659+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
16601660

16611661
doc.getPdfDocument().setDefaultPageSize(new PageSize(695, 842));
16621662
doc.add(new AreaBreak());
16631663
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
16641664
table.setHorizontalBorderSpacing(20);
16651665
table.setVerticalBorderSpacing(20);
16661666
doc.add(table);
1667-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1667+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
16681668

16691669
closeDocumentAndCompareOutputs(doc);
16701670
}
@@ -1691,15 +1691,15 @@ public void tableWithHeaderFooterTest11A() throws IOException, InterruptedExcept
16911691

16921692
table.setSkipLastFooter(true);
16931693
doc.add(table);
1694-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1694+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
16951695

16961696
doc.getPdfDocument().setDefaultPageSize(new PageSize(695, 842));
16971697
doc.add(new AreaBreak());
16981698
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
16991699
table.setHorizontalBorderSpacing(20);
17001700
table.setVerticalBorderSpacing(20);
17011701
doc.add(table);
1702-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1702+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
17031703

17041704
doc.close();
17051705
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder, testName + "_diff"));
@@ -1718,14 +1718,14 @@ public void tableWithHeaderFooterTest12() throws IOException, InterruptedExcepti
17181718
table.addFooterCell(new Cell().setHeight(30).add(new Paragraph("Footer")).setBorder(new SolidBorder(ColorConstants.YELLOW, 20)));
17191719

17201720
doc.add(table);
1721-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1721+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
17221722

17231723
doc.add(new AreaBreak());
17241724
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
17251725
table.setHorizontalBorderSpacing(20);
17261726
table.setVerticalBorderSpacing(20);
17271727
doc.add(table);
1728-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1728+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
17291729

17301730
closeDocumentAndCompareOutputs(doc);
17311731
}
@@ -1880,13 +1880,13 @@ public void tableWithHeaderFooterTest17() throws IOException, InterruptedExcepti
18801880
table.setSkipLastFooter(true);
18811881

18821882
doc.add(table);
1883-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1883+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
18841884

18851885
doc.add(new AreaBreak());
18861886
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
18871887
table.setVerticalBorderSpacing(20);
18881888
doc.add(table);
1889-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1889+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
18901890

18911891
closeDocumentAndCompareOutputs(doc);
18921892
}
@@ -1905,7 +1905,7 @@ public void tableWithHeaderFooterTest18() throws IOException, InterruptedExcepti
19051905
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
19061906
table.setVerticalBorderSpacing(20);
19071907
doc.add(table);
1908-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1908+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
19091909

19101910
doc.add(new AreaBreak());
19111911

@@ -1917,7 +1917,7 @@ public void tableWithHeaderFooterTest18() throws IOException, InterruptedExcepti
19171917
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
19181918
table.setVerticalBorderSpacing(20);
19191919
doc.add(table);
1920-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1920+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
19211921

19221922
doc.add(new AreaBreak());
19231923

@@ -1932,7 +1932,7 @@ public void tableWithHeaderFooterTest18() throws IOException, InterruptedExcepti
19321932
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
19331933
table.setVerticalBorderSpacing(20);
19341934
doc.add(table);
1935-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1935+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
19361936

19371937
closeDocumentAndCompareOutputs(doc);
19381938
}
@@ -1951,7 +1951,7 @@ public void tableWithHeaderFooterTest19() throws IOException, InterruptedExcepti
19511951
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
19521952
table.setVerticalBorderSpacing(20);
19531953
doc.add(table);
1954-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1954+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
19551955

19561956
doc.add(new AreaBreak());
19571957

@@ -1963,7 +1963,7 @@ public void tableWithHeaderFooterTest19() throws IOException, InterruptedExcepti
19631963
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
19641964
table.setVerticalBorderSpacing(20);
19651965
doc.add(table);
1966-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1966+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
19671967

19681968
doc.add(new AreaBreak());
19691969

@@ -1976,7 +1976,7 @@ public void tableWithHeaderFooterTest19() throws IOException, InterruptedExcepti
19761976
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
19771977
table.setVerticalBorderSpacing(20);
19781978
doc.add(table);
1979-
doc.add(new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(new SolidBorder(ColorConstants.ORANGE, 2)).addCell("Is my occupied area correct?"));
1979+
addTableBelowToCheckThatOccupiedAreaIsCorrect(doc);
19801980

19811981
closeDocumentAndCompareOutputs(doc);
19821982
}

0 commit comments

Comments
 (0)