/* * $Id: Cell.java,v 1.106 2005/11/12 18:00:57 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/ */ package com.lowagie.text; import java.awt.Color; import java.util.ArrayList; import java.util.Iterator; import java.util.Properties; import com.lowagie.text.markup.MarkupParser; import com.lowagie.text.pdf.PdfPCell; /** * A <CODE>Cell</CODE> is a <CODE>Rectangle</CODE> containing other * <CODE>Element</CODE>s. * <P> * A <CODE>Cell</CODE> must be added to a <CODE>Table</CODE>. * The <CODE>Table</CODE> will place the <CODE>Cell</CODE> in * a <CODE>Row</CODE>. * <P> * Example: * <BLOCKQUOTE><PRE> * Table table = new Table(3); * table.setBorderWidth(1); * table.setBorderColor(new Color(0, 0, 255)); * table.setCellpadding(5); * table.setCellspacing(5); * <STRONG>Cell cell = new Cell("header");</STRONG> * <STRONG>cell.setHeader(true);</STRONG> * <STRONG>cell.setColspan(3);</STRONG> * table.addCell(cell); * <STRONG>cell = new Cell("example cell with colspan 1 and rowspan 2");</STRONG> * <STRONG>cell.setRowspan(2);</STRONG> * <STRONG>cell.setBorderColor(new Color(255, 0, 0));</STRONG> * table.addCell(cell); * table.addCell("1.1"); * table.addCell("2.1"); * table.addCell("1.2"); * table.addCell("2.2"); * </PRE></BLOCKQUOTE> * * @see Rectangle * @see Element * @see Table * @see Row */ public class Cell extends Rectangle implements TextElementArray { // static final membervariable // This accessor replaces the dangerous static member DUMMY_CELL /** * Get dummy cell used when merging inner tables. * @return a cell with colspan 3 and no border */ public static Cell getDummyCell() { Cell cell = new Cell(true); cell.setColspan(3); cell.setBorder(NO_BORDER); return cell; } // membervariables /** This is the <CODE>ArrayList</CODE> of <CODE>Element</CODE>s. */ protected ArrayList arrayList = null; /** This is the horizontal alignment. */ protected int horizontalAlignment = Element.ALIGN_UNDEFINED; /** This is the vertical alignment. */ protected int verticalAlignment = Element.ALIGN_UNDEFINED; /** This is the vertical alignment. */ protected String width; /** This is the colspan. */ protected int colspan = 1; /** This is the rowspan. */ protected int rowspan = 1; /** This is the leading. */ float leading = Float.NaN; /** Is this <CODE>Cell</CODE> a header? */ protected boolean header; /** Indicates that the largest ascender height should be used to determine the * height of the first line. Note that this only has an effect when rendered * to PDF. Setting this to true can help with vertical alignment problems. */ protected boolean useAscender = false; /** Indicates that the largest descender height should be added to the height of * the last line (so characters like y don't dip into the border). Note that * this only has an effect when rendered to PDF. */ protected boolean useDescender = false; /** * Adjusts the cell contents to compensate for border widths. Note that * this only has an effect when rendered to PDF. */ protected boolean useBorderPadding; // constructors /** * Constructs an empty <CODE>Cell</CODE>. */ public Cell() { // creates a Rectangle with BY DEFAULT a border of 0.5 super(0, 0, 0, 0); setBorder(UNDEFINED); setBorderWidth(0.5f); // initializes the arraylist and adds an element arrayList = new ArrayList(); } /** * Constructs an empty <CODE>Cell</CODE> (for internal use only). * * @param dummy a dummy value */ public Cell(boolean dummy) { this(); arrayList.add(new Paragraph(0)); } /** * Constructs a <CODE>Cell</CODE> with a certain content. * <P> * The <CODE>String</CODE> will be converted into a <CODE>Paragraph</CODE>. * * @param content a <CODE>String</CODE> */ public Cell(String content) { // creates a Rectangle with BY DEFAULT a border of 0.5 super(0, 0, 0, 0); setBorder(UNDEFINED); setBorderWidth(0.5f); // initializes the arraylist and adds an element arrayList = new ArrayList(); try { addElement(new Paragraph(content)); } catch(BadElementException bee) { } } /** * Constructs a <CODE>Cell</CODE> with a certain <CODE>Element</CODE>. * <P> * if the element is a <CODE>ListItem</CODE>, <CODE>Row</CODE> or * <CODE>Cell</CODE>, an exception will be thrown. * * @param element the element * @throws BadElementException when the creator was called with a <CODE>ListItem</CODE>, <CODE>Row</CODE> or <CODE>Cell</CODE> */ public Cell(Element element) throws BadElementException { // creates a Rectangle with BY DEFAULT a border of 0.5 super(0, 0, 0, 0); setBorder(UNDEFINED); setBorderWidth(0.5f); // Update by Benoit WIART <b.wiart@proxiad.com> if(element instanceof Phrase) { Phrase p = (Phrase)element; leading = p.leading(); } // initializes the arraylist and adds an element arrayList = new ArrayList(); addElement(element); } /** * Returns a <CODE>Cell</CODE> that has been constructed taking in account * the value of some <VAR>attributes</VAR>. * * @param attributes Some attributes */ public Cell(Properties attributes) { this(); String value; if ((value = (String)attributes.remove(ElementTags.HORIZONTALALIGN)) != null) { setHorizontalAlignment(value); } if ((value = (String)attributes.remove(ElementTags.VERTICALALIGN)) != null) { setVerticalAlignment(value); } if ((value = (String)attributes.remove(ElementTags.WIDTH)) != null) { setWidth(value); } if ((value = (String)attributes.remove(ElementTags.COLSPAN)) != null) { setColspan(Integer.parseInt(value)); } if ((value = (String)attributes.remove(ElementTags.ROWSPAN)) != null) { setRowspan(Integer.parseInt(value)); } if ((value = (String)attributes.remove(ElementTags.LEADING)) != null) { setLeading(Float.valueOf(value + "f").floatValue()); } if ((value = (String)attributes.remove(ElementTags.HEADER)) != null) { setHeader(new Boolean(value).booleanValue()); } if ((value = (String)attributes.remove(ElementTags.NOWRAP)) != null) { setNoWrap(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 = (String)attributes.remove(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; } } /** * Gets the type of the text element. * * @return a type */ public int type() { return Element.CELL; } /** * Gets all the chunks in this element. * * @return an <CODE>ArrayList</CODE> */ public ArrayList getChunks() { ArrayList tmp = new ArrayList(); for (Iterator i = arrayList.iterator(); i.hasNext(); ) { tmp.addAll(((Element) i.next()).getChunks()); } return tmp; } // methods to set the membervariables /** * Adds an element to this <CODE>Cell</CODE>. * <P> * Remark: you can't add <CODE>ListItem</CODE>s, <CODE>Row</CODE>s, <CODE>Cell</CODE>s, * <CODE>JPEG</CODE>s, <CODE>GIF</CODE>s or <CODE>PNG</CODE>s to a <CODE>Cell</CODE>. * * @param element The <CODE>Element</CODE> to add * @throws BadElementException if the method was called with a <CODE>ListItem</CODE>, <CODE>Row</CODE> or <CODE>Cell</CODE> */ public void addElement(Element element) throws BadElementException { if (isTable()) { Table table = (Table) arrayList.get(0); Cell tmp = new Cell(element); tmp.setBorder(NO_BORDER); tmp.setColspan(table.columns()); table.addCell(tmp); return; } switch(element.type()) { case Element.LISTITEM: case Element.ROW: case Element.CELL: throw new BadElementException("You can't add listitems, rows or cells to a cell."); case Element.LIST: if (Float.isNaN(leading)) { leading = ((List) element).leading(); } if (((List) element).size() == 0) return; arrayList.add(element); return; case Element.ANCHOR: case Element.PARAGRAPH: case Element.PHRASE: if (Float.isNaN(leading)) { leading = ((Phrase) element).leading(); } if (((Phrase) element).isEmpty()) return; arrayList.add(element); return; case Element.CHUNK: if (((Chunk) element).isEmpty()) return; arrayList.add(element); return; case Element.TABLE: Table table = new Table(3); float[] widths = new float[3]; widths[1] = ((Table)element).widthPercentage(); switch(((Table)element).alignment()) { case Element.ALIGN_LEFT: widths[0] = 0f; widths[2] = 100f - widths[1]; break; case Element.ALIGN_CENTER: widths[0] = (100f - widths[1]) / 2f; widths[2] = widths[0]; break; case Element.ALIGN_RIGHT: widths[0] = 100f - widths[1]; widths[2] = 0f; } table.setWidths(widths); Cell tmp; if (arrayList.size() == 0) { table.addCell(getDummyCell()); } else { tmp = new Cell(); tmp.setBorder(NO_BORDER); tmp.setColspan(3); for (Iterator i = arrayList.iterator(); i.hasNext(); ) { tmp.add((Element) i.next()); } table.addCell(tmp); } tmp = new Cell(); tmp.setBorder(NO_BORDER); table.addCell(tmp); table.insertTable((Table)element); table.addCell(tmp); table.addCell(getDummyCell()); clear(); arrayList.add(table); return; default: arrayList.add(element); } } /** * Add an <CODE>Object</CODE> to this cell. * * @param o the object to add * @return always <CODE>true</CODE> */ public boolean add(Object o) { try { this.addElement((Element) o); return true; } catch(ClassCastException cce) { throw new ClassCastException("You can only add objects that implement the Element interface."); } catch(BadElementException bee) { throw new ClassCastException(bee.getMessage()); } } /** * Sets the leading. * * @param value the new value */ public void setLeading(float value) { leading = value; } /** * Sets the horizontal alignment. * * @param value the new value */ public void setHorizontalAlignment(int value) { horizontalAlignment = value; } /** * Sets the alignment of this cell. * * @param alignment the new alignment as a <CODE>String</CODE> */ public void setHorizontalAlignment(String alignment) { if (ElementTags.ALIGN_CENTER.equalsIgnoreCase(alignment)) { this.horizontalAlignment = Element.ALIGN_CENTER; return; } if (ElementTags.ALIGN_RIGHT.equalsIgnoreCase(alignment)) { this.horizontalAlignment = Element.ALIGN_RIGHT; return; } if (ElementTags.ALIGN_JUSTIFIED.equalsIgnoreCase(alignment)) { this.horizontalAlignment = Element.ALIGN_JUSTIFIED; return; } if (ElementTags.ALIGN_JUSTIFIED_ALL.equalsIgnoreCase(alignment)) { this.horizontalAlignment = Element.ALIGN_JUSTIFIED_ALL; return; } this.horizontalAlignment = Element.ALIGN_LEFT; } /** * Sets the vertical alignment. * * @param value the new value */ public void setVerticalAlignment(int value) { verticalAlignment = value; } /** * Sets the alignment of this paragraph. * * @param alignment the new alignment as a <CODE>String</CODE> */ public void setVerticalAlignment(String alignment) { if (ElementTags.ALIGN_MIDDLE.equalsIgnoreCase(alignment)) { this.verticalAlignment = Element.ALIGN_MIDDLE; return; } if (ElementTags.ALIGN_BOTTOM.equalsIgnoreCase(alignment)) { this.verticalAlignment = Element.ALIGN_BOTTOM; return; } if (ElementTags.ALIGN_BASELINE.equalsIgnoreCase(alignment)) { this.verticalAlignment = Element.ALIGN_BASELINE; return; } this.verticalAlignment = Element.ALIGN_TOP; } /** * Sets the width. * * @param value the new value */ public void setWidth(String value) { width = value; } /** * Sets the colspan. * * @param value the new value */ public void setColspan(int value) { colspan = value; } /** * Sets the rowspan. * * @param value the new value */ public void setRowspan(int value) { rowspan = value; } /** * Sets header. * * @param value the new value */ public void setHeader(boolean value) { header = value; } /** * Set nowrap. * * @param value the new value */ public void setNoWrap(boolean value) { maxLines = 1; } // methods to retrieve information /** * Gets the number of <CODE>Element</CODE>s in the Cell. * * @return a <CODE>size</CODE>. */ public int size() { return arrayList.size(); } /** * Checks if the <CODE>Cell</CODE> is empty. * * @return <CODE>false</CODE> if there are non-empty <CODE>Element</CODE>s in the <CODE>Cell</CODE>. */ public boolean isEmpty() { switch(size()) { case 0: return true; case 1: Element element = (Element) arrayList.get(0); switch (element.type()) { case Element.CHUNK: return ((Chunk) element).isEmpty(); case Element.ANCHOR: case Element.PHRASE: case Element.PARAGRAPH: return ((Phrase) element).isEmpty(); case Element.LIST: return ((List) element).size() == 0; } return false; default: return false; } } /** * Makes sure there is at least 1 object in the Cell. * * Otherwise it might not be shown in the table. */ void fill() { if (size() == 0) arrayList.add(new Paragraph(0)); } /** * Checks if the <CODE>Cell</CODE> is empty. * * @return <CODE>false</CODE> if there are non-empty <CODE>Element</CODE>s in the <CODE>Cell</CODE>. */ public boolean isTable() { return (size() == 1) && (((Element)arrayList.get(0)).type() == Element.TABLE); } /** * Gets an iterator of <CODE>Element</CODE>s. * * @return an <CODE>Iterator</CODE>. */ public Iterator getElements() { return arrayList.iterator(); } /** * Gets the horizontal alignment. * * @return a value */ public int horizontalAlignment() { return horizontalAlignment; } /** * Gets the vertical alignment. * * @return a value */ public int verticalAlignment() { return verticalAlignment; } /** * Gets the width. * * @return a value */ public String cellWidth() { return width; } /** * Gets the colspan. * * @return a value */ public int colspan() { return colspan; } /** * Gets the rowspan. * * @return a value */ public int rowspan() { return rowspan; } /** * Gets the leading. * * @return a value */ public float leading() { if (Float.isNaN(leading)) { return 16; } return leading; } /** * Is this <CODE>Cell</CODE> a header? * * @return a value */ public boolean header() { return header; } /** * Get nowrap. * * @return a value */ public boolean noWrap() { return maxLines == 1; } /** * Clears all the <CODE>Element</CODE>s of this <CODE>Cell</CODE>. */ public void clear() { arrayList.clear(); } /** * This method throws an <CODE>UnsupportedOperationException</CODE>. * @return NA */ public float top() { throw new UnsupportedOperationException("Dimensions of a Cell 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 Cell 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 Cell 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 Cell can't be calculated. See the FAQ."); } /** * This method throws an <CODE>UnsupportedOperationException</CODE>. * @param margin * @return NA */ public float top(int margin) { throw new UnsupportedOperationException("Dimensions of a Cell can't be calculated. See the FAQ."); } /** * This method throws an <CODE>UnsupportedOperationException</CODE>. * @param margin * @return NA */ public float bottom(int margin) { throw new UnsupportedOperationException("Dimensions of a Cell can't be calculated. See the FAQ."); } /** * This method throws an <CODE>UnsupportedOperationException</CODE>. * @param margin * @return NA */ public float left(int margin) { throw new UnsupportedOperationException("Dimensions of a Cell 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 Cell 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 Cell 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 Cell 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 Cell 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 Cell are attributed automagically. See the FAQ."); } /** * 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.CELL.equals(tag); } /** Does this <CODE>Cell</CODE> force a group change? */ protected boolean groupChange = true; /** * Does this <CODE>Cell</CODE> force a group change? * * @return a value */ public boolean getGroupChange() { return groupChange; } /** * Sets group change. * * @param value the new value */ public void setGroupChange(boolean value) { groupChange = value; } /** * Getter for {@link #maxLines} * @return the maxLines value */ public int getMaxLines() { return maxLines; } /** * Setter for {@link #maxLines} * @param value the maximum number of lines */ public void setMaxLines(int value) { maxLines = value; } /** * Maximum number of lines allowed in the cell. * The default value of this property is not to limit the maximum number of lines * (contributed by dperezcar@fcc.es) */ protected int maxLines = Integer.MAX_VALUE; /**Setter for {@link #showTruncation} * @param value Can be null for avoiding marking the truncation.*/ public void setShowTruncation(String value) { showTruncation = value; } /** * Getter for {@link #showTruncation} * @return the showTruncation value */ public String getShowTruncation() { return showTruncation; } /** * If a truncation happens due to the {@link #maxLines} property, then this text will * be added to indicate a truncation has happened. * Default value is null, and means avoiding marking the truncation. * A useful value of this property could be e.g. "..." * (contributed by dperezcar@fcc.es) */ String showTruncation; /** * Sets the value of {@link #useAscender}. * @param use use ascender height if true */ public void setUseAscender(boolean use) { useAscender = use; } /** * Gets the value of {@link #useAscender} * @return useAscender */ public boolean isUseAscender() { return useAscender; } /** * Sets the value of {@link #useDescender}. * @param use use descender height if true */ public void setUseDescender(boolean use) { useDescender = use; } /** * gets the value of {@link #useDescender } * @return useDescender */ public boolean isUseDescender() { return useDescender; } /** * Sets the value of {@link #useBorderPadding}. * @param use adjust layour for borders if true */ public void setUseBorderPadding(boolean use) { useBorderPadding = use; } /** * Gets the value of {@link #useBorderPadding}. * @return useBorderPadding */ public boolean isUseBorderPadding() { return useBorderPadding; } /** * Creates a PdfPCell based on this Cell object. * @return a PdfPCell * @throws BadElementException */ public PdfPCell createPdfPCell() throws BadElementException { if (rowspan > 1) throw new BadElementException("PdfPCells can't have a rowspan > 1"); if (isTable()) return new PdfPCell(((Table)arrayList.get(0)).createPdfPTable()); PdfPCell cell = new PdfPCell(); cell.setVerticalAlignment(verticalAlignment); cell.setHorizontalAlignment(horizontalAlignment); cell.setColspan(colspan); cell.setUseBorderPadding(useBorderPadding); cell.setUseDescender(useDescender); cell.setLeading(leading(), 0); cell.cloneNonPositionParameters(this); cell.setNoWrap(noWrap()); for (Iterator i = getElements(); i.hasNext(); ) { Element e = (Element)i.next(); if (e.type() == Element.PHRASE || e.type() == Element.PARAGRAPH) { Paragraph p = new Paragraph((Phrase)e); p.setAlignment(horizontalAlignment); e = p; } cell.addElement(e); } return cell; } }