001/*
002 * $Id: PdfPTable.java 4862 2011-05-11 15:57:42Z redlab_b $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf;
045
046import java.util.ArrayList;
047import java.util.List;
048
049import com.itextpdf.text.Chunk;
050import com.itextpdf.text.DocumentException;
051import com.itextpdf.text.Element;
052import com.itextpdf.text.ElementListener;
053import com.itextpdf.text.Image;
054import com.itextpdf.text.LargeElement;
055import com.itextpdf.text.Phrase;
056import com.itextpdf.text.Rectangle;
057import com.itextpdf.text.api.Spaceable;
058import com.itextpdf.text.error_messages.MessageLocalization;
059import com.itextpdf.text.pdf.events.PdfPTableEventForwarder;
060
061/**
062 * This is a table that can be put at an absolute position but can also
063 * be added to the document as the class <CODE>Table</CODE>.
064 * <P>
065 * A PdfPTableEvent can be associated to the table to do custom drawing
066 * when the table is rendered.
067 * @author Paulo Soares
068 */
069
070public class PdfPTable implements LargeElement, Spaceable{
071
072    /**
073     * The index of the original <CODE>PdfcontentByte</CODE>.
074     */
075    public static final int BASECANVAS = 0;
076
077    /**
078     * The index of the duplicate <CODE>PdfContentByte</CODE> where the background will be drawn.
079     */
080    public static final int BACKGROUNDCANVAS = 1;
081
082    /**
083     * The index of the duplicate <CODE>PdfContentByte</CODE> where the border lines will be drawn.
084     */
085    public static final int LINECANVAS = 2;
086
087    /**
088     * The index of the duplicate <CODE>PdfContentByte</CODE> where the text will be drawn.
089     */
090    public static final int TEXTCANVAS = 3;
091
092    protected ArrayList<PdfPRow> rows = new ArrayList<PdfPRow>();
093    protected float totalHeight = 0;
094    protected PdfPCell currentRow[];
095    /**
096     * The current column index.
097     * @since 5.1.0 renamed from currentRowIdx
098     */
099    protected int currentColIdx = 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 = { false, false };
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(final float relativeWidths[]) {
203        if (relativeWidths == null)
204            throw new NullPointerException(MessageLocalization.getComposedMessage("the.widths.array.in.pdfptable.constructor.can.not.be.null"));
205        if (relativeWidths.length == 0)
206            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("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(final int numColumns) {
221        if (numColumns <= 0)
222            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("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(final 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 = 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(final 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(final 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        currentColIdx = 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(final float relativeWidths[]) throws DocumentException {
308        if (relativeWidths.length != getNumberOfColumns())
309            throw new DocumentException(MessageLocalization.getComposedMessage("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();
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(final 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(final float totalWidth) {
352        if (this.totalWidth == totalWidth)
353            return;
354        this.totalWidth = totalWidth;
355        totalHeight = 0;
356        calculateWidths();
357        calculateHeights();
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(final float columnWidth[]) throws DocumentException {
368        if (columnWidth.length != getNumberOfColumns())
369            throw new DocumentException(MessageLocalization.getComposedMessage("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(final float columnWidth[], final Rectangle pageSize) throws DocumentException {
384        if (columnWidth.length != getNumberOfColumns())
385            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("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     * @return  the total height of the table. Note that it will be 0 if you didn't
406     * specify the width of the table with setTotalWidth().
407     * and made it public
408     */
409    public float calculateHeights() {
410        if (totalWidth <= 0)
411            return 0;
412        totalHeight = 0;
413        for (int k = 0; k < rows.size(); ++k) {
414                totalHeight += getRowHeight(k, true);
415        }
416        return totalHeight;
417    }
418
419    /**
420     * Changes the number of columns. Any existing rows will be deleted.
421     * @param newColCount the new number of columns
422     * @since 5.0.2
423     */
424    public void resetColumnCount(final int newColCount) {
425        if (newColCount <= 0)
426            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.number.of.columns.in.pdfptable.constructor.must.be.greater.than.zero"));
427        relativeWidths = new float[newColCount];
428        for (int k = 0; k < newColCount; ++k)
429            relativeWidths[k] = 1;
430        absoluteWidths = new float[relativeWidths.length];
431        calculateWidths();
432        currentRow = new PdfPCell[absoluteWidths.length];
433        totalHeight = 0;
434    }
435
436    /**
437     * Gets the default <CODE>PdfPCell</CODE> that will be used as
438     * reference for all the <CODE>addCell</CODE> methods except
439     * <CODE>addCell(PdfPCell)</CODE>.
440     *
441     * @return default <CODE>PdfPCell</CODE>
442     */
443    public PdfPCell getDefaultCell() {
444        return defaultCell;
445    }
446
447    /**
448     * Adds a cell element.
449     *
450     * @param cell the cell element
451     */
452    public void addCell(final PdfPCell cell) {
453        rowCompleted = false;
454        PdfPCell ncell = new PdfPCell(cell);
455
456        int colspan = ncell.getColspan();
457        colspan = Math.max(colspan, 1);
458        colspan = Math.min(colspan, currentRow.length - currentColIdx);
459        ncell.setColspan(colspan);
460
461        if (colspan != 1)
462            isColspan = true;
463        int rdir = ncell.getRunDirection();
464        if (rdir == PdfWriter.RUN_DIRECTION_DEFAULT)
465            ncell.setRunDirection(runDirection);
466
467        skipColsWithRowspanAbove();
468
469        boolean cellAdded = false;
470        if (currentColIdx < currentRow.length) {
471                currentRow[currentColIdx] = ncell;
472                currentColIdx += colspan;
473                cellAdded = true;
474        }
475
476        skipColsWithRowspanAbove();
477
478        while (currentColIdx >= currentRow.length) {
479                int numCols = getNumberOfColumns();
480            if (runDirection == PdfWriter.RUN_DIRECTION_RTL) {
481                PdfPCell rtlRow[] = new PdfPCell[numCols];
482                int rev = currentRow.length;
483                for (int k = 0; k < currentRow.length; ++k) {
484                    PdfPCell rcell = currentRow[k];
485                    int cspan = rcell.getColspan();
486                    rev -= cspan;
487                    rtlRow[rev] = rcell;
488                    k += cspan - 1;
489                }
490                currentRow = rtlRow;
491            }
492            PdfPRow row = new PdfPRow(currentRow);
493            if (totalWidth > 0) {
494                row.setWidths(absoluteWidths);
495                totalHeight += row.getMaxHeights();
496            }
497            rows.add(row);
498            currentRow = new PdfPCell[numCols];
499            currentColIdx = 0;
500            skipColsWithRowspanAbove();
501            rowCompleted = true;
502        }
503
504        if (!cellAdded) {
505            currentRow[currentColIdx] = ncell;
506            currentColIdx += colspan;
507        }
508    }
509
510    /**
511     * When updating the row index, cells with rowspan should be taken into account.
512     * This is what happens in this method.
513     * @since   2.1.6
514     */
515    private void skipColsWithRowspanAbove() {
516        int direction = 1;
517        if (runDirection == PdfWriter.RUN_DIRECTION_RTL)
518                direction = -1;
519        while (rowSpanAbove(rows.size(), currentColIdx))
520                currentColIdx += direction;
521    }
522
523    /**
524     * Added by timmo3.  This will return the correct cell taking it's cellspan into account
525     * @param row the row index
526     * @param col the column index
527     * @return PdfPCell at the given row and position or null otherwise
528     */
529    PdfPCell cellAt(final int row, final int col) {
530        PdfPCell[] cells = rows.get(row).getCells();
531        for (int i = 0; i < cells.length; i++) {
532            if (cells[i] != null) {
533                if (col >= i && col < (i + cells[i].getColspan())) {
534                    return cells[i];
535                }
536            }
537        }
538        return null;
539    }
540
541    /**
542     * Checks if there are rows above belonging to a rowspan.
543     * @param   currRow the current row to check
544     * @param   currCol the current column to check
545     * @return  true if there's a cell above that belongs to a rowspan
546     * @since   2.1.6
547     */
548    boolean rowSpanAbove(final int currRow, final int currCol) {
549        if (currCol >= getNumberOfColumns()
550                        || currCol < 0
551                        || currRow < 1)
552                return false;
553        int row = currRow - 1;
554        PdfPRow aboveRow = rows.get(row);
555        if (aboveRow == null)
556                return false;
557        PdfPCell aboveCell = cellAt(row, currCol);
558        while (aboveCell == null && row > 0) {
559                aboveRow  = rows.get(--row);
560                if (aboveRow == null)
561                        return false;
562                aboveCell = cellAt(row, currCol);
563        }
564
565        int distance = currRow - row;
566
567        if (aboveCell.getRowspan() == 1 && distance > 1) {
568                int col = currCol - 1;
569                aboveRow = rows.get(row + 1);
570                distance--;
571                aboveCell = aboveRow.getCells()[col];
572                while (aboveCell == null && col > 0)
573                        aboveCell = aboveRow.getCells()[--col];
574        }
575
576        return aboveCell != null && aboveCell.getRowspan() > distance;
577    }
578
579
580    /**
581     * Adds a cell element.
582     *
583     * @param text the text for the cell
584     */
585    public void addCell(final String text) {
586        addCell(new Phrase(text));
587    }
588
589    /**
590     * Adds a nested table.
591     *
592     * @param table the table to be added to the cell
593     */
594    public void addCell(final PdfPTable table) {
595        defaultCell.setTable(table);
596        addCell(defaultCell);
597        defaultCell.setTable(null);
598    }
599
600    /**
601     * Adds an Image as Cell.
602     *
603     * @param image the <CODE>Image</CODE> to add to the table.
604     * This image will fit in the cell
605     */
606    public void addCell(final Image image) {
607        defaultCell.setImage(image);
608        addCell(defaultCell);
609        defaultCell.setImage(null);
610    }
611
612    /**
613     * Adds a cell element.
614     *
615     * @param phrase the <CODE>Phrase</CODE> to be added to the cell
616     */
617    public void addCell(final Phrase phrase) {
618        defaultCell.setPhrase(phrase);
619        addCell(defaultCell);
620        defaultCell.setPhrase(null);
621    }
622
623    /**
624     * Writes the selected rows to the document.
625     * <CODE>canvases</CODE> is obtained from <CODE>beginWritingRows()</CODE>.
626     *
627     * @param rowStart the first row to be written, zero index
628     * @param rowEnd the last row to be written + 1. If it is -1 all the
629     * rows to the end are written
630     * @param xPos the x write coordinate
631     * @param yPos the y write coordinate
632     * @param canvases an array of 4 <CODE>PdfContentByte</CODE> obtained from
633     * <CODE>beginWrittingRows()</CODE>
634     * @return the y coordinate position of the bottom of the last row
635     * @see #beginWritingRows(com.itextpdf.text.pdf.PdfContentByte)
636     */
637    public float writeSelectedRows(final int rowStart, final int rowEnd, final float xPos, final float yPos, final PdfContentByte[] canvases) {
638        return writeSelectedRows(0, -1, rowStart, rowEnd, xPos, yPos, canvases);
639    }
640
641    /**
642     * Writes the selected rows and columns to the document.
643     * This method does not clip the columns; this is only important
644     * if there are columns with colspan at boundaries.
645     * <CODE>canvases</CODE> is obtained from <CODE>beginWritingRows()</CODE>.
646     * The table event is only fired for complete rows.
647     *
648     * @param colStart the first column to be written, zero index
649     * @param colEnd the last column to be written + 1. If it is -1 all the
650     * columns to the end are written
651     * @param rowStart the first row to be written, zero index
652     * @param rowEnd the last row to be written + 1. If it is -1 all the
653     * rows to the end are written
654     * @param xPos the x write coordinate
655     * @param yPos the y write coordinate
656     * @param canvases an array of 4 <CODE>PdfContentByte</CODE> obtained from
657     * <CODE>beginWritingRows()</CODE>
658     * @return the y coordinate position of the bottom of the last row
659     * @see #beginWritingRows(com.itextpdf.text.pdf.PdfContentByte)
660     */
661    public float writeSelectedRows(final int colStart, final int colEnd, final int rowStart, final int rowEnd, final float xPos, final float yPos, final PdfContentByte[] canvases) {
662        return writeSelectedRows(colStart, colEnd, rowStart, rowEnd, xPos, yPos, canvases, true);
663    }
664
665    /**
666     * Writes the selected rows and columns to the document.
667     * This method does not clip the columns; this is only important
668     * if there are columns with colspan at boundaries.
669     * <CODE>canvases</CODE> is obtained from <CODE>beginWritingRows()</CODE>.
670     * The table event is only fired for complete rows.
671     *
672     * @param colStart the first column to be written, zero index
673     * @param colEnd the last column to be written + 1. If it is -1 all the
674     * columns to the end are written
675     * @param rowStart the first row to be written, zero index
676     * @param rowEnd the last row to be written + 1. If it is -1 all the
677     * rows to the end are written
678     * @param xPos the x write coordinate
679     * @param yPos the y write coordinate
680     * @param canvases an array of 4 <CODE>PdfContentByte</CODE> obtained from
681     * <CODE>beginWritingRows()</CODE>
682     * @param   reusable if set to false, the content in the cells is "consumed";
683         * if true, you can reuse the cells, the row, the parent table as many times you want.
684     * @return the y coordinate position of the bottom of the last row
685     * @see #beginWritingRows(com.itextpdf.text.pdf.PdfContentByte)
686     * @since 5.1.0 added the reusable parameter
687     */
688    public float writeSelectedRows(int colStart, int colEnd, int rowStart, int rowEnd, final float xPos, float yPos, final PdfContentByte[] canvases, final boolean reusable) {
689        if (totalWidth <= 0)
690            throw new RuntimeException(MessageLocalization.getComposedMessage("the.table.width.must.be.greater.than.zero"));
691
692        int totalRows = rows.size();
693        if (rowStart < 0)
694            rowStart = 0;
695        if (rowEnd < 0)
696            rowEnd = totalRows;
697        else
698                rowEnd = Math.min(rowEnd, totalRows);
699        if (rowStart >= rowEnd)
700            return yPos;
701
702        int totalCols = getNumberOfColumns();
703        if (colStart < 0)
704            colStart = 0;
705        else
706                colStart = Math.min(colStart, totalCols);
707        if (colEnd < 0)
708            colEnd = totalCols;
709        else
710                colEnd = Math.min(colEnd, totalCols);
711
712        float yPosStart = yPos;
713        for (int k = rowStart; k < rowEnd; ++k) {
714            PdfPRow row = rows.get(k);
715            if (row != null) {
716                row.writeCells(colStart, colEnd, xPos, yPos, canvases, reusable);
717                yPos -= row.getMaxHeights();
718            }
719        }
720
721        if (tableEvent != null && colStart == 0 && colEnd == totalCols) {
722            float heights[] = new float[rowEnd - rowStart + 1];
723            heights[0] = yPosStart;
724            for (int k = rowStart; k < rowEnd; ++k) {
725                PdfPRow row = rows.get(k);
726                float hr = 0;
727                if (row != null)
728                    hr = row.getMaxHeights();
729                heights[k - rowStart + 1] = heights[k - rowStart] - hr;
730            }
731            tableEvent.tableLayout(this, getEventWidths(xPos, rowStart, rowEnd, headersInEvent), heights, headersInEvent ? headerRows : 0, rowStart, canvases);
732        }
733
734        return yPos;
735    }
736
737    /**
738     * Writes the selected rows to the document.
739     *
740     * @param rowStart the first row to be written, zero index
741     * @param rowEnd the last row to be written + 1. If it is -1 all the
742     * rows to the end are written
743     * @param xPos the x write coordinate
744     * @param yPos the y write coordinate
745     * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
746     * be written to
747     * @return the y coordinate position of the bottom of the last row
748     */
749    public float writeSelectedRows(final int rowStart, final int rowEnd, final float xPos, final float yPos, final PdfContentByte canvas) {
750        return writeSelectedRows(0, -1, rowStart, rowEnd, xPos, yPos, canvas);
751    }
752
753    /**
754     * Writes the selected rows and columns to the document.
755     * This method clips the columns; this is only important
756     * if there are columns with colspan at boundaries.
757     * The table event is only fired for complete rows.
758     *
759     * @param colStart the first column to be written, zero index
760     * @param colEnd the last column to be written + 1. If it is -1 all the
761     * columns to the end are written
762     * @param rowStart the first row to be written, zero index
763     * @param rowEnd the last row to be written + 1. If it is -1 all the
764     * rows to the end are written
765     * @param xPos the x write coordinate
766     * @param yPos the y write coordinate
767     * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
768     * be written to
769     * @return the y coordinate position of the bottom of the last row
770     */
771    public float writeSelectedRows(final int colStart, final int colEnd, final int rowStart, final int rowEnd, final float xPos, final float yPos, final PdfContentByte canvas) {
772        return writeSelectedRows(colStart, colEnd, rowStart, rowEnd, xPos, yPos, canvas, true);
773    }
774
775
776    /**
777     * Writes the selected rows and columns to the document.
778     * This method clips the columns; this is only important
779     * if there are columns with colspan at boundaries.
780     * The table event is only fired for complete rows.
781     *
782     * @param colStart the first column to be written, zero index
783     * @param colEnd the last column to be written + 1. If it is -1 all the
784     * columns to the end are written
785     * @param rowStart the first row to be written, zero index
786     * @param rowEnd the last row to be written + 1. If it is -1 all the
787     * rows to the end are written
788     * @param xPos the x write coordinate
789     * @param yPos the y write coordinate
790     * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
791     * be written to
792     * @param   reusable if set to false, the content in the cells is "consumed";
793         * if true, you can reuse the cells, the row, the parent table as many times you want.
794     * @return the y coordinate position of the bottom of the last row
795     * @since 5.1.0 added the reusable parameter
796     */
797    public float writeSelectedRows(int colStart, int colEnd, final int rowStart, final int rowEnd, final float xPos, final float yPos, final PdfContentByte canvas, final boolean reusable) {
798        int totalCols = getNumberOfColumns();
799        if (colStart < 0)
800            colStart = 0;
801        else
802                colStart = Math.min(colStart, totalCols);
803
804        if (colEnd < 0)
805            colEnd = totalCols;
806        else
807                colEnd = Math.min(colEnd, totalCols);
808
809        boolean clip = colStart != 0 || colEnd != totalCols;
810
811        if (clip) {
812            float w = 0;
813            for (int k = colStart; k < colEnd; ++k)
814                w += absoluteWidths[k];
815            canvas.saveState();
816            float lx = colStart == 0 ? 10000 : 0;
817            float rx = colEnd == totalCols ? 10000 : 0;
818            canvas.rectangle(xPos - lx, -10000, w + lx + rx, PdfPRow.RIGHT_LIMIT);
819            canvas.clip();
820            canvas.newPath();
821        }
822
823        PdfContentByte[] canvases = beginWritingRows(canvas);
824        float y = writeSelectedRows(colStart, colEnd, rowStart, rowEnd, xPos, yPos, canvases, reusable);
825        endWritingRows(canvases);
826
827        if (clip)
828            canvas.restoreState();
829
830        return y;
831    }
832
833    /**
834     * Gets and initializes the 4 layers where the table is written to. The text or graphics are added to
835     * one of the 4 <CODE>PdfContentByte</CODE> returned with the following order:<p>
836     * <ul>
837     * <li><CODE>PdfPtable.BASECANVAS</CODE> - the original <CODE>PdfContentByte</CODE>. Anything placed here
838     * will be under the table.
839     * <li><CODE>PdfPtable.BACKGROUNDCANVAS</CODE> - the layer where the background goes to.
840     * <li><CODE>PdfPtable.LINECANVAS</CODE> - the layer where the lines go to.
841     * <li><CODE>PdfPtable.TEXTCANVAS</CODE> - the layer where the text go to. Anything placed here
842     * will be over the table.
843     * </ul><p>
844     * The layers are placed in sequence on top of each other.
845     *
846     * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
847     * be written to
848     * @return an array of 4 <CODE>PdfContentByte</CODE>
849     * @see #writeSelectedRows(int, int, float, float, PdfContentByte[])
850     */
851    public static PdfContentByte[] beginWritingRows(final PdfContentByte canvas) {
852        return new PdfContentByte[]{
853            canvas,
854            canvas.getDuplicate(),
855            canvas.getDuplicate(),
856            canvas.getDuplicate(),
857        };
858    }
859
860    /**
861     * Finishes writing the table.
862     *
863     * @param canvases the array returned by <CODE>beginWritingRows()</CODE>
864     */
865    public static void endWritingRows(final PdfContentByte[] canvases) {
866        PdfContentByte canvas = canvases[BASECANVAS];
867        canvas.saveState();
868        canvas.add(canvases[BACKGROUNDCANVAS]);
869        canvas.restoreState();
870        canvas.saveState();
871        canvas.setLineCap(2);
872        canvas.resetRGBColorStroke();
873        canvas.add(canvases[LINECANVAS]);
874        canvas.restoreState();
875        canvas.add(canvases[TEXTCANVAS]);
876    }
877
878    /**
879     * Gets the number of rows in this table.
880     *
881     * @return the number of rows in this table
882     */
883    public int size() {
884        return rows.size();
885    }
886
887    /**
888     * Gets the total height of the table.
889     *
890     * @return the total height of the table
891     */
892    public float getTotalHeight() {
893        return totalHeight;
894    }
895
896    /**
897     * Gets the height of a particular row.
898     *
899     * @param idx the row index (starts at 0)
900     * @return the height of a particular row
901     */
902    public float getRowHeight(final int idx) {
903        return getRowHeight(idx, false);
904    }
905    /**
906     * Gets the height of a particular row.
907     *
908     * @param idx the row index (starts at 0)
909     * @param firsttime is this the first time the row heigh is calculated?
910     * @return the height of a particular row
911     * @since   5.0.0
912     */
913    protected float getRowHeight(final int idx, final boolean firsttime) {
914        if (totalWidth <= 0 || idx < 0 || idx >= rows.size())
915            return 0;
916        PdfPRow row = rows.get(idx);
917        if (row == null)
918            return 0;
919        if (firsttime)
920                row.setWidths(absoluteWidths);
921        float height = row.getMaxHeights();
922        PdfPCell cell;
923        PdfPRow tmprow;
924        for (int i = 0; i < relativeWidths.length; i++) {
925                if(!rowSpanAbove(idx, i))
926                        continue;
927                int rs = 1;
928                while (rowSpanAbove(idx - rs, i)) {
929                        rs++;
930                }
931                tmprow = rows.get(idx - rs);
932                cell = tmprow.getCells()[i];
933                float tmp = 0;
934                if (cell != null && cell.getRowspan() == rs + 1) {
935                        tmp = cell.getMaxHeight();
936                        while (rs > 0) {
937                                tmp -= getRowHeight(idx - rs);
938                                rs--;
939                        }
940                }
941                if (tmp > height)
942                        height = tmp;
943        }
944        row.setMaxHeights(height);
945        return height;
946    }
947
948    /**
949     * Gets the maximum height of a cell in a particular row (will only be different
950     * from getRowHeight is one of the cells in the row has a rowspan > 1).
951     *
952     * @param   rowIndex        the row index
953     * @param   cellIndex       the cell index
954     * @return the height of a particular row including rowspan
955     * @since   2.1.6
956     */
957    public float getRowspanHeight(final int rowIndex, final int cellIndex) {
958        if (totalWidth <= 0 || rowIndex < 0 || rowIndex >= rows.size())
959            return 0;
960        PdfPRow row = rows.get(rowIndex);
961        if (row == null || cellIndex >= row.getCells().length)
962            return 0;
963        PdfPCell cell = row.getCells()[cellIndex];
964        if (cell == null)
965                return 0;
966        float rowspanHeight = 0;
967        for (int j = 0; j < cell.getRowspan(); j++) {
968                rowspanHeight += getRowHeight(rowIndex + j);
969        }
970        return rowspanHeight;
971    }
972
973    /**
974         * Checks if a cell in a row has a rowspan greater than 1.
975         * @since 5.1.0
976     */
977    public boolean hasRowspan(final int rowIdx) {
978        if (rowIdx < rows.size() && getRow(rowIdx).hasRowspan()) {
979                return true;
980        }
981        for (int i = 0; i < getNumberOfColumns(); i++) {
982                if (rowSpanAbove(rowIdx - 1, i))
983                        return true;
984        }
985        return false;
986    }
987
988    /**
989     * Makes sure the footers value is lower than the headers value.
990     * @since 5.0.1
991     */
992    public void normalizeHeadersFooters() {
993        if (footerRows > headerRows)
994            footerRows = headerRows;
995    }
996
997    /**
998     * Gets the height of the rows that constitute the header as defined by
999     * <CODE>setHeaderRows()</CODE>.
1000     *
1001     * @return the height of the rows that constitute the header and footer
1002     */
1003    public float getHeaderHeight() {
1004        float total = 0;
1005        int size = Math.min(rows.size(), headerRows);
1006        for (int k = 0; k < size; ++k) {
1007            PdfPRow row = rows.get(k);
1008            if (row != null)
1009                total += row.getMaxHeights();
1010        }
1011        return total;
1012    }
1013
1014    /**
1015     * Gets the height of the rows that constitute the footer as defined by
1016     * <CODE>setFooterRows()</CODE>.
1017     *
1018     * @return the height of the rows that constitute the footer
1019     * @since 2.1.1
1020     */
1021    public float getFooterHeight() {
1022        float total = 0;
1023        int start = Math.max(0, headerRows - footerRows);
1024        int size = Math.min(rows.size(), headerRows);
1025        for (int k = start; k < size; ++k) {
1026            PdfPRow row = rows.get(k);
1027            if (row != null)
1028                total += row.getMaxHeights();
1029        }
1030        return total;
1031    }
1032
1033    /**
1034     * Deletes a row from the table.
1035     *
1036     * @param rowNumber the row to be deleted
1037     * @return <CODE>true</CODE> if the row was deleted
1038     */
1039    public boolean deleteRow(final int rowNumber) {
1040        if (rowNumber < 0 || rowNumber >= rows.size())
1041            return false;
1042        if (totalWidth > 0) {
1043            PdfPRow row = rows.get(rowNumber);
1044            if (row != null)
1045                totalHeight -= row.getMaxHeights();
1046        }
1047        rows.remove(rowNumber);
1048        if (rowNumber < headerRows) {
1049                --headerRows;
1050                if (rowNumber >= headerRows - footerRows)
1051                        --footerRows;
1052        }
1053        return true;
1054    }
1055
1056    /**
1057     * Deletes the last row in the table.
1058     *
1059     * @return <CODE>true</CODE> if the last row was deleted
1060     */
1061    public boolean deleteLastRow() {
1062        return deleteRow(rows.size() - 1);
1063    }
1064
1065    /**
1066     * Removes all of the rows except headers
1067     */
1068    public void deleteBodyRows() {
1069        ArrayList<PdfPRow> rows2 = new ArrayList<PdfPRow>();
1070        for (int k = 0; k < headerRows; ++k)
1071            rows2.add(rows.get(k));
1072        rows = rows2;
1073        totalHeight = 0;
1074        if (totalWidth > 0)
1075            totalHeight = getHeaderHeight();
1076    }
1077
1078    /**
1079     * Returns the number of columns.
1080     *
1081     * @return  the number of columns.
1082     * @since   2.1.1
1083     */
1084    public int getNumberOfColumns() {
1085        return relativeWidths.length;
1086    }
1087
1088    /**
1089     * Gets the number of the rows that constitute the header.
1090     *
1091     * @return the number of the rows that constitute the header
1092     */
1093    public int getHeaderRows() {
1094        return headerRows;
1095    }
1096
1097    /**
1098     * Sets the number of the top rows that constitute the header.
1099     * This header has only meaning if the table is added to <CODE>Document</CODE>
1100     * and the table crosses pages.
1101     *
1102     * @param headerRows the number of the top rows that constitute the header
1103     */
1104    public void setHeaderRows(int headerRows) {
1105        if (headerRows < 0)
1106            headerRows = 0;
1107        this.headerRows = headerRows;
1108    }
1109
1110    /**
1111     * Gets all the chunks in this element.
1112     *
1113     * @return  an <CODE>ArrayList</CODE>
1114     */
1115    public List<Chunk> getChunks() {
1116        return new ArrayList<Chunk>();
1117    }
1118
1119    /**
1120     * Gets the type of the text element.
1121     *
1122     * @return  a type
1123     */
1124    public int type() {
1125        return Element.PTABLE;
1126    }
1127
1128        /**
1129         * @see com.itextpdf.text.Element#isContent()
1130         * @since       iText 2.0.8
1131         */
1132        public boolean isContent() {
1133                return true;
1134        }
1135
1136        /**
1137         * @see com.itextpdf.text.Element#isNestable()
1138         * @since       iText 2.0.8
1139         */
1140        public boolean isNestable() {
1141                return true;
1142        }
1143
1144    /**
1145     * Processes the element by adding it (or the different parts) to an
1146     * <CODE>ElementListener</CODE>.
1147     *
1148     * @param   listener        an <CODE>ElementListener</CODE>
1149     * @return  <CODE>true</CODE> if the element was processed successfully
1150     */
1151    public boolean process(final ElementListener listener) {
1152        try {
1153            return listener.add(this);
1154        }
1155        catch(DocumentException de) {
1156            return false;
1157        }
1158    }
1159
1160    /**
1161     * Gets the width percentage that the table will occupy in the page.
1162     *
1163     * @return the width percentage that the table will occupy in the page
1164     */
1165    public float getWidthPercentage() {
1166        return widthPercentage;
1167    }
1168
1169    /**
1170     * Sets the width percentage that the table will occupy in the page.
1171     *
1172     * @param widthPercentage the width percentage that the table will occupy in the page
1173     */
1174    public void setWidthPercentage(final float widthPercentage) {
1175        this.widthPercentage = widthPercentage;
1176    }
1177
1178    /**
1179     * Gets the horizontal alignment of the table relative to the page.
1180     *
1181     * @return the horizontal alignment of the table relative to the page
1182     */
1183    public int getHorizontalAlignment() {
1184        return horizontalAlignment;
1185    }
1186
1187    /**
1188     * Sets the horizontal alignment of the table relative to the page.
1189     * It only has meaning if the width percentage is less than 100%.
1190     *
1191     * @param horizontalAlignment the horizontal alignment of the table
1192     * relative to the page
1193     */
1194    public void setHorizontalAlignment(final int horizontalAlignment) {
1195        this.horizontalAlignment = horizontalAlignment;
1196    }
1197
1198    /**
1199     * Gets a row with a given index.
1200     *
1201     * @param idx
1202     * @return the row at position idx
1203     */
1204    public PdfPRow getRow(final int idx) {
1205        return rows.get(idx);
1206    }
1207
1208    /**
1209     * Gets an arraylist with all the rows in the table.
1210     *
1211     * @return an arraylist
1212     */
1213    public ArrayList<PdfPRow> getRows() {
1214        return rows;
1215    }
1216
1217    /**
1218     * Gets an arraylist with a selection of rows.
1219     * @param   start   the first row in the selection
1220     * @param   end     the first row that isn't part of the selection
1221     * @return  a selection of rows
1222     * @since   2.1.6
1223     */
1224    public ArrayList<PdfPRow> getRows(final int start, final int end) {
1225        ArrayList<PdfPRow> list = new ArrayList<PdfPRow>();
1226        if (start < 0 || end > size()) {
1227                return list;
1228        }
1229        for (int i = start; i < end; i++) {
1230                list.add(adjustCellsInRow(i, end));
1231        }
1232        return list;
1233    }
1234
1235    /**
1236     * Calculates the extra height needed in a row because of rowspans.
1237     * @param   start   the index of the start row (the one to adjust)
1238     * @param   end             the index of the end row on the page
1239     * @since   2.1.6
1240     */
1241    protected PdfPRow adjustCellsInRow(final int start, final int end) {
1242        PdfPRow row = new PdfPRow(getRow(start));
1243                PdfPCell cell;
1244                PdfPCell[] cells = row.getCells();
1245                for (int i = 0; i < cells.length; i++) {
1246                        cell = cells[i];
1247                        if (cell == null || cell.getRowspan() == 1)
1248                                continue;
1249                        int stop = Math.min(end, start + cell.getRowspan());
1250                        float extra = 0;
1251                        for (int k = start + 1; k < stop; k++) {
1252                                extra += getRow(k).getMaxHeights();
1253                        }
1254                        row.setExtraHeight(i, extra);
1255                }
1256        return row;
1257    }
1258
1259    /** Sets the table event for this table.
1260     * @param event the table event for this table
1261     */
1262    public void setTableEvent(final PdfPTableEvent event) {
1263        if (event == null)
1264                this.tableEvent = null;
1265        else if (this.tableEvent == null)
1266                this.tableEvent = event;
1267        else if (this.tableEvent instanceof PdfPTableEventForwarder)
1268                ((PdfPTableEventForwarder)this.tableEvent).addTableEvent(event);
1269        else {
1270                PdfPTableEventForwarder forward = new PdfPTableEventForwarder();
1271                forward.addTableEvent(this.tableEvent);
1272                forward.addTableEvent(event);
1273                this.tableEvent = forward;
1274        }
1275    }
1276
1277    /**
1278     * Gets the table event for this page.
1279     *
1280     * @return the table event for this page
1281     */
1282    public PdfPTableEvent getTableEvent() {
1283        return tableEvent;
1284    }
1285
1286    /**
1287     * Gets the absolute sizes of each column width.
1288     *
1289     * @return he absolute sizes of each column width
1290     */
1291    public float[] getAbsoluteWidths() {
1292        return absoluteWidths;
1293    }
1294
1295    float [][] getEventWidths(final float xPos, int firstRow, int lastRow, final boolean includeHeaders) {
1296        if (includeHeaders) {
1297            firstRow = Math.max(firstRow, headerRows);
1298            lastRow = Math.max(lastRow, headerRows);
1299        }
1300        float widths[][] = new float[(includeHeaders ? headerRows : 0) + lastRow - firstRow][];
1301        if (isColspan) {
1302            int n = 0;
1303            if (includeHeaders) {
1304                for (int k = 0; k < headerRows; ++k) {
1305                    PdfPRow row = rows.get(k);
1306                    if (row == null)
1307                        ++n;
1308                    else
1309                        widths[n++] = row.getEventWidth(xPos, absoluteWidths);
1310                }
1311            }
1312            for (; firstRow < lastRow; ++firstRow) {
1313                    PdfPRow row = rows.get(firstRow);
1314                    if (row == null)
1315                        ++n;
1316                    else
1317                        widths[n++] = row.getEventWidth(xPos, absoluteWidths);
1318            }
1319        }
1320        else {
1321                int numCols = getNumberOfColumns();
1322            float width[] = new float[numCols + 1];
1323            width[0] = xPos;
1324            for (int k = 0; k < numCols; ++k)
1325                width[k + 1] = width[k] + absoluteWidths[k];
1326            for (int k = 0; k < widths.length; ++k)
1327                widths[k] = width;
1328        }
1329        return widths;
1330    }
1331
1332
1333    /**
1334     * Tells you if the first header needs to be skipped
1335     * (for instance if the header says "continued from the previous page").
1336     *
1337     * @return Value of property skipFirstHeader.
1338     */
1339    public boolean isSkipFirstHeader() {
1340        return skipFirstHeader;
1341    }
1342
1343
1344    /**
1345     * Tells you if the last footer needs to be skipped
1346     * (for instance if the footer says "continued on the next page")
1347     *
1348     * @return Value of property skipLastFooter.
1349     * @since   2.1.6
1350     */
1351    public boolean isSkipLastFooter() {
1352        return skipLastFooter;
1353    }
1354
1355    /**
1356     * Skips the printing of the first header. Used when printing
1357     * tables in succession belonging to the same printed table aspect.
1358     *
1359     * @param skipFirstHeader New value of property skipFirstHeader.
1360     */
1361    public void setSkipFirstHeader(final boolean skipFirstHeader) {
1362        this.skipFirstHeader = skipFirstHeader;
1363    }
1364
1365    /**
1366     * Skips the printing of the last footer. Used when printing
1367     * tables in succession belonging to the same printed table aspect.
1368     *
1369     * @param skipLastFooter New value of property skipLastFooter.
1370     * @since   2.1.6
1371     */
1372    public void setSkipLastFooter(final boolean skipLastFooter) {
1373        this.skipLastFooter = skipLastFooter;
1374    }
1375
1376    /**
1377     * Sets the run direction of the contents of the table.
1378     *
1379     * @param runDirection One of the following values:
1380     * PdfWriter.RUN_DIRECTION_DEFAULT, PdfWriter.RUN_DIRECTION_NO_BIDI,
1381     * PdfWriter.RUN_DIRECTION_LTR or PdfWriter.RUN_DIRECTION_RTL.
1382     */
1383    public void setRunDirection(final int runDirection) {
1384        switch (runDirection) {
1385                case PdfWriter.RUN_DIRECTION_DEFAULT:
1386                case PdfWriter.RUN_DIRECTION_NO_BIDI:
1387                case PdfWriter.RUN_DIRECTION_LTR:
1388                case PdfWriter.RUN_DIRECTION_RTL:
1389                        this.runDirection = runDirection;
1390                        break;
1391                default:
1392                        throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.run.direction.1", runDirection));
1393        }
1394    }
1395
1396    /**
1397     * Returns the run direction of the contents in the table.
1398     *
1399     * @return One of the following values:
1400     * PdfWriter.RUN_DIRECTION_DEFAULT, PdfWriter.RUN_DIRECTION_NO_BIDI,
1401     * PdfWriter.RUN_DIRECTION_LTR or PdfWriter.RUN_DIRECTION_RTL.
1402     */
1403    public int getRunDirection() {
1404        return runDirection;
1405    }
1406
1407    /**
1408     * Getter for property lockedWidth.
1409     *
1410     * @return Value of property lockedWidth.
1411     */
1412    public boolean isLockedWidth() {
1413        return this.lockedWidth;
1414    }
1415
1416    /**
1417     * Uses the value in <CODE>setTotalWidth()</CODE> in <CODE>Document.add()</CODE>.
1418     *
1419     * @param lockedWidth <CODE>true</CODE> to use the value in <CODE>setTotalWidth()</CODE> in <CODE>Document.add()</CODE>
1420     */
1421    public void setLockedWidth(final boolean lockedWidth) {
1422        this.lockedWidth = lockedWidth;
1423    }
1424
1425    /**
1426     * Gets the split value.
1427     *
1428     * @return true to split; false otherwise
1429     */
1430    public boolean isSplitRows() {
1431        return this.splitRows;
1432    }
1433
1434    /**
1435     * When set the rows that won't fit in the page will be split.
1436     * Note that it takes at least twice the memory to handle a split table row
1437     * than a normal table. <CODE>true</CODE> by default.
1438     *
1439     * @param splitRows true to split; false otherwise
1440     */
1441    public void setSplitRows(final boolean splitRows) {
1442        this.splitRows = splitRows;
1443    }
1444
1445    /**
1446     * Sets the spacing before this table.
1447     *
1448     * @param   spacing         the new spacing
1449     */
1450    public void setSpacingBefore(final float spacing) {
1451        this.spacingBefore = spacing;
1452    }
1453
1454    /**
1455     * Sets the spacing after this table.
1456     *
1457     * @param   spacing         the new spacing
1458     */
1459    public void setSpacingAfter(final float spacing) {
1460        this.spacingAfter = spacing;
1461    }
1462
1463    /**
1464     * Gets the spacing before this table.
1465     *
1466     * @return  the spacing
1467     */
1468    public float spacingBefore() {
1469        return spacingBefore;
1470    }
1471
1472    /**
1473     * Gets the spacing after this table.
1474     *
1475     * @return  the spacing
1476     */
1477    public float spacingAfter() {
1478        return spacingAfter;
1479    }
1480
1481    /**
1482     * Gets the value of the last row extension.
1483     *
1484     * @return true if the last row will extend; false otherwise
1485     */
1486    public boolean isExtendLastRow() {
1487        return extendLastRow[0];
1488    }
1489
1490    /**
1491     * When set the last row on every page will be extended to fill
1492     * all the remaining space to the bottom boundary.
1493     *
1494     * @param extendLastRows true to extend the last row; false otherwise
1495     */
1496    public void setExtendLastRow(final boolean extendLastRows) {
1497        extendLastRow[0] = extendLastRows;
1498                extendLastRow[1] = extendLastRows;
1499    }
1500
1501    /**
1502     * When set the last row on every page will be extended to fill
1503     * all the remaining space to the bottom boundary; except maybe the
1504     * final row.
1505     *
1506     * @param extendLastRows true to extend the last row on each page; false otherwise
1507     * @param extendFinalRow false if you don't want to extend the final row of the complete table
1508         * @since iText 5.0.0
1509         */
1510        public void setExtendLastRow(final boolean extendLastRows, final boolean extendFinalRow) {
1511                extendLastRow[0] = extendLastRows;
1512                extendLastRow[1] = extendFinalRow;
1513        }
1514
1515    /**
1516     * Gets the value of the last row extension, taking into account
1517     * if the final row is reached or not.
1518     *
1519     * @return true if the last row will extend; false otherwise
1520     * @since iText 5.0.0
1521     */
1522    public boolean isExtendLastRow(final boolean newPageFollows) {
1523        if (newPageFollows) {
1524            return extendLastRow[0];
1525        }
1526                return extendLastRow[1];
1527    }
1528
1529        /**
1530     * Gets the header status inclusion in PdfPTableEvent.
1531     *
1532     * @return true if the headers are included; false otherwise
1533     */
1534    public boolean isHeadersInEvent() {
1535        return headersInEvent;
1536    }
1537
1538    /**
1539     * When set the PdfPTableEvent will include the headers.
1540     *
1541     * @param headersInEvent true to include the headers; false otherwise
1542     */
1543    public void setHeadersInEvent(final boolean headersInEvent) {
1544        this.headersInEvent = headersInEvent;
1545    }
1546
1547    /**
1548     * Gets the property splitLate.
1549     *
1550     * @return the property splitLate
1551     */
1552    public boolean isSplitLate() {
1553        return splitLate;
1554    }
1555
1556    /**
1557     * If true the row will only split if it's the first one in an empty page.
1558     * It's true by default.
1559     * It's only meaningful if setSplitRows(true).
1560     *
1561     * @param splitLate the property value
1562     */
1563    public void setSplitLate(final boolean splitLate) {
1564        this.splitLate = splitLate;
1565    }
1566
1567    /**
1568     * If true the table will be kept on one page if it fits, by forcing a
1569     * new page if it doesn't fit on the current page. The default is to
1570     * split the table over multiple pages.
1571     *
1572     * @param keepTogether whether to try to keep the table on one page
1573     */
1574    public void setKeepTogether(final boolean keepTogether) {
1575        this.keepTogether = keepTogether;
1576    }
1577
1578    /**
1579     * Getter for property keepTogether
1580     *
1581     * @return true if it is tried to keep the table on one page;
1582     * false otherwise
1583     */
1584    public boolean getKeepTogether() {
1585        return keepTogether;
1586    }
1587
1588    /**
1589     * Gets the number of rows in the footer.
1590     *
1591     * @return the number of rows in the footer
1592     */
1593    public int getFooterRows() {
1594        return this.footerRows;
1595    }
1596
1597    /**
1598     * Sets the number of rows to be used for the footer. The number
1599     * of footer rows are subtracted from the header rows. For
1600     * example, for a table with two header rows and one footer row the
1601     * code would be:
1602     * <pre>
1603     * table.setHeaderRows(3);
1604     * table.setFooterRows(1);
1605     * </pre>
1606     * Row 0 and 1 will be the header rows and row 2 will be the footer row.
1607     *
1608     * @param footerRows the number of rows to be used for the footer
1609     */
1610    public void setFooterRows(int footerRows) {
1611        if (footerRows < 0)
1612            footerRows = 0;
1613        this.footerRows = footerRows;
1614    }
1615
1616    /**
1617     * Completes the current row with the default cell. An incomplete row will
1618     * be dropped but calling this method will make sure that it will be
1619     * present in the table.
1620     */
1621    public void completeRow() {
1622        while (!rowCompleted) {
1623            addCell(defaultCell);
1624        }
1625    }
1626
1627        /**
1628         * @since       iText 2.0.8
1629         * @see com.itextpdf.text.LargeElement#flushContent()
1630         */
1631        public void flushContent() {
1632                deleteBodyRows();
1633                setSkipFirstHeader(true);
1634        }
1635
1636        /**
1637     * @since   iText 2.0.8
1638         * @see com.itextpdf.text.LargeElement#isComplete()
1639         */
1640        public boolean isComplete() {
1641                return complete;
1642        }
1643
1644        /**
1645     * @since   iText 2.0.8
1646         * @see com.itextpdf.text.LargeElement#setComplete(boolean)
1647         */
1648        public void setComplete(final boolean complete) {
1649                this.complete = complete;
1650        }
1651
1652        /* (non-Javadoc)
1653         * @see com.itextpdf.text.api.Spaceable#getSpacingBefore()
1654         */
1655        public float getSpacingBefore() {
1656                return spacingBefore;
1657        }
1658
1659        /* (non-Javadoc)
1660         * @see com.itextpdf.text.api.Spaceable#getSpacingAfter()
1661         */
1662        public float getSpacingAfter() {
1663                return spacingAfter;
1664        }
1665}