From 6025b6016517c6d898d8957d1d7e03ba71431912 Mon Sep 17 00:00:00 2001 From: tknall Date: Fri, 1 Dec 2006 12:20:24 +0000 Subject: Initial import of release 2.2. git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- src/main/java/com/lowagie/text/Table.java | 1916 +++++++++++++++++++++++++++++ 1 file changed, 1916 insertions(+) create mode 100644 src/main/java/com/lowagie/text/Table.java (limited to 'src/main/java/com/lowagie/text/Table.java') 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 Table is a Rectangle that contains Cells, + * ordered in some kind of matrix. + *

+ * 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 endHeaders(). + *

+ * 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 GridBagLayout. + *

+ * Example: + *

+ * // Remark: You MUST know the number of columns when constructing a Table.
+ * //         The number of rows is not important.
+ * Table table = new Table(3);
+ * table.setBorderWidth(1);
+ * table.setBorderColor(new Color(0, 0, 255));
+ * table.setPadding(5);
+ * table.setSpacing(5);
+ * Cell cell = new Cell("header");
+ * cell.setHeader(true);
+ * cell.setColspan(3);
+ * table.addCell(cell);
+ * table.endHeaders();
+ * cell = new Cell("example cell with colspan 1 and rowspan 2");
+ * cell.setRowspan(2);
+ * cell.setBorderColor(new Color(255, 0, 0));
+ * table.addCell(cell);
+ * table.addCell("1.1");
+ * table.addCell("2.1");
+ * table.addCell("1.2");
+ * table.addCell("2.2");
+ * table.addCell("cell test1");
+ * cell = new Cell("big cell");
+ * cell.setRowspan(2);
+ * cell.setColspan(2);
+ * table.addCell(cell);
+ * table.addCell("cell test2");
+ * 
+ * The result of this code is a table: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
+ * header + *
+ * example cell with colspan 1 and rowspan 2 + * + * 1.1 + * + * 2.1 + *
+ * 1.2 + * + * 2.2 + *
+ * cell test1 + * + * big cell + *
+ * cell test2 + *
+ * + * @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 Table. */ + private int columns; + + // this is the current Position in the table + private Point curPosition = new Point(0, 0); + + /** This is the list of Rows. */ + 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 Table 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 Table with a certain number of columns + * and a certain number of Rows. + * + * @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 Table that has been constructed taking in account + * the value of some attributes. + * + * @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 + * ElementListener. + * + * @param listener an ElementListener + * @return true 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. + *

+ * When a table doesn't fit a page, it is split in two parts. + * If you want to avoid this, you should set the tableFitsPage 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. + *

