aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/lowagie/text/Table.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/lowagie/text/Table.java')
-rw-r--r--src/main/java/com/lowagie/text/Table.java1916
1 files changed, 1916 insertions, 0 deletions
diff --git a/src/main/java/com/lowagie/text/Table.java b/src/main/java/com/lowagie/text/Table.java
new file mode 100644
index 0000000..da7e4d8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Table.java
@@ -0,0 +1,1916 @@
+/*
+ * $Id: Table.java,v 1.138 2005/12/09 12:33:26 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU LIBRARY GENERAL PUBLIC LICENSE for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ *
+ * Some methods in this class were contributed by Geert Poels, Kris Jespers and
+ * Steve Ogryzek. Check the CVS repository.
+ */
+
+package com.lowagie.text;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import com.lowagie.text.markup.MarkupParser;
+import com.lowagie.text.pdf.PdfPCell;
+import com.lowagie.text.pdf.PdfPTable;
+/**
+ * A <CODE>Table</CODE> is a <CODE>Rectangle</CODE> that contains <CODE>Cell</CODE>s,
+ * ordered in some kind of matrix.
+ * <P>
+ * Tables that span multiple pages are cut into different parts automatically.
+ * If you want a table header to be repeated on every page, you may not forget to
+ * mark the end of the header section by using the method <CODE>endHeaders()</CODE>.
+ * <P>
+ * The matrix of a table is not necessarily an m x n-matrix. It can contain holes
+ * or cells that are bigger than the unit. Believe me or not, but it took some serious
+ * thinking to make this as userfriendly as possible. I hope you wil find the result
+ * quite simple (I love simple solutions, especially for complex problems).
+ * I didn't want it to be something as complex as the Java <CODE>GridBagLayout</CODE>.
+ * <P>
+ * Example:
+ * <BLOCKQUOTE><PRE>
+ * // Remark: You MUST know the number of columns when constructing a Table.
+ * // The number of rows is not important.
+ * <STRONG>Table table = new Table(3);</STRONG>
+ * <STRONG>table.setBorderWidth(1);</STRONG>
+ * <STRONG>table.setBorderColor(new Color(0, 0, 255));</STRONG>
+ * <STRONG>table.setPadding(5);</STRONG>
+ * <STRONG>table.setSpacing(5);</STRONG>
+ * Cell cell = new Cell("header");
+ * cell.setHeader(true);
+ * cell.setColspan(3);
+ * <STRONG>table.addCell(cell);</STRONG>
+ * <STRONG>table.endHeaders();</STRONG>
+ * cell = new Cell("example cell with colspan 1 and rowspan 2");
+ * cell.setRowspan(2);
+ * cell.setBorderColor(new Color(255, 0, 0));
+ * <STRONG>table.addCell(cell);</STRONG>
+ * <STRONG>table.addCell("1.1");</STRONG>
+ * <STRONG>table.addCell("2.1");</STRONG>
+ * <STRONG>table.addCell("1.2");</STRONG>
+ * <STRONG>table.addCell("2.2");</STRONG>
+ * <STRONG>table.addCell("cell test1");</STRONG>
+ * cell = new Cell("big cell");
+ * cell.setRowspan(2);
+ * cell.setColspan(2);
+ * <STRONG>table.addCell(cell);</STRONG>
+ * <STRONG>table.addCell("cell test2");</STRONG>
+ * </PRE></BLOCKQUOTE>
+ * The result of this code is a table:
+ * <TABLE ALIGN="Center" BORDER="1" BORDERCOLOR="#0000ff" CELLPADDING="5" CELLSPACING="5">
+ * <TR ALIGN="Left" VALIGN="Left">
+ * <TH ALIGN="Left" COLSPAN="3" VALIGN="Left">
+ * header
+ * </TH>
+ * </TR>
+ * <TR ALIGN="Left" VALIGN="Left">
+ * <TD ALIGN="Left" BORDERCOLOR="#ff0000" ROWSPAN="2" VALIGN="Left">
+ * example cell with colspan 1 and rowspan 2
+ * </TD>
+ * <TD ALIGN="Left" VALIGN="Left">
+ * 1.1
+ * </TD>
+ * <TD ALIGN="Left" VALIGN="Left">
+ * 2.1
+ * </TD>
+ * </TR>
+ * <TR ALIGN="Left" VALIGN="Left">
+ * <TD ALIGN="Left" VALIGN="Left">
+ * 1.2
+ * </TD>
+ * <TD ALIGN="Left" VALIGN="Left">
+ * 2.2
+ * </TD>
+ * </TR>
+ * <TR ALIGN="Left" VALIGN="Left">
+ * <TD ALIGN="Left" VALIGN="Left">
+ * cell test1
+ * </TD>
+ * <TD ALIGN="Left" COLSPAN="2" ROWSPAN="2" VALIGN="Left">
+ * big cell
+ * </TD>
+ * </TR>
+ * <TR ALIGN="Left" VALIGN="Left">
+ * <TD ALIGN="Left" VALIGN="Left">
+ * cell test2
+ * </TD>
+ * </TR>
+ * </TABLE>
+ *
+ * @see Rectangle
+ * @see Element
+ * @see Row
+ * @see Cell
+ */
+
+public class Table extends Rectangle implements Element, MarkupAttributes {
+
+ // membervariables
+
+ // these variables contain the data of the table
+
+ /** This is the number of columns in the <CODE>Table</CODE>. */
+ private int columns;
+
+ // this is the current Position in the table
+ private Point curPosition = new Point(0, 0);
+
+ /** This is the list of <CODE>Row</CODE>s. */
+ private ArrayList rows = new ArrayList();
+
+ // these variables contain the layout of the table
+
+ /** This Empty Cell contains the DEFAULT layout of each Cell added with the method addCell(String content). */
+ private Cell defaultLayout = new Cell(true);
+
+ /** This is the number of the last row of the table headers. */
+ private int lastHeaderRow = -1;
+
+ /** This is the horizontal alignment. */
+ private int alignment = Element.ALIGN_CENTER;
+
+ /** This is cellpadding. */
+ private float cellpadding;
+
+ /** This is cellspacing. */
+ private float cellspacing;
+
+ /** This is the width of the table (in percent of the available space). */
+ private float widthPercentage = 80;
+
+ // member variable added by Evelyne De Cordier
+ /** This is the width of the table (in pixels). */
+ private String absWidth = "";
+
+ /** This is an array containing the widths (in percentages) of every column. */
+ private float[] widths;
+
+ /** Boolean to track errors (some checks will be performed) */
+ boolean mDebug = false;
+
+ /** Boolean to track if a table was inserted (to avoid unnecessary computations afterwards) */
+ boolean mTableInserted = false;
+
+ /**
+ * Boolean to automatically fill empty cells before a table is rendered
+ * (takes CPU so may be set to false in case of certainty)
+ */
+ boolean mAutoFillEmptyCells = false;
+
+ /** If true this table may not be split over two pages. */
+ boolean tableFitsPage = false;
+
+ /** If true cells may not be split over two pages. */
+ boolean cellsFitPage = false;
+
+ /** This is the offset of the table. */
+ float offset = Float.NaN;
+
+ /** contains the attributes that are added to each odd (or even) row */
+ protected Hashtable alternatingRowAttributes = null;
+
+ /** if you want to generate tables the old way, set this value to false. */
+ protected boolean convert2pdfptable = false;
+
+ // constructors
+
+ /**
+ * Constructs a <CODE>Table</CODE> with a certain number of columns.
+ *
+ * @param columns The number of columns in the table
+ * @throws BadElementException if the creator was called with less than 1 column
+ */
+
+ public Table(int columns) throws BadElementException {
+ this(columns, 1);
+ }
+
+ /**
+ * Constructs a <CODE>Table</CODE> with a certain number of columns
+ * and a certain number of <CODE>Row</CODE>s.
+ *
+ * @param columns The number of columns in the table
+ * @param rows The number of rows
+ * @throws BadElementException if the creator was called with less than 1 column
+ */
+
+ public Table(int columns, int rows) throws BadElementException {
+ // a Rectangle is create with BY DEFAULT a border with a width of 1
+ super(0, 0, 0, 0);
+ setBorder(BOX);
+ setBorderWidth(1);
+ defaultLayout.setBorder(BOX);
+
+ // a table should have at least 1 column
+ if (columns <= 0) {
+ throw new BadElementException("A table should have at least 1 column.");
+ }
+ this.columns = columns;
+
+ // a certain number of rows are created
+ for (int i = 0; i < rows; i++) {
+ this.rows.add(new Row(columns));
+ }
+ curPosition = new Point(0, 0);
+
+ // the DEFAULT widths are calculated
+ widths = new float[columns];
+ float width = 100f / columns;
+ for (int i = 0; i < columns; i++) {
+ widths[i] = width;
+ }
+ }
+
+ /**
+ * Returns a <CODE>Table</CODE> that has been constructed taking in account
+ * the value of some <VAR>attributes</VAR>.
+ *
+ * @param attributes Some attributes
+ */
+
+ public Table(Properties attributes) {
+ // a Rectangle is create with BY DEFAULT a border with a width of 1
+ super(0, 0, 0, 0);
+ setBorder(BOX);
+ setBorderWidth(1);
+ defaultLayout.setBorder(BOX);
+
+ String value = (String)attributes.remove(ElementTags.COLUMNS);
+ if (value == null) {
+ columns = 1;
+ }
+ else {
+ columns = Integer.parseInt(value);
+ if (columns <= 0) {
+ columns = 1;
+ }
+ }
+
+ rows.add(new Row(columns));
+ curPosition.setLocation(0, curPosition.y);
+
+ if ((value = (String)attributes.remove(ElementTags.LASTHEADERROW)) != null) {
+ setLastHeaderRow(Integer.parseInt(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.ALIGN)) != null) {
+ setAlignment(value);
+ }
+ if ((value = (String)attributes.remove(ElementTags.CELLSPACING)) != null) {
+ setSpacing(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.CELLPADDING)) != null) {
+ setPadding(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.OFFSET)) != null) {
+ setOffset(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.WIDTH)) != null) {
+ if (value.endsWith("%"))
+ setWidth(Float.valueOf(value.substring(0, value.length() - 1) + "f").floatValue());
+ else
+ setAbsWidth(value);
+ }
+ widths = new float[columns];
+ for (int i = 0; i < columns; i++) {
+ widths[i] = 0;
+ }
+ if ((value = (String)attributes.remove(ElementTags.WIDTHS)) != null) {
+ StringTokenizer widthTokens = new StringTokenizer(value, ";");
+ int i = 0;
+ while (widthTokens.hasMoreTokens()) {
+ value = widthTokens.nextToken();
+ widths[i] = Float.valueOf(value + "f").floatValue();
+ i++;
+ }
+ columns = i;
+ }
+ if ((value = (String)attributes.remove(ElementTags.TABLEFITSPAGE)) != null) {
+ tableFitsPage = new Boolean(value).booleanValue();
+ }
+ if ((value = (String)attributes.remove(ElementTags.CELLSFITPAGE)) != null) {
+ cellsFitPage = new Boolean(value).booleanValue();
+ }
+ if ((value = (String)attributes.remove(ElementTags.BORDERWIDTH)) != null) {
+ setBorderWidth(Float.valueOf(value + "f").floatValue());
+ }
+ int border = 0;
+ if ((value = (String)attributes.remove(ElementTags.LEFT)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.LEFT;
+ }
+ if ((value = (String)attributes.remove(ElementTags.RIGHT)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.RIGHT;
+ }
+ if ((value = (String)attributes.remove(ElementTags.TOP)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.TOP;
+ }
+ if ((value = (String)attributes.remove(ElementTags.BOTTOM)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.BOTTOM;
+ }
+ setBorder(border);
+ String r = (String)attributes.remove(ElementTags.RED);
+ String g = (String)attributes.remove(ElementTags.GREEN);
+ String b = (String)attributes.remove(ElementTags.BLUE);
+ if (r != null || g != null || b != null) {
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ if (r != null) red = Integer.parseInt(r);
+ if (g != null) green = Integer.parseInt(g);
+ if (b != null) blue = Integer.parseInt(b);
+ setBorderColor(new Color(red, green, blue));
+ }
+ else if ((value = attributes.getProperty(ElementTags.BORDERCOLOR)) != null) {
+ setBorderColor(MarkupParser.decodeColor(value));
+ }
+ r = (String)attributes.remove(ElementTags.BGRED);
+ g = (String)attributes.remove(ElementTags.BGGREEN);
+ b = (String)attributes.remove(ElementTags.BGBLUE);
+ if (r != null || g != null || b != null) {
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ if (r != null) red = Integer.parseInt(r);
+ if (g != null) green = Integer.parseInt(g);
+ if (b != null) blue = Integer.parseInt(b);
+ setBackgroundColor(new Color(red, green, blue));
+ }
+ else if ((value = (String)attributes.remove(ElementTags.BACKGROUNDCOLOR)) != null) {
+ setBackgroundColor(MarkupParser.decodeColor(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.GRAYFILL)) != null) {
+ setGrayFill(Float.valueOf(value + "f").floatValue());
+ }
+ if (attributes.size() > 0) setMarkupAttributes(attributes);
+ }
+
+ // implementation of the Element-methods
+
+ /**
+ * Processes the element by adding it (or the different parts) to an
+ * <CODE>ElementListener</CODE>.
+ *
+ * @param listener an <CODE>ElementListener</CODE>
+ * @return <CODE>true</CODE> if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+ /**
+ * Performs extra checks when executing table code (currently only when cells are added).
+ * @param aDebug
+ */
+ public void setDebug(boolean aDebug) {
+ mDebug = aDebug;
+ }
+
+ /**
+ * Sets the default layout of the Table to
+ * the provided Cell
+ * @param value a cell with all the defaults
+ */
+ public void setDefaultLayout(Cell value) {
+ defaultLayout = value;
+ }
+
+ /**
+ * Enables/disables automatic insertion of empty cells before table is rendered. (default = false)
+ * As some people may want to create a table, fill only a couple of the cells and don't bother with
+ * investigating which empty ones need to be added, this default behaviour may be very welcome.
+ * Disabling is recommended to increase speed. (empty cells should be added through extra code then)
+ *
+ * @param aDoAutoFill enable/disable autofill
+ */
+
+ public void setAutoFillEmptyCells(boolean aDoAutoFill) {
+ mAutoFillEmptyCells = aDoAutoFill;
+ }
+
+ /**
+ * Allows you to control when a page break occurs.
+ * <P>
+ * When a table doesn't fit a page, it is split in two parts.
+ * If you want to avoid this, you should set the <VAR>tableFitsPage</VAR> value to true.
+ *
+ * @param fitPage enter true if you don't want to split cells
+ */
+
+ public void setTableFitsPage(boolean fitPage) {
+ this.tableFitsPage = fitPage;
+ if (fitPage) setCellsFitPage(true);
+ }
+
+ /**
+ * Allows you to control when a page break occurs.
+ * <P>
+ * When a cell doesn't fit a page, it is split in two parts.
+ * If you want to avoid this, you should set the <VAR>cellsFitPage</VAR> value to true.
+ *
+ * @param fitPage enter true if you don't want to split cells
+ */
+
+ public void setCellsFitPage(boolean fitPage) {
+ this.cellsFitPage = fitPage;
+ }
+
+ /**
+ * Checks if this <CODE>Table</CODE> has to fit a page.
+ *
+ * @return true if the table may not be split
+ */
+
+ public boolean hasToFitPageTable() {
+ return tableFitsPage;
+ }
+
+ /**
+ * Checks if the cells of this <CODE>Table</CODE> have to fit a page.
+ *
+ * @return true if the cells may not be split
+ */
+
+ public boolean hasToFitPageCells() {
+ return cellsFitPage;
+ }
+
+ /**
+ * Sets the offset of this table.
+ *
+ * Normally a newline is added before you add a Table object.
+ * This newline uses the current leading.
+ * If you want to control the space between the table and the previous
+ * element yourself, you have to set the offset of this table.
+ *
+ * @param offset the space between this table and the previous object.
+ */
+
+ public void setOffset(float offset) {
+ this.offset = offset;
+ }
+
+ /**
+ * Gets the offset of this table.
+ *
+ * @return the space between this table and the previous element.
+ */
+
+ public float getOffset() {
+ return offset;
+ }
+
+ /**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.TABLE;
+ }
+
+ /**
+ * Gets all the chunks in this element.
+ *
+ * @return an <CODE>ArrayList</CODE>
+ */
+
+ public ArrayList getChunks() {
+ return new ArrayList();
+ }
+
+ // methods to add content to the table
+
+ /**
+ * Adds a <CODE>Cell</CODE> to the <CODE>Table</CODE> at a certain row and column.
+ *
+ * @param aCell The <CODE>Cell</CODE> to add
+ * @param row The row where the <CODE>Cell</CODE> will be added
+ * @param column The column where the <CODE>Cell</CODE> will be added
+ * @throws BadElementException
+ */
+
+ public void addCell(Cell aCell, int row, int column) throws BadElementException {
+ addCell(aCell, new Point(row,column));
+ }
+
+ /**
+ * Adds a <CODE>Cell</CODE> to the <CODE>Table</CODE> at a certain location.
+ *
+ * @param aCell The <CODE>Cell</CODE> to add
+ * @param aLocation The location where the <CODE>Cell</CODE> will be added
+ * @throws BadElementException
+ */
+
+ public void addCell(Cell aCell, Point aLocation) throws BadElementException {
+ if (aCell == null) throw new NullPointerException("addCell - cell has null-value");
+ if (aLocation == null) throw new NullPointerException("addCell - point has null-value");
+ if (aCell.isTable()) insertTable((Table)aCell.getElements().next(), aLocation);
+ if (mDebug == true) {
+ if (aLocation.x < 0) throw new BadElementException("row coordinate of location must be >= 0");
+ if ((aLocation.y <= 0) && (aLocation.y > columns)) throw new BadElementException("column coordinate of location must be >= 0 and < nr of columns");
+ if (!isValidLocation(aCell, aLocation)) throw new BadElementException("Adding a cell at the location (" + aLocation.x + "," + aLocation.y + ") with a colspan of " + aCell.colspan() + " and a rowspan of " + aCell.rowspan() + " is illegal (beyond boundaries/overlapping).");
+ }
+ if (aCell.border() == UNDEFINED) aCell.setBorder(defaultLayout.border());
+ aCell.fill();
+ placeCell(rows, aCell, aLocation);
+ setCurrentLocationToNextValidPosition(aLocation);
+ }
+
+
+ /**
+ * Adds a <CODE>Cell</CODE> to the <CODE>Table</CODE>.
+ *
+ * @param cell a <CODE>Cell</CODE>
+ */
+
+ public void addCell(Cell cell) {
+ try {
+ addCell(cell, curPosition);
+ }
+ catch(BadElementException bee) {
+ // don't add the cell
+ }
+ }
+
+ /**
+ * Adds a <CODE>Cell</CODE> to the <CODE>Table</CODE>.
+ * <P>
+ * This is a shortcut for <CODE>addCell(Cell cell)</CODE>.
+ * The <CODE>Phrase</CODE> will be converted to a <CODE>Cell</CODE>.
+ *
+ * @param content a <CODE>Phrase</CODE>
+ * @throws BadElementException this should never happen
+ */
+
+ public void addCell(Phrase content) throws BadElementException {
+ addCell(content, curPosition);
+ }
+
+ /**
+ * Adds a <CODE>Cell</CODE> to the <CODE>Table</CODE>.
+ * <P>
+ * This is a shortcut for <CODE>addCell(Cell cell, Point location)</CODE>.
+ * The <CODE>Phrase</CODE> will be converted to a <CODE>Cell</CODE>.
+ *
+ * @param content a <CODE>Phrase</CODE>
+ * @param location a <CODE>Point</CODE>
+ * @throws BadElementException this should never happen
+ */
+
+ public void addCell(Phrase content, Point location) throws BadElementException {
+ Cell cell = new Cell(content);
+ cell.setBorder(defaultLayout.border());
+ cell.setBorderWidth(defaultLayout.borderWidth());
+ cell.setBorderColor(defaultLayout.borderColor());
+ cell.setBackgroundColor(defaultLayout.backgroundColor());
+ cell.setHorizontalAlignment(defaultLayout.horizontalAlignment());
+ cell.setVerticalAlignment(defaultLayout.verticalAlignment());
+ cell.setColspan(defaultLayout.colspan());
+ cell.setRowspan(defaultLayout.rowspan());
+ addCell(cell, location);
+ }
+
+ /**
+ * Adds a <CODE>Cell</CODE> to the <CODE>Table</CODE>.
+ * <P>
+ * This is a shortcut for <CODE>addCell(Cell cell)</CODE>.
+ * The <CODE>String</CODE> will be converted to a <CODE>Cell</CODE>.
+ *
+ * @param content a <CODE>String</CODE>
+ * @throws BadElementException this should never happen
+ */
+
+ public void addCell(String content) throws BadElementException {
+ addCell(new Phrase(content), curPosition);
+ }
+
+ /**
+ * Adds a <CODE>Cell</CODE> to the <CODE>Table</CODE>.
+ * <P>
+ * This is a shortcut for <CODE>addCell(Cell cell, Point location)</CODE>.
+ * The <CODE>String</CODE> will be converted to a <CODE>Cell</CODE>.
+ *
+ * @param content a <CODE>String</CODE>
+ * @param location a <CODE>Point</CODE>
+ * @throws BadElementException this should never happen
+ */
+
+ public void addCell(String content, Point location) throws BadElementException {
+ addCell(new Phrase(content), location);
+ }
+
+ /**
+ * To put a table within the existing table at the current position
+ * generateTable will of course re-arrange the widths of the columns.
+ *
+ * @param aTable the table you want to insert
+ */
+
+ public void insertTable(Table aTable) {
+ if (aTable == null) throw new NullPointerException("insertTable - table has null-value");
+ insertTable(aTable, curPosition);
+ }
+
+ /**
+ * To put a table within the existing table at the given position
+ * generateTable will of course re-arrange the widths of the columns.
+ *
+ * @param aTable The <CODE>Table</CODE> to add
+ * @param row The row where the <CODE>Cell</CODE> will be added
+ * @param column The column where the <CODE>Cell</CODE> will be added
+ */
+
+ public void insertTable(Table aTable, int row, int column) {
+ if (aTable == null) throw new NullPointerException("insertTable - table has null-value");
+ insertTable(aTable, new Point(row, column));
+ }
+
+ /**
+ * To put a table within the existing table at the given position
+ * generateTable will of course re-arrange the widths of the columns.
+ *
+ * @param aTable the table you want to insert
+ * @param aLocation a <CODE>Point</CODE>
+ */
+ public void insertTable(Table aTable, Point aLocation) {
+
+ if (aTable == null) throw new NullPointerException("insertTable - table has null-value");
+ if (aLocation == null) throw new NullPointerException("insertTable - point has null-value");
+ mTableInserted = true;
+ aTable.complete();
+ if (mDebug == true) {
+ if (aLocation.y > columns) System.err.println("insertTable -- wrong columnposition("+ aLocation.y + ") of location; max =" + columns);
+ }
+ int rowCount = aLocation.x + 1 - rows.size();
+ int i = 0;
+ if ( rowCount > 0 ) { //create new rows ?
+ for (; i < rowCount; i++) {
+ rows.add(new Row(columns));
+ }
+ }
+
+ ((Row) rows.get(aLocation.x)).setElement(aTable,aLocation.y);
+
+ setCurrentLocationToNextValidPosition(aLocation);
+ }
+
+/**
+ * Will fill empty cells with valid blank <CODE>Cell</CODE>s
+ */
+
+ public void complete() {
+ if (mTableInserted == true) {
+ mergeInsertedTables(); // integrate tables in the table
+ mTableInserted = false;
+ }
+ if (mAutoFillEmptyCells == true) {
+ fillEmptyMatrixCells();
+ }
+ if (alternatingRowAttributes != null) {
+ Properties even = new Properties();
+ Properties odd = new Properties();
+ String name;
+ String[] value;
+ for (Iterator iterator = alternatingRowAttributes.keySet().iterator(); iterator.hasNext(); ) {
+ name = String.valueOf(iterator.next());
+ value = (String[])alternatingRowAttributes.get(name);
+ even.setProperty(name, value[0]);
+ odd.setProperty(name, value[1]);
+ }
+ Row row;
+ for (int i = lastHeaderRow + 1; i < rows.size(); i++) {
+ row = (Row)rows.get(i);
+ row.setMarkupAttributes(i % 2 == 0 ? even : odd);
+ }
+ }
+ }
+
+ /**
+ * Changes the border in the default layout of the <CODE>Cell</CODE>s
+ * added with method <CODE>addCell(String content)</CODE>.
+ *
+ * @param value the new border value
+ */
+
+ public void setDefaultCellBorder(int value) {
+ defaultLayout.setBorder(value);
+ }
+
+ /**
+ * Changes the width of the borders in the default layout of the <CODE>Cell</CODE>s
+ * added with method <CODE>addCell(String content)</CODE>.
+ *
+ * @param value the new width
+ */
+
+ public void setDefaultCellBorderWidth(float value) {
+ defaultLayout.setBorderWidth(value);
+ }
+
+ /**
+ * Changes the bordercolor in the default layout of the <CODE>Cell</CODE>s
+ * added with method <CODE>addCell(String content)</CODE>.
+ *
+ * @param color the new color
+ */
+
+ public void setDefaultCellBorderColor(Color color) {
+ defaultLayout.setBorderColor(color);
+ }
+
+ /**
+ * Changes the backgroundcolor in the default layout of the <CODE>Cell</CODE>s
+ * added with method <CODE>addCell(String content)</CODE>.
+ *
+ * @param color the new color
+ */
+
+ public void setDefaultCellBackgroundColor(Color color) {
+ defaultLayout.setBackgroundColor(color);
+ }
+
+ /**
+ * Changes the grayfill in the default layout of the <CODE>Cell</CODE>s
+ * added with method <CODE>addCell(String content)</CODE>.
+ *
+ * @param value the new value
+ */
+
+ public void setDefaultCellGrayFill(float value) {
+ if (value >= 0 && value <= 1) {
+ defaultLayout.setGrayFill(value);
+ }
+ }
+
+ /**
+ * Changes the horizontalAlignment in the default layout of the <CODE>Cell</CODE>s
+ * added with method <CODE>addCell(String content)</CODE>.
+ *
+ * @param value the new alignment value
+ */
+
+ public void setDefaultHorizontalAlignment(int value) {
+ defaultLayout.setHorizontalAlignment(value);
+ }
+
+ /**
+ * Changes the verticalAlignment in the default layout of the <CODE>Cell</CODE>s
+ * added with method <CODE>addCell(String content)</CODE>.
+ *
+ * @param value the new alignment value
+ */
+
+ public void setDefaultVerticalAlignment(int value) {
+ defaultLayout.setVerticalAlignment(value);
+ }
+
+ /**
+ * Changes the rowspan in the default layout of the <CODE>Cell</CODE>s
+ * added with method <CODE>addCell(String content)</CODE>.
+ *
+ * @param value the new rowspan value
+ */
+
+ public void setDefaultRowspan(int value) {
+ defaultLayout.setRowspan(value);
+ }
+
+ /**
+ * Changes the colspan in the default layout of the <CODE>Cell</CODE>s
+ * added with method <CODE>addCell(String content)</CODE>.
+ *
+ * @param value the new colspan value
+ */
+
+ public void setDefaultColspan(int value) {
+ defaultLayout.setColspan(value);
+ }
+
+ // methods
+
+ /**
+ * Sets the unset cell properties to be the table defaults.
+ *
+ * @param aCell The cell to set to table defaults as necessary.
+ */
+
+ private void assumeTableDefaults(Cell aCell) {
+
+ if (aCell.border() == Rectangle.UNDEFINED) {
+ aCell.setBorder(defaultLayout.border());
+ }
+ if (aCell.borderWidth() == Rectangle.UNDEFINED) {
+ aCell.setBorderWidth(defaultLayout.borderWidth());
+ }
+ if (aCell.borderColor() == null) {
+ aCell.setBorderColor(defaultLayout.borderColor());
+ }
+ if (aCell.backgroundColor() == null) {
+ aCell.setBackgroundColor(defaultLayout.backgroundColor());
+ }
+ if (aCell.horizontalAlignment() == Element.ALIGN_UNDEFINED) {
+ aCell.setHorizontalAlignment(defaultLayout.horizontalAlignment());
+ }
+ if (aCell.verticalAlignment() == Element.ALIGN_UNDEFINED) {
+ aCell.setVerticalAlignment(defaultLayout.verticalAlignment());
+ }
+ }
+
+ /**
+ * Deletes a column in this table.
+ *
+ * @param column the number of the column that has to be deleted
+ * @throws BadElementException
+ */
+
+ public void deleteColumn(int column) throws BadElementException {
+ float newWidths[] = new float[--columns];
+ for (int i = 0; i < column; i++) {
+ newWidths[i] = widths[i];
+ }
+ for (int i = column; i < columns; i++) {
+ newWidths[i] = widths[i + 1];
+ }
+ setWidths(newWidths);
+ for (int i = 0; i < columns; i++) {
+ newWidths[i] = widths[i];
+ }
+ widths = newWidths;
+ Row row;
+ int size = rows.size();
+ for (int i = 0; i < size; i++) {
+ row = (Row) rows.get(i);
+ row.deleteColumn(column);
+ rows.set(i, row);
+ }
+ if (column == columns) {
+ curPosition.setLocation(curPosition.x+1, 0);
+ }
+ }
+
+ /**
+ * Deletes a row.
+ *
+ * @param row the number of the row to delete
+ * @return boolean <CODE>true</CODE> if the row was deleted; <CODE>false</CODE> if not
+ */
+
+ public boolean deleteRow(int row) {
+ if (row < 0 || row >= rows.size()) {
+ return false;
+ }
+ rows.remove(row);
+ curPosition.setLocation(curPosition.x-1, curPosition.y);
+ return true;
+ }
+
+ /**
+ * Deletes all rows in this table.
+ * (contributed by dperezcar@fcc.es)
+ */
+
+ public void deleteAllRows() {
+ rows.clear();
+ rows.add(new Row(columns));
+ curPosition.setLocation(0, 0);
+ lastHeaderRow = -1;
+ }
+
+ /**
+ * Deletes the last row in this table.
+ *
+ * @return boolean <CODE>true</CODE> if the row was deleted; <CODE>false</CODE> if not
+ */
+
+ public boolean deleteLastRow() {
+ return deleteRow(rows.size() - 1);
+ }
+
+ /**
+ * Marks the last row of the table headers.
+ *
+ * @return the number of the last row of the table headers
+ */
+
+ public int endHeaders() {
+ /* patch sep 8 2001 Francesco De Milato */
+ lastHeaderRow = curPosition.x - 1;
+ return lastHeaderRow;
+ }
+
+ // methods to set the membervariables
+
+ /**
+ * Sets the horizontal alignment.
+ *
+ * @param value the new value
+ */
+
+ public void setLastHeaderRow(int value) {
+ lastHeaderRow = value;
+ }
+
+ /**
+ * Sets the horizontal alignment.
+ *
+ * @param value the new value
+ */
+
+ public void setAlignment(int value) {
+ alignment = value;
+ }
+
+ /**
+ * Sets the alignment of this paragraph.
+ *
+ * @param alignment the new alignment as a <CODE>String</CODE>
+ */
+
+ public void setAlignment(String alignment) {
+ if (ElementTags.ALIGN_LEFT.equalsIgnoreCase(alignment)) {
+ this.alignment = Element.ALIGN_LEFT;
+ return;
+ }
+ if (ElementTags.RIGHT.equalsIgnoreCase(alignment)) {
+ this.alignment = Element.ALIGN_RIGHT;
+ return;
+ }
+ this.alignment = Element.ALIGN_CENTER;
+ }
+
+ /**
+ * Sets the cellpadding.
+ *
+ * @param value the new value
+ */
+
+ public void setSpaceInsideCell(float value) {
+ cellpadding = value;
+ }
+
+ /**
+ * Sets the cellspacing.
+ *
+ * @param value the new value
+ */
+
+ public void setSpaceBetweenCells(float value) {
+ cellspacing = value;
+ }
+
+ /**
+ * Sets the cellpadding.
+ *
+ * @param value the new value
+ */
+
+ public void setPadding(float value) {
+ cellpadding = value;
+ }
+
+ /**
+ * Sets the cellspacing.
+ *
+ * @param value the new value
+ */
+
+ public void setSpacing(float value) {
+ cellspacing = value;
+ }
+
+ /**
+ * Sets the cellspacing (the meaning of cellpadding and cellspacing was inverted by mistake).
+ *
+ * @param value the new value
+ * @deprecated use setSpacing instead
+ */
+
+ public void setCellpadding(float value) {
+ cellspacing = value;
+ }
+
+ /**
+ * Sets the cellpadding (the meaning of cellpadding and cellspacing was inverted by mistake).
+ *
+ * @param value the new value
+ * @deprecated use setPadding instead
+ */
+
+ public void setCellspacing(float value) {
+ cellpadding = value;
+ }
+
+ /**
+ * Sets the width of this table (in percentage of the available space).
+ *
+ * @param width the width
+ */
+
+ public void setWidth(float width) {
+ this.widthPercentage = width;
+ }
+
+ /**
+ * Sets the width of this table (in percentage of the available space).
+ *
+ * @param width the width
+ */
+
+ public void setAbsWidth(String width) {
+ this.absWidth = width;
+ }
+
+ /**
+ * Sets the widths of the different columns (percentages).
+ * <P>
+ * You can give up relative values of borderwidths.
+ * The sum of these values will be considered 100%.
+ * The values will be recalculated as percentages of this sum.
+ * <P>
+ * example:
+ * <BLOCKQUOTE><PRE>
+ * float[] widths = {2, 1, 1};
+ * <STRONG>table.setWidths(widths)</STRONG>
+ * </PRE></BLOCKQUOTE>
+ * The widths will be: a width of 50% for the first column,
+ * 25% for the second and third column.
+ *
+ * @param widths an array with values
+ * @throws BadElementException
+ */
+
+ public void setWidths(float[] widths) throws BadElementException {
+ if (widths.length != columns) {
+ throw new BadElementException("Wrong number of columns.");
+ }
+
+ // The sum of all values is 100%
+ float hundredPercent = 0;
+ for (int i = 0; i < columns; i++) {
+ hundredPercent += widths[i];
+ }
+
+ // The different percentages are calculated
+ float width;
+ this.widths[columns - 1] = 100;
+ for (int i = 0; i < columns - 1; i++) {
+ width = (100.0f * widths[i]) / hundredPercent;
+ this.widths[i] = width;
+ this.widths[columns - 1] -= width;
+ }
+ }
+
+ /**
+ * Sets the widths of the different columns (percentages).
+ * <P>
+ * You can give up relative values of borderwidths.
+ * The sum of these values will be considered 100%.
+ * The values will be recalculated as percentages of this sum.
+ *
+ * @param widths an array with values
+ * @throws DocumentException
+ */
+
+ public void setWidths(int[] widths) throws DocumentException {
+ float tb[] = new float[widths.length];
+ for (int k = 0; k < widths.length; ++k)
+ tb[k] = widths[k];
+ setWidths(tb);
+ }
+ // methods to retrieve the membervariables
+
+ /**
+ * Gets the number of columns.
+ *
+ * @return a value
+ */
+
+ public int columns() {
+ return columns;
+ }
+
+ /**
+ * Gets the number of rows in this <CODE>Table</CODE>.
+ *
+ * @return the number of rows in this <CODE>Table</CODE>
+ */
+
+ public int size() {
+ return rows.size();
+ }
+
+ /**
+ * Gets the proportional widths of the columns in this <CODE>Table</CODE>.
+ *
+ * @return the proportional widths of the columns in this <CODE>Table</CODE>
+ */
+
+ public float[] getProportionalWidths() {
+ return widths;
+ }
+
+ /**
+ * Gets an <CODE>Iterator</CODE> of all the <CODE>Row</CODE>s.
+ *
+ * @return an <CODE>Iterator</CODE>
+ */
+
+ public Iterator iterator() {
+ return rows.iterator();
+ }
+
+ /**
+ * Gets the horizontal alignment.
+ *
+ * @return a value
+ */
+
+ public int alignment() {
+ return alignment;
+ }
+
+ /**
+ * Gets the cellpadding.
+ *
+ * @return a value
+ */
+
+ public float cellpadding() {
+ return cellpadding;
+ }
+
+ /**
+ * Gets the cellspacing.
+ *
+ * @return a value
+ */
+
+ public float cellspacing() {
+ return cellspacing;
+ }
+
+ /**
+ * Gets the table width (a percentage).
+ *
+ * @return the table width
+ */
+
+ public float widthPercentage() {
+ return widthPercentage;
+ }
+
+ /**
+ * Gets the table width (in pixels).
+ *
+ * @return the table width
+ */
+
+ public String absWidth() {
+ return absWidth;
+ }
+
+ /**
+ * Gets the first number of the row that doesn't contain headers.
+ *
+ * @return a rownumber
+ */
+
+ public int firstDataRow() {
+ return lastHeaderRow + 1;
+ }
+
+ /**
+ * Gets the last number of the rows that contain headers.
+ *
+ * @return a rownumber
+ */
+ public int lastHeaderRow() {
+ return this.lastHeaderRow;
+ }
+
+ /**
+ * Gets the dimension of this table
+ *
+ * @return dimension
+ */
+
+ public Dimension getDimension() {
+ return new Dimension(columns, rows.size());
+ }
+
+ /**
+ * returns the element at the position row, column
+ * (Cast to Cell or Table)
+ *
+ * @param row
+ * @param column
+ * @return dimension
+ */
+
+ public Object getElement(int row, int column) {
+ return ((Row) rows.get(row)).getCell(column);
+ }
+
+ /**
+ * Integrates all added tables and recalculates column widths.
+ */
+
+ private void mergeInsertedTables() {
+ int i=0, j=0;
+ float [] lNewWidths = null;
+ int [] lDummyWidths = new int[columns]; // to keep track in how many new cols this one will be split
+ float[][] lDummyColumnWidths = new float[columns][]; // bugfix Tony Copping
+ int [] lDummyHeights = new int[rows.size()]; // to keep track in how many new rows this one will be split
+ ArrayList newRows = null;
+ boolean isTable=false;
+ int lTotalRows = 0, lTotalColumns = 0;
+ int lNewMaxRows = 0, lNewMaxColumns = 0;
+
+ Table lDummyTable = null;
+
+ // first we'll add new columns when needed
+ // check one column at a time, find maximum needed nr of cols
+ // Search internal tables and find one with max columns
+ for (j=0; j < columns; j++) {
+ lNewMaxColumns = 1; // value to hold in how many columns the current one will be split
+ float [] tmpWidths = null;
+ for (i=0; i < rows.size(); i++) {
+ if ( Table.class.isInstance(((Row) rows.get(i)).getCell(j)) ) {
+ isTable=true;
+ lDummyTable = ((Table) ((Row) rows.get(i)).getCell(j));
+ if( tmpWidths == null) {
+ tmpWidths = lDummyTable.widths;
+ lNewMaxColumns=tmpWidths.length;
+ }
+ else {
+ int cols = lDummyTable.getDimension().width;
+ float [] tmpWidthsN = new float[ cols * tmpWidths.length];
+ float tpW=0, btW=0, totW=0;
+ int tpI=0, btI=0, totI=0;
+ tpW+=tmpWidths[0];
+ btW+=lDummyTable.widths[0];
+ while( tpI<tmpWidths.length && btI<cols) {
+ if( btW>tpW) {
+ tmpWidthsN[totI] = tpW-totW;
+ tpI++;
+ if(tpI<tmpWidths.length) {
+ tpW+=tmpWidths[tpI];
+ }
+ }
+ else {
+ tmpWidthsN[totI] = btW-totW;
+ btI++;
+ if(Math.abs(btW - tpW) < 0.0001) {
+ tpI++;
+ if(tpI<tmpWidths.length) {
+ tpW+=tmpWidths[tpI];
+ }
+ }
+ if(btI<cols) {
+ btW+=lDummyTable.widths[btI];
+ }
+ }
+ totW+=tmpWidthsN[totI];
+ totI++;
+ }
+ /*if( tpI<tmpWidths.length)
+ {
+ System.arraycopy(tmpWidths, tpI, tmpWidthsN, totI, tmpWidths.length-tpI);
+ totI +=tmpWidths.length-tpI;
+ }
+ else if(btI<cols)
+ {
+ System.arraycopy(lDummyTable.widths, btI, tmpWidthsN, totI, lDummyTable.widths.length-btI);
+ totI +=lDummyTable.widths.length-btI; }*/
+ tmpWidths = new float[totI];
+ System.arraycopy(tmpWidthsN, 0, tmpWidths, 0, totI);
+ lNewMaxColumns=totI;
+ }
+ /*if ( lDummyTable.getDimension().width > lNewMaxColumns )
+ {
+ lNewMaxColumns = lDummyTable.getDimension().width;
+ lDummyColumnWidths[j] = lDummyTable.widths; // bugfix Tony Copping
+ }*/
+ }
+ }
+ lDummyColumnWidths[j] = tmpWidths;
+ lTotalColumns += lNewMaxColumns;
+ lDummyWidths [j] = lNewMaxColumns;
+ }
+
+ // next we'll add new rows when needed
+ for (i=0; i < rows.size(); i++) {
+ lNewMaxRows = 1; // holds value in how many rows the current one will be split
+ for (j=0; j < columns; j++) {
+ if ( Table.class.isInstance(((Row) rows.get(i)).getCell(j)) ) {
+ isTable=true;
+ lDummyTable = (Table) ((Row) rows.get(i)).getCell(j);
+ if ( lDummyTable.getDimension().height > lNewMaxRows ) {
+ lNewMaxRows = lDummyTable.getDimension().height;
+ }
+ }
+ }
+ lTotalRows += lNewMaxRows;
+ lDummyHeights [i] = lNewMaxRows;
+ }
+
+ if ( (lTotalColumns != columns) || (lTotalRows != rows.size()) || isTable) // NO ADJUSTMENT
+ {
+ // ** WIDTH
+ // set correct width for new columns
+ // divide width over new nr of columns
+ // Take new max columns of internal table and work out widths for each col
+ lNewWidths = new float [lTotalColumns];
+ int lDummy = 0;
+ for (int tel=0; tel < widths.length;tel++) {
+ if ( lDummyWidths[tel] != 1) {
+ // divide
+ for (int tel2 = 0; tel2 < lDummyWidths[tel]; tel2++) {
+ // lNewWidths[lDummy] = widths[tel] / lDummyWidths[tel];
+ lNewWidths[lDummy] = widths[tel] * lDummyColumnWidths[tel][tel2] / 100f; // bugfix Tony Copping
+ lDummy++;
+ }
+ }
+ else {
+ lNewWidths[lDummy] = widths[tel];
+ lDummy++;
+ }
+ }
+
+ // ** FILL OUR NEW TABLE
+ // generate new table
+ // set new widths
+ // copy old values
+ newRows = new ArrayList(lTotalRows);
+ for (i = 0; i < lTotalRows; i++) {
+ newRows.add(new Row(lTotalColumns));
+ }
+ int lDummyRow = 0, lDummyColumn = 0; // to remember where we are in the new, larger table
+ Object lDummyElement = null;
+ for (i=0; i < rows.size(); i++) {
+ lDummyColumn = 0;
+ lNewMaxRows = 1;
+ for (j=0; j < columns; j++) {
+ if ( Table.class.isInstance(((Row) rows.get(i)).getCell(j)) ) // copy values from embedded table
+ {
+ lDummyTable = (Table) ((Row) rows.get(i)).getCell(j);
+
+ // Work out where columns in table table correspond to columns in current table
+ int colMap[] = new int[lDummyTable.widths.length+1];
+ int cb=0, ct=0;
+
+ for( ; cb<lDummyTable.widths.length;cb++) {
+ colMap[cb] = lDummyColumn+ct;
+
+ float wb;
+ wb = lDummyTable.widths[cb];
+
+ float wt=0;
+ while( ct<lDummyWidths[j]) {
+ wt+=lDummyColumnWidths[j][ct++];
+ if(Math.abs(convertWidth(wb) - convertWidth(wt)) < 0.0001) break;
+ }
+ }
+ colMap[cb] = lDummyColumn+ct;
+
+ // need to change this to work out how many cols to span
+ for (int k=0; k < lDummyTable.getDimension().height; k++) {
+ for (int l=0; l < lDummyTable.getDimension().width; l++) {
+ lDummyElement = lDummyTable.getElement(k,l);
+ if (lDummyElement != null) {
+ int col=lDummyColumn+l;
+
+ if ( Cell.class.isInstance(lDummyElement) ) {
+ Cell lDummyC = (Cell)lDummyElement;
+ // Find col to add cell in and set col span
+ col = colMap[l];
+ int ot = colMap[l+lDummyC.colspan()];
+
+ lDummyC.setColspan(ot-col);
+ }
+
+ ((Row) newRows.get(k + lDummyRow)).addElement(lDummyElement,col); // use addElement to set reserved status ok in row
+ }
+ }
+ }
+ }
+ else // copy others values
+ {
+ Object aElement = getElement(i,j);
+
+ if ( Cell.class.isInstance(aElement) ) {
+
+ // adjust spans for cell
+ ((Cell) aElement).setRowspan(((Cell) ((Row) rows.get(i)).getCell(j)).rowspan() + lDummyHeights[i] - 1);
+ ((Cell) aElement).setColspan(((Cell) ((Row) rows.get(i)).getCell(j)).colspan() + lDummyWidths[j] - 1);
+
+ // most likely this cell covers a larger area because of the row/cols splits : define not-to-be-filled cells
+ placeCell(newRows,((Cell) aElement), new Point(lDummyRow,lDummyColumn));
+ }
+ }
+ lDummyColumn += lDummyWidths[j];
+ }
+ lDummyRow += lDummyHeights[i];
+ }
+
+ // Set our new matrix
+ columns = lTotalColumns;
+ rows = newRows;
+ this.widths = lNewWidths;
+ }
+ }
+
+ /**
+ * adds new<CODE>Cell</CODE>'s to empty/null spaces.
+ */
+
+ private void fillEmptyMatrixCells() {
+ try {
+ for (int i=0; i < rows.size(); i++) {
+ for (int j=0; j < columns; j++) {
+ if ( ((Row) rows.get(i)).isReserved(j) == false) {
+ addCell(defaultLayout, new Point(i, j));
+ }
+ }
+ }
+ }
+ catch(BadElementException bee) {
+ throw new ExceptionConverter(bee);
+ }
+ }
+
+ /**
+ * check if <CODE>Cell</CODE> 'fits' the table.
+ * <P>
+ * <UL><LI>rowspan/colspan not beyond borders
+ * <LI>spanned cell don't overlap existing cells</UL>
+ *
+ * @param aCell the cell that has to be checked
+ * @param aLocation the location where the cell has to be placed
+ * @return true if the location was valid
+ */
+ private boolean isValidLocation(Cell aCell, Point aLocation) {
+ // rowspan not beyond last column
+ if ( aLocation.x < rows.size() ) // if false : new location is already at new, not-yet-created area so no check
+ {
+ if ((aLocation.y + aCell.colspan()) > columns) {
+ return false;
+ }
+
+ int difx = ((rows.size() - aLocation.x) > aCell.rowspan()) ? aCell.rowspan() : rows.size() - aLocation.x;
+ int dify = ((columns - aLocation.y) > aCell.colspan()) ? aCell.colspan() : columns - aLocation.y;
+ // no other content at cells targetted by rowspan/colspan
+ for (int i=aLocation.x; i < (aLocation.x + difx); i++) {
+ for (int j=aLocation.y; j < (aLocation.y + dify); j++) {
+ if ( ((Row) rows.get(i)).isReserved(j) == true ) {
+ return false;
+ }
+ }
+ }
+ }
+ else {
+ if ((aLocation.y + aCell.colspan()) > columns) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Inserts a Cell in a cell-array and reserves cells defined by row-/colspan.
+ *
+ * @param someRows some rows
+ * @param aCell the cell that has to be inserted
+ * @param aPosition the position where the cell has to be placed
+ */
+
+ private void placeCell(ArrayList someRows, Cell aCell, Point aPosition) {
+ int i;
+ Row row = null;
+ int lColumns = ((Row) someRows.get(0)).columns();
+ int rowCount = aPosition.x + aCell.rowspan() - someRows.size();
+ assumeTableDefaults(aCell);
+ if ( (aPosition.x + aCell.rowspan()) > someRows.size() ) //create new rows ?
+ {
+ for (i = 0; i < rowCount; i++) {
+ row = new Row(lColumns);
+ someRows.add(row);
+ }
+ }
+
+ // reserve cell in rows below
+ for (i = aPosition.x + 1; i < (aPosition.x + aCell.rowspan()); i++) {
+ if ( !((Row) someRows.get(i)).reserve(aPosition.y, aCell.colspan())) {
+
+ // should be impossible to come here :-)
+ throw new RuntimeException("addCell - error in reserve");
+ }
+ }
+ row = (Row) someRows.get(aPosition.x);
+ row.addElement(aCell, aPosition.y);
+
+ }
+
+ /**
+ * Gives you the posibility to add columns.
+ *
+ * @param aColumns the number of columns to add
+ */
+
+ public void addColumns(int aColumns) {
+ ArrayList newRows = new ArrayList(rows.size());
+
+ int newColumns = columns + aColumns;
+ Row row;
+ for (int i = 0; i < rows.size(); i++) {
+ row = new Row(newColumns);
+ for (int j = 0; j < columns; j++) {
+ row.setElement(((Row) rows.get(i)).getCell(j) ,j);
+ }
+ for (int j = columns; j < newColumns && i < curPosition.x; j++) {
+ row.setElement(defaultLayout, j);
+ }
+ newRows.add(row);
+ }
+
+ // applied 1 column-fix; last column needs to have a width of 0
+ float [] newWidths = new float[newColumns];
+ for (int j = 0; j < columns; j++) {
+ newWidths[j] = widths[j];
+ }
+ for (int j = columns; j < newColumns ; j++) {
+ newWidths[j] = 0;
+ }
+ columns = newColumns;
+ widths = newWidths;
+ rows = newRows;
+ }
+
+ /**
+ * Gets an array with the positions of the borders between every column.
+ * <P>
+ * This method translates the widths expressed in percentages into the
+ * x-coordinate of the borders of the columns on a real document.
+ *
+ * @param left this is the position of the first border at the left (cellpadding not included)
+ * @param totalWidth this is the space between the first border at the left
+ * and the last border at the right (cellpadding not included)
+ * @return an array with borderpositions
+ */
+
+ public float[] getWidths(float left, float totalWidth) {
+ // for x columns, there are x+1 borders
+ float[] w = new float[columns + 1];
+ // the border at the left is calculated
+ switch(alignment) {
+ case Element.ALIGN_LEFT:
+ w[0] = left;
+ break;
+ case Element.ALIGN_RIGHT:
+ w[0] = left + (totalWidth * (100 - widthPercentage)) / 100;
+ break;
+ case Element.ALIGN_CENTER:
+ default:
+ w[0] = left + (totalWidth * (100 - widthPercentage)) / 200;
+ }
+ // the total available width is changed
+ totalWidth = (totalWidth * widthPercentage) / 100;
+ // the inner borders are calculated
+ for (int i = 1; i < columns; i++) {
+ w[i] = w[i - 1] + (widths[i - 1] * totalWidth / 100);
+ }
+ // the border at the right is calculated
+ w[columns] = w[0] + totalWidth;
+ return w;
+ }
+
+ /**
+ * Sets current col/row to valid(empty) pos after addCell/Table
+ * @param aLocation a location in the Table
+ */
+ private void setCurrentLocationToNextValidPosition(Point aLocation) {
+ // set latest location to next valid position
+ int i, j;
+ i = aLocation.x;
+ j = aLocation.y;
+ do {
+ if ( (j + 1) == columns ) { // goto next row
+ i++;
+ j = 0;
+ }
+ else {
+ j++;
+ }
+ }
+ while (
+ (i < rows.size()) && (j < columns) && (((Row) rows.get(i)).isReserved(j) == true)
+ );
+ curPosition = new Point(i, j);
+ }
+
+
+
+ /**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.TABLE.equals(tag);
+ }
+
+ /**
+ * Allows clients to set up alternating attributes for each Row in the Table.
+ * <P>
+ * This code was contributed by Matt Benson.
+ *
+ * @param name the name of the attribute
+ * @param value0 the value of the attribute for even rows
+ * @param value1 the value of the attribute for odd rows
+ */
+ public void setAlternatingRowAttribute(String name, String value0, String value1) {
+ if (value0 == null || value1 == null) {
+ throw new NullPointerException("MarkupTable#setAlternatingRowAttribute(): null values are not permitted.");
+ }
+ if (alternatingRowAttributes == null) alternatingRowAttributes = new Hashtable();
+
+ // we could always use new Arrays but this is big enough
+ String[] value = (String[])(alternatingRowAttributes.get(name));
+ if (value == null) value = new String[2];
+ value[0] = value0;
+ value[1] = value1;
+ alternatingRowAttributes.put(name, value);
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @return NA
+ */
+ public float top() {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @return NA
+ */
+ public float bottom() {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @return NA
+ */
+ public float left() {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @return NA
+ */
+ public float right() {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @param margin NA
+ * @return NA
+ */
+ public float top(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @param margin NA
+ * @return NA
+ */
+ public float bottom(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @param margin NA
+ * @return NA
+ */
+ public float left(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @param margin NA
+ * @return NA
+ */
+ public float right(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @param value NA
+ */
+ public void setTop(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Table are attributed automagically. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @param value NA
+ */
+ public void setBottom(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Table are attributed automagically. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @param value NA
+ */
+ public void setLeft(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Table are attributed automagically. See the FAQ.");
+ }
+
+ /**
+ * This method throws an <CODE>UnsupportedOperationException</CODE>.
+ * @param value NA
+ */
+ public void setRight(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Table are attributed automagically. See the FAQ.");
+ }
+
+ /**
+ * Returns the next row 0-based index where a new cell would be added.
+ * (contributed by dperezcar@fcc.es)
+ * @return x coordinate for the next row
+ */
+ public int getNextRow() {
+ return curPosition.x;
+ }
+
+ /**
+ * Returns the next column 0-based index where a new cell would be added.
+ * (contributed by dperezcar@fcc.es)
+ * @return y coordinate for the next row
+ */
+ public int getNextColumn() {
+ return curPosition.y;
+ }
+
+ private static final double convertWidth( double val) {
+ if( val == 0) {
+ return 0;
+ }
+ try {
+ String tmp = widthFormat.format( val);
+ Number result = widthFormat.parse( tmp);
+
+ return result.doubleValue();
+ }
+ catch( java.text.ParseException pe) {
+ throw new RuntimeException( "Could not convert double to width for val:" + val);
+ }
+ }
+
+ private static DecimalFormat widthFormat = new DecimalFormat( "0.00");
+
+ /**
+ * Create a PdfPTable based on this Table object.
+ * @return a PdfPTable object
+ * @throws BadElementException
+ */
+ public PdfPTable createPdfPTable() throws BadElementException {
+ if (!convert2pdfptable) {
+ throw new BadElementException("No error, just an old style table");
+ }
+ setAutoFillEmptyCells(true);
+ complete();
+ PdfPTable pdfptable = new PdfPTable(widths);
+ pdfptable.setTableEvent(SimpleTable.getDimensionlessInstance(this, cellspacing));
+ pdfptable.setHeaderRows(lastHeaderRow + 1);
+ pdfptable.setSplitLate(cellsFitPage);
+ if (!Float.isNaN(offset)) {
+ pdfptable.setSpacingBefore(offset);
+ }
+ pdfptable.setHorizontalAlignment(alignment);
+ if (absWidth.length() > 0) {
+ try {
+ pdfptable.setTotalWidth(Float.parseFloat(absWidth));
+ }
+ catch(Exception e1) {
+ try {
+ pdfptable.setTotalWidth(Integer.parseInt(absWidth));
+ }
+ catch(Exception e2) {
+ pdfptable.setWidthPercentage(widthPercentage);
+ }
+ }
+ }
+ else {
+ pdfptable.setWidthPercentage(widthPercentage);
+ }
+ Row row;
+ for (Iterator iterator = iterator(); iterator.hasNext(); ) {
+ row = (Row) iterator.next();
+ Element cell;
+ PdfPCell pcell;
+ for (int i = 0; i < row.columns(); i++) {
+ if ((cell = (Element)row.getCell(i)) != null) {
+ if (cell instanceof Table) {
+ pcell = new PdfPCell(((Table)cell).createPdfPTable());
+ }
+ else if (cell instanceof Cell) {
+ pcell = ((Cell)cell).createPdfPCell();
+ pcell.setPadding(cellpadding + cellspacing / 2f);
+ pcell.setCellEvent(SimpleCell.getDimensionlessInstance((Cell)cell, cellspacing));
+ }
+ else {
+ pcell = new PdfPCell();
+ }
+ pdfptable.addCell(pcell);
+ }
+ }
+ }
+ return pdfptable;
+ }
+
+ /**
+ * Method to check if the Table should be converted to a PdfPTable or not.
+ * @return false if the table should be handled the oldfashioned way.
+ */
+ public boolean isConvert2pdfptable() {
+ return convert2pdfptable;
+ }
+ /**
+ * If set to true, iText will try to convert the Table to a PdfPTable.
+ * @param convert2pdfptable true if you want iText to try to convert the Table to a PdfPTable
+ */
+ public void setConvert2pdfptable(boolean convert2pdfptable) {
+ this.convert2pdfptable = convert2pdfptable;
+ }
+} \ No newline at end of file