gt:         if (writer != null && writer.isPaused()) {
context: /*
   * $Id: PdfDocument.java 3939 2009-05-27 13:09:45Z blowagie $
   *
   * 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.pdf;
  
  import harmony.java.awt.Color;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Set;
  import java.util.TreeMap;
  
  import com.lowagie.text.Anchor;
  import com.lowagie.text.Annotation;
  import com.lowagie.text.BadElementException;
  import com.lowagie.text.Chunk;
  import com.lowagie.text.Document;
  import com.lowagie.text.DocumentException;
  import com.lowagie.text.Element;
  import com.lowagie.text.ExceptionConverter;
  import com.lowagie.text.Font;
  import com.lowagie.text.HeaderFooter;
  import com.lowagie.text.Image;
  import com.lowagie.text.List;
  import com.lowagie.text.ListItem;
  import com.lowagie.text.MarkedObject;
  import com.lowagie.text.MarkedSection;
  import com.lowagie.text.Meta;
  import com.lowagie.text.Paragraph;
  import com.lowagie.text.Phrase;
  import com.lowagie.text.Rectangle;
  import com.lowagie.text.Section;
  import com.lowagie.text.SimpleTable;
  import com.lowagie.text.Table;
  import com.lowagie.text.pdf.collection.PdfCollection;
  import com.lowagie.text.pdf.draw.DrawInterface;
  import com.lowagie.text.pdf.internal.PdfAnnotationsImp;
  import com.lowagie.text.pdf.internal.PdfViewerPreferencesImp;
  import java.text.DecimalFormat;
  
  /**
   * <CODE>PdfDocument</CODE> is the class that is used by <CODE>PdfWriter</CODE>
   * to translate a <CODE>Document</CODE> into a PDF with different pages.
   * <P>
   * A <CODE>PdfDocument</CODE> always listens to a <CODE>Document</CODE>
   * and adds the Pdf representation of every <CODE>Element</CODE> that is
   * added to the <CODE>Document</CODE>.
   *
   * @see		com.lowagie.text.Document
   * @see		com.lowagie.text.DocListener
   * @see		PdfWriter
   * @since	2.0.8 (class was package-private before)
   */
  
  public class PdfDocument extends Document {
  
      /**
       * <CODE>PdfInfo</CODE> is the PDF InfoDictionary.
       * <P>
       * A document's trailer may contain a reference to an Info dictionary that provides information
       * about the document. This optional dictionary may contain one or more keys, whose values
       * should be strings.<BR>
       * This object is described in the 'Portable Document Format Reference Manual version 1.3'
       * section 6.10 (page 120-121)
       * @since	2.0.8 (PdfDocument was package-private before)
       */
  
      public static class PdfInfo extends PdfDictionary {
  
          /**
           * Construct a <CODE>PdfInfo</CODE>-object.
           */
  
          PdfInfo() {
              super();
              addProducer();
              addCreationDate();
          }
  
          /**
           * Constructs a <CODE>PdfInfo</CODE>-object.
           *
           * @param		author		name of the author of the document
           * @param		title		title of the document
           * @param		subject		subject of the document
           */
  
          PdfInfo(String author, String title, String subject) {
              this();
              addTitle(title);
              addSubject(subject);
              addAuthor(author);
          }
  
          /**
           * Adds the title of the document.
           *
           * @param	title		the title of the document
           */
  
          void addTitle(String title) {
              put(PdfName.TITLE, new PdfString(title, PdfObject.TEXT_UNICODE));
          }
  
          /**
           * Adds the subject to the document.
           *
           * @param	subject		the subject of the document
           */
  
          void addSubject(String subject) {
              put(PdfName.SUBJECT, new PdfString(subject, PdfObject.TEXT_UNICODE));
          }
  
          /**
           * Adds some keywords to the document.
           *
           * @param	keywords		the keywords of the document
           */
  
          void addKeywords(String keywords) {
              put(PdfName.KEYWORDS, new PdfString(keywords, PdfObject.TEXT_UNICODE));
          }
  
          /**
           * Adds the name of the author to the document.
           *
           * @param	author		the name of the author
           */
  
          void addAuthor(String author) {
              put(PdfName.AUTHOR, new PdfString(author, PdfObject.TEXT_UNICODE));
          }
  
          /**
           * Adds the name of the creator to the document.
           *
           * @param	creator		the name of the creator
           */
  
          void addCreator(String creator) {
              put(PdfName.CREATOR, new PdfString(creator, PdfObject.TEXT_UNICODE));
          }
  
          /**
           * Adds the name of the producer to the document.
           */
  
          void addProducer() {
              put(PdfName.PRODUCER, new PdfString(getVersion()));
          }
  
          /**
           * Adds the date of creation to the document.
           */
  
          void addCreationDate() {
              PdfString date = new PdfDate();
              put(PdfName.CREATIONDATE, date);
              put(PdfName.MODDATE, date);
          }
  
          void addkey(String key, String value) {
              if (key.equals("Producer") || key.equals("CreationDate"))
                  return;
              put(new PdfName(key), new PdfString(value, PdfObject.TEXT_UNICODE));
          }
      }
  
      /**
       * <CODE>PdfCatalog</CODE> is the PDF Catalog-object.
       * <P>
       * The Catalog is a dictionary that is the root node of the document. It contains a reference
       * to the tree of pages contained in the document, a reference to the tree of objects representing
       * the document's outline, a reference to the document's article threads, and the list of named
       * destinations. In addition, the Catalog indicates whether the document's outline or thumbnail
       * page images should be displayed automatically when the document is viewed and whether some location
       * other than the first page should be shown when the document is opened.<BR>
       * In this class however, only the reference to the tree of pages is implemented.<BR>
       * This object is described in the 'Portable Document Format Reference Manual version 1.3'
       * section 6.2 (page 67-71)
       */
  
      static class PdfCatalog extends PdfDictionary {
  
      	/** The writer writing the PDF for which we are creating this catalog object. */
          PdfWriter writer;
  
          /**
           * Constructs a <CODE>PdfCatalog</CODE>.
           *
           * @param		pages		an indirect reference to the root of the document's Pages tree.
           * @param writer the writer the catalog applies to
           */
  
          PdfCatalog(PdfIndirectReference pages, PdfWriter writer) {
              super(CATALOG);
              this.writer = writer;
              put(PdfName.PAGES, pages);
          }
  
          /**
           * Adds the names of the named destinations to the catalog.
           * @param localDestinations the local destinations
           * @param documentLevelJS the javascript used in the document
           * @param documentFileAttachment	the attached files
           * @param writer the writer the catalog applies to
           */
          void addNames(TreeMap localDestinations, HashMap documentLevelJS, HashMap documentFileAttachment, PdfWriter writer) {
              if (localDestinations.isEmpty() && documentLevelJS.isEmpty() && documentFileAttachment.isEmpty())
                  return;
              try {
                  PdfDictionary names = new PdfDictionary();
                  if (!localDestinations.isEmpty()) {
                      PdfArray ar = new PdfArray();
                      for (Iterator i = localDestinations.entrySet().iterator(); i.hasNext();) {
                          Map.Entry entry = (Map.Entry) i.next();
                          String name = (String) entry.getKey();
                          Object obj[] = (Object[]) entry.getValue();
                          if (obj[2] == null) //no destination
                              continue;
                          PdfIndirectReference ref = (PdfIndirectReference)obj[1];
                          ar.add(new PdfString(name, null));
                          ar.add(ref);
                      }
                      if (ar.size() > 0) {
                          PdfDictionary dests = new PdfDictionary();
                          dests.put(PdfName.NAMES, ar);
                          names.put(PdfName.DESTS, writer.addToBody(dests).getIndirectReference());
                      }
                  }
                  if (!documentLevelJS.isEmpty()) {
                      PdfDictionary tree = PdfNameTree.writeTree(documentLevelJS, writer);
                      names.put(PdfName.JAVASCRIPT, writer.addToBody(tree).getIndirectReference());
                  }
                  if (!documentFileAttachment.isEmpty()) {
                      names.put(PdfName.EMBEDDEDFILES, writer.addToBody(PdfNameTree.writeTree(documentFileAttachment, writer)).getIndirectReference());
                  }
                  if (names.size() > 0)
                      put(PdfName.NAMES, writer.addToBody(names).getIndirectReference());
              }
              catch (IOException e) {
                  throw new ExceptionConverter(e);
              }
          }
  
          /**
           * Adds an open action to the catalog.
           * @param	action	the action that will be triggered upon opening the document
           */
          void setOpenAction(PdfAction action) {
              put(PdfName.OPENACTION, action);
          }
  
  
          /**
           * Sets the document level additional actions.
           * @param actions   dictionary of actions
           */
          void setAdditionalActions(PdfDictionary actions) {
              try {
                  put(PdfName.AA, writer.addToBody(actions).getIndirectReference());
              } catch (Exception e) {
                  throw new ExceptionConverter(e);
              }
          }
      }
  
  // CONSTRUCTING A PdfDocument/PdfWriter INSTANCE
  
      /**
       * Constructs a new PDF document.
       */
      public PdfDocument() {
          super();
          addProducer();
          addCreationDate();
      }
  
      /** The <CODE>PdfWriter</CODE>. */
      protected PdfWriter writer;
  
      /**
       * Adds a <CODE>PdfWriter</CODE> to the <CODE>PdfDocument</CODE>.
       *
       * @param writer the <CODE>PdfWriter</CODE> that writes everything
       *                     what is added to this document to an outputstream.
       * @throws DocumentException on error
       */
      public void addWriter(PdfWriter writer) throws DocumentException {
          if (this.writer == null) {
              this.writer = writer;
              annotationsImp = new PdfAnnotationsImp(writer);
              return;
          }
          throw new DocumentException("You can only add a writer to a PdfDocument once.");
      }
  
  // LISTENER METHODS START
  
  //	[L0] ElementListener interface
  
      /** This is the PdfContentByte object, containing the text. */
      protected PdfContentByte text;
  
      /** This is the PdfContentByte object, containing the borders and other Graphics. */
      protected PdfContentByte graphics;
  
      /** This represents the leading of the lines. */
      protected float leading = 0;
  
      /**
       * Getter for the current leading.
       * @return	the current leading
       * @since	2.1.2
       */
      public float getLeading() {
      	return leading;
      }
      
      /**
       * Setter for the current leading.
       * @param	leading the current leading
       * @since	2.1.6
       */
      void setLeading(float leading) {
      	this.leading = leading;
      }
  
      /** This represents the current alignment of the PDF Elements. */
      protected int alignment = Element.ALIGN_LEFT;
  
      /** This is the current height of the document. */
      protected float currentHeight = 0;
  
      /**
       * Signals that onParagraph is valid (to avoid that a Chapter/Section title is treated as a Paragraph).
       * @since 2.1.2
       */
      protected boolean isSectionTitle = false;
  
      /**
       * Signals that the current leading has to be subtracted from a YMark object when positive.
       * @since 2.1.2
       */
      protected int leadingCount = 0;
  
      /** The current active <CODE>PdfAction</CODE> when processing an <CODE>Anchor</CODE>. */
      protected PdfAction anchorAction = null;
  
      /**
       * Signals that an <CODE>Element</CODE> was added to the <CODE>Document</CODE>.
       *
       * @param element the element to add
       * @return <CODE>true</CODE> if the element was added, <CODE>false</CODE> if not.
       * @throws DocumentException when a document isn't open yet, or has been closed
       */
      public boolean add(Element element) throws DocumentException {
          if (writer != null && writer.isPaused()) {
              return false;
          }
          try {
              switch(element.type()) {
                  // Information (headers)
                  case Element.HEADER:
                      info.addkey(((Meta)element).getName(), ((Meta)element).getContent());
                      break;
                  case Element.TITLE:
                      info.addTitle(((Meta)element).getContent());
                      break;
                  case Element.SUBJECT:
                      info.addSubject(((Meta)element).getContent());
                      break;
                  case Element.KEYWORDS:
                      info.addKeywords(((Meta)element).getContent());
                      break;
                  case Element.AUTHOR:
                      info.addAuthor(((Meta)element).getContent());
                      break;
                  case Element.CREATOR:
                      info.addCreator(((Meta)element).getContent());
                      break;
                  case Element.PRODUCER:
                      // you can not change the name of the producer
                      info.addProducer();
                      break;
                  case Element.CREATIONDATE:
                      // you can not set the creation date, only reset it
                      info.addCreationDate();
                      break;
  
                  // content (text)
                  case Element.CHUNK: {
                      // if there isn't a current line available, we make one
                      if (line == null) {
                          carriageReturn();
                      }
  
                      // we cast the element to a chunk
                      PdfChunk chunk = new PdfChunk((Chunk) element, anchorAction);
                      // we try to add the chunk to the line, until we succeed
                      {
                          PdfChunk overflow;
                          while ((overflow = line.add(chunk)) != null) {
                              carriageReturn();
                              chunk = overflow;
                              chunk.trimFirstSpace();
                          }
                      }
                      pageEmpty = false;
                      if (chunk.isAttribute(Chunk.NEWPAGE)) {
                          newPage();
                      }
                      break;
                  }
                  case Element.ANCHOR: {
                  	leadingCount++;
                      Anchor anchor = (Anchor) element;
                      String url = anchor.getReference();
                      leading = anchor.getLeading();
                      if (url != null) {
                          anchorAction = new PdfAction(url);
                      }
                      // we process the element
                      element.process(this);
                      anchorAction = null;
                      leadingCount--;
                      break;
                  }
                  case Element.ANNOTATION: {
                      if (line == null) {
                          carriageReturn();
                      }
                      Annotation annot = (Annotation) element;
                      Rectangle rect = new Rectangle(0, 0);
                      if (line != null)
                      	rect = new Rectangle(annot.llx(indentRight() - line.widthLeft()), annot.lly(indentTop() - currentHeight), annot.urx(indentRight() - line.widthLeft() + 20), annot.ury(indentTop() - currentHeight - 20));
                      PdfAnnotation an = PdfAnnotationsImp.convertAnnotation(writer, annot, rect);
                      annotationsImp.addPlainAnnotation(an);
                      pageEmpty = false;
                      break;
                  }
                  case Element.PHRASE: {
                  	leadingCount++;
                      // we cast the element to a phrase and set the leading of the document
                      leading = ((Phrase) element).getLeading();
                      // we process the element
                      element.process(this);
                      leadingCount--;
                      break;
                  }
                  case Element.PARAGRAPH: {
                  	leadingCount++;
                      // we cast the element to a paragraph
                      Paragraph paragraph = (Paragraph) element;
                      addSpacing(paragraph.getSpacingBefore(), leading, paragraph.getFont());
  
                      // we adjust the parameters of the document
                      alignment = paragraph.getAlignment();
                      leading = paragraph.getTotalLeading();
                      carriageReturn();
  
                      // we don't want to make orphans/widows
                      if (currentHeight + line.height() + leading > indentTop() - indentBottom()) {
                          newPage();
                      }
                      indentation.indentLeft += paragraph.getIndentationLeft();
                      indentation.indentRight += paragraph.getIndentationRight();
                      carriageReturn();
  
                      PdfPageEvent pageEvent = writer.getPageEvent();
                      if (pageEvent != null && !isSectionTitle)
                          pageEvent.onParagraph(writer, this, indentTop() - currentHeight);
  
                      // if a paragraph has to be kept together, we wrap it in a table object
                      if (paragraph.getKeepTogether()) {
                      	carriageReturn();
                          PdfPTable table = new PdfPTable(1);
                          table.setWidthPercentage(100f);
                          PdfPCell cell = new PdfPCell();
                          cell.addElement(paragraph);
                          cell.setBorder(Table.NO_BORDER);
                          cell.setPadding(0);
                          table.addCell(cell);
                          indentation.indentLeft -= paragraph.getIndentationLeft();
                          indentation.indentRight -= paragraph.getIndentationRight();
                          this.add(table);
                          indentation.indentLeft += paragraph.getIndentationLeft();
                          indentation.indentRight += paragraph.getIndentationRight();
                      }
                      else {
                      	line.setExtraIndent(paragraph.getFirstLineIndent());
                      	element.process(this);
                          carriageReturn();
                          addSpacing(paragraph.getSpacingAfter(), paragraph.getTotalLeading(), paragraph.getFont());
                      }
  
                      if (pageEvent != null && !isSectionTitle)
                          pageEvent.onParagraphEnd(writer, this, indentTop() - currentHeight);
  
                      alignment = Element.ALIGN_LEFT;
                      indentation.indentLeft -= paragraph.getIndentationLeft();
                      indentation.indentRight -= paragraph.getIndentationRight();
                      carriageReturn();
                      leadingCount--;
                      break;
                  }
                  case Element.SECTION:
                  case Element.CHAPTER: {
                      // Chapters and Sections only differ in their constructor
                      // so we cast both to a Section
                      Section section = (Section) element;
                      PdfPageEvent pageEvent = writer.getPageEvent();
  
                      boolean hasTitle = section.isNotAddedYet()
                      	&& section.getTitle() != null;
  
                      // if the section is a chapter, we begin a new page
                      if (section.isTriggerNewPage()) {
                          newPage();
                      }
  
                      if (hasTitle) {
                      	float fith = indentTop() - currentHeight;
                      	int rotation = pageSize.getRotation();
                      	if (rotation == 90 || rotation == 180)
                      		fith = pageSize.getHeight() - fith;
                      	PdfDestination destination = new PdfDestination(PdfDestination.FITH, fith);
                      	while (currentOutline.level() >= section.getDepth()) {
                      		currentOutline = currentOutline.parent();
                      	}
                      	PdfOutline outline = new PdfOutline(currentOutline, destination, section.getBookmarkTitle(), section.isBookmarkOpen());
                      	currentOutline = outline;
                      }
  
                      // some values are set
                      carriageReturn();
                      indentation.sectionIndentLeft += section.getIndentationLeft();
                      indentation.sectionIndentRight += section.getIndentationRight();
  
                      if (section.isNotAddedYet() && pageEvent != null)
                          if (element.type() == Element.CHAPTER)
                              pageEvent.onChapter(writer, this, indentTop() - currentHeight, section.getTitle());
                          else
                              pageEvent.onSection(writer, this, indentTop() - currentHeight, section.getDepth(), section.getTitle());
  
                      // the title of the section (if any has to be printed)
                      if (hasTitle) {
                          isSectionTitle = true;
                          add(section.getTitle());
                          isSectionTitle = false;
                      }
                      indentation.sectionIndentLeft += section.getIndentation();
                      // we process the section
                      element.process(this);
                      flushLines();
                      // some parameters are set back to normal again
                      indentation.sectionIndentLeft -= (section.getIndentationLeft() + section.getIndentation());
                      indentation.sectionIndentRight -= section.getIndentationRight();
  
                      if (section.isComplete() && pageEvent != null)
                          if (element.type() == Element.CHAPTER)
                              pageEvent.onChapterEnd(writer, this, indentTop() - currentHeight);
                          else
                              pageEvent.onSectionEnd(writer, this, indentTop() - currentHeight);
  
                      break;
                  }
                  case Element.LIST: {
                      // we cast the element to a List
                      List list = (List) element;
                      if (list.isAlignindent()) {
                      	list.normalizeIndentation();
                      }
                      // we adjust the document
                      indentation.listIndentLeft += list.getIndentationLeft();
                      indentation.indentRight += list.getIndentationRight();
                      // we process the items in the list
                      element.process(this);
                      // some parameters are set back to normal again
                      indentation.listIndentLeft -= list.getIndentationLeft();
                      indentation.indentRight -= list.getIndentationRight();
                      carriageReturn();
                      break;
                  }
                  case Element.LISTITEM: {
                  	leadingCount++;
                      // we cast the element to a ListItem
                      ListItem listItem = (ListItem) element;
  
                      addSpacing(listItem.getSpacingBefore(), leading, listItem.getFont());
  
                      // we adjust the document
                      alignment = listItem.getAlignment();
                      indentation.listIndentLeft += listItem.getIndentationLeft();
                      indentation.indentRight += listItem.getIndentationRight();
                      leading = listItem.getTotalLeading();
                      carriageReturn();
  
                      // we prepare the current line to be able to show us the listsymbol
                      line.setListItem(listItem);
                      // we process the item
                      element.process(this);
  
                      addSpacing(listItem.getSpacingAfter(), listItem.getTotalLeading(), listItem.getFont());
  
                      // if the last line is justified, it should be aligned to the left
                      if (line.hasToBeJustified()) {
                      	line.resetAlignment();
                      }
                      // some parameters are set back to normal again
                      carriageReturn();
                      indentation.listIndentLeft -= listItem.getIndentationLeft();
                      indentation.indentRight -= listItem.getIndentationRight();
                      leadingCount--;
                      break;
                  }
                  case Element.RECTANGLE: {
                      Rectangle rectangle = (Rectangle) element;
                      graphics.rectangle(rectangle);
                      pageEmpty = false;
                      break;
                  }
                  case Element.PTABLE: {
                      PdfPTable ptable = (PdfPTable)element;
                      if (ptable.size() <= ptable.getHeaderRows())
                          break; //nothing to do
  
                      // before every table, we add a new line and flush all lines
                      ensureNewLine();
                      flushLines();
  
                      addPTable(ptable);
                      pageEmpty = false;
                      newLine();
                      break;
                  }
                  case Element.MULTI_COLUMN_TEXT: {
                      ensureNewLine();
                      flushLines();
                      MultiColumnText multiText = (MultiColumnText) element;
                      float height = multiText.write(writer.getDirectContent(), this, indentTop() - currentHeight);
                      currentHeight += height;
                      text.moveText(0, -1f* height);
                      pageEmpty = false;
                      break;
                  }
                  case Element.TABLE : {
                      if (element instanceof SimpleTable) {
                      	PdfPTable ptable = ((SimpleTable)element).createPdfPTable();
                      	if (ptable.size() <= ptable.getHeaderRows())
                      		break; //nothing to do
  
                      	// before every table, we add a new line and flush all lines
                      	ensureNewLine();
                      	flushLines();
                      	addPTable(ptable);
                      	pageEmpty = false;
                      	break;
                      } else if (element instanceof Table) {
                      	try {
                         		PdfPTable ptable = ((Table)element).createPdfPTable();
                         		if (ptable.size() <= ptable.getHeaderRows())
                         			break; //nothing to do
                         		// before every table, we add a new line and flush all lines
                         		ensureNewLine();
                         		flushLines();
                         		addPTable(ptable);
                         		pageEmpty = false;
                         		break;
                      	}
                      	catch(BadElementException bee) {
                      		// constructing the PdfTable
                      		// Before the table, add a blank line using offset or default leading
                      		float offset = ((Table)element).getOffset();
                      		if (Float.isNaN(offset))
                      			offset = leading;
                      		carriageReturn();
                      		lines.add(new PdfLine(indentLeft(), indentRight(), alignment, offset));
                      		currentHeight += offset;
                      		addPdfTable((Table)element);
                      	}
  					} else {
  						return false;
  					}
                      break;
                  }
                  case Element.JPEG:
                  case Element.JPEG2000:
                  case Element.JBIG2:
                  case Element.IMGRAW:
                  case Element.IMGTEMPLATE: {
                      //carriageReturn(); suggestion by Marc Campforts
                      add((Image) element);
                      break;
                  }
                  case Element.YMARK: {
                      DrawInterface zh = (DrawInterface)element;
                      zh.draw(graphics, indentLeft(), indentBottom(), indentRight(), indentTop(), indentTop() - currentHeight - (leadingCount > 0 ? leading : 0));
                      pageEmpty = false;
                      break;
                  }
                  case Element.MARKED: {
                  	MarkedObject mo;
                  	if (element instanceof MarkedSection) {
                  		mo = ((MarkedSection)element).getTitle();
                  		if (mo != null) {
                  			mo.process(this);
                  		}
                  	}
                  	mo = (MarkedObject)element;
                  	mo.process(this);
                  	break;
                  }
                  default:
                      return false;
              }
              lastElementType = element.type();
              return true;
          }
          catch(Exception e) {
              throw new DocumentException(e);
          }
      }
  
  //	[L1] DocListener interface
  
      /**
       * Opens the document.
       * <P>
       * You have to open the document before you can begin to add content
       * to the body of the document.
       */
      public void open() {
          if (!open) {
              super.open();
              writer.open();
              rootOutline = new PdfOutline(writer);
              currentOutline = rootOutline;
          }
          try {
              initPage();
          }
          catch(DocumentException de) {
              throw new ExceptionConverter(de);
          }
      }
  
  //	[L2] DocListener interface
  
      /**
       * Closes the document.
       * <B>
       * Once all the content has been written in the body, you have to close
       * the body. After that nothing can be written to the body anymore.
       */
      public void close() {
          if (close) {
              return;
          }
          try {
          	boolean wasImage = (imageWait != null);
              newPage();
              if (imageWait != null || wasImage) newPage();
              if (annotationsImp.hasUnusedAnnotations())
                  throw new RuntimeException("Not all annotations could be added to the document (the document doesn't have enough pages).");
              PdfPageEvent pageEvent = writer.getPageEvent();
              if (pageEvent != null)
                  pageEvent.onCloseDocument(writer, this);
              super.close();
  
              writer.addLocalDestinations(localDestinations);
              calculateOutlineCount();
              writeOutlines();
          }
          catch(Exception e) {
              throw ExceptionConverter.convertException(e);
          }
  
          writer.close();
      }
  
  //	[L3] DocListener interface
      protected int textEmptySize;
  
      // [C9] Metadata for the page
      /** XMP Metadata for the page. */
      protected byte[] xmpMetadata = null;
  	/**
  	 * Use this method to set the XMP Metadata.
  	 * @param xmpMetadata The xmpMetadata to set.
  	 */
  	public void setXmpMetadata(byte[] xmpMetadata) {
  		this.xmpMetadata = xmpMetadata;
  	}
  
      /**
       * Makes a new page and sends it to the <CODE>PdfWriter</CODE>.
       *
       * @return a <CODE>boolean</CODE>
       */
      public boolean newPage() {
          lastElementType = -1;
          if (writer == null || (writer.getDirectContent().size() == 0 && writer.getDirectContentUnder().size() == 0 && (pageEmpty || writer.isPaused()))) {
          	setNewPageSizeAndMargins();
              return false;
          }
      	if (!open || close) {
      		throw new RuntimeException("The document isn't open.");
      	}
          PdfPageEvent pageEvent = writer.getPageEvent();
          if (pageEvent != null)
              pageEvent.onEndPage(writer, this);
  
          //Added to inform any listeners that we are moving to a new page (added by David Freels)
          super.newPage();
  
          // the following 2 lines were added by Pelikan Stephan
          indentation.imageIndentLeft = 0;
          indentation.imageIndentRight = 0;
  
          try {
              // we flush the arraylist with recently written lines
          	flushLines();
  
          	// we prepare the elements of the page dictionary
  
          	// [U1] page size and rotation
          	int rotation = pageSize.getRotation();
  
          	// [C10]
          	if (writer.isPdfX()) {
          		if (thisBoxSize.containsKey("art") && thisBoxSize.containsKey("trim"))
          			throw new PdfXConformanceException("Only one of ArtBox or TrimBox can exist in the page.");
          		if (!thisBoxSize.containsKey("art") && !thisBoxSize.containsKey("trim")) {
          			if (thisBoxSize.containsKey("crop"))
          				thisBoxSize.put("trim", thisBoxSize.get("crop"));
          			else
          				thisBoxSize.put("trim", new PdfRectangle(pageSize, pageSize.getRotation()));
          		}
          	}
  
          	// [M1]
          	pageResources.addDefaultColorDiff(writer.getDefaultColorspace());
              if (writer.isRgbTransparencyBlending()) {
                  PdfDictionary dcs = new PdfDictionary();
                  dcs.put(PdfName.CS, PdfName.DEVICERGB);
                  pageResources.addDefaultColorDiff(dcs);
              }
          	PdfDictionary resources = pageResources.getResources();
  
          	// we create the page dictionary
  
          	PdfPage page = new PdfPage(new PdfRectangle(pageSize, rotation), thisBoxSize, resources, rotation);
          	page.put(PdfName.TABS, writer.getTabs());
  
              // we complete the page dictionary
  
              // [C9] if there is XMP data to add: add it
              if (xmpMetadata != null) {
              	PdfStream xmp = new PdfStream(xmpMetadata);
              	xmp.put(PdfName.TYPE, PdfName.METADATA);
              	xmp.put(PdfName.SUBTYPE, PdfName.XML);
              	PdfEncryption crypto = writer.getEncryption();
                  if (crypto != null && !crypto.isMetadataEncrypted()) {
                      PdfArray ar = new PdfArray();
                      ar.add(PdfName.CRYPT);
                      xmp.put(PdfName.FILTER, ar);
                  }
              	page.put(PdfName.METADATA, writer.addToBody(xmp).getIndirectReference());
              }
  
          	// [U3] page actions: transition, duration, additional actions
          	if (this.transition!=null) {
          		page.put(PdfName.TRANS, this.transition.getTransitionDictionary());
          		transition = null;
          	}
          	if (this.duration>0) {
          		page.put(PdfName.DUR,new PdfNumber(this.duration));
          		duration = 0;
          	}
          	if (pageAA != null) {
          		page.put(PdfName.AA, writer.addToBody(pageAA).getIndirectReference());
          		pageAA = null;
          	}
  
          	// [U4] we add the thumbs
          	if (thumb != null) {
          		page.put(PdfName.THUMB, thumb);
          		thumb = null;
          	}
  
          	// [U8] we check if the userunit is defined
          	if (writer.getUserunit() > 0f) {
          		page.put(PdfName.USERUNIT, new PdfNumber(writer.getUserunit()));
          	}
  
          	// [C5] and [C8] we add the annotations
          	if (annotationsImp.hasUnusedAnnotations()) {
          		PdfArray array = annotationsImp.rotateAnnotations(writer, pageSize);
          		if (array.size() != 0)
          			page.put(PdfName.ANNOTS, array);
          	}
  
          	// [F12] we add tag info
          	if (writer.isTagged())
          		page.put(PdfName.STRUCTPARENTS, new PdfNumber(writer.getCurrentPageNumber() - 1));
  
              if (text.size() > textEmptySize)
          		text.endText();
          	else
          		text = null;
          	writer.add(page, new PdfContents(writer.getDirectContentUnder(), graphics, text, writer.getDirectContent(), pageSize));
          	// we initialize the new page
          	initPage();
          }
          catch(DocumentException de) {
          	// maybe this never happens, but it's better to check.
          	throw new ExceptionConverter(de);
          }
          catch (IOException ioe) {
              throw new ExceptionConverter(ioe);
          }
          return true;
      }
  
  //	[L4] DocListener interface
  
      /**
       * Sets the pagesize.
       *
       * @param pageSize the new pagesize
       * @return <CODE>true</CODE> if the page size was set
       */
      public boolean setPageSize(Rectangle pageSize) {
          if (writer != null && writer.isPaused()) {
              return false;
          }
          nextPageSize = new Rectangle(pageSize);
          return true;
      }
  
  //	[L5] DocListener interface
  
      /** margin in x direction starting from the left. Will be valid in the next page */
      protected float nextMarginLeft;
  
      /** margin in x direction starting from the right. Will be valid in the next page */
      protected float nextMarginRight;
  
      /** margin in y direction starting from the top. Will be valid in the next page */
      protected float nextMarginTop;
  
      /** margin in y direction starting from the bottom. Will be valid in the next page */
      protected float nextMarginBottom;
  
      /**
       * Sets the margins.
       *
       * @param	marginLeft		the margin on the left
       * @param	marginRight		the margin on the right
       * @param	marginTop		the margin on the top
       * @param	marginBottom	the margin on the bottom
       * @return	a <CODE>boolean</CODE>
       */
      public boolean setMargins(float marginLeft, float marginRight, float marginTop, float marginBottom) {
          if (writer != null && writer.isPaused()) {
              return false;
          }
          nextMarginLeft = marginLeft;
          nextMarginRight = marginRight;
          nextMarginTop = marginTop;
          nextMarginBottom = marginBottom;
          return true;
      }
  
  //	[L6] DocListener interface
  
      /**
       * @see com.lowagie.text.DocListener#setMarginMirroring(boolean)
       */
      public boolean setMarginMirroring(boolean MarginMirroring) {
          if (writer != null && writer.isPaused()) {
              return false;
          }
          return super.setMarginMirroring(MarginMirroring);
      }
      
      /**
       * @see com.lowagie.text.DocListener#setMarginMirroring(boolean)
       * @since	2.1.6
       */
      public boolean setMarginMirroringTopBottom(boolean MarginMirroringTopBottom) {
          if (writer != null && writer.isPaused()) {
              return false;
          }
          return super.setMarginMirroringTopBottom(MarginMirroringTopBottom);
      }
  
  //	[L7] DocListener interface
  
      /**
       * Sets the page number.
       *
       * @param	pageN		the new page number
       */
      public void setPageCount(int pageN) {
          if (writer != null && writer.isPaused()) {
              return;
          }
          super.setPageCount(pageN);
      }
  
  //	[L8] DocListener interface
  
      /**
       * Sets the page number to 0.
       */
      public void resetPageCount() {
          if (writer != null && writer.isPaused()) {
              return;
          }
          super.resetPageCount();
      }
  
  //	[L9] DocListener interface
  
      /**
       * Changes the header of this document.
       *
       * @param header the new header
       */
      public void setHeader(HeaderFooter header) {
          if (writer != null && writer.isPaused()) {
              return;
          }
          super.setHeader(header);
      }
  
  //	[L10] DocListener interface
  
      /**
       * Resets the header of this document.
       */
      public void resetHeader() {
          if (writer != null && writer.isPaused()) {
              return;
          }
          super.resetHeader();
      }
  
  //	[L11] DocListener interface
  
      /**
       * Changes the footer of this document.
       *
       * @param	footer		the new footer
       */
      public void setFooter(HeaderFooter footer) {
          if (writer != null && writer.isPaused()) {
              return;
          }
          super.setFooter(footer);
      }
  
  //	[L12] DocListener interface
  
      /**
       * Resets the footer of this document.
       */
      public void resetFooter() {
  