1 /*
2 * $Id: PdfPTable.java 3373 2008-05-12 16:21:24Z xlv $
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 /** This is a table that can be put at an absolute position but can also
64 * be added to the document as the class <CODE>Table</CODE>.
65 * In the last case when crossing pages the table always break at full rows; if a
66 * row is bigger than the page it is dropped silently to avoid infinite loops.
67 * <P>
68 * A PdfPTableEvent can be associated to the table to do custom drawing
69 * when the table is rendered.
70 * @author Paulo Soares (psoares@consiste.pt)
71 */
72
73 public class PdfPTable implements LargeElement{
74
75 /** The index of the original <CODE>PdfcontentByte</CODE>.
76 */
77 public static final int BASECANVAS = 0;
78 /** The index of the duplicate <CODE>PdfContentByte</CODE> where the background will be drawn.
79 */
80 public static final int BACKGROUNDCANVAS = 1;
81 /** The index of the duplicate <CODE>PdfContentByte</CODE> where the border lines will be drawn.
82 */
83 public static final int LINECANVAS = 2;
84 /** The index of the duplicate <CODE>PdfContentByte</CODE> where the text will be drawn.
85 */
86 public static final int TEXTCANVAS = 3;
87
88 protected ArrayList rows = new ArrayList();
89 protected float totalHeight = 0;
90 protected PdfPCell currentRow[];
91 protected int currentRowIdx = 0;
92 protected PdfPCell defaultCell = new PdfPCell((Phrase)null);
93 protected float totalWidth = 0;
94 protected float relativeWidths[];
95 protected float absoluteWidths[];
96 protected PdfPTableEvent tableEvent;
97
98 /** Holds value of property headerRows. */
99 protected int headerRows;
100
101 /** Holds value of property widthPercentage. */
102 protected float widthPercentage = 80;
103
104 /** Holds value of property horizontalAlignment. */
105 private int horizontalAlignment = Element.ALIGN_CENTER;
106
107 /** Holds value of property skipFirstHeader. */
108 private boolean skipFirstHeader = false;
109
110 protected boolean isColspan = false;
111
112 protected int runDirection = PdfWriter.RUN_DIRECTION_DEFAULT;
113
114 /**
115 * Holds value of property lockedWidth.
116 */
117 private boolean lockedWidth = false;
118
119 /**
120 * Holds value of property splitRows.
121 */
122 private boolean splitRows = true;
123
124 /** The spacing before the table. */
125 protected float spacingBefore;
126
127 /** The spacing after the table. */
128 protected float spacingAfter;
129
130 /**
131 * Holds value of property extendLastRow.
132 */
133 private boolean extendLastRow;
134
135 /**
136 * Holds value of property headersInEvent.
137 */
138 private boolean headersInEvent;
139
140 /**
141 * Holds value of property splitLate.
142 */
143 private boolean splitLate = true;
144
145 /**
146 * Defines if the table should be kept
147 * on one page if possible
148 */
149 private boolean keepTogether;
150
151 /**
152 * Indicates if the PdfPTable is complete once added to the document.
153 * @since iText 2.0.8
154 */
155 protected boolean complete = true;
156
157 /**
158 * Holds value of property footerRows.
159 */
160 private int footerRows;
161
162 protected PdfPTable() {
163 }
164
165 /** Constructs a <CODE>PdfPTable</CODE> with the relative column widths.
166 * @param relativeWidths the relative column widths
167 */
168 public PdfPTable(float relativeWidths[]) {
169 if (relativeWidths == null)
170 throw new NullPointerException("The widths array in PdfPTable constructor can not be null.");
171 if (relativeWidths.length == 0)
172 throw new IllegalArgumentException("The widths array in PdfPTable constructor can not have zero length.");
173 this.relativeWidths = new float[relativeWidths.length];
174 System.arraycopy(relativeWidths, 0, this.relativeWidths, 0, relativeWidths.length);
175 absoluteWidths = new float[relativeWidths.length];
176 calculateWidths();
177 currentRow = new PdfPCell[absoluteWidths.length];
178 keepTogether = false;
179 }
180
181 /** Constructs a <CODE>PdfPTable</CODE> with <CODE>numColumns</CODE> columns.
182 * @param numColumns the number of columns
183 */
184 public PdfPTable(int numColumns) {
185 if (numColumns <= 0)
186 throw new IllegalArgumentException("The number of columns in PdfPTable constructor must be greater than zero.");
187 relativeWidths = new float[numColumns];
188 for (int k = 0; k < numColumns; ++k)
189 relativeWidths[k] = 1;
190 absoluteWidths = new float[relativeWidths.length];
191 calculateWidths();
192 currentRow = new PdfPCell[absoluteWidths.length];
193 keepTogether = false;
194 }
195
196 /** Constructs a copy of a <CODE>PdfPTable</CODE>.
197 * @param table the <CODE>PdfPTable</CODE> to be copied
198 */
199 public PdfPTable(PdfPTable table) {
200 copyFormat(table);
201 for (int k = 0; k < currentRow.length; ++k) {
202 if (table.currentRow[k] == null)
203 break;
204 currentRow[k] = new PdfPCell(table.currentRow[k]);
205 }
206 for (int k = 0; k < table.rows.size(); ++k) {
207 PdfPRow row = (PdfPRow)(table.rows.get(k));
208 if (row != null)
209 row = new PdfPRow(row);
210 rows.add(row);
211 }
212 }
213
214 /**
215 * Makes a shallow copy of a table (format without content).
216 * @param table
217 * @return a shallow copy of the table
218 */
219 public static PdfPTable shallowCopy(PdfPTable table) {
220 PdfPTable nt = new PdfPTable();
221 nt.copyFormat(table);
222 return nt;
223 }
224
225 /**
226 * Copies the format of the sourceTable without copying the content.
227 * @param sourceTable
228 */
229 private void copyFormat(PdfPTable sourceTable) {
230 relativeWidths = new float[sourceTable.relativeWidths.length];
231 absoluteWidths = new float[sourceTable.relativeWidths.length];
232 System.arraycopy(sourceTable.relativeWidths, 0, relativeWidths, 0, relativeWidths.length);
233 System.arraycopy(sourceTable.absoluteWidths, 0, absoluteWidths, 0, relativeWidths.length);
234 totalWidth = sourceTable.totalWidth;
235 totalHeight = sourceTable.totalHeight;
236 currentRowIdx = 0;
237 tableEvent = sourceTable.tableEvent;
238 runDirection = sourceTable.runDirection;
239 defaultCell = new PdfPCell(sourceTable.defaultCell);
240 currentRow = new PdfPCell[sourceTable.currentRow.length];
241 isColspan = sourceTable.isColspan;
242 splitRows = sourceTable.splitRows;
243 spacingAfter = sourceTable.spacingAfter;
244 spacingBefore = sourceTable.spacingBefore;
245 headerRows = sourceTable.headerRows;
246 footerRows = sourceTable.footerRows;
247 lockedWidth = sourceTable.lockedWidth;
248 extendLastRow = sourceTable.extendLastRow;
249 headersInEvent = sourceTable.headersInEvent;
250 widthPercentage = sourceTable.widthPercentage;
251 splitLate = sourceTable.splitLate;
252 skipFirstHeader = sourceTable.skipFirstHeader;
253 horizontalAlignment = sourceTable.horizontalAlignment;
254 keepTogether = sourceTable.keepTogether;
255 complete = sourceTable.complete;
256 }
257
258 /** Sets the relative widths of the table.
259 * @param relativeWidths the relative widths of the table.
260 * @throws DocumentException if the number of widths is different than the number
261 * of columns
262 */
263 public void setWidths(float relativeWidths[]) throws DocumentException {
264 if (relativeWidths.length != this.relativeWidths.length)
265 throw new DocumentException("Wrong number of columns.");
266 this.relativeWidths = new float[relativeWidths.length];
267 System.arraycopy(relativeWidths, 0, this.relativeWidths, 0, relativeWidths.length);
268 absoluteWidths = new float[relativeWidths.length];
269 totalHeight = 0;
270 calculateWidths();
271 calculateHeights();
272 }
273
274 /** Sets the relative widths of the table.
275 * @param relativeWidths the relative widths of the table.
276 * @throws DocumentException if the number of widths is different than the number
277 * of columns
278 */
279 public void setWidths(int relativeWidths[]) throws DocumentException {
280 float tb[] = new float[relativeWidths.length];
281 for (int k = 0; k < relativeWidths.length; ++k)
282 tb[k] = relativeWidths[k];
283 setWidths(tb);
284 }
285
286 private void calculateWidths() {
287 if (totalWidth <= 0)
288 return;
289 float total = 0;
290 for (int k = 0; k < absoluteWidths.length; ++k) {
291 total += relativeWidths[k];
292 }
293 for (int k = 0; k < absoluteWidths.length; ++k) {
294 absoluteWidths[k] = totalWidth * relativeWidths[k] / total;
295 }
296 }
297
298 /** Sets the full width of the table.
299 * @param totalWidth the full width of the table.
300 */
301 public void setTotalWidth(float totalWidth) {
302 if (this.totalWidth == totalWidth)
303 return;
304 this.totalWidth = totalWidth;
305 totalHeight = 0;
306 calculateWidths();
307 calculateHeights();
308 }
309
310 /** Sets the full width of the table from the absolute column width.
311 * @param columnWidth the absolute width of each column
312 * @throws DocumentException if the number of widths is different than the number
313 * of columns
314 */
315 public void setTotalWidth(float columnWidth[]) throws DocumentException {
316 if (columnWidth.length != this.relativeWidths.length)
317 throw new DocumentException("Wrong number of columns.");
318 totalWidth = 0;
319 for (int k = 0; k < columnWidth.length; ++k)
320 totalWidth += columnWidth[k];
321 setWidths(columnWidth);
322 }
323
324 /** Sets the percentage width of the table from the absolute column width.
325 * @param columnWidth the absolute width of each column
326 * @param pageSize the page size
327 * @throws DocumentException
328 */
329 public void setWidthPercentage(float columnWidth[], Rectangle pageSize) throws DocumentException {
330 if (columnWidth.length != this.relativeWidths.length)
331 throw new IllegalArgumentException("Wrong number of columns.");
332 float totalWidth = 0;
333 for (int k = 0; k < columnWidth.length; ++k)
334 totalWidth += columnWidth[k];
335 widthPercentage = totalWidth / (pageSize.getRight() - pageSize.getLeft()) * 100f;
336 setWidths(columnWidth);
337 }
338
339 /** Gets the full width of the table.
340 * @return the full width of the table
341 */
342 public float getTotalWidth() {
343 return totalWidth;
344 }
345
346 void calculateHeights() {
347 if (totalWidth <= 0)
348 return;
349 totalHeight = 0;
350 for (int k = 0; k < rows.size(); ++k) {
351 PdfPRow row = (PdfPRow)rows.get(k);
352 if (row != null) {
353 row.setWidths(absoluteWidths);
354 totalHeight += row.getMaxHeights();
355 }
356 }
357 }
358
359 /**
360 * Calculates the heights of the table.
361 */
362 public void calculateHeightsFast() {
363 if (totalWidth <= 0)
364 return;
365 totalHeight = 0;
366 for (int k = 0; k < rows.size(); ++k) {
367 PdfPRow row = (PdfPRow)rows.get(k);
368 if (row != null)
369 totalHeight += row.getMaxHeights();
370 }
371 }
372
373 /** Gets the default <CODE>PdfPCell</CODE> that will be used as
374 * reference for all the <CODE>addCell</CODE> methods except
375 * <CODE>addCell(PdfPCell)</CODE>.
376 * @return default <CODE>PdfPCell</CODE>
377 */
378 public PdfPCell getDefaultCell() {
379 return defaultCell;
380 }
381
382 /** Adds a cell element.
383 * @param cell the cell element
384 */
385 public void addCell(PdfPCell cell) {
386 PdfPCell ncell = new PdfPCell(cell);
387 int colspan = ncell.getColspan();
388 colspan = Math.max(colspan, 1);
389 colspan = Math.min(colspan, currentRow.length - currentRowIdx);
390 ncell.setColspan(colspan);
391 if (colspan != 1)
392 isColspan = true;
393 int rdir = ncell.getRunDirection();
394 if (rdir == PdfWriter.RUN_DIRECTION_DEFAULT)
395 ncell.setRunDirection(runDirection);
396 currentRow[currentRowIdx] = ncell;
397 currentRowIdx += colspan;
398 if (currentRowIdx >= currentRow.length) {
399 if (runDirection == PdfWriter.RUN_DIRECTION_RTL) {
400 PdfPCell rtlRow[] = new PdfPCell[absoluteWidths.length];
401 int rev = currentRow.length;
402 for (int k = 0; k < currentRow.length; ++k) {
403 PdfPCell rcell = currentRow[k];
404 int cspan = rcell.getColspan();
405 rev -= cspan;
406 rtlRow[rev] = rcell;
407 k += cspan - 1;
408 }
409 currentRow = rtlRow;
410 }
411 PdfPRow row = new PdfPRow(currentRow);
412 if (totalWidth > 0) {
413 row.setWidths(absoluteWidths);
414 totalHeight += row.getMaxHeights();
415 }
416 rows.add(row);
417 currentRow = new PdfPCell[absoluteWidths.length];
418 currentRowIdx = 0;
419 }
420 }
421
422 /** Adds a cell element.
423 * @param text the text for the cell
424 */
425 public void addCell(String text) {
426 addCell(new Phrase(text));
427 }
428
429 /**
430 * Adds a nested table.
431 * @param table the table to be added to the cell
432 */
433 public void addCell(PdfPTable table) {
434 defaultCell.setTable(table);
435 addCell(defaultCell);
436 defaultCell.setTable(null);
437 }
438
439 /**
440 * Adds an Image as Cell.
441 * @param image the <CODE>Image</CODE> to add to the table. This image will fit in the cell
442 */
443 public void addCell(Image image) {
444 defaultCell.setImage(image);
445 addCell(defaultCell);
446 defaultCell.setImage(null);
447 }
448
449 /**
450 * Adds a cell element.
451 * @param phrase the <CODE>Phrase</CODE> to be added to the cell
452 */
453 public void addCell(Phrase phrase) {
454 defaultCell.setPhrase(phrase);
455 addCell(defaultCell);
456 defaultCell.setPhrase(null);
457 }
458
459 /**
460 * Writes the selected rows to the document.
461 * <P>
462 * <CODE>canvases</CODE> is obtained from <CODE>beginWritingRows()</CODE>.
463 * @param rowStart the first row to be written, zero index
464 * @param rowEnd the last row to be written + 1. If it is -1 all the
465 * rows to the end are written
466 * @param xPos the x write coordinate
467 * @param yPos the y write coordinate
468 * @param canvases an array of 4 <CODE>PdfContentByte</CODE> obtained from
469 * <CODE>beginWrittingRows()</CODE>
470 * @return the y coordinate position of the bottom of the last row
471 * @see #beginWritingRows(com.lowagie.text.pdf.PdfContentByte)
472 */
473 public float writeSelectedRows(int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte[] canvases) {
474 return writeSelectedRows(0, -1, rowStart, rowEnd, xPos, yPos, canvases);
475 }
476
477 /** Writes the selected rows and columns to the document.
478 * This method does not clip the columns; this is only important
479 * if there are columns with colspan at boundaries.
480 * <P>
481 * <CODE>canvases</CODE> is obtained from <CODE>beginWritingRows()</CODE>.
482 * <P>
483 * The table event is only fired for complete rows.
484 * @param colStart the first column to be written, zero index
485 * @param colEnd the last column to be written + 1. If it is -1 all the
486 * columns to the end are written
487 * @param rowStart the first row to be written, zero index
488 * @param rowEnd the last row to be written + 1. If it is -1 all the
489 * rows to the end are written
490 * @param xPos the x write coordinate
491 * @param yPos the y write coordinate
492 * @param canvases an array of 4 <CODE>PdfContentByte</CODE> obtained from
493 * <CODE>beginWrittingRows()</CODE>
494 * @return the y coordinate position of the bottom of the last row
495 * @see #beginWritingRows(com.lowagie.text.pdf.PdfContentByte)
496 */
497 public float writeSelectedRows(int colStart, int colEnd, int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte[] canvases) {
498 if (totalWidth <= 0)
499 throw new RuntimeException("The table width must be greater than zero.");
500 int size = rows.size();
501 if (rowEnd < 0)
502 rowEnd = size;
503 rowEnd = Math.min(rowEnd, size);
504 if (rowStart < 0)
505 rowStart = 0;
506 if (rowStart >= rowEnd)
507 return yPos;
508 if (colEnd < 0)
509 colEnd = absoluteWidths.length;
510 colEnd = Math.min(colEnd, absoluteWidths.length);
511 if (colStart < 0)
512 colStart = 0;
513 colStart = Math.min(colStart, absoluteWidths.length);
514 float yPosStart = yPos;
515 for (int k = rowStart; k < rowEnd; ++k) {
516 PdfPRow row = (PdfPRow)rows.get(k);
517 if (row != null) {
518 row.writeCells(colStart, colEnd, xPos, yPos, canvases);
519 yPos -= row.getMaxHeights();
520 }
521 }
522 if (tableEvent != null && colStart == 0 && colEnd == absoluteWidths.length) {
523 float heights[] = new float[rowEnd - rowStart + 1];
524 heights[0] = yPosStart;
525 for (int k = rowStart; k < rowEnd; ++k) {
526 PdfPRow row = (PdfPRow)rows.get(k);
527 float hr = 0;
528 if (row != null)
529 hr = row.getMaxHeights();
530 heights[k - rowStart + 1] = heights[k - rowStart] - hr;
531 }
532 tableEvent.tableLayout(this, getEventWidths(xPos, rowStart, rowEnd, headersInEvent), heights, headersInEvent ? headerRows : 0, rowStart, canvases);
533 }
534 return yPos;
535 }
536
537 /**
538 * Writes the selected rows to the document.
539 *
540 * @param rowStart the first row to be written, zero index
541 * @param rowEnd the last row to be written + 1. If it is -1 all the
542 * rows to the end are written
543 * @param xPos the x write coordinate
544 * @param yPos the y write coordinate
545 * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
546 * be written to
547 * @return the y coordinate position of the bottom of the last row
548 */
549 public float writeSelectedRows(int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte canvas) {
550 return writeSelectedRows(0, -1, rowStart, rowEnd, xPos, yPos, canvas);
551 }
552
553 /**
554 * Writes the selected rows to the document.
555 * This method clips the columns; this is only important
556 * if there are columns with colspan at boundaries.
557 * <P>
558 * The table event is only fired for complete rows.
559 *
560 * @param colStart the first column to be written, zero index
561 * @param colEnd the last column to be written + 1. If it is -1 all the
562 * @param rowStart the first row to be written, zero index
563 * @param rowEnd the last row to be written + 1. If it is -1 all the
564 * rows to the end are written
565 * @param xPos the x write coordinate
566 * @param yPos the y write coordinate
567 * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
568 * be written to
569 * @return the y coordinate position of the bottom of the last row
570 */
571 public float writeSelectedRows(int colStart, int colEnd, int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte canvas) {
572 if (colEnd < 0)
573 colEnd = absoluteWidths.length;
574 colEnd = Math.min(colEnd, absoluteWidths.length);
575 if (colStart < 0)
576 colStart = 0;
577 colStart = Math.min(colStart, absoluteWidths.length);
578 if (colStart != 0 || colEnd != absoluteWidths.length) {
579 float w = 0;
580 for (int k = colStart; k < colEnd; ++k)
581 w += absoluteWidths[k];
582 canvas.saveState();
583 float lx = 0;
584 float rx = 0;
585 if (colStart == 0)
586 lx = 10000;
587 if (colEnd == absoluteWidths.length)
588 rx = 10000;
589 canvas.rectangle(xPos - lx, -10000, w + lx + rx, 20000);
590 canvas.clip();
591 canvas.newPath();
592 }
593 PdfContentByte[] canvases = beginWritingRows(canvas);
594 float y = writeSelectedRows(colStart, colEnd, rowStart, rowEnd, xPos, yPos, canvases);
595 endWritingRows(canvases);
596 if (colStart != 0 || colEnd != absoluteWidths.length)
597 canvas.restoreState();
598 return y;
599 }
600
601 /** Gets and initializes the 4 layers where the table is written to. The text or graphics are added to
602 * one of the 4 <CODE>PdfContentByte</CODE> returned with the following order:<p>
603 * <ul>
604 * <li><CODE>PdfPtable.BASECANVAS</CODE> - the original <CODE>PdfContentByte</CODE>. Anything placed here
605 * will be under the table.
606 * <li><CODE>PdfPtable.BACKGROUNDCANVAS</CODE> - the layer where the background goes to.
607 * <li><CODE>PdfPtable.LINECANVAS</CODE> - the layer where the lines go to.
608 * <li><CODE>PdfPtable.TEXTCANVAS</CODE> - the layer where the text go to. Anything placed here
609 * will be over the table.
610 * </ul><p>
611 * The layers are placed in sequence on top of each other.
612 * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
613 * be written to
614 * @return an array of 4 <CODE>PdfContentByte</CODE>
615 * @see #writeSelectedRows(int, int, float, float, PdfContentByte[])
616 */
617 public static PdfContentByte[] beginWritingRows(PdfContentByte canvas) {
618 return new PdfContentByte[]{
619 canvas,
620 canvas.getDuplicate(),
621 canvas.getDuplicate(),
622 canvas.getDuplicate(),
623 };
624 }
625
626 /** Finishes writing the table.
627 * @param canvases the array returned by <CODE>beginWritingRows()</CODE>
628 */
629 public static void endWritingRows(PdfContentByte[] canvases) {
630 PdfContentByte canvas = canvases[BASECANVAS];
631 canvas.saveState();
632 canvas.add(canvases[BACKGROUNDCANVAS]);
633 canvas.restoreState();
634 canvas.saveState();
635 canvas.setLineCap(2);
636 canvas.resetRGBColorStroke();
637 canvas.add(canvases[LINECANVAS]);
638 canvas.restoreState();
639 canvas.add(canvases[TEXTCANVAS]);
640 }
641
642 /** Gets the number of rows in this table.
643 * @return the number of rows in this table
644 */
645 public int size() {
646 return rows.size();
647 }
648
649 /** Gets the total height of the table.
650 * @return the total height of the table
651 */
652 public float getTotalHeight() {
653 return totalHeight;
654 }
655
656 /** Gets the height of a particular row.
657 * @param idx the row index (starts at 0)
658 * @return the height of a particular row
659 */
660 public float getRowHeight(int idx) {
661 if (totalWidth <= 0 || idx < 0 || idx >= rows.size())
662 return 0;
663 PdfPRow row = (PdfPRow)rows.get(idx);
664 if (row == null)
665 return 0;
666 return row.getMaxHeights();
667 }
668
669 /** Gets the height of the rows that constitute the header as defined by
670 * <CODE>setHeaderRows()</CODE>.
671 * @return the height of the rows that constitute the header and footer
672 */
673 public float getHeaderHeight() {
674 float total = 0;
675 int size = Math.min(rows.size(), headerRows);
676 for (int k = 0; k < size; ++k) {
677 PdfPRow row = (PdfPRow)rows.get(k);
678 if (row != null)
679 total += row.getMaxHeights();
680 }
681 return total;
682 }
683
684 /** Gets the height of the rows that constitute the header as defined by
685 * <CODE>setFooterRows()</CODE>.
686 * @return the height of the rows that constitute the footer
687 * @since 2.1.1
688 */
689 public float getFooterHeight() {
690 float total = 0;
691 int start = Math.min(0, headerRows - footerRows);
692 int size = Math.min(rows.size(), footerRows);
693 for (int k = start; k < size; ++k) {
694 PdfPRow row = (PdfPRow)rows.get(k);
695 if (row != null)
696 total += row.getMaxHeights();
697 }
698 return total;
699 }
700
701 /** Deletes a row from the table.
702 * @param rowNumber the row to be deleted
703 * @return <CODE>true</CODE> if the row was deleted
704 */
705 public boolean deleteRow(int rowNumber) {
706 if (rowNumber < 0 || rowNumber >= rows.size()) {
707 return false;
708 }
709 if (totalWidth > 0) {
710 PdfPRow row = (PdfPRow)rows.get(rowNumber);
711 if (row != null)
712 totalHeight -= row.getMaxHeights();
713 }
714 rows.remove(rowNumber);
715 return true;
716 }
717
718 /** Deletes the last row in the table.
719 * @return <CODE>true</CODE> if the last row was deleted
720 */
721 public boolean deleteLastRow() {
722 return deleteRow(rows.size() - 1);
723 }
724
725 /**
726 * Removes all of the rows except headers
727 */
728 public void deleteBodyRows() {
729 ArrayList rows2 = new ArrayList();
730 for (int k = 0; k < headerRows; ++k)
731 rows2.add(rows.get(k));
732 rows = rows2;
733 totalHeight = 0;
734 if (totalWidth > 0)
735 totalHeight = getHeaderHeight();
736 }
737
738 /** Returns the number of columns.
739 * @return the number of columns.
740 * @since 2.1.1
741 */
742 public int getNumberOfColumns() {
743 return relativeWidths.length;
744 }
745
746 /** Gets the number of the rows that constitute the header.
747 * @return the number of the rows that constitute the header
748 */
749 public int getHeaderRows() {
750 return headerRows;
751 }
752
753 /** Sets the number of the top rows that constitute the header.
754 * This header has only meaning if the table is added to <CODE>Document</CODE>
755 * and the table crosses pages.
756 * @param headerRows the number of the top rows that constitute the header
757 */
758 public void setHeaderRows(int headerRows) {
759 if (headerRows < 0)
760 headerRows = 0;
761 this.headerRows = headerRows;
762 }
763
764 /**
765 * Gets all the chunks in this element.
766 *
767 * @return an <CODE>ArrayList</CODE>
768 */
769 public ArrayList getChunks() {
770 return new ArrayList();
771 }
772
773 /**
774 * Gets the type of the text element.
775 *
776 * @return a type
777 */
778 public int type() {
779 return Element.PTABLE;
780 }
781
782 /**
783 * @see com.lowagie.text.Element#isContent()
784 * @since iText 2.0.8
785 */
786 public boolean isContent() {
787 return true;
788 }
789
790 /**
791 * @see com.lowagie.text.Element#isNestable()
792 * @since iText 2.0.8
793 */
794 public boolean isNestable() {
795 return true;
796 }
797
798 /**
799 * Processes the element by adding it (or the different parts) to an
800 * <CODE>ElementListener</CODE>.
801 *
802 * @param listener an <CODE>ElementListener</CODE>
803 * @return <CODE>true</CODE> if the element was processed successfully
804 */
805 public boolean process(ElementListener listener) {
806 try {
807 return listener.add(this);
808 }
809 catch(DocumentException de) {
810 return false;
811 }
812 }
813
814 /** Gets the width percentage that the table will occupy in the page.
815 * @return the width percentage that the table will occupy in the page
816 */
817 public float getWidthPercentage() {
818 return widthPercentage;
819 }
820
821 /** Sets the width percentage that the table will occupy in the page.
822 * @param widthPercentage the width percentage that the table will occupy in the page
823 */
824 public void setWidthPercentage(float widthPercentage) {
825 this.widthPercentage = widthPercentage;
826 }
827
828 /** Gets the horizontal alignment of the table relative to the page.
829 * @return the horizontal alignment of the table relative to the page
830 */
831 public int getHorizontalAlignment() {
832 return horizontalAlignment;
833 }
834
835 /** Sets the horizontal alignment of the table relative to the page.
836 * It only has meaning if the width percentage is less than
837 * 100%.
838 * @param horizontalAlignment the horizontal alignment of the table relative to the page
839 */
840 public void setHorizontalAlignment(int horizontalAlignment) {
841 this.horizontalAlignment = horizontalAlignment;
842 }
843
844 /**
845 * Gets a row with a given index
846 * (added by Jin-Hsia Yang).
847 * @param idx
848 * @return the row at position idx
849 */
850 public PdfPRow getRow(int idx) {
851 return (PdfPRow)rows.get(idx);
852 }
853
854 /**
855 * Gets an arraylist with all the rows in the table.
856 * @return an arraylist
857 */
858 public ArrayList getRows() {
859 return rows;
860 }
861
862 /** Sets the table event for this table.
863 * @param event the table event for this table
864 */
865 public void setTableEvent(PdfPTableEvent event) {
866 if (event == null) this.tableEvent = null;
867 else if (this.tableEvent == null) this.tableEvent = event;
868 else if (this.tableEvent instanceof PdfPTableEventForwarder) ((PdfPTableEventForwarder)this.tableEvent).addTableEvent(event);
869 else {
870 PdfPTableEventForwarder forward = new PdfPTableEventForwarder();
871 forward.addTableEvent(this.tableEvent);
872 forward.addTableEvent(event);
873 this.tableEvent = forward;
874 }
875 }
876
877 /** Gets the table event for this page.
878 * @return the table event for this page
879 */
880 public PdfPTableEvent getTableEvent() {
881 return tableEvent;
882 }
883
884 /** Gets the absolute sizes of each column width.
885 * @return he absolute sizes of each column width
886 */
887 public float[] getAbsoluteWidths() {
888 return absoluteWidths;
889 }
890
891 float [][] getEventWidths(float xPos, int firstRow, int lastRow, boolean includeHeaders) {
892 if (includeHeaders) {
893 firstRow = Math.max(firstRow, headerRows);
894 lastRow = Math.max(lastRow, headerRows);
895 }
896 float widths[][] = new float[(includeHeaders ? headerRows : 0) + lastRow - firstRow][];
897 if (isColspan) {
898 int n = 0;
899 if (includeHeaders) {
900 for (int k = 0; k < headerRows; ++k) {
901 PdfPRow row = (PdfPRow)rows.get(k);
902 if (row == null)
903 ++n;
904 else
905 widths[n++] = row.getEventWidth(xPos);
906 }
907 }
908 for (; firstRow < lastRow; ++firstRow) {
909 PdfPRow row = (PdfPRow)rows.get(firstRow);
910 if (row == null)
911 ++n;
912 else
913 widths[n++] = row.getEventWidth(xPos);
914 }
915 }
916 else {
917 float width[] = new float[absoluteWidths.length + 1];
918 width[0] = xPos;
919 for (int k = 0; k < absoluteWidths.length; ++k)
920 width[k + 1] = width[k] + absoluteWidths[k];
921 for (int k = 0; k < widths.length; ++k)
922 widths[k] = width;
923 }
924 return widths;
925 }
926
927
928 /** Getter for property skipFirstHeader.
929 * @return Value of property skipFirstHeader.
930 */
931 public boolean isSkipFirstHeader() {
932 return skipFirstHeader;
933 }
934
935 /** Skips the printing of the first header. Used when printing
936 * tables in succession belonging to the same printed table aspect.
937 * @param skipFirstHeader New value of property skipFirstHeader.
938 */
939 public void setSkipFirstHeader(boolean skipFirstHeader) {
940 this.skipFirstHeader = skipFirstHeader;
941 }
942
943 /**
944 * Sets the run direction of the contents of the table.
945 * @param runDirection
946 */
947 public void setRunDirection(int runDirection) {
948 if (runDirection < PdfWriter.RUN_DIRECTION_DEFAULT || runDirection > PdfWriter.RUN_DIRECTION_RTL)
949 throw new RuntimeException("Invalid run direction: " + runDirection);
950 this.runDirection = runDirection;
951 }
952
953 /**
954 * Returns the run direction of the contents in the table.
955 * @return One of the following values: PdfWriter.RUN_DIRECTION_DEFAULT, PdfWriter.RUN_DIRECTION_NO_BIDI, PdfWriter.RUN_DIRECTION_LTR or PdfWriter.RUN_DIRECTION_RTL.
956 */
957 public int getRunDirection() {
958 return runDirection;
959 }
960
961 /**
962 * Getter for property lockedWidth.
963 * @return Value of property lockedWidth.
964 */
965 public boolean isLockedWidth() {
966 return this.lockedWidth;
967 }
968
969 /**
970 * Uses the value in <CODE>setTotalWidth()</CODE> in <CODE>Document.add()</CODE>.
971 * @param lockedWidth <CODE>true</CODE> to use the value in <CODE>setTotalWidth()</CODE> in <CODE>Document.add()</CODE>
972 */
973 public void setLockedWidth(boolean lockedWidth) {
974 this.lockedWidth = lockedWidth;
975 }
976
977 /**
978 * Gets the split value.
979 * @return true to split; false otherwise
980 */
981 public boolean isSplitRows() {
982 return this.splitRows;
983 }
984
985 /**
986 * When set the rows that won't fit in the page will be split.
987 * Note that it takes at least twice the memory to handle a split table row
988 * than a normal table. <CODE>true</CODE> by default.
989 * @param splitRows true to split; false otherwise
990 */
991 public void setSplitRows(boolean splitRows) {
992 this.splitRows = splitRows;
993 }
994
995 /**
996 * Sets the spacing before this table.
997 *
998 * @param spacing the new spacing
999 */
1000
1001 public void setSpacingBefore(float spacing) {
1002 this.spacingBefore = spacing;
1003 }
1004
1005 /**
1006 * Sets the spacing after this table.
1007 *
1008 * @param spacing the new spacing
1009 */
1010
1011 public void setSpacingAfter(float spacing) {
1012 this.spacingAfter = spacing;
1013 }
1014
1015 /**
1016 * Gets the spacing before this table.
1017 *
1018 * @return the spacing
1019 */
1020
1021 public float spacingBefore() {
1022 return spacingBefore;
1023 }
1024
1025 /**
1026 * Gets the spacing after this table.
1027 *
1028 * @return the spacing
1029 */
1030
1031 public float spacingAfter() {
1032 return spacingAfter;
1033 }
1034
1035 /**
1036 * Gets the value of the last row extension.
1037 * @return true if the last row will extend; false otherwise
1038 */
1039 public boolean isExtendLastRow() {
1040 return this.extendLastRow;
1041 }
1042
1043 /**
1044 * When set the last row will be extended to fill all the remaining space to the
1045 * bottom boundary.
1046 * @param extendLastRow true to extend the last row; false otherwise
1047 */
1048 public void setExtendLastRow(boolean extendLastRow) {
1049 this.extendLastRow = extendLastRow;
1050 }
1051
1052 /**
1053 * Gets the header status inclusion in PdfPTableEvent.
1054 * @return true if the headers are included; false otherwise
1055 */
1056 public boolean isHeadersInEvent() {
1057 return this.headersInEvent;
1058 }
1059
1060 /**
1061 * When set the PdfPTableEvent will include the headers.
1062 * @param headersInEvent true to include the headers; false otherwise
1063 */
1064 public void setHeadersInEvent(boolean headersInEvent) {
1065 this.headersInEvent = headersInEvent;
1066 }
1067
1068 /**
1069 * Gets the property splitLate.
1070 * @return the property splitLate
1071 */
1072 public boolean isSplitLate() {
1073 return this.splitLate;
1074 }
1075
1076 /**
1077 * If true the row will only split if it's the first one in an empty page.
1078 * It's true by default.
1079 *<p>
1080 * It's only meaningful if setSplitRows(true).
1081 * @param splitLate the property value
1082 */
1083 public void setSplitLate(boolean splitLate) {
1084 this.splitLate = splitLate;
1085 }
1086
1087 /**
1088 * If true the table will be kept on one page if it fits, by forcing a
1089 * new page if it doesn't fit on the current page. The default is to
1090 * split the table over multiple pages.
1091 *
1092 * @param p_KeepTogether whether to try to keep the table on one page
1093 */
1094 public void setKeepTogether(boolean p_KeepTogether) {
1095 keepTogether = p_KeepTogether;
1096 }
1097
1098 public boolean getKeepTogether() {
1099 return keepTogether;
1100 }
1101
1102 /**
1103 * Gets the number of rows in the footer.
1104 * @return the number of rows in the footer
1105 */
1106 public int getFooterRows() {
1107 return this.footerRows;
1108 }
1109
1110 /**
1111 * Sets the number of rows to be used for the footer. The number
1112 * of footer rows are subtracted from the header rows. For
1113 * example, for a table with two header rows and one footer row the
1114 * code would be:
1115 * <p>
1116 * <PRE>
1117 * table.setHeaderRows(3);
1118 * table.setFooterRows(1);
1119 * </PRE>
1120 * <p>
1121 * Row 0 and 1 will be the header rows and row 2 will be the footer row.
1122 * @param footerRows the number of rows to be used for the footer
1123 */
1124 public void setFooterRows(int footerRows) {
1125 if (footerRows < 0)
1126 footerRows = 0;
1127 this.footerRows = footerRows;
1128 }
1129
1130 /**
1131 * Completes the current row with the default cell. An incomplete row will be dropped
1132 * but calling this method will make sure that it will be present in the table.
1133 */
1134 public void completeRow() {
1135 while (currentRowIdx > 0) {
1136 addCell(defaultCell);
1137 }
1138 }
1139
1140 /**
1141 * @since iText 2.0.8
1142 * @see com.lowagie.text.LargeElement#flushContent()
1143 */
1144 public void flushContent() {
1145 deleteBodyRows();
1146 setSkipFirstHeader(true);
1147 }
1148
1149 /**
1150 * @since iText 2.0.8
1151 * @see com.lowagie.text.LargeElement#isComplete()
1152 */
1153 public boolean isComplete() {
1154 return complete;
1155 }
1156
1157 /**
1158 * @since iText 2.0.8
1159 * @see com.lowagie.text.LargeElement#setComplete(boolean)
1160 */
1161 public void setComplete(boolean complete) {
1162 this.complete = complete;
1163 }
1164 }