Save This Page
Home » iText-src-2.1.3 » com.lowagie » text » pdf » [javadoc | source]
    1   /*
    2    * $Id: PdfDocument.java 3373 2008-05-12 16:21:24Z xlv $
    3    *
    4    * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
    5    *
    6    * The contents of this file are subject to the Mozilla Public License Version 1.1
    7    * (the "License"); you may not use this file except in compliance with the License.
    8    * You may obtain a copy of the License at http://www.mozilla.org/MPL/
    9    *
   10    * Software distributed under the License is distributed on an "AS IS" basis,
   11    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   12    * for the specific language governing rights and limitations under the License.
   13    *
   14    * The Original Code is 'iText, a free JAVA-PDF library'.
   15    *
   16    * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
   17    * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
   18    * All Rights Reserved.
   19    * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
   20    * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
   21    *
   22    * Contributor(s): all the names of the contributors are added in the source code
   23    * where applicable.
   24    *
   25    * Alternatively, the contents of this file may be used under the terms of the
   26    * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
   27    * provisions of LGPL are applicable instead of those above.  If you wish to
   28    * allow use of your version of this file only under the terms of the LGPL
   29    * License and not to allow others to use your version of this file under
   30    * the MPL, indicate your decision by deleting the provisions above and
   31    * replace them with the notice and other provisions required by the LGPL.
   32    * If you do not delete the provisions above, a recipient may use your version
   33    * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
   34    *
   35    * This library is free software; you can redistribute it and/or modify it
   36    * under the terms of the MPL as stated above or under the terms of the GNU
   37    * Library General Public License as published by the Free Software Foundation;
   38    * either version 2 of the License, or any later version.
   39    *
   40    * This library is distributed in the hope that it will be useful, but WITHOUT
   41    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   42    * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
   43    * details.
   44    *
   45    * If you didn't download this code from the following link, you should check if
   46    * you aren't using an obsolete version:
   47    * http://www.lowagie.com/iText/
   48    */
   49   
   50   package com.lowagie.text.pdf;
   51   
   52   import java.awt.Color;
   53   import java.io.IOException;
   54   import java.util.ArrayList;
   55   import java.util.HashMap;
   56   import java.util.HashSet;
   57   import java.util.Iterator;
   58   import java.util.Map;
   59   import java.util.Set;
   60   import java.util.TreeMap;
   61   
   62   import com.lowagie.text.Anchor;
   63   import com.lowagie.text.Annotation;
   64   import com.lowagie.text.BadElementException;
   65   import com.lowagie.text.Chunk;
   66   import com.lowagie.text.Document;
   67   import com.lowagie.text.DocumentException;
   68   import com.lowagie.text.Element;
   69   import com.lowagie.text.ExceptionConverter;
   70   import com.lowagie.text.Font;
   71   import com.lowagie.text.HeaderFooter;
   72   import com.lowagie.text.Image;
   73   import com.lowagie.text.List;
   74   import com.lowagie.text.ListItem;
   75   import com.lowagie.text.MarkedObject;
   76   import com.lowagie.text.MarkedSection;
   77   import com.lowagie.text.Meta;
   78   import com.lowagie.text.Paragraph;
   79   import com.lowagie.text.Phrase;
   80   import com.lowagie.text.Rectangle;
   81   import com.lowagie.text.Section;
   82   import com.lowagie.text.SimpleTable;
   83   import com.lowagie.text.Table;
   84   import com.lowagie.text.pdf.collection.PdfCollection;
   85   import com.lowagie.text.pdf.draw.DrawInterface;
   86   import com.lowagie.text.pdf.internal.PdfAnnotationsImp;
   87   import com.lowagie.text.pdf.internal.PdfViewerPreferencesImp;
   88   import java.text.DecimalFormat;
   89   
   90   /**
   91    * <CODE>PdfDocument</CODE> is the class that is used by <CODE>PdfWriter</CODE>
   92    * to translate a <CODE>Document</CODE> into a PDF with different pages.
   93    * <P>
   94    * A <CODE>PdfDocument</CODE> always listens to a <CODE>Document</CODE>
   95    * and adds the Pdf representation of every <CODE>Element</CODE> that is
   96    * added to the <CODE>Document</CODE>.
   97    *
   98    * @see		com.lowagie.text.Document
   99    * @see		com.lowagie.text.DocListener
  100    * @see		PdfWriter
  101    * @since	2.0.8 (class was package-private before)
  102    */
  103   
  104   public class PdfDocument extends Document {
  105   	
  106       /**
  107        * <CODE>PdfInfo</CODE> is the PDF InfoDictionary.
  108        * <P>
  109        * A document's trailer may contain a reference to an Info dictionary that provides information
  110        * about the document. This optional dictionary may contain one or more keys, whose values
  111        * should be strings.<BR>
  112        * This object is described in the 'Portable Document Format Reference Manual version 1.3'
  113        * section 6.10 (page 120-121)
  114        * @since	2.0.8 (PdfDocument was package-private before)
  115        */
  116       
  117       public static class PdfInfo extends PdfDictionary {
  118           
  119           /**
  120            * Construct a <CODE>PdfInfo</CODE>-object.
  121            */
  122           
  123           PdfInfo() {
  124               super();
  125               addProducer();
  126               addCreationDate();
  127           }
  128           
  129           /**
  130            * Constructs a <CODE>PdfInfo</CODE>-object.
  131            *
  132            * @param		author		name of the author of the document
  133            * @param		title		title of the document
  134            * @param		subject		subject of the document
  135            */
  136           
  137           PdfInfo(String author, String title, String subject) {
  138               this();
  139               addTitle(title);
  140               addSubject(subject);
  141               addAuthor(author);
  142           }
  143           
  144           /**
  145            * Adds the title of the document.
  146            *
  147            * @param	title		the title of the document
  148            */
  149           
  150           void addTitle(String title) {
  151               put(PdfName.TITLE, new PdfString(title, PdfObject.TEXT_UNICODE));
  152           }
  153           
  154           /**
  155            * Adds the subject to the document.
  156            *
  157            * @param	subject		the subject of the document
  158            */
  159           
  160           void addSubject(String subject) {
  161               put(PdfName.SUBJECT, new PdfString(subject, PdfObject.TEXT_UNICODE));
  162           }
  163           
  164           /**
  165            * Adds some keywords to the document.
  166            *
  167            * @param	keywords		the keywords of the document
  168            */
  169           
  170           void addKeywords(String keywords) {
  171               put(PdfName.KEYWORDS, new PdfString(keywords, PdfObject.TEXT_UNICODE));
  172           }
  173           
  174           /**
  175            * Adds the name of the author to the document.
  176            *
  177            * @param	author		the name of the author
  178            */
  179           
  180           void addAuthor(String author) {
  181               put(PdfName.AUTHOR, new PdfString(author, PdfObject.TEXT_UNICODE));
  182           }
  183           
  184           /**
  185            * Adds the name of the creator to the document.
  186            *
  187            * @param	creator		the name of the creator
  188            */
  189           
  190           void addCreator(String creator) {
  191               put(PdfName.CREATOR, new PdfString(creator, PdfObject.TEXT_UNICODE));
  192           }
  193           
  194           /**
  195            * Adds the name of the producer to the document.
  196            */
  197           
  198           void addProducer() {
  199               put(PdfName.PRODUCER, new PdfString(getVersion()));
  200           }
  201           
  202           /**
  203            * Adds the date of creation to the document.
  204            */
  205           
  206           void addCreationDate() {
  207               PdfString date = new PdfDate();
  208               put(PdfName.CREATIONDATE, date);
  209               put(PdfName.MODDATE, date);
  210           }
  211           
  212           void addkey(String key, String value) {
  213               if (key.equals("Producer") || key.equals("CreationDate"))
  214                   return;
  215               put(new PdfName(key), new PdfString(value, PdfObject.TEXT_UNICODE));
  216           }
  217       }
  218       
  219       /**
  220        * <CODE>PdfCatalog</CODE> is the PDF Catalog-object.
  221        * <P>
  222        * The Catalog is a dictionary that is the root node of the document. It contains a reference
  223        * to the tree of pages contained in the document, a reference to the tree of objects representing
  224        * the document's outline, a reference to the document's article threads, and the list of named
  225        * destinations. In addition, the Catalog indicates whether the document's outline or thumbnail
  226        * page images should be displayed automatically when the document is viewed and whether some location
  227        * other than the first page should be shown when the document is opened.<BR>
  228        * In this class however, only the reference to the tree of pages is implemented.<BR>
  229        * This object is described in the 'Portable Document Format Reference Manual version 1.3'
  230        * section 6.2 (page 67-71)
  231        */
  232       
  233       static class PdfCatalog extends PdfDictionary {
  234           
  235       	/** The writer writing the PDF for which we are creating this catalog object. */
  236           PdfWriter writer;
  237           
  238           /**
  239            * Constructs a <CODE>PdfCatalog</CODE>.
  240            *
  241            * @param		pages		an indirect reference to the root of the document's Pages tree.
  242            * @param writer the writer the catalog applies to
  243            */
  244           
  245           PdfCatalog(PdfIndirectReference pages, PdfWriter writer) {
  246               super(CATALOG);
  247               this.writer = writer;
  248               put(PdfName.PAGES, pages);
  249           }
  250           
  251           /**
  252            * Adds the names of the named destinations to the catalog.
  253            * @param localDestinations the local destinations
  254            * @param documentLevelJS the javascript used in the document
  255            * @param documentFileAttachment	the attached files
  256            * @param writer the writer the catalog applies to
  257            */
  258           void addNames(TreeMap localDestinations, HashMap documentLevelJS, HashMap documentFileAttachment, PdfWriter writer) {
  259               if (localDestinations.isEmpty() && documentLevelJS.isEmpty() && documentFileAttachment.isEmpty())
  260                   return;
  261               try {
  262                   PdfDictionary names = new PdfDictionary();
  263                   if (!localDestinations.isEmpty()) {
  264                       PdfArray ar = new PdfArray();
  265                       for (Iterator i = localDestinations.entrySet().iterator(); i.hasNext();) {
  266                           Map.Entry entry = (Map.Entry) i.next();
  267                           String name = (String) entry.getKey();
  268                           Object obj[] = (Object[]) entry.getValue();
  269                           PdfIndirectReference ref = (PdfIndirectReference)obj[1];
  270                           ar.add(new PdfString(name, null));
  271                           ar.add(ref);
  272                       }
  273                       PdfDictionary dests = new PdfDictionary();
  274                       dests.put(PdfName.NAMES, ar);
  275                       names.put(PdfName.DESTS, writer.addToBody(dests).getIndirectReference());
  276                   }
  277                   if (!documentLevelJS.isEmpty()) {
  278                       PdfDictionary tree = PdfNameTree.writeTree(documentLevelJS, writer);
  279                       names.put(PdfName.JAVASCRIPT, writer.addToBody(tree).getIndirectReference());
  280                   }
  281                   if (!documentFileAttachment.isEmpty()) {
  282                       names.put(PdfName.EMBEDDEDFILES, writer.addToBody(PdfNameTree.writeTree(documentFileAttachment, writer)).getIndirectReference());
  283                   }
  284                   put(PdfName.NAMES, writer.addToBody(names).getIndirectReference());
  285               }
  286               catch (IOException e) {
  287                   throw new ExceptionConverter(e);
  288               }
  289           }
  290           
  291           /**
  292            * Adds an open action to the catalog.
  293            * @param	action	the action that will be triggered upon opening the document
  294            */
  295           void setOpenAction(PdfAction action) {
  296               put(PdfName.OPENACTION, action);
  297           }
  298           
  299           
  300           /**
  301            * Sets the document level additional actions.
  302            * @param actions   dictionary of actions
  303            */
  304           void setAdditionalActions(PdfDictionary actions) {
  305               try {
  306                   put(PdfName.AA, writer.addToBody(actions).getIndirectReference());
  307               } catch (Exception e) {
  308                   throw new ExceptionConverter(e);
  309               }
  310           }
  311       }
  312   
  313   // CONSTRUCTING A PdfDocument/PdfWriter INSTANCE
  314       
  315       /**
  316        * Constructs a new PDF document.
  317        * @throws DocumentException on error
  318        */
  319       public PdfDocument() {
  320           super();
  321           addProducer();
  322           addCreationDate();
  323       }
  324   
  325       /** The <CODE>PdfWriter</CODE>. */
  326       protected PdfWriter writer;
  327       
  328       /**
  329        * Adds a <CODE>PdfWriter</CODE> to the <CODE>PdfDocument</CODE>.
  330        *
  331        * @param writer the <CODE>PdfWriter</CODE> that writes everything
  332        *                     what is added to this document to an outputstream.
  333        * @throws DocumentException on error
  334        */
  335       public void addWriter(PdfWriter writer) throws DocumentException {
  336           if (this.writer == null) {
  337               this.writer = writer;
  338               annotationsImp = new PdfAnnotationsImp(writer);
  339               return;
  340           }
  341           throw new DocumentException("You can only add a writer to a PdfDocument once.");
  342       }
  343       
  344   // LISTENER METHODS START
  345       
  346   //	[L0] ElementListener interface
  347       
  348       /** This is the PdfContentByte object, containing the text. */
  349       protected PdfContentByte text;
  350       
  351       /** This is the PdfContentByte object, containing the borders and other Graphics. */
  352       protected PdfContentByte graphics;
  353       
  354       /** This represents the leading of the lines. */
  355       protected float leading = 0;
  356       
  357       /**
  358        * Getter for the current leading.
  359        * @return	the current leading
  360        * @since	2.1.2
  361        */
  362       public float getLeading() {
  363       	return leading;
  364       }
  365       
  366       /** This represents the current alignment of the PDF Elements. */
  367       protected int alignment = Element.ALIGN_LEFT;
  368       
  369       /** This is the current height of the document. */
  370       protected float currentHeight = 0;
  371       
  372       /**
  373        * Signals that onParagraph is valid (to avoid that a Chapter/Section title is treated as a Paragraph).
  374        * @since 2.1.2
  375        */
  376       protected boolean isSectionTitle = false;
  377       
  378       /**
  379        * Signals that the current leading has to be subtracted from a YMark object when positive.
  380        * @since 2.1.2
  381        */
  382       protected int leadingCount = 0;
  383       
  384       /** The current active <CODE>PdfAction</CODE> when processing an <CODE>Anchor</CODE>. */
  385       protected PdfAction anchorAction = null;
  386       
  387       /**
  388        * Signals that an <CODE>Element</CODE> was added to the <CODE>Document</CODE>.
  389        *
  390        * @param element the element to add
  391        * @return <CODE>true</CODE> if the element was added, <CODE>false</CODE> if not.
  392        * @throws DocumentException when a document isn't open yet, or has been closed
  393        */
  394       public boolean add(Element element) throws DocumentException {
  395           if (writer != null && writer.isPaused()) {
  396               return false;
  397           }
  398           try {
  399               switch(element.type()) {
  400                   // Information (headers)
  401                   case Element.HEADER:
  402                       info.addkey(((Meta)element).getName(), ((Meta)element).getContent());
  403                       break;
  404                   case Element.TITLE:
  405                       info.addTitle(((Meta)element).getContent());
  406                       break;
  407                   case Element.SUBJECT:
  408                       info.addSubject(((Meta)element).getContent());
  409                       break;
  410                   case Element.KEYWORDS:
  411                       info.addKeywords(((Meta)element).getContent());
  412                       break;
  413                   case Element.AUTHOR:
  414                       info.addAuthor(((Meta)element).getContent());
  415                       break;
  416                   case Element.CREATOR:
  417                       info.addCreator(((Meta)element).getContent());
  418                       break;
  419                   case Element.PRODUCER:
  420                       // you can not change the name of the producer
  421                       info.addProducer();
  422                       break;
  423                   case Element.CREATIONDATE:
  424                       // you can not set the creation date, only reset it
  425                       info.addCreationDate();
  426                       break;
  427                       
  428                   // content (text)
  429                   case Element.CHUNK: {
  430                       // if there isn't a current line available, we make one
  431                       if (line == null) {
  432                           carriageReturn();
  433                       }
  434                       
  435                       // we cast the element to a chunk
  436                       PdfChunk chunk = new PdfChunk((Chunk) element, anchorAction);
  437                       // we try to add the chunk to the line, until we succeed
  438                       {
  439                           PdfChunk overflow;
  440                           while ((overflow = line.add(chunk)) != null) {
  441                               carriageReturn();
  442                               chunk = overflow;
  443                               chunk.trimFirstSpace();
  444                           }
  445                       }
  446                       pageEmpty = false;
  447                       if (chunk.isAttribute(Chunk.NEWPAGE)) {
  448                           newPage();
  449                       }
  450                       break;
  451                   }
  452                   case Element.ANCHOR: {
  453                   	leadingCount++;
  454                       Anchor anchor = (Anchor) element;
  455                       String url = anchor.getReference();
  456                       leading = anchor.getLeading();
  457                       if (url != null) {
  458                           anchorAction = new PdfAction(url);
  459                       }
  460                       // we process the element
  461                       element.process(this);
  462                       anchorAction = null;
  463                       leadingCount--;
  464                       break;
  465                   }
  466                   case Element.ANNOTATION: {
  467                       if (line == null) {
  468                           carriageReturn();
  469                       }
  470                       Annotation annot = (Annotation) element;
  471                       Rectangle rect = new Rectangle(0, 0);
  472                       if (line != null)
  473                       	rect = new Rectangle(annot.llx(indentRight() - line.widthLeft()), annot.lly(indentTop() - currentHeight), annot.urx(indentRight() - line.widthLeft() + 20), annot.ury(indentTop() - currentHeight - 20));
  474                       PdfAnnotation an = PdfAnnotationsImp.convertAnnotation(writer, annot, rect);
  475                       annotationsImp.addPlainAnnotation(an);
  476                       pageEmpty = false;
  477                       break;
  478                   }
  479                   case Element.PHRASE: {
  480                   	leadingCount++;
  481                       // we cast the element to a phrase and set the leading of the document
  482                       leading = ((Phrase) element).getLeading();
  483                       // we process the element
  484                       element.process(this);
  485                       leadingCount--;
  486                       break;
  487                   }
  488                   case Element.PARAGRAPH: {
  489                   	leadingCount++;
  490                       // we cast the element to a paragraph
  491                       Paragraph paragraph = (Paragraph) element;
  492                       addSpacing(paragraph.spacingBefore(), leading, paragraph.getFont());
  493                       
  494                       // we adjust the parameters of the document
  495                       alignment = paragraph.getAlignment();
  496                       leading = paragraph.getTotalLeading();
  497                       carriageReturn();
  498                       
  499                       // we don't want to make orphans/widows
  500                       if (currentHeight + line.height() + leading > indentTop() - indentBottom()) {
  501                           newPage();
  502                       }
  503                       indentation.indentLeft += paragraph.getIndentationLeft();
  504                       indentation.indentRight += paragraph.getIndentationRight();
  505                       carriageReturn();
  506        
  507                       PdfPageEvent pageEvent = writer.getPageEvent();
  508                       if (pageEvent != null && !isSectionTitle)
  509                           pageEvent.onParagraph(writer, this, indentTop() - currentHeight);
  510                       
  511                       // if a paragraph has to be kept together, we wrap it in a table object
  512                       if (paragraph.getKeepTogether()) {
  513                       	carriageReturn();
  514                           PdfPTable table = new PdfPTable(1);
  515                           table.setWidthPercentage(100f);
  516                           PdfPCell cell = new PdfPCell();
  517                           cell.addElement(paragraph);
  518                           cell.setBorder(Table.NO_BORDER);
  519                           cell.setPadding(0);
  520                           table.addCell(cell);
  521                           indentation.indentLeft -= paragraph.getIndentationLeft();
  522                           indentation.indentRight -= paragraph.getIndentationRight();
  523                           this.add(table);
  524                           indentation.indentLeft += paragraph.getIndentationLeft();
  525                           indentation.indentRight += paragraph.getIndentationRight();
  526                       }
  527                       else {
  528                       	line.setExtraIndent(paragraph.getFirstLineIndent());
  529                       	element.process(this);
  530                           carriageReturn();
  531                           addSpacing(paragraph.spacingAfter(), paragraph.getTotalLeading(), paragraph.getFont());
  532                       }
  533   
  534                       if (pageEvent != null && !isSectionTitle)
  535                           pageEvent.onParagraphEnd(writer, this, indentTop() - currentHeight);
  536                       
  537                       alignment = Element.ALIGN_LEFT;
  538                       indentation.indentLeft -= paragraph.getIndentationLeft();
  539                       indentation.indentRight -= paragraph.getIndentationRight();
  540                       carriageReturn();
  541                       leadingCount--;
  542                       break;
  543                   }
  544                   case Element.SECTION:
  545                   case Element.CHAPTER: {
  546                       // Chapters and Sections only differ in their constructor
  547                       // so we cast both to a Section
  548                       Section section = (Section) element;
  549                       PdfPageEvent pageEvent = writer.getPageEvent();
  550                       
  551                       boolean hasTitle = section.isNotAddedYet()
  552                       	&& section.getTitle() != null;
  553                       
  554                       // if the section is a chapter, we begin a new page
  555                       if (section.isTriggerNewPage()) {
  556                           newPage();
  557                       }
  558   
  559                       if (hasTitle) {
  560                       	float fith = indentTop() - currentHeight;
  561                       	int rotation = pageSize.getRotation();
  562                       	if (rotation == 90 || rotation == 180)
  563                       		fith = pageSize.getHeight() - fith;
  564                       	PdfDestination destination = new PdfDestination(PdfDestination.FITH, fith);
  565                       	while (currentOutline.level() >= section.getDepth()) {
  566                       		currentOutline = currentOutline.parent();
  567                       	}
  568                       	PdfOutline outline = new PdfOutline(currentOutline, destination, section.getBookmarkTitle(), section.isBookmarkOpen());
  569                       	currentOutline = outline;
  570                       }
  571                       
  572                       // some values are set
  573                       carriageReturn();
  574                       indentation.sectionIndentLeft += section.getIndentationLeft();
  575                       indentation.sectionIndentRight += section.getIndentationRight();
  576         
  577                       if (section.isNotAddedYet() && pageEvent != null)
  578                           if (element.type() == Element.CHAPTER)
  579                               pageEvent.onChapter(writer, this, indentTop() - currentHeight, section.getTitle());
  580                           else
  581                               pageEvent.onSection(writer, this, indentTop() - currentHeight, section.getDepth(), section.getTitle());
  582                       
  583                       // the title of the section (if any has to be printed)
  584                       if (hasTitle) {
  585                           isSectionTitle = true;
  586                           add(section.getTitle());
  587                           isSectionTitle = false;
  588                       }
  589                       indentation.sectionIndentLeft += section.getIndentation();
  590                       // we process the section
  591                       element.process(this);
  592                       flushLines();
  593                       // some parameters are set back to normal again
  594                       indentation.sectionIndentLeft -= (section.getIndentationLeft() + section.getIndentation());
  595                       indentation.sectionIndentRight -= section.getIndentationRight();
  596   
  597                       if (section.isComplete() && pageEvent != null)
  598                           if (element.type() == Element.CHAPTER)
  599                               pageEvent.onChapterEnd(writer, this, indentTop() - currentHeight);
  600                           else
  601                               pageEvent.onSectionEnd(writer, this, indentTop() - currentHeight);
  602   
  603                       break;
  604                   }
  605                   case Element.LIST: {
  606                       // we cast the element to a List
  607                       List list = (List) element;
  608                       if (list.isAlignindent()) {
  609                       	list.normalizeIndentation();
  610                       }
  611                       // we adjust the document
  612                       indentation.listIndentLeft += list.getIndentationLeft();
  613                       indentation.indentRight += list.getIndentationRight();
  614                       // we process the items in the list
  615                       element.process(this);
  616                       // some parameters are set back to normal again
  617                       indentation.listIndentLeft -= list.getIndentationLeft();
  618                       indentation.indentRight -= list.getIndentationRight();
  619                       carriageReturn();
  620                       break;
  621                   }
  622                   case Element.LISTITEM: {
  623                   	leadingCount++;
  624                       // we cast the element to a ListItem
  625                       ListItem listItem = (ListItem) element;
  626                       
  627                       addSpacing(listItem.spacingBefore(), leading, listItem.getFont());
  628                      
  629                       // we adjust the document
  630                       alignment = listItem.getAlignment();
  631                       indentation.listIndentLeft += listItem.getIndentationLeft();
  632                       indentation.indentRight += listItem.getIndentationRight();
  633                       leading = listItem.getTotalLeading();
  634                       carriageReturn();
  635                       
  636                       // we prepare the current line to be able to show us the listsymbol
  637                       line.setListItem(listItem);
  638                       // we process the item
  639                       element.process(this);
  640   
  641                       addSpacing(listItem.spacingAfter(), listItem.getTotalLeading(), listItem.getFont());
  642                      
  643                       // if the last line is justified, it should be aligned to the left
  644                       if (line.hasToBeJustified()) {
  645                       	line.resetAlignment();
  646                       }
  647                       // some parameters are set back to normal again
  648                       carriageReturn();
  649                       indentation.listIndentLeft -= listItem.getIndentationLeft();
  650                       indentation.indentRight -= listItem.getIndentationRight();
  651                       leadingCount--;
  652                       break;
  653                   }
  654                   case Element.RECTANGLE: {
  655                       Rectangle rectangle = (Rectangle) element;
  656                       graphics.rectangle(rectangle);
  657                       pageEmpty = false;
  658                       break;
  659                   }
  660                   case Element.PTABLE: {
  661                       PdfPTable ptable = (PdfPTable)element;
  662                       if (ptable.size() <= ptable.getHeaderRows())
  663                           break; //nothing to do
  664   
  665                       // before every table, we add a new line and flush all lines
  666                       ensureNewLine();
  667                       flushLines();
  668                       
  669                       addPTable(ptable);
  670                       pageEmpty = false;
  671                       newLine();
  672                       break;
  673                   }
  674                   case Element.MULTI_COLUMN_TEXT: {
  675                       ensureNewLine();
  676                       flushLines();
  677                       MultiColumnText multiText = (MultiColumnText) element;
  678                       float height = multiText.write(writer.getDirectContent(), this, indentTop() - currentHeight);
  679                       currentHeight += height;
  680                       text.moveText(0, -1f* height);
  681                       pageEmpty = false;
  682                       break;
  683                   }
  684                   case Element.TABLE : {
  685                       if (element instanceof SimpleTable) {
  686                       	PdfPTable ptable = ((SimpleTable)element).createPdfPTable();
  687                       	if (ptable.size() <= ptable.getHeaderRows())
  688                       		break; //nothing to do
  689               		
  690                       	// before every table, we add a new line and flush all lines
  691                       	ensureNewLine();
  692                       	flushLines();
  693                       	addPTable(ptable);                    
  694                       	pageEmpty = false;
  695                       	break;
  696                       } else if (element instanceof Table) {
  697                       	try {
  698                          		PdfPTable ptable = ((Table)element).createPdfPTable();
  699                          		if (ptable.size() <= ptable.getHeaderRows())
  700                          			break; //nothing to do
  701                          		// before every table, we add a new line and flush all lines
  702                          		ensureNewLine();
  703                          		flushLines();
  704                          		addPTable(ptable);                    
  705                          		pageEmpty = false;
  706                          		break;
  707                       	}
  708                       	catch(BadElementException bee) {
  709                       		// constructing the PdfTable
  710                       		// Before the table, add a blank line using offset or default leading
  711                       		float offset = ((Table)element).getOffset();
  712                       		if (Float.isNaN(offset))
  713                       			offset = leading;
  714                       		carriageReturn();
  715                       		lines.add(new PdfLine(indentLeft(), indentRight(), alignment, offset));
  716                       		currentHeight += offset;
  717                       		addPdfTable((Table)element);
  718                       	}
  719   					} else {
  720   						return false;
  721   					}
  722                       break;
  723                   }
  724                   case Element.JPEG:
  725                   case Element.JPEG2000:
  726                   case Element.IMGRAW:
  727                   case Element.IMGTEMPLATE: {
  728                       //carriageReturn(); suggestion by Marc Campforts
  729                       add((Image) element);
  730                       break;
  731                   }
  732                   case Element.YMARK: {
  733                       DrawInterface zh = (DrawInterface)element;
  734                       zh.draw(graphics, indentLeft(), indentBottom(), indentRight(), indentTop(), indentTop() - currentHeight - (leadingCount > 0 ? leading : 0));
  735                       pageEmpty = false;
  736                       break;
  737                   }
  738                   case Element.MARKED: {
  739                   	MarkedObject mo;
  740                   	if (element instanceof MarkedSection) {
  741                   		mo = ((MarkedSection)element).getTitle();
  742                   		if (mo != null) {
  743                   			mo.process(this);
  744                   		}
  745                   	}
  746                   	mo = (MarkedObject)element;
  747                   	mo.process(this);
  748                   	break;
  749                   }
  750                   default:
  751                       return false;
  752               }
  753               lastElementType = element.type();
  754               return true;
  755           }
  756           catch(Exception e) {
  757               throw new DocumentException(e);
  758           }
  759       }
  760   
  761   //	[L1] DocListener interface
  762       
  763       /**
  764        * Opens the document.
  765        * <P>
  766        * You have to open the document before you can begin to add content
  767        * to the body of the document.
  768        */
  769       public void open() {
  770           if (!open) {
  771               super.open();
  772               writer.open();
  773               rootOutline = new PdfOutline(writer);
  774               currentOutline = rootOutline;
  775           }
  776           try {
  777               initPage();
  778           }
  779           catch(DocumentException de) {
  780               throw new ExceptionConverter(de);
  781           }
  782       }
  783   
  784   //	[L2] DocListener interface
  785       
  786       /**
  787        * Closes the document.
  788        * <B>
  789        * Once all the content has been written in the body, you have to close
  790        * the body. After that nothing can be written to the body anymore.
  791        */  
  792       public void close() {
  793           if (close) {
  794               return;
  795           }
  796           try {
  797           	boolean wasImage = (imageWait != null);
  798               newPage();
  799               if (imageWait != null || wasImage) newPage();
  800               if (annotationsImp.hasUnusedAnnotations())
  801                   throw new RuntimeException("Not all annotations could be added to the document (the document doesn't have enough pages).");
  802               PdfPageEvent pageEvent = writer.getPageEvent();
  803               if (pageEvent != null)
  804                   pageEvent.onCloseDocument(writer, this);
  805               super.close();
  806               
  807               writer.addLocalDestinations(localDestinations);
  808               calculateOutlineCount();
  809               writeOutlines();
  810           }
  811           catch(Exception e) {
  812               throw new ExceptionConverter(e);
  813           }
  814           
  815           writer.close();
  816       }
  817   
  818   //	[L3] DocListener interface
  819       protected int textEmptySize;
  820       
  821       // [C9] Metadata for the page
  822       /** XMP Metadata for the page. */
  823       protected byte[] xmpMetadata = null;
  824   	/**
  825   	 * Use this method to set the XMP Metadata.
  826   	 * @param xmpMetadata The xmpMetadata to set.
  827   	 */
  828   	public void setXmpMetadata(byte[] xmpMetadata) {
  829   		this.xmpMetadata = xmpMetadata;
  830   	}
  831       
  832       /**
  833        * Makes a new page and sends it to the <CODE>PdfWriter</CODE>.
  834        *
  835        * @return a <CODE>boolean</CODE>
  836        * @throws DocumentException on error
  837        */ 
  838       public boolean newPage() {
  839           lastElementType = -1;
  840           if (writer == null || (writer.getDirectContent().size() == 0 && writer.getDirectContentUnder().size() == 0 && (pageEmpty || writer.isPaused()))) {
  841           	setNewPageSizeAndMargins();
  842               return false;
  843           }
  844       	if (!open || close) {
  845       		throw new RuntimeException("The document isn't open.");
  846       	}
  847           PdfPageEvent pageEvent = writer.getPageEvent();
  848           if (pageEvent != null)
  849               pageEvent.onEndPage(writer, this);
  850           
  851           //Added to inform any listeners that we are moving to a new page (added by David Freels)
  852           super.newPage();
  853           
  854           // the following 2 lines were added by Pelikan Stephan
  855           indentation.imageIndentLeft = 0;
  856           indentation.imageIndentRight = 0;
  857           
  858           try {
  859               // we flush the arraylist with recently written lines
  860           	flushLines();
  861           	
  862           	// we prepare the elements of the page dictionary
  863           	
  864           	// [U1] page size and rotation
  865           	int rotation = pageSize.getRotation();
  866           	
  867           	// [C10]
  868           	if (writer.isPdfX()) {
  869           		if (thisBoxSize.containsKey("art") && thisBoxSize.containsKey("trim"))
  870           			throw new PdfXConformanceException("Only one of ArtBox or TrimBox can exist in the page.");
  871           		if (!thisBoxSize.containsKey("art") && !thisBoxSize.containsKey("trim")) {
  872           			if (thisBoxSize.containsKey("crop"))
  873           				thisBoxSize.put("trim", thisBoxSize.get("crop"));
  874           			else
  875           				thisBoxSize.put("trim", new PdfRectangle(pageSize, pageSize.getRotation()));
  876           		}
  877           	}
  878           	
  879           	// [M1]
  880           	pageResources.addDefaultColorDiff(writer.getDefaultColorspace());
  881               if (writer.isRgbTransparencyBlending()) {
  882                   PdfDictionary dcs = new PdfDictionary();
  883                   dcs.put(PdfName.CS, PdfName.DEVICERGB);
  884                   pageResources.addDefaultColorDiff(dcs);
  885               }
  886           	PdfDictionary resources = pageResources.getResources();
  887           	
  888           	// we create the page dictionary
  889           	
  890           	PdfPage page = new PdfPage(new PdfRectangle(pageSize, rotation), thisBoxSize, resources, rotation);
  891   
  892           	// we complete the page dictionary
  893           	
  894               // [C9] if there is XMP data to add: add it
  895               if (xmpMetadata != null) {
  896               	PdfStream xmp = new PdfStream(xmpMetadata);
  897               	xmp.put(PdfName.TYPE, PdfName.METADATA);
  898               	xmp.put(PdfName.SUBTYPE, PdfName.XML);
  899               	PdfEncryption crypto = writer.getEncryption();
  900                   if (crypto != null && !crypto.isMetadataEncrypted()) {
  901                       PdfArray ar = new PdfArray();
  902                       ar.add(PdfName.CRYPT);
  903                       xmp.put(PdfName.FILTER, ar);
  904                   }
  905               	page.put(PdfName.METADATA, writer.addToBody(xmp).getIndirectReference());
  906               }
  907           	
  908           	// [U3] page actions: transition, duration, additional actions
  909           	if (this.transition!=null) {
  910           		page.put(PdfName.TRANS, this.transition.getTransitionDictionary());
  911           		transition = null;
  912           	}
  913           	if (this.duration>0) {
  914           		page.put(PdfName.DUR,new PdfNumber(this.duration));
  915           		duration = 0;
  916           	}
  917           	if (pageAA != null) {
  918           		page.put(PdfName.AA, writer.addToBody(pageAA).getIndirectReference());
  919           		pageAA = null;
  920           	}
  921           	
  922           	// [U4] we add the thumbs
  923           	if (thumb != null) {
  924           		page.put(PdfName.THUMB, thumb);
  925           		thumb = null;
  926           	}
  927           	
  928           	// [U8] we check if the userunit is defined
  929           	if (writer.getUserunit() > 0f) {
  930           		page.put(PdfName.USERUNIT, new PdfNumber(writer.getUserunit()));
  931           	}
  932           	
  933           	// [C5] and [C8] we add the annotations
  934           	if (annotationsImp.hasUnusedAnnotations()) {
  935           		PdfArray array = annotationsImp.rotateAnnotations(writer, pageSize);
  936           		if (array.size() != 0)
  937           			page.put(PdfName.ANNOTS, array);
  938           	}
  939           	
  940           	// [F12] we add tag info
  941           	if (writer.isTagged())
  942   	             page.put(PdfName.STRUCTPARENTS, new PdfNumber(writer.getCurrentPageNumber() - 1));
  943           	
  944           	if (text.size() > textEmptySize)
  945           		text.endText();
  946           	else
  947           		text = null;
  948           	writer.add(page, new PdfContents(writer.getDirectContentUnder(), graphics, text, writer.getDirectContent(), pageSize));
  949           	// we initialize the new page
  950           	initPage();
  951           }
  952           catch(DocumentException de) {
  953           	// maybe this never happens, but it's better to check.
  954           	throw new ExceptionConverter(de);
  955           }
  956           catch (IOException ioe) {
  957               throw new ExceptionConverter(ioe);
  958           }
  959           return true;
  960       }
  961   
  962   //	[L4] DocListener interface
  963       
  964       /**
  965        * Sets the pagesize.
  966        *
  967        * @param pageSize the new pagesize
  968        * @return <CODE>true</CODE> if the page size was set
  969        */  
  970       public boolean setPageSize(Rectangle pageSize) {
  971           if (writer != null && writer.isPaused()) {
  972               return false;
  973           }
  974           nextPageSize = new Rectangle(pageSize);
  975           return true;
  976       }
  977   
  978   //	[L5] DocListener interface
  979   
  980       /** margin in x direction starting from the left. Will be valid in the next page */
  981       protected float nextMarginLeft;
  982       
  983       /** margin in x direction starting from the right. Will be valid in the next page */
  984       protected float nextMarginRight;
  985       
  986       /** margin in y direction starting from the top. Will be valid in the next page */
  987       protected float nextMarginTop;
  988       
  989       /** margin in y direction starting from the bottom. Will be valid in the next page */
  990       protected float nextMarginBottom;
  991       
  992       /**
  993        * Sets the margins.
  994        *
  995        * @param	marginLeft		the margin on the left
  996        * @param	marginRight		the margin on the right
  997        * @param	marginTop		the margin on the top
  998        * @param	marginBottom	the margin on the bottom
  999        * @return	a <CODE>boolean</CODE>
 1000        */
 1001       public boolean setMargins(float marginLeft, float marginRight, float marginTop, float marginBottom) {
 1002           if (writer != null && writer.isPaused()) {
 1003               return false;
 1004           }
 1005           nextMarginLeft = marginLeft;
 1006           nextMarginRight = marginRight;
 1007           nextMarginTop = marginTop;
 1008           nextMarginBottom = marginBottom;
 1009           return true;
 1010       }
 1011   
 1012   //	[L6] DocListener interface
 1013       
 1014       /**
 1015        * @see com.lowagie.text.DocListener#setMarginMirroring(boolean)
 1016        */
 1017       public boolean setMarginMirroring(boolean MarginMirroring) {
 1018           if (writer != null && writer.isPaused()) {
 1019               return false;
 1020           }
 1021           return super.setMarginMirroring(MarginMirroring);
 1022       }
 1023   
 1024   //	[L7] DocListener interface
 1025       
 1026       /**
 1027        * Sets the page number.
 1028        *
 1029        * @param	pageN		the new page number
 1030        */ 
 1031       public void setPageCount(int pageN) {
 1032           if (writer != null && writer.isPaused()) {
 1033               return;
 1034           }
 1035           super.setPageCount(pageN);
 1036       }
 1037   
 1038   //	[L8] DocListener interface
 1039       
 1040       /**
 1041        * Sets the page number to 0.
 1042        */ 
 1043       public void resetPageCount() {
 1044           if (writer != null && writer.isPaused()) {
 1045               return;
 1046           }
 1047           super.resetPageCount();
 1048       }
 1049   
 1050   //	[L9] DocListener interface
 1051       
 1052       /**
 1053        * Changes the header of this document.
 1054        *
 1055        * @param header the new header
 1056        */
 1057       public void setHeader(HeaderFooter header) {
 1058           if (writer != null && writer.isPaused()) {
 1059               return;
 1060           }
 1061           super.setHeader(header);
 1062       }
 1063   
 1064   //	[L10] DocListener interface
 1065       
 1066       /**
 1067        * Resets the header of this document.
 1068        */
 1069       public void resetHeader() {
 1070           if (writer != null && writer.isPaused()) {
 1071               return;
 1072           }
 1073           super.resetHeader();
 1074       }
 1075   
 1076   //	[L11] DocListener interface
 1077   
 1078       /**
 1079        * Changes the footer of this document.
 1080        *
 1081        * @param	footer		the new footer
 1082        */
 1083       public void setFooter(HeaderFooter footer) {
 1084           if (writer != null && writer.isPaused()) {
 1085               return;
 1086           }
 1087           super.setFooter(footer);
 1088       }
 1089   
 1090   //	[L12] DocListener interface
 1091    
 1092       /**
 1093        * Resets the footer of this document.
 1094        */
 1095       public void resetFooter() {
 1096           if (writer != null && writer.isPaused()) {
 1097               return;
 1098           }
 1099           super.resetFooter();
 1100       }
 1101       
 1102   // DOCLISTENER METHODS END
 1103   
 1104       /** Signals that OnOpenDocument should be called. */
 1105       protected boolean firstPageEvent = true;
 1106       
 1107       /**
 1108        * Initializes a page.
 1109        * <P>
 1110        * If the footer/header is set, it is printed.
 1111        * @throws DocumentException on error
 1112        */
 1113       protected void initPage() throws DocumentException { 
 1114           // the pagenumber is incremented
 1115           pageN++;
 1116   
 1117           // initialization of some page objects
 1118           annotationsImp.resetAnnotations();
 1119           pageResources = new PageResources();
 1120           
 1121           writer.resetContent();
 1122           graphics = new PdfContentByte(writer);
 1123           text = new PdfContentByte(writer);
 1124           text.reset();
 1125           text.beginText();
 1126           textEmptySize = text.size();
 1127   
 1128       	markPoint = 0;
 1129           setNewPageSizeAndMargins();
 1130           imageEnd = -1;
 1131           indentation.imageIndentRight = 0;
 1132           indentation.imageIndentLeft = 0;
 1133           indentation.indentBottom = 0;
 1134           indentation.indentTop = 0;
 1135           currentHeight = 0;
 1136           
 1137           // backgroundcolors, etc...
 1138           thisBoxSize = new HashMap(boxSize);
 1139           if (pageSize.getBackgroundColor() != null
 1140           || pageSize.hasBorders()
 1141           || pageSize.getBorderColor() != null) {
 1142               add(pageSize);
 1143           }
 1144   
 1145           float oldleading = leading;
 1146           int oldAlignment = alignment;
 1147           // if there is a footer, the footer is added
 1148           doFooter();
 1149           // we move to the left/top position of the page
 1150           text.moveText(left(), top());
 1151           doHeader();
 1152           pageEmpty = true;
 1153           // if there is an image waiting to be drawn, draw it
 1154           try {
 1155               if (imageWait != null) {
 1156                   add(imageWait);
 1157                   imageWait = null;
 1158               }
 1159           }
 1160           catch(Exception e) {
 1161               throw new ExceptionConverter(e);
 1162           }
 1163           leading = oldleading;
 1164           alignment = oldAlignment;
 1165           carriageReturn();
 1166           
 1167           PdfPageEvent pageEvent = writer.getPageEvent();
 1168           if (pageEvent != null) {
 1169               if (firstPageEvent) {
 1170                   pageEvent.onOpenDocument(writer, this);
 1171               }
 1172               pageEvent.onStartPage(writer, this);
 1173           }
 1174           firstPageEvent = false;
 1175       }
 1176   
 1177       /** The line that is currently being written. */
 1178       protected PdfLine line = null;
 1179       /** The lines that are written until now. */
 1180       protected ArrayList lines = new ArrayList();
 1181       
 1182       /**
 1183        * Adds the current line to the list of lines and also adds an empty line.
 1184        * @throws DocumentException on error
 1185        */
 1186       protected void newLine() throws DocumentException {
 1187           lastElementType = -1;
 1188           carriageReturn();
 1189           if (lines != null && !lines.isEmpty()) {
 1190               lines.add(line);
 1191               currentHeight += line.height();
 1192           }
 1193           line = new PdfLine(indentLeft(), indentRight(), alignment, leading);
 1194       }
 1195       
 1196       /**
 1197        * If the current line is not empty or null, it is added to the arraylist
 1198        * of lines and a new empty line is added.
 1199        * @throws DocumentException on error
 1200        */  
 1201       protected void carriageReturn() {
 1202           // the arraylist with lines may not be null
 1203           if (lines == null) {
 1204               lines = new ArrayList();
 1205           }
 1206           // If the current line is not null
 1207           if (line != null) {
 1208               // we check if the end of the page is reached (bugfix by Francois Gravel)
 1209               if (currentHeight + line.height() + leading < indentTop() - indentBottom()) {
 1210                   // if so nonempty lines are added and the height is augmented
 1211                   if (line.size() > 0) {
 1212                       currentHeight += line.height();
 1213                       lines.add(line);
 1214                       pageEmpty = false;
 1215                   }
 1216               }
 1217               // if the end of the line is reached, we start a new page
 1218               else {
 1219                   newPage();
 1220               }
 1221           }
 1222           if (imageEnd > -1 && currentHeight > imageEnd) {
 1223               imageEnd = -1;
 1224               indentation.imageIndentRight = 0;
 1225               indentation.imageIndentLeft = 0;
 1226           }
 1227           // a new current line is constructed
 1228           line = new PdfLine(indentLeft(), indentRight(), alignment, leading);
 1229       }
 1230       
 1231       /**
 1232        * Gets the current vertical page position.
 1233        * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects 
 1234        *   for elements that do not terminate the lines they've started because those lines will get
 1235        *   terminated. 
 1236        * @return The current vertical page position.
 1237        */
 1238       public float getVerticalPosition(boolean ensureNewLine) {
 1239           // ensuring that a new line has been started.
 1240           if (ensureNewLine) {
 1241             ensureNewLine();
 1242           }
 1243           return top() -  currentHeight - indentation.indentTop;
 1244       }
 1245   
 1246       /** Holds the type of the last element, that has been added to the document. */
 1247       protected int lastElementType = -1;
 1248       
 1249       /**
 1250        * Ensures that a new line has been started. 
 1251        */
 1252       protected void ensureNewLine() {
 1253         try {
 1254           if ((lastElementType == Element.PHRASE) || 
 1255               (lastElementType == Element.CHUNK)) {
 1256             newLine();
 1257             flushLines();
 1258           }
 1259         } catch (DocumentException ex) {
 1260           throw new ExceptionConverter(ex);
 1261           }
 1262       }
 1263       
 1264       /**
 1265        * Writes all the lines to the text-object.
 1266        *
 1267        * @return the displacement that was caused
 1268        * @throws DocumentException on error
 1269        */ 
 1270       protected float flushLines() throws DocumentException {
 1271           // checks if the ArrayList with the lines is not null
 1272           if (lines == null) {
 1273               return 0;
 1274           }
 1275           // checks if a new Line has to be made.
 1276           if (line != null && line.size() > 0) {
 1277               lines.add(line);
 1278               line = new PdfLine(indentLeft(), indentRight(), alignment, leading);
 1279           }
 1280           
 1281           // checks if the ArrayList with the lines is empty
 1282           if (lines.isEmpty()) {
 1283               return 0;
 1284           }
 1285           
 1286           // initialization of some parameters
 1287           Object currentValues[] = new Object[2];
 1288           PdfFont currentFont = null;
 1289           float displacement = 0;
 1290           PdfLine l;
 1291           Float lastBaseFactor = new Float(0);
 1292           currentValues[1] = lastBaseFactor;
 1293           // looping over all the lines
 1294           for (Iterator i = lines.iterator(); i.hasNext(); ) {
 1295                           
 1296               // this is a line in the loop
 1297               l = (PdfLine) i.next();
 1298               
 1299               float moveTextX = l.indentLeft() - indentLeft() + indentation.indentLeft + indentation.listIndentLeft + indentation.sectionIndentLeft;
 1300               text.moveText(moveTextX, -l.height());
 1301               // is the line preceded by a symbol?
 1302               if (l.listSymbol() != null) {
 1303                   ColumnText.showTextAligned(graphics, Element.ALIGN_LEFT, new Phrase(l.listSymbol()), text.getXTLM() - l.listIndent(), text.getYTLM(), 0);
 1304               }
 1305               
 1306               currentValues[0] = currentFont;
 1307               
 1308               writeLineToContent(l, text, graphics, currentValues, writer.getSpaceCharRatio());
 1309               
 1310               currentFont = (PdfFont)currentValues[0];
 1311               displacement += l.height();
 1312               text.moveText(-moveTextX, 0);
 1313               
 1314           }
 1315           lines = new ArrayList();
 1316           return displacement;
 1317       }
 1318       
 1319       /** The characters to be applied the hanging punctuation. */
 1320       static final String hangingPunctuation = ".,;:'";
 1321       
 1322       /**
 1323        * Writes a text line to the document. It takes care of all the attributes.
 1324        * <P>
 1325        * Before entering the line position must have been established and the
 1326        * <CODE>text</CODE> argument must be in text object scope (<CODE>beginText()</CODE>).
 1327        * @param line the line to be written
 1328        * @param text the <CODE>PdfContentByte</CODE> where the text will be written to
 1329        * @param graphics the <CODE>PdfContentByte</CODE> where the graphics will be written to
 1330        * @param currentValues the current font and extra spacing values
 1331        * @param ratio
 1332        * @throws DocumentException on error
 1333        */
 1334       void writeLineToContent(PdfLine line, PdfContentByte text, PdfContentByte graphics, Object currentValues[], float ratio)  throws DocumentException {
 1335           PdfFont currentFont = (PdfFont)(currentValues[0]);
 1336           float lastBaseFactor = ((Float)(currentValues[1])).floatValue();
 1337           PdfChunk chunk;
 1338           int numberOfSpaces;
 1339           int lineLen;
 1340           boolean isJustified;
 1341           float hangingCorrection = 0;
 1342           float hScale = 1;
 1343           float lastHScale = Float.NaN;
 1344           float baseWordSpacing = 0;
 1345           float baseCharacterSpacing = 0;
 1346           float glueWidth = 0;
 1347           
 1348           numberOfSpaces = line.numberOfSpaces();
 1349           lineLen = line.GetLineLengthUtf32();
 1350           // does the line need to be justified?
 1351           isJustified = line.hasToBeJustified() && (numberOfSpaces != 0 || lineLen > 1);
 1352           int separatorCount = line.getSeparatorCount();
 1353           if (separatorCount > 0) {
 1354           	glueWidth = line.widthLeft() / separatorCount;
 1355           }
 1356           else if (isJustified) {
 1357               if (line.isNewlineSplit() && line.widthLeft() >= (lastBaseFactor * (ratio * numberOfSpaces + lineLen - 1))) {
 1358                   if (line.isRTL()) {
 1359                       text.moveText(line.widthLeft() - lastBaseFactor * (ratio * numberOfSpaces + lineLen - 1), 0);
 1360                   }
 1361                   baseWordSpacing = ratio * lastBaseFactor;
 1362                   baseCharacterSpacing = lastBaseFactor;
 1363               }
 1364               else {
 1365                   float width = line.widthLeft();
 1366                   PdfChunk last = line.getChunk(line.size() - 1);
 1367                   if (last != null) {
 1368                       String s = last.toString();
 1369                       char c;
 1370                       if (s.length() > 0 && hangingPunctuation.indexOf((c = s.charAt(s.length() - 1))) >= 0) {
 1371                           float oldWidth = width;
 1372                           width += last.font().width(c) * 0.4f;
 1373                           hangingCorrection = width - oldWidth;
 1374                       }
 1375                   }
 1376                   float baseFactor = width / (ratio * numberOfSpaces + lineLen - 1);
 1377                   baseWordSpacing = ratio * baseFactor;
 1378                   baseCharacterSpacing = baseFactor;
 1379                   lastBaseFactor = baseFactor;
 1380               }
 1381           }
 1382           
 1383           int lastChunkStroke = line.getLastStrokeChunk();
 1384           int chunkStrokeIdx = 0;
 1385           float xMarker = text.getXTLM();
 1386           float baseXMarker = xMarker;
 1387           float yMarker = text.getYTLM();
 1388           boolean adjustMatrix = false;
 1389           float tabPosition = 0;
 1390           
 1391           // looping over all the chunks in 1 line
 1392           for (Iterator j = line.iterator(); j.hasNext(); ) {
 1393               chunk = (PdfChunk) j.next();
 1394               Color color = chunk.color();
 1395               hScale = 1;
 1396               
 1397               if (chunkStrokeIdx <= lastChunkStroke) {
 1398                   float width;
 1399                   if (isJustified) {
 1400                       width = chunk.getWidthCorrected(baseCharacterSpacing, baseWordSpacing);
 1401                   }
 1402                   else {
 1403                       width = chunk.width();
 1404                   }
 1405                   if (chunk.isStroked()) {
 1406                       PdfChunk nextChunk = line.getChunk(chunkStrokeIdx + 1);
 1407                       if (chunk.isSeparator()) {
 1408                       	width = glueWidth;
 1409                       	Object[] sep = (Object[])chunk.getAttribute(Chunk.SEPARATOR);
 1410                           DrawInterface di = (DrawInterface)sep[0];
 1411                           Boolean vertical = (Boolean)sep[1];
 1412                           float fontSize = chunk.font().size();
 1413                           float ascender = chunk.font().getFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
 1414                           float descender = chunk.font().getFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
 1415                           if (vertical.booleanValue()) {
 1416                           	di.draw(graphics, baseXMarker, yMarker + descender, baseXMarker + line.getOriginalWidth(), ascender - descender, yMarker);     	
 1417                           }
 1418                           else {
 1419                           	di.draw(graphics, xMarker, yMarker + descender, xMarker + width, ascender - descender, yMarker);
 1420                           }
 1421                       }
 1422                       if (chunk.isTab()) {
 1423                       	Object[] tab = (Object[])chunk.getAttribute(Chunk.TAB);
 1424                           DrawInterface di = (DrawInterface)tab[0];
 1425                           tabPosition = ((Float)tab[1]).floatValue() + ((Float)tab[3]).floatValue();
 1426                           float fontSize = chunk.font().size();
 1427                           float ascender = chunk.font().getFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
 1428                           float descender = chunk.font().getFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
 1429                           if (tabPosition > xMarker) {
 1430                           	di.draw(graphics, xMarker, yMarker + descender, tabPosition, ascender - descender, yMarker);
 1431                           }
 1432                           float tmp = xMarker;
 1433                       	xMarker = tabPosition;
 1434                       	tabPosition = tmp;
 1435                       }
 1436                       if (chunk.isAttribute(Chunk.BACKGROUND)) {
 1437                           float subtract = lastBaseFactor;
 1438                           if (nextChunk != null && nextChunk.isAttribute(Chunk.BACKGROUND))
 1439                               subtract = 0;
 1440                           if (nextChunk == null)
 1441                               subtract += hangingCorrection;
 1442                           float fontSize = chunk.font().size();
 1443                           float ascender = chunk.font().getFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
 1444                           float descender = chunk.font().getFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
 1445                           Object bgr[] = (Object[])chunk.getAttribute(Chunk.BACKGROUND);
 1446                           graphics.setColorFill((Color)bgr[0]);
 1447                           float extra[] = (float[])bgr[1];
 1448                           graphics.rectangle(xMarker - extra[0],
 1449                               yMarker + descender - extra[1] + chunk.getTextRise(),
 1450                               width - subtract + extra[0] + extra[2],
 1451                               ascender - descender + extra[1] + extra[3]);
 1452                           graphics.fill();
 1453                           graphics.setGrayFill(0);
 1454                       }
 1455                       if (chunk.isAttribute(Chunk.UNDERLINE)) {
 1456                           float subtract = lastBaseFactor;
 1457                           if (nextChunk != null && nextChunk.isAttribute(Chunk.UNDERLINE))
 1458                               subtract = 0;
 1459                           if (nextChunk == null)
 1460                               subtract += hangingCorrection;
 1461                           Object unders[][] = (Object[][])chunk.getAttribute(Chunk.UNDERLINE);
 1462                           Color scolor = null;
 1463                           for (int k = 0; k < unders.length; ++k) {
 1464                               Object obj[] = unders[k];
 1465                               scolor = (Color)obj[0];
 1466                               float ps[] = (float[])obj[1];
 1467                               if (scolor == null)
 1468                                   scolor = color;
 1469                               if (scolor != null)
 1470                                   graphics.setColorStroke(scolor);
 1471                               float fsize = chunk.font().size();
 1472                               graphics.setLineWidth(ps[0] + fsize * ps[1]);
 1473                               float shift = ps[2] + fsize * ps[3];
 1474                               int cap2 = (int)ps[4];
 1475                               if (cap2 != 0)
 1476                                   graphics.setLineCap(cap2);
 1477                               graphics.moveTo(xMarker, yMarker + shift);
 1478                               graphics.lineTo(xMarker + width - subtract, yMarker + shift);
 1479                               graphics.stroke();
 1480                               if (scolor != null)
 1481                                   graphics.resetGrayStroke();
 1482                               if (cap2 != 0)
 1483                                   graphics.setLineCap(0);
 1484                           }
 1485                           graphics.setLineWidth(1);
 1486                       }
 1487                       if (chunk.isAttribute(Chunk.ACTION)) {
 1488                           float subtract = lastBaseFactor;
 1489                           if (nextChunk != null && nextChunk.isAttribute(Chunk.ACTION))
 1490                               subtract = 0;
 1491                           if (nextChunk == null)
 1492                               subtract += hangingCorrection;
 1493                           text.addAnnotation(new PdfAnnotation(writer, xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size(), (PdfAction)chunk.getAttribute(Chunk.ACTION)));
 1494                       }
 1495                       if (chunk.isAttribute(Chunk.REMOTEGOTO)) {
 1496                           float subtract = lastBaseFactor;
 1497                           if (nextChunk != null && nextChunk.isAttribute(Chunk.REMOTEGOTO))
 1498                               subtract = 0;
 1499                           if (nextChunk == null)
 1500                               subtract += hangingCorrection;
 1501                           Object obj[] = (Object[])chunk.getAttribute(Chunk.REMOTEGOTO);
 1502                           String filename = (String)obj[0];
 1503                           if (obj[1] instanceof String)
 1504                               remoteGoto(filename, (String)obj[1], xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
 1505                           else
 1506                               remoteGoto(filename, ((Integer)obj[1]).intValue(), xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
 1507                       }
 1508                       if (chunk.isAttribute(Chunk.LOCALGOTO)) {
 1509                           float subtract = lastBaseFactor;
 1510                           if (nextChunk != null && nextChunk.isAttribute(Chunk.LOCALGOTO))
 1511                               subtract = 0;
 1512                           if (nextChunk == null)
 1513                               subtract += hangingCorrection;
 1514                           localGoto((String)chunk.getAttribute(Chunk.LOCALGOTO), xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
 1515                       }
 1516                       if (chunk.isAttribute(Chunk.LOCALDESTINATION)) {
 1517                           float subtract = lastBaseFactor;
 1518                           if (nextChunk != null && nextChunk.isAttribute(Chunk.LOCALDESTINATION))
 1519                               subtract = 0;
 1520                           if (nextChunk == null)
 1521                               subtract += hangingCorrection;
 1522                           localDestination((String)chunk.getAttribute(Chunk.LOCALDESTINATION), new PdfDestination(PdfDestination.XYZ, xMarker, yMarker + chunk.font().size(), 0));
 1523                       }
 1524                       if (chunk.isAttribute(Chunk.GENERICTAG)) {
 1525                           float subtract = lastBaseFactor;
 1526                           if (nextChunk != null && nextChunk.isAttribute(Chunk.GENERICTAG))
 1527                               subtract = 0;
 1528                           if (nextChunk == null)
 1529                               subtract += hangingCorrection;
 1530                           Rectangle rect = new Rectangle(xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
 1531                           PdfPageEvent pev = writer.getPageEvent();
 1532                           if (pev != null)
 1533                               pev.onGenericTag(writer, this, rect, (String)chunk.getAttribute(Chunk.GENERICTAG));
 1534                       }
 1535                       if (chunk.isAttribute(Chunk.PDFANNOTATION)) {
 1536                           float subtract = lastBaseFactor;
 1537                           if (nextChunk != null && nextChunk.isAttribute(Chunk.PDFANNOTATION))
 1538                               subtract = 0;
 1539                           if (nextChunk == null)
 1540                               subtract += hangingCorrection;
 1541                           float fontSize = chunk.font().size();
 1542                           float ascender = chunk.font().getFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
 1543                           float descender = chunk.font().getFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
 1544                           PdfAnnotation annot = PdfFormField.shallowDuplicate((PdfAnnotation)chunk.getAttribute(Chunk.PDFANNOTATION));
 1545                           annot.put(PdfName.RECT, new PdfRectangle(xMarker, yMarker + descender, xMarker + width - subtract, yMarker + ascender));
 1546                           text.addAnnotation(annot);
 1547                       }
 1548                       float params[] = (float[])chunk.getAttribute(Chunk.SKEW);
 1549                       Float hs = (Float)chunk.getAttribute(Chunk.HSCALE);
 1550                       if (params != null || hs != null) {
 1551                           float b = 0, c = 0;
 1552                           if (params != null) {
 1553                               b = params[0];
 1554                               c = params[1];
 1555                           }
 1556                           if (hs != null)
 1557                               hScale = hs.floatValue();
 1558                           text.setTextMatrix(hScale, b, c, 1, xMarker, yMarker);
 1559                       }
 1560                       if (chunk.isImage()) {
 1561                           Image image = chunk.getImage();
 1562                           float matrix[] = image.matrix();
 1563                           matrix[Image.CX] = xMarker + chunk.getImageOffsetX() - matrix[Image.CX];
 1564                           matrix[Image.CY] = yMarker + chunk.getImageOffsetY() - matrix[Image.CY];
 1565                           graphics.addImage(image, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
 1566                           text.moveText(xMarker + lastBaseFactor + image.getScaledWidth() - text.getXTLM(), 0);
 1567                       }
 1568                   }
 1569                   xMarker += width;
 1570                   ++chunkStrokeIdx;
 1571               }
 1572   
 1573               if (chunk.font().compareTo(currentFont) != 0) {
 1574                   currentFont = chunk.font();
 1575                   text.setFontAndSize(currentFont.getFont(), currentFont.size());
 1576               }
 1577               float rise = 0;
 1578               Object textRender[] = (Object[])chunk.getAttribute(Chunk.TEXTRENDERMODE);
 1579               int tr = 0;
 1580               float strokeWidth = 1;
 1581               Color strokeColor = null;
 1582               Float fr = (Float)chunk.getAttribute(Chunk.SUBSUPSCRIPT);
 1583               if (textRender != null) {
 1584                   tr = ((Integer)textRender[0]).intValue() & 3;
 1585                   if (tr != PdfContentByte.TEXT_RENDER_MODE_FILL)
 1586                       text.setTextRenderingMode(tr);
 1587                   if (tr == PdfContentByte.TEXT_RENDER_MODE_STROKE || tr == PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE) {
 1588                       strokeWidth = ((Float)textRender[1]).floatValue();
 1589                       if (strokeWidth != 1)
 1590                           text.setLineWidth(strokeWidth);
 1591                       strokeColor = (Color)textRender[2];
 1592                       if (strokeColor == null)
 1593                           strokeColor = color;
 1594                       if (strokeColor != null)
 1595                           text.setColorStroke(strokeColor);
 1596                   }
 1597               }
 1598               if (fr != null)
 1599                   rise = fr.floatValue();
 1600               if (color != null)
 1601                   text.setColorFill(color);
 1602               if (rise != 0)
 1603                   text.setTextRise(rise);
 1604               if (chunk.isImage()) {
 1605                   adjustMatrix = true;
 1606               }
 1607               else if (chunk.isHorizontalSeparator()) {
 1608               	PdfTextArray array = new PdfTextArray();
 1609               	array.add(-glueWidth * 1000f / chunk.font.size() / hScale);
 1610               	text.showText(array);
 1611               }
 1612               else if (chunk.isTab()) {
 1613               	PdfTextArray array = new PdfTextArray();
 1614               	array.add((tabPosition - xMarker) * 1000f / chunk.font.size() / hScale);
 1615               	text.showText(array);
 1616               }
 1617               // If it is a CJK chunk or Unicode TTF we will have to simulate the
 1618               // space adjustment.
 1619               else if (isJustified && numberOfSpaces > 0 && chunk.isSpecialEncoding()) {
 1620                   if (hScale != lastHScale) {
 1621                       lastHScale = hScale;
 1622                       text.setWordSpacing(baseWordSpacing / hScale);
 1623                       text.setCharacterSpacing(baseCharacterSpacing / hScale);
 1624                   }
 1625                   String s = chunk.toString();
 1626                   int idx = s.indexOf(' ');
 1627                   if (idx < 0)
 1628                       text.showText(s);
 1629                   else {
 1630                       float spaceCorrection = - baseWordSpacing * 1000f / chunk.font.size() / hScale;
 1631                       PdfTextArray textArray = new PdfTextArray(s.substring(0, idx));
 1632                       int lastIdx = idx;
 1633                       while ((idx = s.indexOf(' ', lastIdx + 1)) >= 0) {
 1634                           textArray.add(spaceCorrection);
 1635                           textArray.add(s.substring(lastIdx, idx));
 1636                           lastIdx = idx;
 1637                       }
 1638                       textArray.add(spaceCorrection);
 1639                       textArray.add(s.substring(lastIdx));
 1640                       text.showText(textArray);
 1641                   }
 1642               }
 1643               else {
 1644                   if (isJustified && hScale != lastHScale) {
 1645                       lastHScale = hScale;
 1646                       text.setWordSpacing(baseWordSpacing / hScale);
 1647                       text.setCharacterSpacing(baseCharacterSpacing / hScale);
 1648                   }
 1649                   text.showText(chunk.toString());
 1650               }
 1651               
 1652               if (rise != 0)
 1653                   text.setTextRise(0);
 1654               if (color != null)
 1655                   text.resetRGBColorFill();
 1656               if (tr != PdfContentByte.TEXT_RENDER_MODE_FILL)
 1657                   text.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);
 1658               if (strokeColor != null)
 1659                   text.resetRGBColorStroke();
 1660               if (strokeWidth != 1)
 1661                   text.setLineWidth(1);            
 1662               if (chunk.isAttribute(Chunk.SKEW) || chunk.isAttribute(Chunk.HSCALE)) {
 1663                   adjustMatrix = true;
 1664                   text.setTextMatrix(xMarker, yMarker);
 1665               }
 1666           }
 1667           if (isJustified) {
 1668               text.setWordSpacing(0);
 1669               text.setCharacterSpacing(0);
 1670               if (line.isNewlineSplit())
 1671                   lastBaseFactor = 0;
 1672           }
 1673           if (adjustMatrix)
 1674               text.moveText(baseXMarker - text.getXTLM(), 0);
 1675           currentValues[0] = currentFont;
 1676           currentValues[1] = new Float(lastBaseFactor);
 1677       }
 1678   
 1679       protected Indentation indentation = new Indentation();
 1680   
 1681       /**
 1682        * @since	2.0.8 (PdfDocument was package-private before)
 1683        */
 1684       public static class Indentation {
 1685           
 1686           /** This represents the current indentation of the PDF Elements on the left side. */
 1687           float indentLeft = 0;
 1688           
 1689           /** Indentation to the left caused by a section. */
 1690           float sectionIndentLeft = 0;
 1691           
 1692           /** This represents the current indentation of the PDF Elements on the left side. */
 1693           float listIndentLeft = 0;
 1694           
 1695           /** This is the indentation caused by an image on the left. */
 1696           float imageIndentLeft = 0;
 1697           
 1698           /** This represents the current indentation of the PDF Elements on the right side. */
 1699           float indentRight = 0;
 1700           
 1701           /** Indentation to the right caused by a section. */
 1702           float sectionIndentRight = 0;
 1703           
 1704           /** This is the indentation caused by an image on the right. */
 1705           float imageIndentRight = 0;
 1706           
 1707           /** This represents the current indentation of the PDF Elements on the top side. */
 1708           float indentTop = 0;
 1709           
 1710           /** This represents the current indentation of the PDF Elements on the bottom side. */
 1711           float indentBottom = 0;
 1712       }
 1713       
 1714       /**
 1715        * Gets the indentation on the left side.
 1716        *
 1717        * @return	a margin
 1718        */
 1719       
 1720       protected float indentLeft() {
 1721           return left(indentation.indentLeft + indentation.listIndentLeft + indentation.imageIndentLeft + indentation.sectionIndentLeft);
 1722       }
 1723       
 1724       /**
 1725        * Gets the indentation on the right side.
 1726        *
 1727        * @return	a margin
 1728        */
 1729       
 1730       protected float indentRight() {
 1731           return right(indentation.indentRight + indentation.sectionIndentRight + indentation.imageIndentRight);
 1732       }
 1733       
 1734       /**
 1735        * Gets the indentation on the top side.
 1736        *
 1737        * @return	a margin
 1738        */
 1739       
 1740       protected float indentTop() {
 1741           return top(indentation.indentTop);
 1742       }
 1743       
 1744       /**
 1745        * Gets the indentation on the bottom side.
 1746        *
 1747        * @return	a margin
 1748        */
 1749       
 1750       float indentBottom() {
 1751           return bottom(indentation.indentBottom);
 1752       }
 1753       
 1754       /**
 1755        * Adds extra space.
 1756        * This method should probably be rewritten.
 1757        */
 1758       protected void addSpacing(float extraspace, float oldleading, Font f) {
 1759       	if (extraspace == 0) return;
 1760       	if (pageEmpty) return;
 1761       	if (currentHeight + line.height() + leading > indentTop() - indentBottom()) return;
 1762           leading = extraspace;
 1763           carriageReturn();
 1764           if (f.isUnderlined() || f.isStrikethru()) {
 1765               f = new Font(f);
 1766               int style = f.getStyle();
 1767               style &= ~Font.UNDERLINE;
 1768               style &= ~Font.STRIKETHRU;
 1769               f.setStyle(Font.UNDEFINED);
 1770               f.setStyle(style);
 1771           }
 1772           Chunk space = new Chunk(" ", f);
 1773           space.process(this);
 1774           carriageReturn();
 1775           leading = oldleading;
 1776       }
 1777       
 1778   //	Info Dictionary and Catalog
 1779       
 1780       /** some meta information about the Document. */
 1781       protected PdfInfo info = new PdfInfo();
 1782       
 1783       /**
 1784        * Gets the <CODE>PdfInfo</CODE>-object.
 1785        *
 1786        * @return	<CODE>PdfInfo</COPE>
 1787        */
 1788       
 1789       PdfInfo getInfo() {
 1790           return info;
 1791       }
 1792       
 1793       /**
 1794        * Gets the <CODE>PdfCatalog</CODE>-object.
 1795        *
 1796        * @param pages an indirect reference to this document pages
 1797        * @return <CODE>PdfCatalog</CODE>
 1798        */
 1799       
 1800       PdfCatalog getCatalog(PdfIndirectReference pages) {
 1801           PdfCatalog catalog = new PdfCatalog(pages, writer);
 1802           
 1803           // [C1] outlines
 1804           if (rootOutline.getKids().size() > 0) {
 1805               catalog.put(PdfName.PAGEMODE, PdfName.USEOUTLINES);
 1806               catalog.put(PdfName.OUTLINES, rootOutline.indirectReference());
 1807           }
 1808           
 1809           // [C2] version
 1810           writer.getPdfVersion().addToCatalog(catalog);
 1811           
 1812           // [C3] preferences
 1813           viewerPreferences.addToCatalog(catalog);
 1814           
 1815           // [C4] pagelabels
 1816           if (pageLabels != null) {
 1817               catalog.put(PdfName.PAGELABELS, pageLabels.getDictionary(writer));
 1818           }
 1819           
 1820           // [C5] named objects
 1821           catalog.addNames(localDestinations, getDocumentLevelJS(), documentFileAttachment, writer);
 1822           
 1823           // [C6] actions
 1824           if (openActionName != null) {
 1825               PdfAction action = getLocalGotoAction(openActionName);
 1826               catalog.setOpenAction(action);
 1827           }
 1828           else if (openActionAction != null)
 1829               catalog.setOpenAction(openActionAction);
 1830           if (additionalActions != null)   {
 1831               catalog.setAdditionalActions(additionalActions);
 1832           }
 1833           
 1834           // [C7] portable collections
 1835           if (collection != null) {
 1836           	catalog.put(PdfName.COLLECTION, collection);
 1837           }
 1838   
 1839           // [C8] AcroForm
 1840           if (annotationsImp.hasValidAcroForm()) {
 1841               try {
 1842                   catalog.put(PdfName.ACROFORM, writer.addToBody(annotationsImp.getAcroForm()).getIndirectReference());
 1843               }
 1844               catch (IOException e) {
 1845                   throw new ExceptionConverter(e);
 1846               }
 1847           }
 1848           
 1849           return catalog;
 1850       }
 1851       
 1852   //	[C1] outlines
 1853       
 1854       /** This is the root outline of the document. */
 1855       protected PdfOutline rootOutline;
 1856       
 1857       /** This is the current <CODE>PdfOutline</CODE> in the hierarchy of outlines. */
 1858       protected PdfOutline currentOutline;
 1859       
 1860       /**
 1861        * Adds a named outline to the document .
 1862        * @param outline the outline to be added
 1863        * @param name the name of this local destination
 1864        */
 1865       void addOutline(PdfOutline outline, String name) {
 1866           localDestination(name, outline.getPdfDestination());
 1867       }
 1868       
 1869       /**
 1870        * Gets the root outline. All the outlines must be created with a parent.
 1871        * The first level is created with this outline.
 1872        * @return the root outline
 1873        */
 1874       public PdfOutline getRootOutline() {
 1875           return rootOutline;
 1876       }
 1877   
 1878       
 1879       /**
 1880        * Updates the count in the outlines.
 1881        */
 1882       void calculateOutlineCount() {
 1883           if (rootOutline.getKids().size() == 0)
 1884               return;
 1885           traverseOutlineCount(rootOutline);
 1886       }
 1887       
 1888       /**
 1889        * Recursive method to update the count in the outlines.
 1890        */
 1891       void traverseOutlineCount(PdfOutline outline) {
 1892           ArrayList kids = outline.getKids();
 1893           PdfOutline parent = outline.parent();
 1894           if (kids.isEmpty()) {
 1895               if (parent != null) {
 1896                   parent.setCount(parent.getCount() + 1);
 1897               }
 1898           }
 1899           else {
 1900               for (int k = 0; k < kids.size(); ++k) {
 1901                   traverseOutlineCount((PdfOutline)kids.get(k));
 1902               }
 1903               if (parent != null) {
 1904                   if (outline.isOpen()) {
 1905                       parent.setCount(outline.getCount() + parent.getCount() + 1);
 1906                   }
 1907                   else {
 1908                       parent.setCount(parent.getCount() + 1);
 1909                       outline.setCount(-outline.getCount());
 1910                   }
 1911               }
 1912           }
 1913       }
 1914       
 1915       /**
 1916        * Writes the outline tree to the body of the PDF document.
 1917        */
 1918       void writeOutlines() throws IOException {
 1919           if (rootOutline.getKids().size() == 0)
 1920               return;
 1921           outlineTree(rootOutline);
 1922           writer.addToBody(rootOutline, rootOutline.indirectReference());
 1923       }
 1924       
 1925       /**
 1926        * Recursive method used to write outlines.
 1927        */
 1928       void outlineTree(PdfOutline outline) throws IOException {
 1929           outline.setIndirectReference(writer.getPdfIndirectReference());
 1930           if (outline.parent() != null)
 1931               outline.put(PdfName.PARENT, outline.parent().indirectReference());
 1932           ArrayList kids = outline.getKids();
 1933           int size = kids.size();
 1934           for (int k = 0; k < size; ++k)
 1935               outlineTree((PdfOutline)kids.get(k));
 1936           for (int k = 0; k < size; ++k) {
 1937               if (k > 0)
 1938                   ((PdfOutline)kids.get(k)).put(PdfName.PREV, ((PdfOutline)kids.get(k - 1)).indirectReference());
 1939               if (k < size - 1)
 1940                   ((PdfOutline)kids.get(k)).put(PdfName.NEXT, ((PdfOutline)kids.get(k + 1)).indirectReference());
 1941           }
 1942           if (size > 0) {
 1943               outline.put(PdfName.FIRST, ((PdfOutline)kids.get(0)).indirectReference());
 1944               outline.put(PdfName.LAST, ((PdfOutline)kids.get(size - 1)).indirectReference());
 1945           }
 1946           for (int k = 0; k < size; ++k) {
 1947               PdfOutline kid = (PdfOutline)kids.get(k);
 1948               writer.addToBody(kid, kid.indirectReference());
 1949           }
 1950       }
 1951       
 1952   //  [C3] PdfViewerPreferences interface
 1953   	
 1954   	/** Contains the Viewer preferences of this PDF document. */
 1955       protected PdfViewerPreferencesImp viewerPreferences = new PdfViewerPreferencesImp();
 1956       /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#setViewerPreferences(int) */
 1957       void setViewerPreferences(int preferences) {
 1958           this.viewerPreferences.setViewerPreferences(preferences);
 1959       }
 1960   
 1961       /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#addViewerPreference(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfObject) */
 1962       void addViewerPreference(PdfName key, PdfObject value) {
 1963       	this.viewerPreferences.addViewerPreference(key, value);
 1964       }
 1965    
 1966   //	[C4] Page labels
 1967   
 1968       protected PdfPageLabels pageLabels;
 1969       /**
 1970        * Sets the page labels
 1971        * @param pageLabels the page labels
 1972        */
 1973       void setPageLabels(PdfPageLabels pageLabels) {
 1974           this.pageLabels = pageLabels;
 1975       }
 1976       
 1977   //	[C5] named objects: local destinations, javascript, embedded files
 1978       
 1979       /**
 1980        * Implements a link to other part of the document. The jump will
 1981        * be made to a local destination with the same name, that must exist.
 1982        * @param name the name for this link
 1983        * @param llx the lower left x corner of the activation area
 1984        * @param lly the lower left y corner of the activation area
 1985        * @param urx the upper right x corner of the activation area
 1986        * @param ury the upper right y corner of the activation area
 1987        */
 1988       void localGoto(String name, float llx, float lly, float urx, float ury) {
 1989           PdfAction action = getLocalGotoAction(name);
 1990           annotationsImp.addPlainAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, action));
 1991       }
 1992       
 1993       /**
 1994        * Implements a link to another document.
 1995        * @param filename the filename for the remote document
 1996        * @param name the name to jump to
 1997        * @param llx the lower left x corner of the activation area
 1998        * @param lly the lower left y corner of the activation area
 1999        * @param urx the upper right x corner of the activation area
 2000        * @param ury the upper right y corner of the activation area
 2001        */
 2002       void remoteGoto(String filename, String name, float llx, float lly, float urx, float ury) {
 2003           annotationsImp.addPlainAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, new PdfAction(filename, name)));
 2004       }
 2005       
 2006       /**
 2007        * Implements a link to another document.
 2008        * @param filename the filename for the remote document
 2009        * @param page the page to jump to
 2010        * @param llx the lower left x corner of the activation area
 2011        * @param lly the lower left y corner of the activation area
 2012        * @param urx the upper right x corner of the activation area
 2013        * @param ury the upper right y corner of the activation area
 2014        */
 2015       void remoteGoto(String filename, int page, float llx, float lly, float urx, float ury) {
 2016           addAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, new PdfAction(filename, page)));
 2017       }
 2018       
 2019       /** Implements an action in an area.
 2020        * @param action the <CODE>PdfAction</CODE>
 2021        * @param llx the lower left x corner of the activation area
 2022        * @param lly the lower left y corner of the activation area
 2023        * @param urx the upper right x corner of the activation area
 2024        * @param ury the upper right y corner of the activation area
 2025        */
 2026       void setAction(PdfAction action, float llx, float lly, float urx, float ury) {
 2027           addAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, action));
 2028       }
 2029       
 2030       /**
 2031        * Stores the destinations keyed by name. Value is
 2032        * <CODE>Object[]{PdfAction,PdfIndirectReference,PdfDestintion}</CODE>.
 2033        */
 2034       protected TreeMap localDestinations = new TreeMap();
 2035       
 2036       PdfAction getLocalGotoAction(String name) {
 2037           PdfAction action;
 2038           Object obj[] = (Object[])localDestinations.get(name);
 2039           if (obj == null)
 2040               obj = new Object[3];
 2041           if (obj[0] == null) {
 2042               if (obj[1] == null) {
 2043                   obj[1] = writer.getPdfIndirectReference();
 2044               }
 2045               action = new PdfAction((PdfIndirectReference)obj[1]);
 2046               obj[0] = action;
 2047               localDestinations.put(name, obj);
 2048           }
 2049           else {
 2050               action = (PdfAction)obj[0];
 2051           }
 2052           return action;
 2053       }
 2054       
 2055       /**
 2056        * The local destination to where a local goto with the same
 2057        * name will jump to.
 2058        * @param name the name of this local destination
 2059        * @param destination the <CODE>PdfDestination</CODE> with the jump coordinates
 2060        * @return <CODE>true</CODE> if the local destination was added,
 2061        * <CODE>false</CODE> if a local destination with the same name
 2062        * already existed
 2063        */
 2064       boolean localDestination(String name, PdfDestination destination) {
 2065           Object obj[] = (Object[])localDestinations.get(name);
 2066           if (obj == null)
 2067               obj = new Object[3];
 2068           if (obj[2] != null)
 2069               return false;
 2070           obj[2] = destination;
 2071           localDestinations.put(name, obj);
 2072           destination.addPage(writer.getCurrentPage());
 2073           return true;
 2074       }
 2075       
 2076       /**
 2077        * Stores a list of document level JavaScript actions.
 2078        */
 2079       int jsCounter;
 2080       protected HashMap documentLevelJS = new HashMap();
 2081       protected static final DecimalFormat SIXTEEN_DIGITS = new DecimalFormat("0000000000000000");
 2082       void addJavaScript(PdfAction js) {
 2083           if (js.get(PdfName.JS) == null)
 2084               throw new RuntimeException("Only JavaScript actions are allowed.");
 2085           try {
 2086               documentLevelJS.put(SIXTEEN_DIGITS.format(jsCounter++), writer.addToBody(js).getIndirectReference());
 2087           }
 2088           catch (IOException e) {
 2089               throw new ExceptionConverter(e);
 2090           }
 2091       }
 2092       void addJavaScript(String name, PdfAction js) {
 2093           if (js.get(PdfName.JS) == null)
 2094               throw new RuntimeException("Only JavaScript actions are allowed.");
 2095           try {
 2096               documentLevelJS.put(name, writer.addToBody(js).getIndirectReference());
 2097           }
 2098           catch (IOException e) {
 2099               throw new ExceptionConverter(e);
 2100           }
 2101       }
 2102       
 2103       HashMap getDocumentLevelJS() {
 2104       	return documentLevelJS;
 2105       }
 2106       
 2107       protected HashMap documentFileAttachment = new HashMap();
 2108   
 2109       void addFileAttachment(String description, PdfFileSpecification fs) throws IOException {
 2110           if (description == null) {
 2111           	PdfString desc = (PdfString)fs.get(PdfName.DESC);
 2112           	if (desc == null) {
 2113           		description = ""; 
 2114           	}
 2115           	else {
 2116           		description = PdfEncodings.convertToString(desc.getBytes(), null);
 2117           	}
 2118           }
 2119           fs.addDescription(description, true);
 2120           if (description.length() == 0)
 2121               description = "Unnamed";
 2122           String fn = PdfEncodings.convertToString(new PdfString(description, PdfObject.TEXT_UNICODE).getBytes(), null);
 2123           int k = 0;
 2124           while (documentFileAttachment.containsKey(fn)) {
 2125               ++k;
 2126               fn = PdfEncodings.convertToString(new PdfString(description + " " + k, PdfObject.TEXT_UNICODE).getBytes(), null);
 2127           }
 2128           documentFileAttachment.put(fn, fs.getReference());
 2129       }
 2130       
 2131       HashMap getDocumentFileAttachment() {
 2132           return documentFileAttachment;
 2133       }
 2134   	
 2135   //	[C6] document level actions
 2136       
 2137       protected String openActionName;
 2138       
 2139       void setOpenAction(String name) {
 2140           openActionName = name;
 2141           openActionAction = null;
 2142       }
 2143   
 2144       protected PdfAction openActionAction;
 2145       void setOpenAction(PdfAction action) {
 2146           openActionAction = action;
 2147           openActionName = null;
 2148       }
 2149   
 2150       protected PdfDictionary additionalActions;
 2151       void addAdditionalAction(PdfName actionType, PdfAction action)  {
 2152           if (additionalActions == null)  {
 2153               additionalActions = new PdfDictionary();
 2154           }
 2155           if (action == null)
 2156               additionalActions.remove(actionType);
 2157           else
 2158               additionalActions.put(actionType, action);
 2159           if (additionalActions.size() == 0)
 2160               additionalActions = null;
 2161       }
 2162       
 2163   //	[C7] portable collections
 2164       
 2165       protected PdfCollection collection;
 2166   
 2167       /**
 2168        * Sets the collection dictionary.
 2169        * @param collection a dictionary of type PdfCollection
 2170        */
 2171   	public void setCollection(PdfCollection collection) {
 2172   		this.collection = collection;
 2173   	}
 2174   	
 2175   //	[C8] AcroForm
 2176       
 2177   	PdfAnnotationsImp annotationsImp;
 2178       
 2179       /**
 2180        * Gets the AcroForm object.
 2181        * @return the PdfAcroform object of the PdfDocument
 2182        */
 2183       PdfAcroForm getAcroForm() {
 2184           return annotationsImp.getAcroForm();
 2185       }
 2186       
 2187       void setSigFlags(int f) {
 2188           annotationsImp.setSigFlags(f);
 2189       }
 2190       
 2191       void addCalculationOrder(PdfFormField formField) {
 2192           annotationsImp.addCalculationOrder(formField);
 2193       }
 2194       
 2195       void addAnnotation(PdfAnnotation annot) {
 2196           pageEmpty = false;
 2197           annotationsImp.addAnnotation(annot);
 2198       }
 2199   
 2200   //	[F12] tagged PDF
 2201       
 2202       protected int markPoint;
 2203       
 2204   	int getMarkPoint() {
 2205   	    return markPoint;
 2206   	}
 2207   	 
 2208   	void incMarkPoint() {
 2209   	    ++markPoint;
 2210   	}
 2211       
 2212   //	[U1] page sizes
 2213       
 2214       /** This is the size of the next page. */
 2215       protected Rectangle nextPageSize = null;
 2216       
 2217       /** This is the size of the several boxes of the current Page. */
 2218       protected HashMap thisBoxSize = new HashMap();
 2219       
 2220       /** This is the size of the several boxes that will be used in
 2221        * the next page. */
 2222       protected HashMap boxSize = new HashMap();
 2223   	
 2224       void setCropBoxSize(Rectangle crop) {
 2225           setBoxSize("crop", crop);
 2226       }
 2227       
 2228       void setBoxSize(String boxName, Rectangle size) {
 2229           if (size == null)
 2230               boxSize.remove(boxName);
 2231           else
 2232               boxSize.put(boxName, new PdfRectangle(size));
 2233       }
 2234       
 2235       protected void setNewPageSizeAndMargins() {
 2236           pageSize = nextPageSize;
 2237       	if (marginMirroring && (getPageNumber() & 1) == 0) {
 2238               marginRight = nextMarginLeft;
 2239               marginLeft = nextMarginRight;
 2240           }
 2241           else {
 2242               marginLeft = nextMarginLeft;
 2243               marginRight = nextMarginRight;
 2244           }
 2245           marginTop = nextMarginTop;
 2246           marginBottom = nextMarginBottom;
 2247       }
 2248       
 2249       /**
 2250        * Gives the size of a trim, art, crop or bleed box, or null if not defined.
 2251        * @param boxName crop, trim, art or bleed
 2252        */
 2253       Rectangle getBoxSize(String boxName) {
 2254       	PdfRectangle r = (PdfRectangle)thisBoxSize.get(boxName);
 2255       	if (r != null) return r.getRectangle();
 2256       	return null;
 2257       }
 2258   
 2259   //	[U2] empty pages
 2260   
 2261       /** This checks if the page is empty. */
 2262       protected boolean pageEmpty = true;
 2263       
 2264       void setPageEmpty(boolean pageEmpty) {
 2265           this.pageEmpty = pageEmpty;
 2266       }
 2267       
 2268   //	[U3] page actions
 2269       
 2270       /** The duration of the page */
 2271       protected int duration=-1; // negative values will indicate no duration
 2272           
 2273       /** The page transition */
 2274       protected PdfTransition transition=null; 
 2275           
 2276       /**
 2277        * Sets the display duration for the page (for presentations)
 2278        * @param seconds   the number of seconds to display the page
 2279        */
 2280       void setDuration(int seconds) {
 2281           if (seconds > 0)
 2282               this.duration=seconds;
 2283           else
 2284               this.duration=-1;
 2285       }
 2286           
 2287       /**
 2288        * Sets the transition for the page
 2289        * @param transition   the PdfTransition object
 2290        */
 2291       void setTransition(PdfTransition transition) {
 2292           this.transition=transition;
 2293       }
 2294       
 2295       protected PdfDictionary pageAA = null;
 2296       void setPageAction(PdfName actionType, PdfAction action) {
 2297           if (pageAA == null) {
 2298               pageAA = new PdfDictionary();
 2299           }
 2300           pageAA.put(actionType, action);
 2301       }
 2302   
 2303   //	[U8] thumbnail images
 2304       
 2305       protected PdfIndirectReference thumb;
 2306       void setThumbnail(Image image) throws PdfException, DocumentException {
 2307           thumb = writer.getImageReference(writer.addDirectImageSimple(image));
 2308       }
 2309       
 2310   //	[M0] Page resources contain references to fonts, extgstate, images,...
 2311       
 2312       /** This are the page resources of the current Page. */
 2313       protected PageResources pageResources;
 2314       
 2315       PageResources getPageResources() {
 2316           return pageResources;
 2317       }
 2318       
 2319   //	[M3] Images
 2320       
 2321       /** Holds value of property strictImageSequence. */
 2322       protected boolean strictImageSequence = false;   
 2323       
 2324       /** Getter for property strictImageSequence.
 2325        * @return Value of property strictImageSequence.
 2326        *
 2327        */
 2328       boolean isStrictImageSequence() {
 2329           return this.strictImageSequence;
 2330       }
 2331       
 2332       /** Setter for property strictImageSequence.
 2333        * @param strictImageSequence New value of property strictImageSequence.
 2334        *
 2335        */
 2336       void setStrictImageSequence(boolean strictImageSequence) {
 2337           this.strictImageSequence = strictImageSequence;
 2338       }
 2339       
 2340       /** This is the position where the image ends. */
 2341       protected float imageEnd = -1;
 2342    
 2343   	/**
 2344   	 * Method added by Pelikan Stephan
 2345   	 */
 2346   	public void clearTextWrap() {
 2347   		float tmpHeight = imageEnd - currentHeight;
 2348   		if (line != null) {
 2349   			tmpHeight += line.height();
 2350   		}
 2351   		if ((imageEnd > -1) && (tmpHeight > 0)) {
 2352   			carriageReturn();
 2353   			currentHeight += tmpHeight;
 2354   		}
 2355   	}
 2356       
 2357       /** This is the image that could not be shown on a previous page. */
 2358       protected Image imageWait = null;
 2359       
 2360       /**
 2361        * Adds an image to the document.
 2362        * @param image the <CODE>Image</CODE> to add
 2363        * @throws PdfException on error
 2364        * @throws DocumentException on error
 2365        */
 2366       
 2367       protected void add(Image image) throws PdfException, DocumentException {
 2368           
 2369           if (image.hasAbsoluteY()) {
 2370               graphics.addImage(image);
 2371               pageEmpty = false;
 2372               return;
 2373           }
 2374           
 2375           // if there isn't enough room for the image on this page, save it for the next page
 2376           if (currentHeight != 0 && indentTop() - currentHeight - image.getScaledHeight() < indentBottom()) {
 2377               if (!strictImageSequence && imageWait == null) {
 2378                   imageWait = image;
 2379                   return;
 2380               }
 2381               newPage();
 2382               if (currentHeight != 0 && indentTop() - currentHeight - image.getScaledHeight() < indentBottom()) {
 2383                   imageWait = image;
 2384                   return;
 2385               }
 2386           }
 2387           pageEmpty = false;
 2388           // avoid endless loops
 2389           if (image == imageWait)
 2390               imageWait = null;
 2391           boolean textwrap = (image.getAlignment() & Image.TEXTWRAP) == Image.TEXTWRAP
 2392           && !((image.getAlignment() & Image.MIDDLE) == Image.MIDDLE);
 2393           boolean underlying = (image.getAlignment() & Image.UNDERLYING) == Image.UNDERLYING;
 2394           float diff = leading / 2;
 2395           if (textwrap) {
 2396               diff += leading;
 2397           }
 2398           float lowerleft = indentTop() - currentHeight - image.getScaledHeight() -diff;
 2399           float mt[] = image.matrix();
 2400           float startPosition = indentLeft() - mt[4];
 2401           if ((image.getAlignment() & Image.RIGHT) == Image.RIGHT) startPosition = indentRight() - image.getScaledWidth() - mt[4];
 2402           if ((image.getAlignment() & Image.MIDDLE) == Image.MIDDLE) startPosition = indentLeft() + ((indentRight() - indentLeft() - image.getScaledWidth()) / 2) - mt[4];
 2403           if (image.hasAbsoluteX()) startPosition = image.getAbsoluteX();
 2404           if (textwrap) {
 2405               if (imageEnd < 0 || imageEnd < currentHeight + image.getScaledHeight() + diff) {
 2406                   imageEnd = currentHeight + image.getScaledHeight() + diff;
 2407               }
 2408               if ((image.getAlignment() & Image.RIGHT) == Image.RIGHT) {
 2409               	// indentation suggested by Pelikan Stephan
 2410               	indentation.imageIndentRight += image.getScaledWidth() + image.getIndentationLeft();
 2411               }
 2412               else {
 2413               	// indentation suggested by Pelikan Stephan
 2414               	indentation.imageIndentLeft += image.getScaledWidth() + image.getIndentationRight();
 2415               }
 2416           }
 2417           else {
 2418           	if ((image.getAlignment() & Image.RIGHT) == Image.RIGHT) startPosition -= image.getIndentationRight();
 2419           	else if ((image.getAlignment() & Image.MIDDLE) == Image.MIDDLE) startPosition += image.getIndentationLeft() - image.getIndentationRight();
 2420           	else startPosition += image.getIndentationLeft();
 2421           }
 2422           graphics.addImage(image, mt[0], mt[1], mt[2], mt[3], startPosition, lowerleft - mt[5]);
 2423           if (!(textwrap || underlying)) {
 2424               currentHeight += image.getScaledHeight() + diff;
 2425               flushLines();
 2426               text.moveText(0, - (image.getScaledHeight() + diff));
 2427               newLine();
 2428           }
 2429       }
 2430      
 2431   //	[M4] Adding a PdfPTable
 2432       
 2433       /** Adds a <CODE>PdfPTable</CODE> to the document.
 2434        * @param ptable the <CODE>PdfPTable</CODE> to be added to the document.
 2435        * @throws DocumentException on error
 2436        */
 2437       void addPTable(PdfPTable ptable) throws DocumentException {
 2438           ColumnText ct = new ColumnText(writer.getDirectContent());
 2439           if (currentHeight > 0) {
 2440               Paragraph p = new Paragraph();
 2441               p.setLeading(0);
 2442               ct.addElement(p);
 2443               // if the table prefers to be on a single page, and it wouldn't
 2444   	        //fit on the current page, start a new page.
 2445   	        if (ptable.getKeepTogether() && !fitsPage(ptable, 0f))  {
 2446   	        	newPage();
 2447   	        }
 2448           }
 2449           ct.addElement(ptable);
 2450           boolean he = ptable.isHeadersInEvent();
 2451           ptable.setHeadersInEvent(true);
 2452           int loop = 0;
 2453           while (true) {
 2454               ct.setSimpleColumn(indentLeft(), indentBottom(), indentRight(), indentTop() - currentHeight);
 2455               int status = ct.go();
 2456               if ((status & ColumnText.NO_MORE_TEXT) != 0) {
 2457                   text.moveText(0, ct.getYLine() - indentTop() + currentHeight);
 2458                   currentHeight = indentTop() - ct.getYLine();
 2459                   break;
 2460               }
 2461               if (indentTop() - currentHeight == ct.getYLine())
 2462                   ++loop;
 2463               else
 2464                   loop = 0;
 2465               if (loop == 3) {
 2466                   add(new Paragraph("ERROR: Infinite table loop"));
 2467                   break;
 2468               }
 2469               newPage();
 2470           }
 2471           ptable.setHeadersInEvent(he);
 2472       }
 2473       
 2474       /**
 2475        * Checks if a <CODE>PdfPTable</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
 2476        *
 2477        * @param	table	the table that has to be checked
 2478        * @param	margin	a certain margin
 2479        * @return	<CODE>true</CODE> if the <CODE>PdfPTable</CODE> fits the page, <CODE>false</CODE> otherwise.
 2480        */
 2481       
 2482       boolean fitsPage(PdfPTable table, float margin) {
 2483       	if (!table.isLockedWidth()) {
 2484       		float totalWidth = (indentRight() - indentLeft()) * table.getWidthPercentage() / 100;
 2485       		table.setTotalWidth(totalWidth);
 2486       	}
 2487           // ensuring that a new line has been started.
 2488           ensureNewLine();
 2489           return table.getTotalHeight() <= indentTop() - currentHeight - indentBottom() - margin;
 2490       }
 2491   
 2492   //	[M4'] Adding a Table
 2493       
 2494   	/**
 2495   	 * This is a helper class for adding a Table to a document.
 2496   	 * @since	2.0.8 (PdfDocument was package-private before)
 2497   	 */
 2498       protected static class RenderingContext {
 2499           float pagetop = -1;
 2500           float oldHeight = -1;
 2501   
 2502           PdfContentByte cellGraphics = null;
 2503           
 2504           float lostTableBottom;
 2505           
 2506           float maxCellBottom;
 2507           float maxCellHeight;
 2508           
 2509           Map rowspanMap;
 2510           Map pageMap = new HashMap();
 2511           /**
 2512            * A PdfPTable
 2513            */
 2514           public PdfTable table;
 2515           
 2516           /**
 2517            * Consumes the rowspan
 2518            * @param c
 2519            * @return a rowspan.
 2520            */
 2521           public int consumeRowspan(PdfCell c) {
 2522               if (c.rowspan() == 1) {
 2523                   return 1;
 2524               }
 2525               
 2526               Integer i = (Integer) rowspanMap.get(c);
 2527               if (i == null) {
 2528                   i = new Integer(c.rowspan());
 2529               }
 2530               
 2531               i = new Integer(i.intValue() - 1);
 2532               rowspanMap.put(c, i);
 2533   
 2534               if (i.intValue() < 1) {
 2535                   return 1;
 2536               }
 2537               return i.intValue();
 2538           }
 2539   
 2540           /**
 2541            * Looks at the current rowspan.
 2542            * @param c
 2543            * @return the current rowspan
 2544            */
 2545           public int currentRowspan(PdfCell c) {
 2546               Integer i = (Integer) rowspanMap.get(c);
 2547               if (i == null) {
 2548                   return c.rowspan();
 2549               } else {
 2550                   return i.intValue();
 2551               }
 2552           }
 2553           
 2554           public int cellRendered(PdfCell cell, int pageNumber) {
 2555               Integer i = (Integer) pageMap.get(cell);
 2556               if (i == null) {
 2557                   i = new Integer(1);
 2558               } else {
 2559                   i = new Integer(i.intValue() + 1);
 2560               }
 2561               pageMap.put(cell, i);
 2562   
 2563               Integer pageInteger = new Integer(pageNumber);
 2564               Set set = (Set) pageMap.get(pageInteger);
 2565               
 2566               if (set == null) {
 2567                   set = new HashSet();
 2568                   pageMap.put(pageInteger, set);
 2569               }
 2570               
 2571               set.add(cell);
 2572               
 2573               return i.intValue();
 2574           }
 2575   
 2576           public int numCellRendered(PdfCell cell) {
 2577               Integer i = (Integer) pageMap.get(cell);
 2578               if (i == null) {
 2579                   i = new Integer(0);
 2580               } 
 2581               return i.intValue();
 2582           }
 2583           
 2584           public boolean isCellRenderedOnPage(PdfCell cell, int pageNumber) {
 2585               Integer pageInteger = new Integer(pageNumber);
 2586               Set set = (Set) pageMap.get(pageInteger);
 2587               
 2588               if (set != null) {
 2589                   return set.contains(cell);
 2590               }
 2591               
 2592               return false;
 2593           }
 2594       };
 2595   
 2596   	/**
 2597   	 * Adds a new table to the document.
 2598   	 * @param t				Table to add.  Rendered rows will be deleted after processing.
 2599   	 * @throws DocumentException
 2600   	 * @since	iText 2.0.8
 2601   	 */
 2602       private void addPdfTable(Table t) throws DocumentException {
 2603           // before every table, we flush all lines
 2604           flushLines();
 2605   
 2606       	PdfTable table = new PdfTable(t, indentLeft(), indentRight(), indentTop() - currentHeight);
 2607           RenderingContext ctx = new RenderingContext();
 2608           ctx.pagetop = indentTop();
 2609           ctx.oldHeight = currentHeight;
 2610           ctx.cellGraphics = new PdfContentByte(writer);
 2611           ctx.rowspanMap = new HashMap();
 2612           ctx.table = table;
 2613           
 2614   		// initialization of parameters
 2615   		PdfCell cell;
 2616   
 2617   		// drawing the table
 2618   		ArrayList headercells = table.getHeaderCells();
 2619           ArrayList cells = table.getCells();
 2620           ArrayList rows = extractRows(cells, ctx);
 2621           boolean isContinue = false;
 2622   		while (!cells.isEmpty()) {
 2623   			// initialization of some extra parameters;
 2624   			ctx.lostTableBottom = 0;
 2625                           
 2626   			// loop over the cells
 2627   			boolean cellsShown = false;
 2628   
 2629               // draw the cells (line by line)
 2630               Iterator iterator = rows.iterator();
 2631                 
 2632               boolean atLeastOneFits = false;
 2633               while (iterator.hasNext()) {
 2634                   ArrayList row = (ArrayList) iterator.next();
 2635                   analyzeRow(rows, ctx);
 2636                   renderCells(ctx, row, table.hasToFitPageCells() & atLeastOneFits);
 2637                                   
 2638                   if (!mayBeRemoved(row)) {
 2639                       break;
 2640                   }
 2641                   consumeRowspan(row, ctx);
 2642                   iterator.remove();
 2643                   atLeastOneFits = true;
 2644               }
 2645   
 2646   //          compose cells array list for subsequent code
 2647               cells.clear();
 2648               Set opt = new HashSet();
 2649               iterator = rows.iterator();
 2650               while (iterator.hasNext()) {
 2651                   ArrayList row = (ArrayList) iterator.next();
 2652                   
 2653                   Iterator cellIterator = row.iterator();
 2654                   while (cellIterator.hasNext()) {
 2655                       cell = (PdfCell) cellIterator.next();
 2656                       
 2657                       if (!opt.contains(cell)) {
 2658                           cells.add(cell);
 2659                           opt.add(cell);
 2660                       }
 2661                   }
 2662               }
 2663               
 2664   			// we paint the graphics of the table after looping through all the cells
 2665   			Rectangle tablerec = new Rectangle(table);
 2666   			tablerec.setBorder(table.getBorder());
 2667   			tablerec.setBorderWidth(table.getBorderWidth());
 2668   			tablerec.setBorderColor(table.getBorderColor());
 2669   			tablerec.setBackgroundColor(table.getBackgroundColor());
 2670   			PdfContentByte under = writer.getDirectContentUnder();
 2671   			under.rectangle(tablerec.rectangle(top(), indentBottom()));
 2672   			under.add(ctx.cellGraphics);
 2673   			// bugfix by Gerald Fehringer: now again add the border for the table
 2674   			// since it might have been covered by cell backgrounds
 2675   			tablerec.setBackgroundColor(null);
 2676   			tablerec = tablerec.rectangle(top(), indentBottom());
 2677   			tablerec.setBorder(table.getBorder());
 2678   			under.rectangle(tablerec);
 2679   			// end bugfix
 2680   
 2681               ctx.cellGraphics = new PdfContentByte(null);
 2682   			// if the table continues on the next page
 2683               
 2684   			if (!rows.isEmpty()) {
 2685   				isContinue = true;
 2686   				graphics.setLineWidth(table.getBorderWidth());
 2687   				if (cellsShown && (table.getBorder() & Rectangle.BOTTOM) == Rectangle.BOTTOM) {
 2688   					// Draw the bottom line
 2689                                   
 2690   					// the color is set to the color of the element
 2691   					Color tColor = table.getBorderColor();
 2692   					if (tColor != null) {
 2693   						graphics.setColorStroke(tColor);
 2694   					}
 2695   					graphics.moveTo(table.getLeft(), Math.max(table.getBottom(), indentBottom()));
 2696   					graphics.lineTo(table.getRight(), Math.max(table.getBottom(), indentBottom()));
 2697   					graphics.stroke();
 2698   					if (tColor != null) {
 2699   						graphics.resetRGBColorStroke();
 2700   					}
 2701   				}
 2702                               
 2703   				// old page
 2704   				pageEmpty = false;
 2705                   float difference = ctx.lostTableBottom;
 2706                   
 2707   				// new page
 2708   				newPage();
 2709                   
 2710   				// G.F.: if something added in page event i.e. currentHeight > 0
 2711   				float heightCorrection = 0;
 2712   				boolean somethingAdded = false;
 2713   				if (currentHeight > 0) {
 2714   					heightCorrection = 6;
 2715   					currentHeight += heightCorrection;
 2716   					somethingAdded = true;
 2717   					newLine();
 2718   					flushLines();
 2719   					indentation.indentTop = currentHeight - leading;
 2720   					currentHeight = 0;
 2721   				}
 2722   				else {
 2723                       flushLines();
 2724   				}
 2725                               
 2726   				// this part repeats the table headers (if any)
 2727   				int size = headercells.size();
 2728   				if (size > 0) {
 2729   					// this is the top of the headersection
 2730   					cell = (PdfCell) headercells.get(0);
 2731   					float oldTop = cell.getTop(0);
 2732   					// loop over all the cells of the table header
 2733   					for (int i = 0; i < size; i++) {
 2734   						cell = (PdfCell) headercells.get(i);
 2735   						// calculation of the new cellpositions
 2736   						cell.setTop(indentTop() - oldTop + cell.getTop(0));
 2737   						cell.setBottom(indentTop() - oldTop + cell.getBottom(0));
 2738   						ctx.pagetop = cell.getBottom();
 2739   						// we paint the borders of the cell
 2740   						ctx.cellGraphics.rectangle(cell.rectangle(indentTop(), indentBottom()));
 2741   						// we write the text of the cell
 2742   						ArrayList images = cell.getImages(indentTop(), indentBottom());
 2743   						for (Iterator im = images.iterator(); im.hasNext();) {
 2744   							cellsShown = true;
 2745   							Image image = (Image) im.next();
 2746   							graphics.addImage(image);
 2747   						}
 2748   						lines = cell.getLines(indentTop(), indentBottom());
 2749   						float cellTop = cell.getTop(indentTop());
 2750   						text.moveText(0, cellTop-heightCorrection);
 2751   						float cellDisplacement = flushLines() - cellTop+heightCorrection;
 2752   						text.moveText(0, cellDisplacement);
 2753   					}
 2754                                   
 2755   					currentHeight = indentTop() - ctx.pagetop + table.cellspacing();
 2756   					text.moveText(0, ctx.pagetop - indentTop() - currentHeight);
 2757   				}
 2758   				else {
 2759   					if (somethingAdded) {
 2760   						ctx.pagetop = indentTop();
 2761   						text.moveText(0, -table.cellspacing());
 2762   					}
 2763   				}
 2764   				ctx.oldHeight = currentHeight - heightCorrection;
 2765                               
 2766   				// calculating the new positions of the table and the cells
 2767   				size = Math.min(cells.size(), table.columns());
 2768   				int i = 0;
 2769   				while (i < size) {
 2770   					cell = (PdfCell) cells.get(i);
 2771   					if (cell.getTop(-table.cellspacing()) > ctx.lostTableBottom) {
 2772   						float newBottom = ctx.pagetop - difference + cell.getBottom();
 2773   						float neededHeight = cell.remainingHeight();
 2774   						if (newBottom > ctx.pagetop - neededHeight) {
 2775   							difference += newBottom - (ctx.pagetop - neededHeight);
 2776   						}
 2777   					}
 2778   					i++;
 2779   				}
 2780   				size = cells.size();
 2781   				table.setTop(indentTop());
 2782   				table.setBottom(ctx.pagetop - difference + table.getBottom(table.cellspacing()));
 2783   				for (i = 0; i < size; i++) {
 2784   					cell = (PdfCell) cells.get(i);
 2785   					float newBottom = ctx.pagetop - difference + cell.getBottom();
 2786   					float newTop = ctx.pagetop - difference + cell.getTop(-table.cellspacing());
 2787   					if (newTop > indentTop() - currentHeight) {
 2788   						newTop = indentTop() - currentHeight;
 2789   					}
 2790                  
 2791   					cell.setTop(newTop );
 2792   					cell.setBottom(newBottom );
 2793   				}
 2794   			}
 2795   		}
 2796           
 2797           float tableHeight = table.getTop() - table.getBottom();
 2798           // bugfix by Adauto Martins when have more than two tables and more than one page 
 2799           // If continuation of table in other page (bug report #1460051)
 2800           if (isContinue) {
 2801           	currentHeight = tableHeight;
 2802           	text.moveText(0, -(tableHeight - (ctx.oldHeight * 2)));
 2803           } else {
 2804           	currentHeight = ctx.oldHeight + tableHeight;
 2805           	text.moveText(0, -tableHeight);
 2806           }
 2807           // end bugfix
 2808           pageEmpty = false;
 2809       }
 2810       
 2811       protected void analyzeRow(ArrayList rows, RenderingContext ctx) {
 2812           ctx.maxCellBottom = indentBottom();
 2813   
 2814           // determine whether row(index) is in a rowspan
 2815           int rowIndex = 0;
 2816   
 2817           ArrayList row = (ArrayList) rows.get(rowIndex);
 2818           int maxRowspan = 1;
 2819           Iterator iterator = row.iterator();
 2820           while (iterator.hasNext()) {
 2821               PdfCell cell = (PdfCell) iterator.next();
 2822               maxRowspan = Math.max(ctx.currentRowspan(cell), maxRowspan);
 2823           }
 2824           rowIndex += maxRowspan;
 2825           
 2826           boolean useTop = true;
 2827           if (rowIndex == rows.size()) {
 2828               rowIndex = rows.size() - 1;
 2829               useTop = false;
 2830           }
 2831           
 2832           if (rowIndex < 0 || rowIndex >= rows.size()) return;
 2833           
 2834           row = (ArrayList) rows.get(rowIndex);
 2835           iterator = row.iterator();
 2836           while (iterator.hasNext()) {
 2837               PdfCell cell = (PdfCell) iterator.next();
 2838               Rectangle cellRect = cell.rectangle(ctx.pagetop, indentBottom());
 2839               if (useTop) {
 2840                   ctx.maxCellBottom = Math.max(ctx.maxCellBottom, cellRect.getTop());
 2841               } else {
 2842                   if (ctx.currentRowspan(cell) == 1) {
 2843                       ctx.maxCellBottom = Math.max(ctx.maxCellBottom, cellRect.getBottom());
 2844                   }
 2845               }
 2846           }
 2847       }
 2848   
 2849       protected boolean mayBeRemoved(ArrayList row) {
 2850           Iterator iterator = row.iterator();
 2851           boolean mayBeRemoved = true;
 2852           while (iterator.hasNext()) {
 2853               PdfCell cell = (PdfCell) iterator.next();
 2854              
 2855               mayBeRemoved &= cell.mayBeRemoved();
 2856           }
 2857           return mayBeRemoved;
 2858       }
 2859   
 2860       protected void consumeRowspan(ArrayList row, RenderingContext ctx) {
 2861           Iterator iterator = row.iterator();
 2862           while (iterator.hasNext()) {
 2863               PdfCell c = (PdfCell) iterator.next();
 2864               ctx.consumeRowspan(c);
 2865           }
 2866       }
 2867       
 2868       protected ArrayList extractRows(ArrayList cells, RenderingContext ctx) {
 2869           PdfCell cell;
 2870           PdfCell previousCell = null;
 2871           ArrayList rows = new ArrayList();
 2872           java.util.List rowCells = new ArrayList();
 2873           
 2874           Iterator iterator = cells.iterator();
 2875           while (iterator.hasNext()) {
 2876               cell = (PdfCell) iterator.next();
 2877   
 2878               boolean isAdded = false;
 2879   
 2880               boolean isEndOfRow = !iterator.hasNext();
 2881               boolean isCurrentCellPartOfRow = !iterator.hasNext();
 2882               
 2883               if (previousCell != null) {
 2884                   if (cell.getLeft() <= previousCell.getLeft()) {
 2885                       isEndOfRow = true;
 2886                       isCurrentCellPartOfRow = false;
 2887                   }
 2888               }
 2889               
 2890               if (isCurrentCellPartOfRow) {
 2891                   rowCells.add(cell);
 2892                   isAdded = true;
 2893               }
 2894               
 2895               if (isEndOfRow) {
 2896                   if (!rowCells.isEmpty()) {
 2897                       // add to rowlist
 2898                       rows.add(rowCells);
 2899                   }
 2900                   
 2901                   // start a new list for next line
 2902                   rowCells = new ArrayList();                
 2903               }
 2904   
 2905               if (!isAdded) {
 2906                   rowCells.add(cell);
 2907               }
 2908               
 2909               previousCell = cell;
 2910           }
 2911           
 2912           if (!rowCells.isEmpty()) {
 2913               rows.add(rowCells);
 2914           }
 2915           
 2916           // fill row information with rowspan cells to get complete "scan lines"
 2917           for (int i = rows.size() - 1; i >= 0; i--) {
 2918               ArrayList row = (ArrayList) rows.get(i);
 2919               // iterator through row
 2920               for (int j = 0; j < row.size(); j++) {
 2921                   PdfCell c = (PdfCell) row.get(j);
 2922                   int rowspan = c.rowspan();                
 2923                   // fill in missing rowspan cells to complete "scan line"
 2924                   for (int k = 1; k < rowspan && rows.size() < i+k; k++) {
 2925                       ArrayList spannedRow = ((ArrayList) rows.get(i + k));
 2926                       if (spannedRow.size() > j)
 2927                       	spannedRow.add(j, c);
 2928                   }
 2929               }
 2930           }
 2931                   
 2932           return rows;
 2933       }
 2934   
 2935       protected void renderCells(RenderingContext ctx, java.util.List cells, boolean hasToFit) throws DocumentException {
 2936           PdfCell cell;
 2937           Iterator iterator;
 2938           if (hasToFit) {
 2939               iterator = cells.iterator();
 2940               while (iterator.hasNext()) {
 2941               	cell = (PdfCell) iterator.next();
 2942               	if (!cell.isHeader()) {
 2943               		if (cell.getBottom() < indentBottom()) return;
 2944               	}
 2945               }
 2946           }
 2947           iterator = cells.iterator();
 2948           
 2949           while (iterator.hasNext()) {
 2950               cell = (PdfCell) iterator.next();
 2951               if (!ctx.isCellRenderedOnPage(cell, getPageNumber())) {
 2952   
 2953                   float correction = 0;
 2954                   if (ctx.numCellRendered(cell) >= 1) {
 2955                       correction = 1.0f;
 2956                   }
 2957               
 2958                   lines = cell.getLines(ctx.pagetop, indentBottom() - correction);
 2959                   
 2960                   // if there is still text to render we render it
 2961                   if (lines != null && !lines.isEmpty()) {
 2962                       // we write the text
 2963                       float cellTop = cell.getTop(ctx.pagetop - ctx.oldHeight);
 2964                       text.moveText(0, cellTop);
 2965                       float cellDisplacement = flushLines() - cellTop;
 2966                       
 2967                       text.moveText(0, cellDisplacement);
 2968                       if (ctx.oldHeight + cellDisplacement > currentHeight) {
 2969                           currentHeight = ctx.oldHeight + cellDisplacement;
 2970                       }
 2971   
 2972                       ctx.cellRendered(cell, getPageNumber());
 2973                   } 
 2974                   float indentBottom = Math.max(cell.getBottom(), indentBottom());
 2975                   Rectangle tableRect = ctx.table.rectangle(ctx.pagetop, indentBottom());
 2976                   indentBottom = Math.max(tableRect.getBottom(), indentBottom);
 2977                   
 2978                   // we paint the borders of the cells
 2979                   Rectangle cellRect = cell.rectangle(tableRect.getTop(), indentBottom);
 2980    				//cellRect.setBottom(cellRect.bottom());
 2981                   if (cellRect.getHeight() > 0) {
 2982                       ctx.lostTableBottom = indentBottom;
 2983                       ctx.cellGraphics.rectangle(cellRect);
 2984                   }
 2985       
 2986                   // and additional graphics
 2987                   ArrayList images = cell.getImages(ctx.pagetop, indentBottom());
 2988                   for (Iterator i = images.iterator(); i.hasNext();) {
 2989                       Image image = (Image) i.next();
 2990                       graphics.addImage(image);
 2991                   }
 2992                   
 2993               }
 2994           }
 2995       }
 2996       
 2997       /**
 2998        * Returns the bottomvalue of a <CODE>Table</CODE> if it were added to this document.
 2999        *
 3000        * @param	table	the table that may or may not be added to this document
 3001        * @return	a bottom value
 3002        */
 3003       float bottom(Table table) {
 3004           // constructing a PdfTable
 3005           PdfTable tmp = new PdfTable(table, indentLeft(), indentRight(), indentTop() - currentHeight);
 3006           return tmp.getBottom();
 3007       }
 3008       
 3009   //	[M5] header/footer
 3010       protected void doFooter() throws DocumentException {
 3011       	if (footer == null) return;
 3012   		// Begin added by Edgar Leonardo Prieto Perilla
 3013       	// Avoid footer indentation
 3014       	float tmpIndentLeft = indentation.indentLeft;
 3015       	float tmpIndentRight = indentation.indentRight;
 3016       	// Begin added: Bonf (Marc Schneider) 2003-07-29
 3017           float tmpListIndentLeft = indentation.listIndentLeft;
 3018           float tmpImageIndentLeft = indentation.imageIndentLeft;
 3019           float tmpImageIndentRight = indentation.imageIndentRight;
 3020           // End added: Bonf (Marc Schneider) 2003-07-29
 3021   
 3022           indentation.indentLeft = indentation.indentRight = 0;
 3023           // Begin added: Bonf (Marc Schneider) 2003-07-29
 3024           indentation.listIndentLeft = 0;
 3025           indentation.imageIndentLeft = 0;
 3026           indentation.imageIndentRight = 0;
 3027           // End added: Bonf (Marc Schneider) 2003-07-29
 3028           // End Added by Edgar Leonardo Prieto Perilla
 3029           footer.setPageNumber(pageN);
 3030           leading = footer.paragraph().getTotalLeading();
 3031           add(footer.paragraph());
 3032           // adding the footer limits the height
 3033           indentation.indentBottom = currentHeight;
 3034           text.moveText(left(), indentBottom());
 3035           flushLines();
 3036           text.moveText(-left(), -bottom());
 3037           footer.setTop(bottom(currentHeight));
 3038           footer.setBottom(bottom() - (0.75f * leading));
 3039           footer.setLeft(left());
 3040           footer.setRight(right());
 3041           graphics.rectangle(footer);
 3042           indentation.indentBottom = currentHeight + leading * 2;
 3043           currentHeight = 0;
 3044           // Begin added by Edgar Leonardo Prieto Perilla
 3045           indentation.indentLeft = tmpIndentLeft;
 3046           indentation.indentRight = tmpIndentRight;
 3047           // Begin added: Bonf (Marc Schneider) 2003-07-29
 3048           indentation.listIndentLeft = tmpListIndentLeft;
 3049           indentation.imageIndentLeft = tmpImageIndentLeft;
 3050           indentation.imageIndentRight = tmpImageIndentRight;
 3051           // End added: Bonf (Marc Schneider) 2003-07-29
 3052           // End added by Edgar Leonardo Prieto Perilla
 3053       }
 3054       
 3055       protected void doHeader() throws DocumentException {
 3056           // if there is a header, the header = added
 3057           if (header == null) return;
 3058   		// Begin added by Edgar Leonardo Prieto Perilla
 3059   		// Avoid header indentation
 3060   		float tmpIndentLeft = indentation.indentLeft;
 3061   		float tmpIndentRight = indentation.indentRight;
 3062           // Begin added: Bonf (Marc Schneider) 2003-07-29
 3063           float tmpListIndentLeft = indentation.listIndentLeft;
 3064           float tmpImageIndentLeft = indentation.imageIndentLeft;
 3065           float tmpImageIndentRight = indentation.imageIndentRight;
 3066           // End added: Bonf (Marc Schneider) 2003-07-29
 3067           indentation.indentLeft = indentation.indentRight = 0;
 3068           //  Added: Bonf
 3069           indentation.listIndentLeft = 0;
 3070           indentation.imageIndentLeft = 0;
 3071           indentation.imageIndentRight = 0;
 3072           // End added: Bonf
 3073           // Begin added by Edgar Leonardo Prieto Perilla
 3074   		header.setPageNumber(pageN);
 3075           leading = header.paragraph().getTotalLeading();
 3076           text.moveText(0, leading);
 3077           add(header.paragraph());
 3078           newLine();
 3079           indentation.indentTop = currentHeight - leading;
 3080           header.setTop(top() + leading);
 3081           header.setBottom(indentTop() + leading * 2 / 3);
 3082           header.setLeft(left());
 3083           header.setRight(right());
 3084           graphics.rectangle(header);
 3085           flushLines();
 3086           currentHeight = 0;
 3087           // Begin added by Edgar Leonardo Prieto Perilla
 3088           // Restore indentation
 3089   		indentation.indentLeft = tmpIndentLeft;
 3090   		indentation.indentRight = tmpIndentRight;
 3091           // Begin added: Bonf (Marc Schneider) 2003-07-29
 3092           indentation.listIndentLeft = tmpListIndentLeft;
 3093           indentation.imageIndentLeft = tmpImageIndentLeft;
 3094           indentation.imageIndentRight = tmpImageIndentRight;
 3095           // End added: Bonf (Marc Schneider) 2003-07-29
 3096   		// End Added by Edgar Leonardo Prieto Perilla
 3097       }
 3098   }

Save This Page
Home » iText-src-2.1.3 » com.lowagie » text » pdf » [javadoc | source]