Skip to content

Commit b1ec385

Browse files
committed
Update table fixed layout, fix issue with table with header. Make fixed layout for large table more flexible.
DEVSIX-1252
1 parent cf2ff95 commit b1ec385

File tree

4 files changed

+109
-58
lines changed

4 files changed

+109
-58
lines changed

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

Lines changed: 50 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,18 @@ public class Table extends BlockElement<Table> implements ILargeElement {
8888

8989
/**
9090
* Constructs a {@code Table} with the preferable column widths.
91-
* <p/>
92-
* Since 7.0.2 table layout was introduced. Auto layout is default, except large tables.
91+
* <br/>
92+
* Since 7.0.2 table layout algorithms were introduced. Auto layout is default, except large tables.
9393
* For large table 100% width and fixed layout set implicitly.
94-
* <p/>
94+
* <br/>
9595
* Note, the eventual columns width depends on selected layout, table width,
9696
* cell's width, cell's min-widths, and cell's max-widths.
9797
* Table layout algorithm has the same behaviour as expected for CSS table-layout property,
98-
* where {@code columnWidths} is &lt;colgroup&gt;'s width values.
98+
* where {@code columnWidths} is &lt;colgroup&gt;'s widths.
9999
* For more information see {@link #setAutoLayout()} and {@link #setFixedLayout()}.
100100
*
101-
* @param columnWidths preferable column widths in points.
101+
* @param columnWidths preferable column widths in points. Values must be greater than or equal to zero,
102+
* otherwise it will be interpreted as undefined.
102103
* @param largeTable whether parts of the table will be written before all data is added.
103104
* @see #setAutoLayout()
104105
* @see #setFixedLayout()
@@ -117,17 +118,18 @@ public Table(float[] columnWidths, boolean largeTable) {
117118

118119
/**
119120
* Constructs a {@code Table} with the preferable column widths.
120-
* <p/>
121-
* Since 7.0.2 table layout was introduced. Auto layout is default, except large tables.
121+
* <br/>
122+
* Since 7.0.2 table layout algorithms were introduced. Auto layout is default, except large tables.
122123
* For large table 100% width and fixed layout set implicitly.
123-
* <p/>
124+
* <br/>
124125
* Note, the eventual columns width depends on selected layout, table width,
125126
* cell's width, cell's min-widths, and cell's max-widths.
126127
* Table layout algorithm has the same behaviour as expected for CSS table-layout property,
127-
* where {@code columnWidths} is &lt;colgroup&gt;'s width values.
128+
* where {@code columnWidths} is &lt;colgroup&gt;'s widths.
128129
* For more information see {@link #setAutoLayout()} and {@link #setFixedLayout()}.
129130
*
130-
* @param columnWidths preferable column widths, points and/or percents.
131+
* @param columnWidths preferable column widths, points and/or percents. Values must be greater than or equal to zero,
132+
* otherwise it will be interpreted as undefined.
131133
* @param largeTable whether parts of the table will be written before all data is added.
132134
* @see #setAutoLayout()
133135
* @see #setFixedLayout()
@@ -148,16 +150,17 @@ public Table(UnitValue[] columnWidths, boolean largeTable) {
148150

149151
/**
150152
* Constructs a {@code Table} with the preferable column widths.
151-
* <p/>
152-
* Since 7.0.2 table layout was introduced. Auto layout is default.
153-
* <p/>
153+
* <br/>
154+
* Since 7.0.2 table layout algorithms were introduced. Auto layout is default.
155+
* <br/>
154156
* Note, the eventual columns width depends on selected layout, table width,
155157
* cell's width, cell's min-widths, and cell's max-widths.
156158
* Table layout algorithm has the same behaviour as expected for CSS table-layout property,
157-
* where {@code columnWidths} is &lt;colgroup&gt;'s width values.
159+
* where {@code columnWidths} is &lt;colgroup&gt;'s widths.
158160
* For more information see {@link #setAutoLayout()} and {@link #setFixedLayout()}.
159161
*
160-
* @param columnWidths preferable column widths, points and/or percents.
162+
* @param columnWidths preferable column widths, points and/or percents. Values must be greater than or equal to zero,
163+
* otherwise it will be interpreted as undefined.
161164
* @see #setAutoLayout()
162165
* @see #setFixedLayout()
163166
*/
@@ -167,16 +170,17 @@ public Table(UnitValue[] columnWidths) {
167170

168171
/**
169172
* Constructs a {@code Table} with the preferable column widths.
170-
* <p/>
171-
* Since 7.0.2 table layout was introduced. Auto layout is default.
172-
* <p/>
173+
* <br/>
174+
* Since 7.0.2 table layout algorithms were introduced. Auto layout is default.
175+
* <br/>
173176
* Note, the eventual columns width depends on selected layout, table width,
174177
* cell's width, cell's min-widths, and cell's max-widths.
175178
* Table layout algorithm has the same behaviour as expected for CSS table-layout property,
176-
* where {@code columnWidths} is &lt;colgroup&gt;'s width values.
179+
* where {@code columnWidths} is &lt;colgroup&gt;'s widths.
177180
* For more information see {@link #setAutoLayout()} and {@link #setFixedLayout()}.
178181
*
179-
* @param pointColumnWidths preferable column widths in points.
182+
* @param pointColumnWidths preferable column widths in points. Values must be greater than or equal to zero,
183+
* otherwise it will be interpreted as undefined.
180184
* @see #setAutoLayout()
181185
* @see #setFixedLayout()
182186
*/
@@ -187,14 +191,14 @@ public Table(float[] pointColumnWidths) {
187191
/**
188192
* Constructs a {@code Table} with number of columns. Each column will get equal percent width.
189193
* 100% table width set implicitly for backward compatibility.
190-
* <p/>
191-
* Since 7.0.2 table layout was introduced. Auto layout is default, except large tables.
194+
* <br/>
195+
* Since 7.0.2 table layout algorithms were introduced. Auto layout is default, except large tables.
192196
* For large table fixed layout set implicitly.
193-
* <p/>
197+
* <br/>
194198
* Note, the eventual columns width depends on selected layout, table width,
195199
* cell's width, cell's min-widths, and cell's max-widths.
196200
* Table layout algorithm has the same behaviour as expected for CSS table-layout property,
197-
* where {@code columnWidths} is &lt;colgroup&gt;'s width values.
201+
* where {@code columnWidths} is &lt;colgroup&gt;'s widths.
198202
* For more information see {@link #setAutoLayout()} and {@link #setFixedLayout()}.
199203
*
200204
* @param numColumns the number of columns, each column will have equal percent width.
@@ -215,7 +219,7 @@ public Table(int numColumns, boolean largeTable) {
215219
}
216220
//TODO remove in 7.1. It shall work as html tables.
217221
useAllAvailableWidth();
218-
this.columnWidths = normalizeColumnWidths(numColumns, true);
222+
this.columnWidths = normalizeColumnWidths(numColumns);
219223
initializeLargeTable(largeTable);
220224
initializeRows();
221225
}
@@ -230,7 +234,7 @@ public Table(int numColumns, boolean largeTable) {
230234
* Note, the eventual columns width depends on selected layout, table width,
231235
* cell's width, cell's min-widths, and cell's max-widths.
232236
* Table layout algorithm has the same behaviour as expected for CSS table-layout property,
233-
* where {@code columnWidths} is &lt;colgroup&gt;'s width values.
237+
* where {@code columnWidths} is &lt;colgroup&gt;'s widths.
234238
* For more information see {@link #setAutoLayout()} and {@link #setFixedLayout()}.
235239
*
236240
* @param numColumns the number of columns, each column will have equal percent width.
@@ -271,34 +275,41 @@ private static UnitValue[] normalizeColumnWidths(UnitValue[] unitColumnWidths) {
271275
return normalized;
272276
}
273277

274-
private static boolean hasNegativeValue(UnitValue[] unitColumnWidths) {
275-
for (UnitValue uv : unitColumnWidths) {
276-
if (uv.getValue() < 0) return true;
277-
}
278-
return false;
279-
}
280-
281-
private static UnitValue[] normalizeColumnWidths(int numberOfColumns, boolean usePercents) {
278+
private static UnitValue[] normalizeColumnWidths(int numberOfColumns) {
282279
UnitValue[] normalized = new UnitValue[numberOfColumns];
283280
for (int i = 0; i < numberOfColumns; i++) {
284-
normalized[i] = usePercents
285-
? UnitValue.createPercentValue((float) 100 / numberOfColumns)
286-
: UnitValue.createPointValue(-1);
281+
normalized[i] = UnitValue.createPercentValue((float) 100 / numberOfColumns);
287282
}
288283
return normalized;
289284
}
290285

291286
/**
292-
* Set fixed layout. {@link Property#WIDTH} must be set.
287+
* Set fixed layout. Analog of {@code table-layout:fixed} CSS property.
288+
* Note, the table must have width property, otherwise auto layout will be used.
293289
* <p/>
294-
* 1.
290+
* Algorithm description
291+
* <br/>
292+
* 1. Scan columns for width property and set it. (Columns have set in constructor, analog of {@code <colgroup>} element in HTML)<br/>
293+
* 2. Scan the very first row of table for width property and set it to undefined columns (cell width has lower priority in comparing with column).
294+
* If cell has colspan, each column will get equal width: {@code (width/colspan)}.<br/>
295+
* 3. If sum of columns is less, than table width, there are two options:<br/>
296+
* 3.1. If undefined columns still exist, they will get the rest remaining width.<br/>
297+
* 3.2. Otherwise all columns will be expanded proportionally based on its width.<br/>
298+
*
295299
* @return this element.
296300
*/
297301
public Table setFixedLayout() {
298302
setProperty(Property.TABLE_LAYOUT, "fixed");
299303
return this;
300304
}
301305

306+
/**
307+
* Set auto layout.
308+
* <p/>
309+
* 1.
310+
* @return this element.
311+
*/
312+
302313
public Table setAutoLayout() {
303314
setProperty(Property.TABLE_LAYOUT, "auto");
304315
return this;
@@ -868,9 +879,6 @@ private void ensureFooterIsInitialized() {
868879
private void initializeLargeTable(boolean largeTable) {
869880
this.isComplete = !largeTable;
870881
if (largeTable) {
871-
if (hasNegativeValue(this.columnWidths)) {
872-
throw new IllegalArgumentException("Large table must have valid column widths.");
873-
}
874882
setWidth(UnitValue.createPercentValue(100));
875883
setFixedLayout();
876884
}

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

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -430,23 +430,35 @@ float[] fixedLayout() {
430430
//fill columns with -1 from cell info.
431431
int processedColumns = 0;
432432
float remainWidth = tableWidth;
433-
for (int i = 0; i < numberOfColumns; i++) {
434-
if (columnWidths[i] == -1) {
435-
CellRenderer cell = tableRenderer.rows.get(0)[i];
436-
if (cell != null) {
437-
Float cellWidth = cell.retrieveUnitValue(tableWidth, Property.WIDTH);
438-
if (cellWidth != null && cellWidth >= 0) {
439-
int colspan = cell.getModelElement().getColspan();
440-
for (int j = 0; j < colspan; j++) {
441-
columnWidths[i + j] = (float) cellWidth / colspan;
433+
CellRenderer[] firtsRow;
434+
if (tableRenderer.headerRenderer != null && tableRenderer.headerRenderer.rows.size() > 0) {
435+
firtsRow = tableRenderer.headerRenderer.rows.get(0);
436+
} else if (tableRenderer.rows.size() > 0) {
437+
firtsRow = tableRenderer.rows.get(0);
438+
} else {
439+
//most likely it is large table
440+
firtsRow = null;
441+
}
442+
443+
if (firtsRow != null) {
444+
for (int i = 0; i < numberOfColumns; i++) {
445+
if (columnWidths[i] == -1) {
446+
CellRenderer cell = firtsRow[i];
447+
if (cell != null) {
448+
Float cellWidth = cell.retrieveUnitValue(tableWidth, Property.WIDTH);
449+
if (cellWidth != null && cellWidth >= 0) {
450+
int colspan = cell.getModelElement().getColspan();
451+
for (int j = 0; j < colspan; j++) {
452+
columnWidths[i + j] = (float) cellWidth / colspan;
453+
}
454+
remainWidth -= columnWidths[i];
455+
processedColumns++;
442456
}
443-
remainWidth -= columnWidths[i];
444-
processedColumns++;
445457
}
458+
} else {
459+
remainWidth -= columnWidths[i];
460+
processedColumns++;
446461
}
447-
} else {
448-
remainWidth -= columnWidths[i];
449-
processedColumns++;
450462
}
451463
}
452464

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,6 @@ public void tableTest04() throws IOException, InterruptedException, ParserConfig
304304

305305
@Test
306306
public void tableTest05() throws IOException, InterruptedException, ParserConfigurationException, SAXException {
307-
String outFileName = destinationFolder + "tableTest05.pdf";
308-
String cmpFileName = sourceFolder + "cmp_tableTest05.pdf";
309307
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destinationFolder + "tableTest05.pdf"));
310308
pdfDocument.setTagged();
311309

@@ -403,6 +401,39 @@ public void tableTest07() throws IOException, InterruptedException, ParserConfig
403401
compareResult("tableTest07.pdf", "cmp_tableTest07.pdf");
404402
}
405403

404+
@Test
405+
public void tableTest08() throws IOException, InterruptedException, ParserConfigurationException, SAXException {
406+
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destinationFolder + "tableTest08.pdf"));
407+
pdfDocument.setTagged();
408+
409+
Document doc = new Document(pdfDocument);
410+
411+
Table table = new Table(new UnitValue[5], true);
412+
doc.add(table);
413+
414+
Cell cell = new Cell(1, 5).add(new Paragraph("Table XYZ (Continued)"));
415+
table.addHeaderCell(cell);
416+
for (int i = 0; i < 5; ++i) {
417+
table.addHeaderCell(new Cell().add("Header " + (i + 1)));
418+
}
419+
cell = new Cell(1, 5).add(new Paragraph("Continue on next page"));
420+
table.addFooterCell(cell);
421+
table.setSkipFirstHeader(true);
422+
table.setSkipLastFooter(true);
423+
424+
for (int i = 0; i < 350; i++) {
425+
table.addCell(new Cell().add(new Paragraph(String.valueOf(i+1))));
426+
table.flush();
427+
}
428+
429+
table.complete();
430+
doc.add(new Table(1).setBorder(new SolidBorder(Color.ORANGE, 2)).addCell("Is my occupied area correct?"));
431+
432+
doc.close();
433+
434+
compareResult("tableTest08.pdf", "cmp_tableTest08.pdf");
435+
}
436+
406437
@Test
407438
public void listTest01() throws IOException, InterruptedException, ParserConfigurationException, SAXException {
408439
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destinationFolder + "listTest01.pdf"));

0 commit comments

Comments
 (0)