1 /*
2 * $Id: PdfPTable.java 4010 2009-07-07 11:05:23Z blowagie $
3 *
4 * Copyright 2001, 2002 Paulo Soares
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * (the "License"); you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the License.
13 *
14 * The Original Code is 'iText, a free JAVA-PDF library'.
15 *
16 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17 * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18 * All Rights Reserved.
19 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20 * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21 *
22 * Contributor(s): all the names of the contributors are added in the source code
23 * where applicable.
24 *
25 * Alternatively, the contents of this file may be used under the terms of the
26 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27 * provisions of LGPL are applicable instead of those above. If you wish to
28 * allow use of your version of this file only under the terms of the LGPL
29 * License and not to allow others to use your version of this file under
30 * the MPL, indicate your decision by deleting the provisions above and
31 * replace them with the notice and other provisions required by the LGPL.
32 * If you do not delete the provisions above, a recipient may use your version
33 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34 *
35 * This library is free software; you can redistribute it and/or modify it
36 * under the terms of the MPL as stated above or under the terms of the GNU
37 * Library General Public License as published by the Free Software Foundation;
38 * either version 2 of the License, or any later version.
39 *
40 * This library is distributed in the hope that it will be useful, but WITHOUT
41 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42 * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43 * details.
44 *
45 * If you didn't download this code from the following link, you should check if
46 * you aren't using an obsolete version:
47 * http://www.lowagie.com/iText/
48 */
49
50 package com.lowagie.text.pdf;
51
52 import java.util.ArrayList;
53
54 import com.lowagie.text.DocumentException;
55 import com.lowagie.text.Element;
56 import com.lowagie.text.ElementListener;
57 import com.lowagie.text.Image;
58 import com.lowagie.text.LargeElement;
59 import com.lowagie.text.Phrase;
60 import com.lowagie.text.Rectangle;
61 import com.lowagie.text.pdf.events.PdfPTableEventForwarder;
62
63 /**
64 * This is a table that can be put at an absolute position but can also
65 * be added to the document as the class <CODE>Table</CODE>.
66 * In the last case when crossing pages the table always break at full rows; if a
67 * row is bigger than the page it is dropped silently to avoid infinite loops.
68 * <P>
69 * A PdfPTableEvent can be associated to the table to do custom drawing
70 * when the table is rendered.
71 * @author Paulo Soares (psoares@consiste.pt)
72 */
73
74 public class PdfPTable implements LargeElement{
75
76 /**
77 * The index of the original <CODE>PdfcontentByte</CODE>.
78 */
79 public static final int BASECANVAS = 0;
80
81 /**
82 * The index of the duplicate <CODE>PdfContentByte</CODE> where the background will be drawn.
83 */
84 public static final int BACKGROUNDCANVAS = 1;
85
86 /**
87 * The index of the duplicate <CODE>PdfContentByte</CODE> where the border lines will be drawn.
88 */
89 public static final int LINECANVAS = 2;
90
91 /**
92 * The index of the duplicate <CODE>PdfContentByte</CODE> where the text will be drawn.
93 */
94 public static final int TEXTCANVAS = 3;
95
96 protected ArrayList rows = new ArrayList();
97 protected float totalHeight = 0;
98 protected PdfPCell currentRow[];
99 protected int currentRowIdx = 0;
100 protected PdfPCell defaultCell = new PdfPCell((Phrase)null);
101 protected float totalWidth = 0;
102 protected float relativeWidths[];
103 protected float absoluteWidths[];
104 protected PdfPTableEvent tableEvent;
105
106 /**
107 * Holds value of property headerRows.
108 */
109 protected int headerRows;
110
111 /**
112 * Holds value of property widthPercentage.
113 */
114 protected float widthPercentage = 80;
115
116 /**
117 * Holds value of property horizontalAlignment.
118 */
119 private int horizontalAlignment = Element.ALIGN_CENTER;
120
121 /**
122 * Holds value of property skipFirstHeader.
123 */
124 private boolean skipFirstHeader = false;
125 /**
126 * Holds value of property skipLastFooter.
127 * @since 2.1.6
128 */
129 private boolean skipLastFooter = false;
130
131 protected boolean isColspan = false;
132
133 protected int runDirection = PdfWriter.RUN_DIRECTION_DEFAULT;
134
135 /**
136 * Holds value of property lockedWidth.
137 */
138 private boolean lockedWidth = false;
139
140 /**
141 * Holds value of property splitRows.
142 */
143 private boolean splitRows = true;
144
145 /**
146 * The spacing before the table.
147 */
148 protected float spacingBefore;
149
150 /**
151 * The spacing after the table.
152 */
153 protected float spacingAfter;
154
155 /**
156 * Holds value of property extendLastRow.
157 */
158 private boolean extendLastRow;
159
160 /**
161 * Holds value of property headersInEvent.
162 */
163 private boolean headersInEvent;
164
165 /**
166 * Holds value of property splitLate.
167 */
168 private boolean splitLate = true;
169
170 /**
171 * Defines if the table should be kept
172 * on one page if possible
173 */
174 private boolean keepTogether;
175
176 /**
177 * Indicates if the PdfPTable is complete once added to the document.
178 *
179 * @since iText 2.0.8
180 */
181 protected boolean complete = true;
182
183 /**
184 * Holds value of property footerRows.
185 */
186 private int footerRows;
187
188 /**
189 * Keeps track of the completeness of the current row.
190 * @since 2.1.6
191 */
192 protected boolean rowCompleted = true;
193
194 protected PdfPTable() {
195 }
196
197 /**
198 * Constructs a <CODE>PdfPTable</CODE> with the relative column widths.
199 *
200 * @param relativeWidths the relative column widths
201 */
202 public PdfPTable(float relativeWidths[]) {
203 if (relativeWidths == null)
204 throw new NullPointerException("The widths array in PdfPTable constructor can not be null.");
205 if (relativeWidths.length == 0)
206 throw new IllegalArgumentException("The widths array in PdfPTable constructor can not have zero length.");
207 this.relativeWidths = new float[relativeWidths.length];
208 System.arraycopy(relativeWidths, 0, this.relativeWidths, 0, relativeWidths.length);
209 absoluteWidths = new float[relativeWidths.length];
210 calculateWidths();
211 currentRow = new PdfPCell[absoluteWidths.length];
212 keepTogether = false;
213 }
214
215 /**
216 * Constructs a <CODE>PdfPTable</CODE> with <CODE>numColumns</CODE> columns.
217 *
218 * @param numColumns the number of columns
219 */
220 public PdfPTable(int numColumns) {
221 if (numColumns <= 0)
222 throw new IllegalArgumentException("The number of columns in PdfPTable constructor must be greater than zero.");
223 relativeWidths = new float[numColumns];
224 for (int k = 0; k < numColumns; ++k)
225 relativeWidths[k] = 1;
226 absoluteWidths = new float[relativeWidths.length];
227 calculateWidths();
228 currentRow = new PdfPCell[absoluteWidths.length];
229 keepTogether = false;
230 }
231
232 /**
233 * Constructs a copy of a <CODE>PdfPTable</CODE>.
234 *
235 * @param table the <CODE>PdfPTable</CODE> to be copied
236 */
237 public PdfPTable(PdfPTable table) {
238 copyFormat(table);
239 for (int k = 0; k < currentRow.length; ++k) {
240 if (table.currentRow[k] == null)
241 break;
242 currentRow[k] = new PdfPCell(table.currentRow[k]);
243 }
244 for (int k = 0; k < table.rows.size(); ++k) {
245 PdfPRow row = (PdfPRow)(table.rows.get(k));
246 if (row != null)
247 row = new PdfPRow(row);
248 rows.add(row);
249 }
250 }
251
252 /**
253 * Makes a shallow copy of a table (format without content).
254 *
255 * @param table
256 * @return a shallow copy of the table
257 */
258 public static PdfPTable shallowCopy(PdfPTable table) {
259 PdfPTable nt = new PdfPTable();
260 nt.copyFormat(table);
261 return nt;
262 }
263
264 /**
265 * Copies the format of the sourceTable without copying the content.
266 *
267 * @param sourceTable
268 * @since 2.1.6 private is now protected
269 */
270 protected void copyFormat(PdfPTable sourceTable) {
271 relativeWidths = new float[sourceTable.getNumberOfColumns()];
272 absoluteWidths = new float[sourceTable.getNumberOfColumns()];
273 System.arraycopy(sourceTable.relativeWidths, 0, relativeWidths, 0, getNumberOfColumns());
274 System.arraycopy(sourceTable.absoluteWidths, 0, absoluteWidths, 0, getNumberOfColumns());
275 totalWidth = sourceTable.totalWidth;
276 totalHeight = sourceTable.totalHeight;
277 currentRowIdx = 0;
278 tableEvent = sourceTable.tableEvent;
279 runDirection = sourceTable.runDirection;
280 defaultCell = new PdfPCell(sourceTable.defaultCell);
281 currentRow = new PdfPCell[sourceTable.currentRow.length];
282 isColspan = sourceTable.isColspan;
283 splitRows = sourceTable.splitRows;
284 spacingAfter = sourceTable.spacingAfter;
285 spacingBefore = sourceTable.spacingBefore;
286 headerRows = sourceTable.headerRows;
287 footerRows = sourceTable.footerRows;
288 lockedWidth = sourceTable.lockedWidth;
289 extendLastRow = sourceTable.extendLastRow;
290 headersInEvent = sourceTable.headersInEvent;
291 widthPercentage = sourceTable.widthPercentage;
292 splitLate = sourceTable.splitLate;
293 skipFirstHeader = sourceTable.skipFirstHeader;
294 skipLastFooter = sourceTable.skipLastFooter;
295 horizontalAlignment = sourceTable.horizontalAlignment;
296 keepTogether = sourceTable.keepTogether;
297 complete = sourceTable.complete;
298 }
299
300 /**
301 * Sets the relative widths of the table.
302 *
303 * @param relativeWidths the relative widths of the table.
304 * @throws DocumentException if the number of widths is different than the number
305 * of columns
306 */
307 public void setWidths(float relativeWidths[]) throws DocumentException {
308 if (relativeWidths.length != getNumberOfColumns())
309 throw new DocumentException("Wrong number of columns.");
310 this.relativeWidths = new float[relativeWidths.length];
311 System.arraycopy(relativeWidths, 0, this.relativeWidths, 0, relativeWidths.length);
312 absoluteWidths = new float[relativeWidths.length];
313 totalHeight = 0;
314 calculateWidths();
315 calculateHeights(true);
316 }
317
318 /**
319 * Sets the relative widths of the table.
320 *
321 * @param relativeWidths the relative widths of the table.
322 * @throws DocumentException if the number of widths is different than the number
323 * of columns
324 */
325 public void setWidths(int relativeWidths[]) throws DocumentException {
326 float tb[] = new float[relativeWidths.length];
327 for (int k = 0; k < relativeWidths.length; ++k)
328 tb[k] = relativeWidths[k];
329 setWidths(tb);
330 }
331
332 /**
333 * @since 2.1.6 private is now protected
334 */
335 protected void calculateWidths() {
336 if (totalWidth <= 0)
337 return;
338 float total = 0;
339 int numCols = getNumberOfColumns();
340 for (int k = 0; k < numCols; ++k)
341 total += relativeWidths[k];
342 for (int k = 0; k < numCols; ++k)
343 absoluteWidths[k] = totalWidth * relativeWidths[k] / total;
344 }
345
346 /**
347 * Sets the full width of the table.
348 *
349 * @param totalWidth the full width of the table.
350 */
351 public void setTotalWidth(float totalWidth) {
352 if (this.totalWidth == totalWidth)
353 return;
354 this.totalWidth = totalWidth;
355 totalHeight = 0;
356 calculateWidths();
357 calculateHeights(true);
358 }
359
360 /**
361 * Sets the full width of the table from the absolute column width.
362 *
363 * @param columnWidth the absolute width of each column
364 * @throws DocumentException if the number of widths is different than the number
365 * of columns
366 */
367 public void setTotalWidth(float columnWidth[]) throws DocumentException {
368 if (columnWidth.length != getNumberOfColumns())
369 throw new DocumentException("Wrong number of columns.");
370 totalWidth = 0;
371 for (int k = 0; k < columnWidth.length; ++k)
372 totalWidth += columnWidth[k];
373 setWidths(columnWidth);
374 }
375
376 /**
377 * Sets the percentage width of the table from the absolute column width.
378 *
379 * @param columnWidth the absolute width of each column
380 * @param pageSize the page size
381 * @throws DocumentException
382 */
383 public void setWidthPercentage(float columnWidth[], Rectangle pageSize) throws DocumentException {
384 if (columnWidth.length != getNumberOfColumns())
385 throw new IllegalArgumentException("Wrong number of columns.");
386 float totalWidth = 0;
387 for (int k = 0; k < columnWidth.length; ++k)
388 totalWidth += columnWidth[k];
389 widthPercentage = totalWidth / (pageSize.getRight() - pageSize.getLeft()) * 100f;
390 setWidths(columnWidth);
391 }
392
393 /**
394 * Gets the full width of the table.
395 *
396 * @return the full width of the table
397 */
398 public float getTotalWidth() {
399 return totalWidth;
400 }
401
402 /**
403 * Calculates the heights of the table.
404 *
405 * @param firsttime if true, the heights of the rows will be recalculated.
406 * This takes time; normally the heights of the rows are already calcultated,
407 * so in most cases, it's save to use false as parameter.
408 * @return the total height of the table. Note that it will be 0 if you didn't
409 * specify the width of the table with setTotalWidth().
410 * @since 2.1.5 added a parameter and a return type to an existing method,
411 * and made it public
412 */
413 public float calculateHeights(boolean firsttime) {
414 if (totalWidth <= 0)
415 return 0;
416 totalHeight = 0;
417 for (int k = 0; k < rows.size(); ++k) {
418 totalHeight += getRowHeight(k, firsttime);
419 }
420 return totalHeight;
421 }
422
423 /**
424 * Calculates the heights of the table.
425 */
426 public void calculateHeightsFast() {
427 calculateHeights(false);
428 }
429
430 /**
431 * Gets the default <CODE>PdfPCell</CODE> that will be used as
432 * reference for all the <CODE>addCell</CODE> methods except
433 * <CODE>addCell(PdfPCell)</CODE>.
434 *
435 * @return default <CODE>PdfPCell</CODE>
436 */
437 public PdfPCell getDefaultCell() {
438 return defaultCell;
439 }
440
441 /**
442 * Adds a cell element.
443 *
444 * @param cell the cell element
445 */
446 public void addCell(PdfPCell cell) {
447 rowCompleted = false;
448 PdfPCell ncell = new PdfPCell(cell);
449
450 int colspan = ncell.getColspan();
451 colspan = Math.max(colspan, 1);
452 colspan = Math.min(colspan, currentRow.length - currentRowIdx);
453 ncell.setColspan(colspan);
454
455 if (colspan != 1)
456 isColspan = true;
457 int rdir = ncell.getRunDirection();
458 if (rdir == PdfWriter.RUN_DIRECTION_DEFAULT)
459 ncell.setRunDirection(runDirection);
460
461 skipColsWithRowspanAbove();
462
463 boolean cellAdded = false;
464 if (currentRowIdx < currentRow.length) {
465 currentRow[currentRowIdx] = ncell;
466 currentRowIdx += colspan;
467 cellAdded = true;
468 }
469
470 skipColsWithRowspanAbove();
471
472 if (currentRowIdx >= currentRow.length) {
473 int numCols = getNumberOfColumns();
474 if (runDirection == PdfWriter.RUN_DIRECTION_RTL) {
475 PdfPCell rtlRow[] = new PdfPCell[numCols];
476 int rev = currentRow.length;
477 for (int k = 0; k < currentRow.length; ++k) {
478 PdfPCell rcell = currentRow[k];
479 int cspan = rcell.getColspan();
480 rev -= cspan;
481 rtlRow[rev] = rcell;
482 k += cspan - 1;
483 }
484 currentRow = rtlRow;
485 }
486 PdfPRow row = new PdfPRow(currentRow);
487 if (totalWidth > 0) {
488 row.setWidths(absoluteWidths);
489 totalHeight += row.getMaxHeights();
490 }
491 rows.add(row);
492 currentRow = new PdfPCell[numCols];
493 currentRowIdx = 0;
494 rowCompleted = true;
495 }
496
497 if (!cellAdded) {
498 currentRow[currentRowIdx] = ncell;
499 currentRowIdx += colspan;
500 }
501 }
502
503 /**
504 * When updating the row index, cells with rowspan should be taken into account.
505 * This is what happens in this method.
506 * @since 2.1.6
507 */
508 private void skipColsWithRowspanAbove() {
509 int direction = 1;
510 if (runDirection == PdfWriter.RUN_DIRECTION_RTL)
511 direction = -1;
512 while (rowSpanAbove(rows.size(), currentRowIdx))
513 currentRowIdx += direction;
514 }
515
516 /**
517 * Checks if there are rows above belonging to a rowspan.
518 * @param currRow the current row to check
519 * @param currCol the current column to check
520 * @return true if there's a cell above that belongs to a rowspan
521 * @since 2.1.6
522 */
523 boolean rowSpanAbove(int currRow, int currCol) {
524
525 if ((currCol >= getNumberOfColumns())
526 || (currCol < 0)
527 || (currRow == 0))
528 return false;
529
530 int row = currRow - 1;
531 PdfPRow aboveRow = (PdfPRow)rows.get(row);
532 if (aboveRow == null)
533 return false;
534 PdfPCell aboveCell = (PdfPCell)aboveRow.getCells()[currCol];
535 while ((aboveCell == null) && (row > 0)) {
536 aboveRow = (PdfPRow)rows.get(--row);
537 if (aboveRow == null)
538 return false;
539 aboveCell = (PdfPCell)aboveRow.getCells()[currCol];
540 }
541
542 int distance = currRow - row;
543
544 if (aboveCell == null) {
545 int col = currCol - 1;
546 aboveCell = (PdfPCell)aboveRow.getCells()[col];
547 while ((aboveCell == null) && (row > 0))
548 aboveCell = (PdfPCell)aboveRow.getCells()[--col];
549 return aboveCell != null && aboveCell.getRowspan() > distance;
550 }
551
552 if ((aboveCell.getRowspan() == 1) && (distance > 1)) {
553 int col = currCol - 1;
554 aboveRow = (PdfPRow)rows.get(row + 1);
555 distance--;
556 aboveCell = (PdfPCell)aboveRow.getCells()[col];
557 while ((aboveCell == null) && (col > 0))
558 aboveCell = (PdfPCell)aboveRow.getCells()[--col];
559 }
560
561 return aboveCell != null && aboveCell.getRowspan() > distance;
562 }
563
564
565 /**
566 * Adds a cell element.
567 *
568 * @param text the text for the cell
569 */
570 public void addCell(String text) {
571 addCell(new Phrase(text));
572 }
573
574 /**
575 * Adds a nested table.
576 *
577 * @param table the table to be added to the cell
578 */
579 public void addCell(PdfPTable table) {
580 defaultCell.setTable(table);
581 addCell(defaultCell);
582 defaultCell.setTable(null);
583 }
584
585 /**
586 * Adds an Image as Cell.
587 *
588 * @param image the <CODE>Image</CODE> to add to the table.
589 * This image will fit in the cell
590 */
591 public void addCell(Image image) {
592 defaultCell.setImage(image);
593 addCell(defaultCell);
594 defaultCell.setImage(null);
595 }
596
597 /**
598 * Adds a cell element.
599 *
600 * @param phrase the <CODE>Phrase</CODE> to be added to the cell
601 */
602 public void addCell(Phrase phrase) {
603 defaultCell.setPhrase(phrase);
604 addCell(defaultCell);
605 defaultCell.setPhrase(null);
606 }
607
608 /**
609 * Writes the selected rows to the document.
610 * <CODE>canvases</CODE> is obtained from <CODE>beginWritingRows()</CODE>.
611 *
612 * @param rowStart the first row to be written, zero index
613 * @param rowEnd the last row to be written + 1. If it is -1 all the
614 * rows to the end are written
615 * @param xPos the x write coordinate
616 * @param yPos the y write coordinate
617 * @param canvases an array of 4 <CODE>PdfContentByte</CODE> obtained from
618 * <CODE>beginWrittingRows()</CODE>
619 * @return the y coordinate position of the bottom of the last row
620 * @see #beginWritingRows(com.lowagie.text.pdf.PdfContentByte)
621 */
622 public float writeSelectedRows(int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte[] canvases) {
623 return writeSelectedRows(0, -1, rowStart, rowEnd, xPos, yPos, canvases);
624 }
625
626 /**
627 * Writes the selected rows and columns to the document.
628 * This method does not clip the columns; this is only important
629 * if there are columns with colspan at boundaries.
630 * <CODE>canvases</CODE> is obtained from <CODE>beginWritingRows()</CODE>.
631 * The table event is only fired for complete rows.
632 *
633 * @param colStart the first column to be written, zero index
634 * @param colEnd the last column to be written + 1. If it is -1 all the
635 * columns to the end are written
636 * @param rowStart the first row to be written, zero index
637 * @param rowEnd the last row to be written + 1. If it is -1 all the
638 * rows to the end are written
639 * @param xPos the x write coordinate
640 * @param yPos the y write coordinate
641 * @param canvases an array of 4 <CODE>PdfContentByte</CODE> obtained from
642 * <CODE>beginWritingRows()</CODE>
643 * @return the y coordinate position of the bottom of the last row
644 * @see #beginWritingRows(com.lowagie.text.pdf.PdfContentByte)
645 */
646 public float writeSelectedRows(int colStart, int colEnd, int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte[] canvases) {
647 if (totalWidth <= 0)
648 throw new RuntimeException("The table width must be greater than zero.");
649
650 int totalRows = rows.size();
651 if (rowStart < 0)
652 rowStart = 0;
653 if (rowEnd < 0)
654 rowEnd = totalRows;
655 else
656 rowEnd = Math.min(rowEnd, totalRows);
657 if (rowStart >= rowEnd)
658 return yPos;
659
660 int totalCols = getNumberOfColumns();
661 if (colStart < 0)
662 colStart = 0;
663 else
664 colStart = Math.min(colStart, totalCols);
665 if (colEnd < 0)
666 colEnd = totalCols;
667 else
668 colEnd = Math.min(colEnd, totalCols);
669
670 float yPosStart = yPos;
671 for (int k = rowStart; k < rowEnd; ++k) {
672 PdfPRow row = (PdfPRow)rows.get(k);
673 if (row != null) {
674 row.writeCells(colStart, colEnd, xPos, yPos, canvases);
675 yPos -= row.getMaxHeights();
676 }
677 }
678
679 if (tableEvent != null && colStart == 0 && colEnd == totalCols) {
680 float heights[] = new float[rowEnd - rowStart + 1];
681 heights[0] = yPosStart;
682 for (int k = rowStart; k < rowEnd; ++k) {
683 PdfPRow row = (PdfPRow)rows.get(k);
684 float hr = 0;
685 if (row != null)
686 hr = row.getMaxHeights();
687 heights[k - rowStart + 1] = heights[k - rowStart] - hr;
688 }
689 tableEvent.tableLayout(this, getEventWidths(xPos, rowStart, rowEnd, headersInEvent), heights, headersInEvent ? headerRows : 0, rowStart, canvases);
690 }
691
692 return yPos;
693 }
694
695 /**
696 * Writes the selected rows to the document.
697 *
698 * @param rowStart the first row to be written, zero index
699 * @param rowEnd the last row to be written + 1. If it is -1 all the
700 * rows to the end are written
701 * @param xPos the x write coordinate
702 * @param yPos the y write coordinate
703 * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
704 * be written to
705 * @return the y coordinate position of the bottom of the last row
706 */
707 public float writeSelectedRows(int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte canvas) {
708 return writeSelectedRows(0, -1, rowStart, rowEnd, xPos, yPos, canvas);
709 }
710
711 /**
712 * Writes the selected rows and columns to the document.
713 * This method clips the columns; this is only important
714 * if there are columns with colspan at boundaries.
715 * The table event is only fired for complete rows.
716 *
717 * @param colStart the first column to be written, zero index
718 * @param colEnd the last column to be written + 1. If it is -1 all the
719 * columns to the end are written
720 * @param rowStart the first row to be written, zero index
721 * @param rowEnd the last row to be written + 1. If it is -1 all the
722 * rows to the end are written
723 * @param xPos the x write coordinate
724 * @param yPos the y write coordinate
725 * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
726 * be written to
727 * @return the y coordinate position of the bottom of the last row
728 */
729 public float writeSelectedRows(int colStart, int colEnd, int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte canvas) {
730 int totalCols = getNumberOfColumns();
731 if (colStart < 0)
732 colStart = 0;
733 else
734 colStart = Math.min(colStart, totalCols);
735
736 if (colEnd < 0)
737 colEnd = totalCols;
738 else
739 colEnd = Math.min(colEnd, totalCols);
740
741 boolean clip = (colStart != 0 || colEnd != totalCols);
742
743 if (clip) {
744 float w = 0;
745 for (int k = colStart; k < colEnd; ++k)
746 w += absoluteWidths[k];
747 canvas.saveState();
748 float lx = (colStart == 0) ? 10000 : 0;
749 float rx = (colEnd == totalCols) ? 10000 : 0;
750 canvas.rectangle(xPos - lx, -10000, w + lx + rx, PdfPRow.RIGHT_LIMIT);
751 canvas.clip();
752 canvas.newPath();
753 }
754
755 PdfContentByte[] canvases = beginWritingRows(canvas);
756 float y = writeSelectedRows(colStart, colEnd, rowStart, rowEnd, xPos, yPos, canvases);
757 endWritingRows(canvases);
758
759 if (clip)
760 canvas.restoreState();
761
762 return y;
763 }
764
765 /**
766 * Gets and initializes the 4 layers where the table is written to. The text or graphics are added to
767 * one of the 4 <CODE>PdfContentByte</CODE> returned with the following order:<p>
768 * <ul>
769 * <li><CODE>PdfPtable.BASECANVAS</CODE> - the original <CODE>PdfContentByte</CODE>. Anything placed here
770 * will be under the table.
771 * <li><CODE>PdfPtable.BACKGROUNDCANVAS</CODE> - the layer where the background goes to.
772 * <li><CODE>PdfPtable.LINECANVAS</CODE> - the layer where the lines go to.
773 * <li><CODE>PdfPtable.TEXTCANVAS</CODE> - the layer where the text go to. Anything placed here
774 * will be over the table.
775 * </ul><p>
776 * The layers are placed in sequence on top of each other.
777 *
778 * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
779 * be written to
780 * @return an array of 4 <CODE>PdfContentByte</CODE>
781 * @see #writeSelectedRows(int, int, float, float, PdfContentByte[])
782 */
783 public static PdfContentByte[] beginWritingRows(PdfContentByte canvas) {
784 return new PdfContentByte[]{
785 canvas,
786 canvas.getDuplicate(),
787 canvas.getDuplicate(),
788 canvas.getDuplicate(),
789 };
790 }
791
792 /**
793 * Finishes writing the table.
794 *
795 * @param canvases the array returned by <CODE>beginWritingRows()</CODE>
796 */
797 public static void endWritingRows(PdfContentByte[] canvases) {
798 PdfContentByte canvas = canvases[BASECANVAS];
799 canvas.saveState();
800 canvas.add(canvases[BACKGROUNDCANVAS]);
801 canvas.restoreState();
802 canvas.saveState();
803 canvas.setLineCap(2);
804 canvas.resetRGBColorStroke();
805 canvas.add(canvases[LINECANVAS]);
806 canvas.restoreState();
807 canvas.add(canvases[TEXTCANVAS]);
808 }
809
810 /**
811 * Gets the number of rows in this table.
812 *
813 * @return the number of rows in this table
814 */
815 public int size() {
816 return rows.size();
817 }
818
819 /**
820 * Gets the total height of the table.
821 *
822 * @return the total height of the table
823 */
824 public float getTotalHeight() {
825 return totalHeight;
826 }
827
828 /**
829 * Gets the height of a particular row.
830 *
831 * @param idx the row index (starts at 0)
832 * @return the height of a particular row
833 */
834 public float getRowHeight(int idx) {
835 return getRowHeight(idx, false);
836 }
837 /**
838 * Gets the height of a particular row.
839 *
840 * @param idx the row index (starts at 0)
841 * @param firsttime is this the first time the row heigh is calculated?
842 * @return the height of a particular row
843 * @since 3.0.0
844 */
845 public float getRowHeight(int idx, boolean firsttime) {
846 if (totalWidth <= 0 || idx < 0 || idx >= rows.size())
847 return 0;
848 PdfPRow row = (PdfPRow)rows.get(idx);
849 if (row == null)
850 return 0;
851 if (firsttime)
852 row.setWidths(absoluteWidths);
853 float height = row.getMaxHeights();
854 PdfPCell cell;
855 PdfPRow tmprow;
856 for (int i = 0; i < relativeWidths.length; i++) {
857 if(!rowSpanAbove(idx, i))
858 continue;
859 int rs = 1;
860 while (rowSpanAbove(idx - rs, i)) {
861 rs++;
862 }
863 tmprow = (PdfPRow)rows.get(idx - rs);
864 cell = tmprow.getCells()[i];
865 float tmp = 0;
866 if (cell.getRowspan() == rs + 1) {
867 tmp = cell.getMaxHeight();
868 while (rs > 0) {
869 tmp -= getRowHeight(idx - rs);
870 rs--;
871 }
872 }
873 if (tmp > height)
874 height = tmp;
875 }
876 row.setMaxHeights(height);
877 return height;
878 }
879
880 /**
881 * Gets the maximum height of a cell in a particular row (will only be different
882 * from getRowHeight is one of the cells in the row has a rowspan > 1).
883 *
884 * @param rowIndex the row index
885 * @param cellIndex the cell index
886 * @return the height of a particular row including rowspan
887 * @since 2.1.6
888 */
889 public float getRowspanHeight(int rowIndex, int cellIndex) {
890 if (totalWidth <= 0 || rowIndex < 0 || rowIndex >= rows.size())
891 return 0;
892 PdfPRow row = (PdfPRow)rows.get(rowIndex);
893 if (row == null || cellIndex >= row.getCells().length)
894 return 0;
895 PdfPCell cell = row.getCells()[cellIndex];
896 if (cell == null)
897 return 0;
898 float rowspanHeight = 0;
899 for (int j = 0; j < cell.getRowspan(); j++) {
900 rowspanHeight += getRowHeight(rowIndex + j);
901 }
902 return rowspanHeight;
903 }
904
905 /**
906 * Gets the height of the rows that constitute the header as defined by
907 * <CODE>setHeaderRows()</CODE>.
908 *
909 * @return the height of the rows that constitute the header and footer
910 */
911 public float getHeaderHeight() {
912 float total = 0;
913 int size = Math.min(rows.size(), headerRows);
914 for (int k = 0; k < size; ++k) {
915 PdfPRow row = (PdfPRow)rows.get(k);
916 if (row != null)
917 total += row.getMaxHeights();
918 }
919 return total;
920 }
921
922 /**
923 * Gets the height of the rows that constitute the footer as defined by
924 * <CODE>setFooterRows()</CODE>.
925 *
926 * @return the height of the rows that constitute the footer
927 * @since 2.1.1
928 */
929 public float getFooterHeight() {
930 float total = 0;
931 int start = Math.max(0, headerRows - footerRows);
932 int size = Math.min(rows.size(), headerRows);
933 for (int k = start; k < size; ++k) {
934 PdfPRow row = (PdfPRow)rows.get(k);
935 if (row != null)
936 total += row.getMaxHeights();
937 }
938 return total;
939 }
940
941 /**
942 * Deletes a row from the table.
943 *
944 * @param rowNumber the row to be deleted
945 * @return <CODE>true</CODE> if the row was deleted
946 */
947 public boolean deleteRow(int rowNumber) {
948 if (rowNumber < 0 || rowNumber >= rows.size())
949 return false;
950 if (totalWidth > 0) {
951 PdfPRow row = (PdfPRow)rows.get(rowNumber);
952 if (row != null)
953 totalHeight -= row.getMaxHeights();
954 }
955 rows.remove(rowNumber);
956 if (rowNumber < headerRows) {
957 --headerRows;
958 if (rowNumber >= (headerRows - footerRows))
959 --footerRows;
960 }
961 return true;
962 }
963
964 /**
965 * Deletes the last row in the table.
966 *
967 * @return <CODE>true</CODE> if the last row was deleted
968 */
969 public boolean deleteLastRow() {
970 return deleteRow(rows.size() - 1);
971 }
972
973 /**
974 * Removes all of the rows except headers
975 */
976 public void deleteBodyRows() {
977 ArrayList rows2 = new ArrayList();
978 for (int k = 0; k < headerRows; ++k)
979 rows2.add(rows.get(k));
980 rows = rows2;
981 totalHeight = 0;
982 if (totalWidth > 0)
983 totalHeight = getHeaderHeight();
984 }
985
986 /**
987 * Returns the number of columns.
988 *
989 * @return the number of columns.
990 * @since 2.1.1
991 */
992 public int getNumberOfColumns() {
993 return relativeWidths.length;
994 }
995
996 /**
997 * Gets the number of the rows that constitute the header.
998 *
999 * @return the number of the rows that constitute the header
1000 */
1001 public int getHeaderRows() {
1002 return headerRows;
1003 }
1004
1005 /**
1006 * Sets the number of the top rows that constitute the header.
1007 * This header has only meaning if the table is added to <CODE>Document</CODE>
1008 * and the table crosses pages.
1009 *
1010 * @param headerRows the number of the top rows that constitute the header
1011 */
1012 public void setHeaderRows(int headerRows) {
1013 if (headerRows < 0)
1014 headerRows = 0;
1015 this.headerRows = headerRows;
1016 }
1017
1018 /**
1019 * Gets all the chunks in this element.
1020 *
1021 * @return an <CODE>ArrayList</CODE>
1022 */
1023 public ArrayList getChunks() {
1024 return new ArrayList();
1025 }
1026
1027 /**
1028 * Gets the type of the text element.
1029 *
1030 * @return a type
1031 */
1032 public int type() {
1033 return Element.PTABLE;
1034 }
1035
1036 /**
1037 * @see com.lowagie.text.Element#isContent()
1038 * @since iText 2.0.8
1039 */
1040 public boolean isContent() {
1041 return true;
1042 }
1043
1044 /**
1045 * @see com.lowagie.text.Element#isNestable()
1046 * @since iText 2.0.8
1047 */
1048 public boolean isNestable() {
1049 return true;
1050 }
1051
1052 /**
1053 * Processes the element by adding it (or the different parts) to an
1054 * <CODE>ElementListener</CODE>.
1055 *
1056 * @param listener an <CODE>ElementListener</CODE>
1057 * @return <CODE>true</CODE> if the element was processed successfully
1058 */
1059 public boolean process(ElementListener listener) {
1060 try {
1061 return listener.add(this);
1062 }
1063 catch(DocumentException de) {
1064 return false;
1065 }
1066 }
1067
1068 /**
1069 * Gets the width percentage that the table will occupy in the page.
1070 *
1071 * @return the width percentage that the table will occupy in the page
1072 */
1073 public float getWidthPercentage() {
1074 return widthPercentage;
1075 }
1076
1077 /**
1078 * Sets the width percentage that the table will occupy in the page.
1079 *
1080 * @param widthPercentage the width percentage that the table will occupy in the page
1081 */
1082 public void setWidthPercentage(float widthPercentage) {
1083 this.widthPercentage = widthPercentage;
1084 }
1085
1086 /**
1087 * Gets the horizontal alignment of the table relative to the page.
1088 *
1089 * @return the horizontal alignment of the table relative to the page
1090 */
1091 public int getHorizontalAlignment() {
1092 return horizontalAlignment;
1093 }
1094
1095 /**
1096 * Sets the horizontal alignment of the table relative to the page.
1097 * It only has meaning if the width percentage is less than 100%.
1098 *
1099 * @param horizontalAlignment the horizontal alignment of the table
1100 * relative to the page
1101 */
1102 public void setHorizontalAlignment(int horizontalAlignment) {
1103 this.horizontalAlignment = horizontalAlignment;
1104 }
1105
1106 /**
1107 * Gets a row with a given index
1108 * (added by Jin-Hsia Yang).
1109 *
1110 * @param idx
1111 * @return the row at position idx
1112 */
1113 public PdfPRow getRow(int idx) {
1114 return (PdfPRow)rows.get(idx);
1115 }
1116
1117 /**
1118 * Gets an arraylist with all the rows in the table.
1119 *
1120 * @return an arraylist
1121 */
1122 public ArrayList getRows() {
1123 return rows;
1124 }
1125
1126 /**
1127 * Gets an arraylist with a selection of rows.
1128 * @param start the first row in the selection
1129 * @param end the first row that isn't part of the selection
1130 * @return a selection of rows
1131 * @since 2.1.6
1132 */
1133 public ArrayList getRows(int start, int end) {
1134 ArrayList list = new ArrayList();
1135 if (start < 0 || end > size()) {
1136 return list;
1137 }
1138 PdfPRow firstRow = adjustCellsInRow(start, end);
1139 int colIndex = 0;
1140 PdfPCell cell;
1141 while (colIndex < getNumberOfColumns()) {
1142 int rowIndex = start;
1143 while (rowSpanAbove(rowIndex--, colIndex)) {
1144 PdfPRow row = getRow(rowIndex);
1145 if (row != null) {
1146 PdfPCell replaceCell = row.getCells()[colIndex];
1147 if (replaceCell != null) {
1148 firstRow.getCells()[colIndex] = new PdfPCell(replaceCell);
1149 float extra = 0;
1150 int stop = Math.min(rowIndex + replaceCell.getRowspan(), end);
1151 for (int j = start + 1; j < stop; j++) {
1152 extra += getRowHeight(j);
1153 }
1154 firstRow.setExtraHeight(colIndex, extra);
1155 float diff = getRowspanHeight(rowIndex, colIndex)
1156 - getRowHeight(start) - extra;
1157 firstRow.getCells()[colIndex].consumeHeight(diff);
1158 }
1159 }
1160 }
1161 cell = firstRow.getCells()[colIndex];
1162 if (cell == null)
1163 colIndex++;
1164 else
1165 colIndex += cell.getColspan();
1166 }
1167 list.add(firstRow);
1168 for (int i = start + 1; i < end; i++) {
1169 list.add(adjustCellsInRow(i, end));
1170 }
1171 return list;
1172 }
1173
1174 /**
1175 * Calculates the extra height needed in a row because of rowspans.
1176 * @param start the index of the start row (the one to adjust)
1177 * @param end the index of the end row on the page
1178 * @since 2.1.6
1179 */
1180 protected PdfPRow adjustCellsInRow(int start, int end) {
1181 PdfPRow row = new PdfPRow(getRow(start));
1182 row.initExtraHeights();
1183 PdfPCell cell;
1184 PdfPCell[] cells = row.getCells();
1185 for (int i = 0; i < cells.length; i++) {
1186 cell = cells[i];
1187 if (cell == null || cell.getRowspan() == 1)
1188 continue;
1189 int stop = Math.min(end, start + cell.getRowspan());
1190 float extra = 0;
1191 for (int k = start + 1; k < stop; k++) {
1192 extra += getRowHeight(k);
1193 }
1194 row.setExtraHeight(i, extra);
1195 }
1196 return row;
1197 }
1198
1199 /** Sets the table event for this table.
1200 * @param event the table event for this table
1201 */
1202 public void setTableEvent(PdfPTableEvent event) {
1203 if (event == null)
1204 this.tableEvent = null;
1205 else if (this.tableEvent == null)
1206 this.tableEvent = event;
1207 else if (this.tableEvent instanceof PdfPTableEventForwarder)
1208 ((PdfPTableEventForwarder)this.tableEvent).addTableEvent(event);
1209 else {
1210 PdfPTableEventForwarder forward = new PdfPTableEventForwarder();
1211 forward.addTableEvent(this.tableEvent);
1212 forward.addTableEvent(event);
1213 this.tableEvent = forward;
1214 }
1215 }
1216
1217 /**
1218 * Gets the table event for this page.
1219 *
1220 * @return the table event for this page
1221 */
1222 public PdfPTableEvent getTableEvent() {
1223 return tableEvent;
1224 }
1225
1226 /**
1227 * Gets the absolute sizes of each column width.
1228 *
1229 * @return he absolute sizes of each column width
1230 */
1231 public float[] getAbsoluteWidths() {
1232 return absoluteWidths;
1233 }
1234
1235 float [][] getEventWidths(float xPos, int firstRow, int lastRow, boolean includeHeaders) {
1236 if (includeHeaders) {
1237 firstRow = Math.max(firstRow, headerRows);
1238 lastRow = Math.max(lastRow, headerRows);
1239 }
1240 float widths[][] = new float[(includeHeaders ? headerRows : 0) + lastRow - firstRow][];
1241 if (isColspan) {
1242 int n = 0;
1243 if (includeHeaders) {
1244 for (int k = 0; k < headerRows; ++k) {
1245 PdfPRow row = (PdfPRow)rows.get(k);
1246 if (row == null)
1247 ++n;
1248 else
1249 widths[n++] = row.getEventWidth(xPos);
1250 }
1251 }
1252 for (; firstRow < lastRow; ++firstRow) {
1253 PdfPRow row = (PdfPRow)rows.get(firstRow);
1254 if (row == null)
1255 ++n;
1256 else
1257 widths[n++] = row.getEventWidth(xPos);
1258 }
1259 }
1260 else {
1261 int numCols = getNumberOfColumns();
1262 float width[] = new float[numCols + 1];
1263 width[0] = xPos;
1264 for (int k = 0; k < numCols; ++k)
1265 width[k + 1] = width[k] + absoluteWidths[k];
1266 for (int k = 0; k < widths.length; ++k)
1267 widths[k] = width;
1268 }
1269 return widths;
1270 }
1271
1272
1273 /**
1274 * Tells you if the first header needs to be skipped
1275 * (for instance if the header says "continued from the previous page").
1276 *
1277 * @return Value of property skipFirstHeader.
1278 */
1279 public boolean isSkipFirstHeader() {
1280 return skipFirstHeader;
1281 }
1282
1283
1284 /**
1285 * Tells you if the last footer needs to be skipped
1286 * (for instance if the footer says "continued on the next page")
1287 *
1288 * @return Value of property skipLastFooter.
1289 * @since 2.1.6
1290 */
1291 public boolean isSkipLastFooter() {
1292 return skipLastFooter;
1293 }
1294
1295 /**
1296 * Skips the printing of the first header. Used when printing
1297 * tables in succession belonging to the same printed table aspect.
1298 *
1299 * @param skipFirstHeader New value of property skipFirstHeader.
1300 */
1301 public void setSkipFirstHeader(boolean skipFirstHeader) {
1302 this.skipFirstHeader = skipFirstHeader;
1303 }
1304
1305 /**
1306 * Skips the printing of the last footer. Used when printing
1307 * tables in succession belonging to the same printed table aspect.
1308 *
1309 * @param skipLastFooter New value of property skipLastFooter.
1310 * @since 2.1.6
1311 */
1312 public void setSkipLastFooter(boolean skipLastFooter) {
1313 this.skipLastFooter = skipLastFooter;
1314 }
1315
1316 /**
1317 * Sets the run direction of the contents of the table.
1318 *
1319 * @param runDirection One of the following values:
1320 * PdfWriter.RUN_DIRECTION_DEFAULT, PdfWriter.RUN_DIRECTION_NO_BIDI,
1321 * PdfWriter.RUN_DIRECTION_LTR or PdfWriter.RUN_DIRECTION_RTL.
1322 */
1323 public void setRunDirection(int runDirection) {
1324 switch (runDirection) {
1325 case PdfWriter.RUN_DIRECTION_DEFAULT:
1326 case PdfWriter.RUN_DIRECTION_NO_BIDI:
1327 case PdfWriter.RUN_DIRECTION_LTR:
1328 case PdfWriter.RUN_DIRECTION_RTL:
1329 this.runDirection = runDirection;
1330 break;
1331 default:
1332 throw new RuntimeException("Invalid run direction: " + runDirection);
1333 }
1334 }
1335
1336 /**
1337 * Returns the run direction of the contents in the table.
1338 *
1339 * @return One of the following values:
1340 * PdfWriter.RUN_DIRECTION_DEFAULT, PdfWriter.RUN_DIRECTION_NO_BIDI,
1341 * PdfWriter.RUN_DIRECTION_LTR or PdfWriter.RUN_DIRECTION_RTL.
1342 */
1343 public int getRunDirection() {
1344 return runDirection;
1345 }
1346
1347 /**
1348 * Getter for property lockedWidth.
1349 *
1350 * @return Value of property lockedWidth.
1351 */
1352 public boolean isLockedWidth() {
1353 return this.lockedWidth;
1354 }
1355
1356 /**
1357 * Uses the value in <CODE>setTotalWidth()</CODE> in <CODE>Document.add()</CODE>.
1358 *
1359 * @param lockedWidth <CODE>true</CODE> to use the value in <CODE>setTotalWidth()</CODE> in <CODE>Document.add()</CODE>
1360 */
1361 public void setLockedWidth(boolean lockedWidth) {
1362 this.lockedWidth = lockedWidth;
1363 }
1364
1365 /**
1366 * Gets the split value.
1367 *
1368 * @return true to split; false otherwise
1369 */
1370 public boolean isSplitRows() {
1371 return this.splitRows;
1372 }
1373
1374 /**
1375 * When set the rows that won't fit in the page will be split.
1376 * Note that it takes at least twice the memory to handle a split table row
1377 * than a normal table. <CODE>true</CODE> by default.
1378 *
1379 * @param splitRows true to split; false otherwise
1380 */
1381 public void setSplitRows(boolean splitRows) {
1382 this.splitRows = splitRows;
1383 }
1384
1385 /**
1386 * Sets the spacing before this table.
1387 *
1388 * @param spacing the new spacing
1389 */
1390 public void setSpacingBefore(float spacing) {
1391 this.spacingBefore = spacing;
1392 }
1393
1394 /**
1395 * Sets the spacing after this table.
1396 *
1397 * @param spacing the new spacing
1398 */
1399 public void setSpacingAfter(float spacing) {
1400 this.spacingAfter = spacing;
1401 }
1402
1403 /**
1404 * Gets the spacing before this table.
1405 *
1406 * @return the spacing
1407 */
1408 public float spacingBefore() {
1409 return spacingBefore;
1410 }
1411
1412 /**
1413 * Gets the spacing after this table.
1414 *
1415 * @return the spacing
1416 */
1417 public float spacingAfter() {
1418 return spacingAfter;
1419 }
1420
1421 /**
1422 * Gets the value of the last row extension.
1423 *
1424 * @return true if the last row will extend; false otherwise
1425 */
1426 public boolean isExtendLastRow() {
1427 return extendLastRow;
1428 }
1429
1430 /**
1431 * When set the last row will be extended to fill all the remaining space
1432 * to the bottom boundary.
1433 *
1434 * @param extendLastRow true to extend the last row; false otherwise
1435 */
1436 public void setExtendLastRow(boolean extendLastRow) {
1437 this.extendLastRow = extendLastRow;
1438 }
1439
1440 /**
1441 * Gets the header status inclusion in PdfPTableEvent.
1442 *
1443 * @return true if the headers are included; false otherwise
1444 */
1445 public boolean isHeadersInEvent() {
1446 return headersInEvent;
1447 }
1448
1449 /**
1450 * When set the PdfPTableEvent will include the headers.
1451 *
1452 * @param headersInEvent true to include the headers; false otherwise
1453 */
1454 public void setHeadersInEvent(boolean headersInEvent) {
1455 this.headersInEvent = headersInEvent;
1456 }
1457
1458 /**
1459 * Gets the property splitLate.
1460 *
1461 * @return the property splitLate
1462 */
1463 public boolean isSplitLate() {
1464 return splitLate;
1465 }
1466
1467 /**
1468 * If true the row will only split if it's the first one in an empty page.
1469 * It's true by default.
1470 * It's only meaningful if setSplitRows(true).
1471 *
1472 * @param splitLate the property value
1473 */
1474 public void setSplitLate(boolean splitLate) {
1475 this.splitLate = splitLate;
1476 }
1477
1478 /**
1479 * If true the table will be kept on one page if it fits, by forcing a
1480 * new page if it doesn't fit on the current page. The default is to
1481 * split the table over multiple pages.
1482 *
1483 * @param keepTogether whether to try to keep the table on one page
1484 */
1485 public void setKeepTogether(boolean keepTogether) {
1486 this.keepTogether = keepTogether;
1487 }
1488
1489 /**
1490 * Getter for property keepTogether
1491 *
1492 * @return true if it is tried to keep the table on one page;
1493 * false otherwise
1494 */
1495 public boolean getKeepTogether() {
1496 return keepTogether;
1497 }
1498
1499 /**
1500 * Gets the number of rows in the footer.
1501 *
1502 * @return the number of rows in the footer
1503 */
1504 public int getFooterRows() {
1505 return this.footerRows;
1506 }
1507
1508 /**
1509 * Sets the number of rows to be used for the footer. The number
1510 * of footer rows are subtracted from the header rows. For
1511 * example, for a table with two header rows and one footer row the
1512 * code would be:
1513 * <pre>
1514 * table.setHeaderRows(3);
1515 * table.setFooterRows(1);
1516 * </pre>
1517 * Row 0 and 1 will be the header rows and row 2 will be the footer row.
1518 *
1519 * @param footerRows the number of rows to be used for the footer
1520 */
1521 public void setFooterRows(int footerRows) {
1522 if (footerRows < 0)
1523 footerRows = 0;
1524 this.footerRows = footerRows;
1525 }
1526
1527 /**
1528 * Completes the current row with the default cell. An incomplete row will
1529 * be dropped but calling this method will make sure that it will be
1530 * present in the table.
1531 */
1532 public void completeRow() {
1533 while (!rowCompleted) {
1534 addCell(defaultCell);
1535 }
1536 }
1537
1538 /**
1539 * @since iText 2.0.8
1540 * @see com.lowagie.text.LargeElement#flushContent()
1541 */
1542 public void flushContent() {
1543 deleteBodyRows();
1544 setSkipFirstHeader(true);
1545 }
1546
1547 /**
1548 * @since iText 2.0.8
1549 * @see com.lowagie.text.LargeElement#isComplete()
1550 */
1551 public boolean isComplete() {
1552 return complete;
1553 }
1554
1555 /**
1556 * @since iText 2.0.8
1557 * @see com.lowagie.text.LargeElement#setComplete(boolean)
1558 */
1559 public void setComplete(boolean complete) {
1560 this.complete = complete;
1561 }
1562 }