+ * When a cell doesn't fit a page, it is split in two parts. + * If you want to avoid this, you should set the cellsFitPage 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 Table 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 Table 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 ArrayList + */ + + public ArrayList getChunks() { + return new ArrayList(); + } + + // methods to add content to the table + + /** + * Adds a Cell to the Table at a certain row and column. + * + * @param aCell The Cell to add + * @param row The row where the Cell will be added + * @param column The column where the Cell will be added + * @throws BadElementException + */ + + public void addCell(Cell aCell, int row, int column) throws BadElementException { + addCell(aCell, new Point(row,column)); + } + + /** + * Adds a Cell to the Table at a certain location. + * + * @param aCell The Cell to add + * @param aLocation The location where the Cell 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 Cell to the Table. + * + * @param cell a Cell + */ + + public void addCell(Cell cell) { + try { + addCell(cell, curPosition); + } + catch(BadElementException bee) { + // don't add the cell + } + } + + /** + * Adds a Cell to the Table. + *

+ * This is a shortcut for addCell(Cell cell). + * The Phrase will be converted to a Cell. + * + * @param content a Phrase + * @throws BadElementException this should never happen + */ + + public void addCell(Phrase content) throws BadElementException { + addCell(content, curPosition); + } + + /** + * Adds a Cell to the Table. + *

+ * This is a shortcut for addCell(Cell cell, Point location). + * The Phrase will be converted to a Cell. + * + * @param content a Phrase + * @param location a Point + * @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 Cell to the Table. + *

+ * This is a shortcut for addCell(Cell cell). + * The String will be converted to a Cell. + * + * @param content a String + * @throws BadElementException this should never happen + */ + + public void addCell(String content) throws BadElementException { + addCell(new Phrase(content), curPosition); + } + + /** + * Adds a Cell to the Table. + *

+ * This is a shortcut for addCell(Cell cell, Point location). + * The String will be converted to a Cell. + * + * @param content a String + * @param location a Point + * @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 Table to add + * @param row The row where the Cell will be added + * @param column The column where the Cell 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 Point + */ + 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 Cells + */ + + 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 Cells + * added with method addCell(String content). + * + * @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 Cells + * added with method addCell(String content). + * + * @param value the new width + */ + + public void setDefaultCellBorderWidth(float value) { + defaultLayout.setBorderWidth(value); + } + + /** + * Changes the bordercolor in the default layout of the Cells + * added with method addCell(String content). + * + * @param color the new color + */ + + public void setDefaultCellBorderColor(Color color) { + defaultLayout.setBorderColor(color); + } + + /** + * Changes the backgroundcolor in the default layout of the Cells + * added with method addCell(String content). + * + * @param color the new color + */ + + public void setDefaultCellBackgroundColor(Color color) { + defaultLayout.setBackgroundColor(color); + } + + /** + * Changes the grayfill in the default layout of the Cells + * added with method addCell(String content). + * + * @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 Cells + * added with method addCell(String content). + * + * @param value the new alignment value + */ + + public void setDefaultHorizontalAlignment(int value) { + defaultLayout.setHorizontalAlignment(value); + } + + /** + * Changes the verticalAlignment in the default layout of the Cells + * added with method addCell(String content). + * + * @param value the new alignment value + */ + + public void setDefaultVerticalAlignment(int value) { + defaultLayout.setVerticalAlignment(value); + } + + /** + * Changes the rowspan in the default layout of the Cells + * added with method addCell(String content). + * + * @param value the new rowspan value + */ + + public void setDefaultRowspan(int value) { + defaultLayout.setRowspan(value); + } + + /** + * Changes the colspan in the default layout of the Cells + * added with method addCell(String content). + * + * @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 true if the row was deleted; false 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 true if the row was deleted; false 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 String + */ + + 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). + *

+ * 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. + *

+ * example: + *

+     * float[] widths = {2, 1, 1};
+     * table.setWidths(widths)
+     * 
+ * 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). + *

+ * 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 Table. + * + * @return the number of rows in this Table + */ + + public int size() { + return rows.size(); + } + + /** + * Gets the proportional widths of the columns in this Table. + * + * @return the proportional widths of the columns in this Table + */ + + public float[] getProportionalWidths() { + return widths; + } + + /** + * Gets an Iterator of all the Rows. + * + * @return an Iterator + */ + + 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( tpItpW) { + tmpWidthsN[totI] = tpW-totW; + tpI++; + if(tpI 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( ; cbCell'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 Cell 'fits' the table. + *

+ *

+ * + * @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. + *

+ * 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. + *

+ * 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 UnsupportedOperationException. + * @return NA + */ + public float top() { + throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ."); + } + + /** + * This method throws an UnsupportedOperationException. + * @return NA + */ + public float bottom() { + throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ."); + } + + /** + * This method throws an UnsupportedOperationException. + * @return NA + */ + public float left() { + throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ."); + } + + /** + * This method throws an UnsupportedOperationException. + * @return NA + */ + public float right() { + throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ."); + } + + /** + * This method throws an UnsupportedOperationException. + * @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 UnsupportedOperationException. + * @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 UnsupportedOperationException. + * @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 UnsupportedOperationException. + * @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 UnsupportedOperationException. + * @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 UnsupportedOperationException. + * @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 UnsupportedOperationException. + * @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 UnsupportedOperationException. + * @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 -- cgit v1.2.3