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(intintfloatfloat, 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 }