using System; using System.Collections; using System.util; using iTextSharp.text.html; using iTextSharp.text.pdf; /* * $Id: Table.cs,v 1.23 2008/05/13 11:25:13 psoares33 Exp $ * * * 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. */ namespace iTextSharp.text { /// /// 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). /// /// /// /// // 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 ///
///
/// /// /// /// public class Table : Rectangle, ILargeElement { // 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 System.Drawing.Point curPosition = new System.Drawing.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 defaultCell = new Cell(true); /// This is the number of the last row of the table headers. private int lastHeaderRow = -1; /// This is the horizontal Element. 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 width = 80; /** Is the width a percentage (false) or an absolute width (true)? */ private bool locked = false; /// This is an array containing the widths (in percentages) of every column. private float[] widths; /// bool to track if a table was inserted (to avoid unnecessary computations afterwards) private bool 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) /// protected internal bool autoFillEmptyCells = false; /// If true this table may not be split over two pages. bool tableFitsPage = false; /// If true cells may not be split over two pages. bool cellsFitPage = false; /// This is the offset of the table. float offset = float.NaN; /** if you want to generate tables the old way, set this value to false. */ protected bool convert2pdfptable = false; /** * Indicates if this is the first time the section was added. * @since iText 2.0.8 */ protected bool notAddedYet = true; /** * Indicates if the PdfPTable is complete once added to the document. * @since iText 2.0.8 */ protected bool complete = true; // constructors /// /// Constructs a Table with a certain number of columns. /// /// The number of columns in the table /// /// Has three overloads /// public Table(int columns) : this(columns, 1) {} /// /// Constructs a Table with a certain number of columns /// and a certain number of Rows. /// /// The number of columns in the table /// The number of rows /// /// Has three overloads /// public Table(int columns, int rows) : base(0, 0, 0, 0) { Border = BOX; BorderWidth = 1; defaultCell.Border = 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 System.Drawing.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; } } // implementation of the Element-methods /// /// Processes the element by adding it (or the different parts) to an /// IElementListener. /// /// an IElementListener /// true if the element was processed successfully public override bool Process(IElementListener listener) { try { return listener.Add(this); } catch (DocumentException) { return false; } } /** * Sets the default layout of the Table to * the provided Cell * @param value a cell with all the defaults */ public Cell DefaultLayout { set { defaultCell = value; } get { return defaultCell; } } /** * Sets the default layout of the Table to * the provided Cell * @param value a cell with all the defaults */ public Cell DefaultCell { set { defaultCell = value; } get { return defaultCell; } } /// /// 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) /// /// enable/disable autofill public bool AutoFillEmptyCells { set { autoFillEmptyCells = value; } } /// /// 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. /// /// a value public bool TableFitsPage { set { this.tableFitsPage = value; if (value) CellsFitPage = true; } get { return tableFitsPage; } } /// /// 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. /// /// a value public bool CellsFitPage { set { this.cellsFitPage = value; } get { return cellsFitPage; } } /// /// Get/set the offset of this table. /// /// the space between this table and the previous element. public float Offset { get { return offset; } set { this.offset = value; } } /// /// Gets the type of the text element. /// /// a type public override int Type { get { return Element.TABLE; } } /** * Gets all the chunks in this element. * * @return an ArrayList */ // public ArrayList Chunks { // get { // return new ArrayList(); // } // } /** * @see com.lowagie.text.Element#isNestable() * @since iText 2.0.8 */ public override bool IsNestable() { return true; } // methods to add content to the table /// /// Adds a Cell to the Table at a certain row and column. /// /// The Cell to add /// The row where the Cell will be added /// The column where the Cell will be added public void AddCell(Cell aCell, int row, int column) { AddCell(aCell, new System.Drawing.Point(row,column)); } /// /// Adds a Cell to the Table at a certain location. /// /// The Cell to add /// The location where the Cell will be added public void AddCell(Cell aCell, object aLocation) { System.Drawing.Point p; if (aCell == null) throw new Exception("addCell - cell has null-value"); if (aLocation == null) throw new Exception("addCell - point has null-value"); else p = (System.Drawing.Point)aLocation; if (aCell.IsTable()) { IEnumerator i = aCell.Elements.GetEnumerator(); i.MoveNext(); InsertTable((Table)i.Current, p); } if (p.X < 0) throw new BadElementException("row coordinate of location must be >= 0"); if ((p.Y <= 0) && (p.Y > columns)) throw new BadElementException("column coordinate of location must be >= 0 and < nr of columns"); if (!IsValidLocation(aCell, p)) throw new BadElementException("Adding a cell at the location (" + p.X + "," + p.Y + ") with a colspan of " + aCell.Colspan + " and a rowspan of " + aCell.Rowspan + " is illegal (beyond boundaries/overlapping)."); if (aCell.Border == UNDEFINED) aCell.Border = defaultCell.Border; aCell.Fill(); PlaceCell(rows, aCell, p); CurrentLocationToNextValidPosition = p; } /// /// Adds a Cell to the Table. /// /// a Cell public void AddCell(Cell cell) { try { AddCell(cell, curPosition); } catch (BadElementException) { // 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. /// /// a Phrase public void AddCell(Phrase content) { AddCell(content, curPosition); } /// /// Adds a Cell to the Table. /// /// a Phrase /// a System.Drawing.Point public void AddCell(Phrase content, System.Drawing.Point location) { Cell cell = new Cell(content); cell.Border = defaultCell.Border; cell.BorderWidth = defaultCell.BorderWidth; cell.BorderColor = defaultCell.BorderColor; cell.BackgroundColor = defaultCell.BackgroundColor; cell.HorizontalAlignment = defaultCell.HorizontalAlignment; cell.VerticalAlignment = defaultCell.VerticalAlignment; cell.Colspan = defaultCell.Colspan; cell.Rowspan = defaultCell.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. /// /// a string public void AddCell(string content) { AddCell(new Phrase(content), curPosition); } /// /// Adds a Cell to the Table. /// /// /// This is a shortcut for AddCell(Cell cell, System.Drawing.Point location). /// The string will be converted to a Cell. /// /// a string /// a point public void AddCell(string content, System.Drawing.Point location) { 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. /// /// the table you want to insert public void InsertTable(Table aTable) { if (aTable == null) throw new Exception("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. /// /// The Table to add /// The row where the Cell will be added /// The column where the Cell will be added public void InsertTable(Table aTable, int row, int column) { if (aTable == null) throw new Exception("insertTable - table has null-value"); InsertTable(aTable, new System.Drawing.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. /// /// the table you want to insert /// a System.Drawing.Point public void InsertTable(Table aTable, System.Drawing.Point p) { if (aTable == null) throw new Exception("insertTable - table has null-value"); mTableInserted = true; aTable.Complete(); if (p.Y > columns) throw new ArgumentException("insertTable -- wrong columnposition("+ p.Y + ") of location; max =" + columns); int rowCount = p.X + 1 - rows.Count; int i = 0; if ( rowCount > 0 ) { //create new rows ? for (; i < rowCount; i++) { rows.Add(new Row(columns)); } } ((Row) rows[p.X]).SetElement(aTable,p.Y); CurrentLocationToNextValidPosition = p; } /// /// Will fill empty cells with valid blank Cells /// public void Complete() { if (mTableInserted == true) { MergeInsertedTables(); // integrate tables in the table mTableInserted = false; } if (autoFillEmptyCells == true) { FillEmptyMatrixCells(); } } /// /// Changes the border in the default layout of the Cells /// added with method AddCell(string content). /// /// the new border value public int DefaultCellBorder { set { defaultCell.Border = value; } } /// /// Changes the width of the borders in the default layout of the Cells /// added with method AddCell(string content). /// /// the new width public float DefaultCellBorderWidth { set { defaultCell.BorderWidth = value; } } /// /// Changes the bordercolor in the default layout of the Cells /// added with method AddCell(string content). /// public Color DefaultCellBorderColor { set { defaultCell.BorderColor = value; } } /// /// Changes the backgroundcolor in the default layout of the Cells /// added with method AddCell(string content). /// /// the new color public Color DefaultCellBackgroundColor { set { defaultCell.BackgroundColor = value; } } /// /// Changes the grayfill in the default layout of the Cells /// added with method AddCell(string content). /// /// the new value public float DefaultCellGrayFill { set { if (value >= 0 && value <= 1) { defaultCell.GrayFill = value; } } } /// /// Changes the horizontalalignment in the default layout of the Cells /// added with method AddCell(string content). /// /// the new alignment value public int DefaultHorizontalAlignment { set { defaultCell.HorizontalAlignment = value; } } /// /// Changes the verticalAlignment in the default layout of the Cells /// added with method AddCell(string content). /// /// the new alignment value public int DefaultVerticalAlignment { set { defaultCell.VerticalAlignment = value; } } /// /// Changes the rowspan in the default layout of the Cells /// added with method AddCell(string content). /// /// the new rowspan value public int DefaultRowspan { set { defaultCell.Rowspan = value; } } /// /// Changes the colspan in the default layout of the Cells /// added with method AddCell(string content). /// /// the new colspan value public int DefaultColspan { set { defaultCell.Colspan = value; } } // methods /// /// Sets the unset cell properties to be the table defaults. /// /// The cell to set to table defaults as necessary. private void AssumeTableDefaults(Cell aCell) { if (aCell.Border == Rectangle.UNDEFINED) { aCell.Border = defaultCell.Border; } if (aCell.BorderWidth == Rectangle.UNDEFINED) { aCell.BorderWidth = defaultCell.BorderWidth; } if (aCell.BorderColor == null) { aCell.BorderColor = defaultCell.BorderColor; } if (aCell.BackgroundColor == null) { aCell.BackgroundColor = defaultCell.BackgroundColor; } if (aCell.HorizontalAlignment == Element.ALIGN_UNDEFINED) { aCell.HorizontalAlignment = defaultCell.HorizontalAlignment; } if (aCell.VerticalAlignment == Element.ALIGN_UNDEFINED) { aCell.VerticalAlignment = defaultCell.VerticalAlignment; } } /// /// Deletes a column in this table. /// /// the number of the column that has to be deleted public void DeleteColumn(int column) { float[] newWidths = new float[--columns]; System.Array.Copy(widths, 0, newWidths, 0, column); System.Array.Copy(widths, column + 1, newWidths, column, columns - column); Widths = newWidths; System.Array.Copy(widths, 0, newWidths, 0, columns); widths = newWidths; Row row; int size = rows.Count; for (int i = 0; i < size; i++) { row = (Row) rows[i]; row.DeleteColumn(column); rows[i] = row; } if (column == columns) { curPosition.X++; curPosition.Y = 0; } } /// /// Deletes a row. /// /// the number of the row to delete /// true if the row was deleted; false if not public bool DeleteRow(int row) { if (row < 0 || row >= rows.Count) { return false; } rows.RemoveAt(row); curPosition.X--; return true; } /** * Deletes all rows in this table. * (contributed by dperezcar@fcc.es) */ public void DeleteAllRows() { rows.Clear(); rows.Add(new Row(columns)); curPosition.X = 0; curPosition.Y = 0; lastHeaderRow = -1; } /// /// Deletes the last row in this table. /// /// true if the row was deleted; false if not public bool DeleteLastRow() { return DeleteRow(rows.Count - 1); } /// /// Marks the last row of the table headers. /// /// 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 Element. /// /// the new value public int LastHeaderRow { set { lastHeaderRow = value; } get { return lastHeaderRow; } } /// /// Sets the alignment of this paragraph. /// /// the new alignment as a string public void SetAlignment(string alignment) { if (Util.EqualsIgnoreCase(alignment, ElementTags.ALIGN_LEFT)) { this.alignment = Element.ALIGN_LEFT; return; } if (Util.EqualsIgnoreCase(alignment, ElementTags.ALIGN_RIGHT)) { this.alignment = Element.ALIGN_RIGHT; return; } this.alignment = Element.ALIGN_CENTER; } /// /// Sets the cellpadding. /// /// the new value public float Padding { set { cellpadding = value; } } /// /// Sets the cellspacing. /// /// the new value public float Spacing { set { cellspacing = value; } } /// /// 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. /// /// ///
        /// 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. ///
/// an array with values public float[] Widths { set { if (value.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 += value[i]; } // The different percentages are calculated float width; this.widths[columns - 1] = 100; for (int i = 0; i < columns - 1; i++) { width = (100.0f * value[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. /// /// an array with values public void SetWidths(int[] widths) { float[] tb = new float[widths.Length]; for (int k = 0; k < widths.Length; ++k) tb[k] = widths[k]; this.Widths = tb; } // methods to retrieve the membervariables /// /// Gets the number of columns. /// /// a value public int Columns { get { return columns; } } /// /// Gets the number of rows in this Table. /// /// the number of rows in this Table public int Size { get { return rows.Count; } } /// /// Gets the proportional widths of the columns in this Table. /// /// the proportional widths of the columns in this Table public float[] ProportionalWidths { get { return widths; } } /// /// Gets an Iterator of all the Rows. /// /// an IEnumerator public IEnumerator GetEnumerator() { return rows.GetEnumerator(); } /// /// Get/set the horizontal Element. /// /// a value public int Alignment{ get { return alignment; } set { alignment = value; } } /// /// Get/set the cellpadding. /// /// the cellpadding public float Cellpadding { get { return cellpadding; } set { this.cellpadding = value; } } /// /// Get/set the cellspacing. /// /// the cellspacing public float Cellspacing { get { return cellspacing; } set { this.cellspacing = value; } } /// /// Get/set the table width (a percentage). /// /// the table width (a percentage) public override float Width { get { return width; } set { this.width = value; } } /// /// Gets the dimension of this table /// /// the dimension public System.Drawing.Dimension Dimension { get { return new System.Drawing.Dimension(columns, rows.Count); } } /// /// returns the element at the position row, column /// (Cast to Cell or Table) /// /// /// /// an object public object GetElement(int row, int column) { return ((Row) rows[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.Count]; // to keep track in how many new rows this one will be split ArrayList newRows = null; bool 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.Count; i++) { if (((Row) rows[i]).GetCell(j) is Table) { isTable=true; lDummyTable = ((Table) ((Row) rows[i]).GetCell(j)); if ( tmpWidths == null) { tmpWidths = lDummyTable.widths; lNewMaxColumns=tmpWidths.Length; } else { int cols = lDummyTable.Dimension.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 lNewMaxRows ) { lNewMaxRows = lDummyTable.Dimension.height; } } } lTotalRows += lNewMaxRows; lDummyHeights [i] = lNewMaxRows; } if ( (lTotalColumns != columns) || (lTotalRows != rows.Count) || 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] * 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.Count; i++) { lDummyColumn = 0; lNewMaxRows = 1; for (j=0; j < columns; j++) { if (((Row) rows[i]).GetCell(j) is Table ) // copy values from embedded table { lDummyTable = (Table) ((Row) rows[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 /// Integrates all added tables and recalculates column widths. /// private void FillEmptyMatrixCells() { for (int i=0; i < rows.Count; i++) { for (int j=0; j < columns; j++) { if ( ((Row) rows[i]).IsReserved(j) == false) { AddCell(defaultCell, new System.Drawing.Point(i, j)); } } } } /// /// check if Cell 'fits' the table. /// /// ///
  • rowspan/colspan not beyond borders ///
  • spanned cell don't overlap existing cells
///
/// the cell that has to be checked /// the location where the cell has to be placed /// private bool IsValidLocation(Cell aCell, System.Drawing.Point aLocation) { // rowspan not beyond last column if ( aLocation.X < rows.Count ) { // 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.Count - aLocation.X) > aCell.Rowspan) ? aCell.Rowspan : rows.Count - 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[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. /// /// some rows /// the cell that has to be inserted /// the position where the cell has to be placed private void PlaceCell(ArrayList someRows, Cell aCell, System.Drawing.Point aPosition) { int i; Row row = null; int rowCount = aPosition.X + aCell.Rowspan - someRows.Count; AssumeTableDefaults(aCell); if ( (aPosition.X + aCell.Rowspan) > someRows.Count ) { //create new rows ? for (i = 0; i < rowCount; i++) { row = new Row(columns); someRows.Add(row); } } // reserve cell in rows below for (i = aPosition.X + 1; i < (aPosition.X + aCell.Rowspan); i++) { if ( !((Row) someRows[i]).Reserve(aPosition.Y, aCell.Colspan)) { // should be impossible to come here :-) throw new Exception("addCell - error in reserve"); } } row = (Row) someRows[aPosition.X]; row.AddElement(aCell, aPosition.Y); } /// /// Gives you the posibility to add columns. /// /// the number of columns to add public void AddColumns(int aColumns) { ArrayList newRows = new ArrayList(rows.Count); int newColumns = columns + aColumns; Row row; for (int i = 0; i < rows.Count; i++) { row = new Row(newColumns); for (int j = 0; j < columns; j++) { row.SetElement(((Row) rows[i]).GetCell(j) ,j); } for (int j = columns; j < newColumns && i < curPosition.X; j++) { row.SetElement(null, j); } newRows.Add(row); } // applied 1 column-fix; last column needs to have a width of 0 float [] newWidths = new float[newColumns]; System.Array.Copy(widths, 0, newWidths, 0, columns); 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. /// /// this is the position of the first border at the left (cellpadding not included) /// /// this is the space between the first border at the left /// and the last border at the right (cellpadding not included) /// /// 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]; float wPercentage; if (locked) { wPercentage = 100 * width / totalWidth; } else { wPercentage = width; } // 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 - wPercentage)) / 100; break; case Element.ALIGN_CENTER: default: w[0] = left + (totalWidth * (100 - wPercentage)) / 200; break; } // the total available width is changed totalWidth = (totalWidth * wPercentage) / 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 /// /// a System.Drawing.Point private System.Drawing.Point CurrentLocationToNextValidPosition { set { // set latest location to next valid position int i, j; i = value.X; j = value.Y; do { if ( (j + 1) == columns ) { // goto next row i++; j = 0; } else { j++; } } while ( (i < rows.Count) && (j < columns) && (((Row) rows[i]).IsReserved(j) == true) ); curPosition = new System.Drawing.Point(i, j); } } private void ErrorDimensions() { throw new Exception("Dimensions of a Table can't be calculated. 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 NextRow { get { 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 NextColumn { get { return curPosition.Y; } } public override float Bottom { get { ErrorDimensions(); return 0; } set { ErrorDimensions(); } } public override float Left { get { ErrorDimensions(); return 0; } set { ErrorDimensions(); } } public override float Right { get { ErrorDimensions(); return 0; } set { ErrorDimensions(); } } public override float Top { get { ErrorDimensions(); return 0; } set { ErrorDimensions(); } } public override float GetBottom(float margin) { ErrorDimensions(); return 0; } public override float GetLeft(float margin) { ErrorDimensions(); return 0; } public override float GetRight(float margin) { ErrorDimensions(); return 0; } public override float GetTop(float margin) { ErrorDimensions(); return 0; } /** * Create a PdfPTable based on this Table object. * @return a PdfPTable object * @throws BadElementException */ public PdfPTable CreatePdfPTable() { if (!convert2pdfptable) { throw new BadElementException("No error, just an old style table"); } AutoFillEmptyCells = true; Complete(); PdfPTable pdfptable = new PdfPTable(widths); pdfptable.ElementComplete = complete; if (NotAddedYet) pdfptable.SkipFirstHeader = true; pdfptable.TableEvent = SimpleTable.GetDimensionlessInstance(this, cellspacing); pdfptable.HeaderRows = lastHeaderRow + 1; pdfptable.SplitLate = cellsFitPage; pdfptable.KeepTogether = tableFitsPage; if (!float.IsNaN(offset)) { pdfptable.SpacingBefore = offset; } pdfptable.HorizontalAlignment = alignment; if (locked) { pdfptable.TotalWidth = width; pdfptable.LockedWidth = true; } else { pdfptable.WidthPercentage = width; } foreach (Row row in this) { IElement cell; PdfPCell pcell; for (int i = 0; i < row.Columns; i++) { if ((cell = (IElement)row.GetCell(i)) != null) { if (cell is Table) { pcell = new PdfPCell(((Table)cell).CreatePdfPTable()); } else if (cell is Cell) { pcell = ((Cell)cell).CreatePdfPCell(); pcell.Padding = cellpadding + cellspacing / 2f; pcell.CellEvent = SimpleCell.GetDimensionlessInstance((Cell)cell, cellspacing); } else { pcell = new PdfPCell(); } pdfptable.AddCell(pcell); } } } return pdfptable; } /** * 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 bool Convert2pdfptable { get { return convert2pdfptable; } set { this.convert2pdfptable = value; } } public bool Locked { get { return locked; } set { locked = value; } } /** * Indicates if this is the first time the section is added. * @since iText2.0.8 * @return true if the section wasn't added yet */ public bool NotAddedYet { get { return notAddedYet; } set { notAddedYet = value; } } /** * @since iText 2.0.8 * @see com.lowagie.text.LargeElement#flushContent() */ public void FlushContent() { this.NotAddedYet = false; ArrayList headerrows = new ArrayList(); for (int i = 0; i < LastHeaderRow + 1; i++) { headerrows.Add(rows[i]); } rows = headerrows; } /** * @since iText 2.0.8 * @see com.lowagie.text.LargeElement#isComplete() */ public bool ElementComplete { get { return complete; } set { complete = value; } } } }