Save This Page
Home » iText-2.1.7 » com.lowagie » text » pdf » [javadoc | source]
    1   /*
    2    * $Id: PdfWriter.java 3948 2009-06-03 15:17:22Z blowagie $
    3    *
    4    * Copyright 1999, 2000, 2001, 2002 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.awt.color.ICC_Profile;
   54   import java.io.ByteArrayOutputStream;
   55   import java.io.IOException;
   56   import java.io.OutputStream;
   57   import java.util.ArrayList;
   58   import java.util.Arrays;
   59   import java.util.LinkedHashMap;
   60   import java.util.HashMap;
   61   import java.util.HashSet;
   62   import java.util.Iterator;
   63   import java.util.Map;
   64   import java.util.TreeMap;
   65   import java.util.TreeSet;
   66   import java.security.cert.Certificate;
   67   
   68   import com.lowagie.text.DocListener;
   69   import com.lowagie.text.DocWriter;
   70   import com.lowagie.text.Document;
   71   import com.lowagie.text.DocumentException;
   72   import com.lowagie.text.ExceptionConverter;
   73   import com.lowagie.text.Image;
   74   import com.lowagie.text.ImgJBIG2;
   75   import com.lowagie.text.ImgWMF;
   76   import com.lowagie.text.Rectangle;
   77   import com.lowagie.text.Table;
   78   import com.lowagie.text.pdf.collection.PdfCollection;
   79   import com.lowagie.text.pdf.events.PdfPageEventForwarder;
   80   import com.lowagie.text.pdf.interfaces.PdfAnnotations;
   81   import com.lowagie.text.pdf.interfaces.PdfDocumentActions;
   82   import com.lowagie.text.pdf.interfaces.PdfEncryptionSettings;
   83   import com.lowagie.text.pdf.interfaces.PdfPageActions;
   84   import com.lowagie.text.pdf.interfaces.PdfVersion;
   85   import com.lowagie.text.pdf.interfaces.PdfViewerPreferences;
   86   import com.lowagie.text.pdf.interfaces.PdfXConformance;
   87   import com.lowagie.text.pdf.interfaces.PdfRunDirection;
   88   import com.lowagie.text.pdf.internal.PdfVersionImp;
   89   import com.lowagie.text.pdf.internal.PdfXConformanceImp;
   90   import com.lowagie.text.xml.xmp.XmpWriter;
   91   
   92   /**
   93    * A <CODE>DocWriter</CODE> class for PDF.
   94    * <P>
   95    * When this <CODE>PdfWriter</CODE> is added
   96    * to a certain <CODE>PdfDocument</CODE>, the PDF representation of every Element
   97    * added to this Document will be written to the outputstream.</P>
   98    */
   99   
  100   public class PdfWriter extends DocWriter implements
  101   	PdfViewerPreferences,
  102   	PdfEncryptionSettings,
  103   	PdfVersion,
  104   	PdfDocumentActions,
  105   	PdfPageActions,
  106   	PdfXConformance,
  107   	PdfRunDirection,
  108   	PdfAnnotations {
  109   
  110   	/**
  111   	 * The highest generation number possible.
  112   	 * @since	iText 2.1.6
  113   	 */
  114   	public static final int GENERATION_MAX = 65535;
  115   	
  116   // INNER CLASSES
  117   
  118       /**
  119        * This class generates the structure of a PDF document.
  120        * <P>
  121        * This class covers the third section of Chapter 5 in the 'Portable Document Format
  122        * Reference Manual version 1.3' (page 55-60). It contains the body of a PDF document
  123        * (section 5.14) and it can also generate a Cross-reference Table (section 5.15).
  124        *
  125        * @see		PdfWriter
  126        * @see		PdfObject
  127        * @see		PdfIndirectObject
  128        */
  129   
  130       public static class PdfBody {
  131   
  132           // inner classes
  133   
  134           /**
  135            * <CODE>PdfCrossReference</CODE> is an entry in the PDF Cross-Reference table.
  136            */
  137   
  138           static class PdfCrossReference implements Comparable {
  139   
  140               // membervariables
  141               private int type;
  142   
  143               /**	Byte offset in the PDF file. */
  144               private int offset;
  145   
  146               private int refnum;
  147               /**	generation of the object. */
  148               private int generation;
  149   
  150               // constructors
  151               /**
  152                * Constructs a cross-reference element for a PdfIndirectObject.
  153                * @param refnum
  154                * @param	offset		byte offset of the object
  155                * @param	generation	generation number of the object
  156                */
  157   
  158               PdfCrossReference(int refnum, int offset, int generation) {
  159                   type = 0;
  160                   this.offset = offset;
  161                   this.refnum = refnum;
  162                   this.generation = generation;
  163               }
  164   
  165               /**
  166                * Constructs a cross-reference element for a PdfIndirectObject.
  167                * @param refnum
  168                * @param	offset		byte offset of the object
  169                */
  170   
  171               PdfCrossReference(int refnum, int offset) {
  172                   type = 1;
  173                   this.offset = offset;
  174                   this.refnum = refnum;
  175                   this.generation = 0;
  176               }
  177   
  178               PdfCrossReference(int type, int refnum, int offset, int generation) {
  179                   this.type = type;
  180                   this.offset = offset;
  181                   this.refnum = refnum;
  182                   this.generation = generation;
  183               }
  184   
  185               int getRefnum() {
  186                   return refnum;
  187               }
  188   
  189               /**
  190                * Returns the PDF representation of this <CODE>PdfObject</CODE>.
  191                * @param os
  192                * @throws IOException
  193                */
  194   
  195               public void toPdf(OutputStream os) throws IOException {
  196                   StringBuffer off = new StringBuffer("0000000000").append(offset);
  197                   off.delete(0, off.length() - 10);
  198                   StringBuffer gen = new StringBuffer("00000").append(generation);
  199                   gen.delete(0, gen.length() - 5);
  200   
  201                   off.append(' ').append(gen).append(generation == GENERATION_MAX ? " f \n" : " n \n");
  202                   os.write(getISOBytes(off.toString()));
  203               }
  204   
  205               /**
  206                * Writes PDF syntax to the OutputStream
  207                * @param midSize
  208                * @param os
  209                * @throws IOException
  210                */
  211               public void toPdf(int midSize, OutputStream os) throws IOException {
  212                   os.write((byte)type);
  213                   while (--midSize >= 0)
  214                       os.write((byte)((offset >>> (8 * midSize)) & 0xff));
  215                   os.write((byte)((generation >>> 8) & 0xff));
  216                   os.write((byte)(generation & 0xff));
  217               }
  218   
  219               /**
  220                * @see java.lang.Comparable#compareTo(java.lang.Object)
  221                */
  222               public int compareTo(Object o) {
  223                   PdfCrossReference other = (PdfCrossReference)o;
  224                   return (refnum < other.refnum ? -1 : (refnum==other.refnum ? 0 : 1));
  225               }
  226   
  227               /**
  228                * @see java.lang.Object#equals(java.lang.Object)
  229                */
  230               public boolean equals(Object obj) {
  231                   if (obj instanceof PdfCrossReference) {
  232                       PdfCrossReference other = (PdfCrossReference)obj;
  233                       return (refnum == other.refnum);
  234                   }
  235                   else
  236                       return false;
  237               }
  238   
  239               /**
  240                * @see java.lang.Object#hashCode()
  241                */
  242               public int hashCode() {
  243   				return refnum;
  244   			}
  245   
  246           }
  247   
  248           private static final int OBJSINSTREAM = 200;
  249   
  250           // membervariables
  251   
  252           /** array containing the cross-reference table of the normal objects. */
  253           private TreeSet xrefs;
  254           private int refnum;
  255           /** the current byte position in the body. */
  256           private int position;
  257           private PdfWriter writer;
  258           private ByteBuffer index;
  259           private ByteBuffer streamObjects;
  260           private int currentObjNum;
  261           private int numObj = 0;
  262   
  263           // constructors
  264   
  265           /**
  266            * Constructs a new <CODE>PdfBody</CODE>.
  267            * @param writer
  268            */
  269           PdfBody(PdfWriter writer) {
  270               xrefs = new TreeSet();
  271               xrefs.add(new PdfCrossReference(0, 0, GENERATION_MAX));
  272               position = writer.getOs().getCounter();
  273               refnum = 1;
  274               this.writer = writer;
  275           }
  276   
  277           // methods
  278   
  279           void setRefnum(int refnum) {
  280               this.refnum = refnum;
  281           }
  282   
  283           private PdfWriter.PdfBody.PdfCrossReference addToObjStm(PdfObject obj, int nObj) throws IOException {
  284               if (numObj >= OBJSINSTREAM)
  285                   flushObjStm();
  286               if (index == null) {
  287                   index = new ByteBuffer();
  288                   streamObjects = new ByteBuffer();
  289                   currentObjNum = getIndirectReferenceNumber();
  290                   numObj = 0;
  291               }
  292               int p = streamObjects.size();
  293               int idx = numObj++;
  294               PdfEncryption enc = writer.crypto;
  295               writer.crypto = null;
  296               obj.toPdf(writer, streamObjects);
  297               writer.crypto = enc;
  298               streamObjects.append(' ');
  299               index.append(nObj).append(' ').append(p).append(' ');
  300               return new PdfWriter.PdfBody.PdfCrossReference(2, nObj, currentObjNum, idx);
  301           }
  302   
  303           private void flushObjStm() throws IOException {
  304               if (numObj == 0)
  305                   return;
  306               int first = index.size();
  307               index.append(streamObjects);
  308               PdfStream stream = new PdfStream(index.toByteArray());
  309               stream.flateCompress(writer.getCompressionLevel());
  310               stream.put(PdfName.TYPE, PdfName.OBJSTM);
  311               stream.put(PdfName.N, new PdfNumber(numObj));
  312               stream.put(PdfName.FIRST, new PdfNumber(first));
  313               add(stream, currentObjNum);
  314               index = null;
  315               streamObjects = null;
  316               numObj = 0;
  317           }
  318   
  319           /**
  320            * Adds a <CODE>PdfObject</CODE> to the body.
  321            * <P>
  322            * This methods creates a <CODE>PdfIndirectObject</CODE> with a
  323            * certain number, containing the given <CODE>PdfObject</CODE>.
  324            * It also adds a <CODE>PdfCrossReference</CODE> for this object
  325            * to an <CODE>ArrayList</CODE> that will be used to build the
  326            * Cross-reference Table.
  327            *
  328            * @param		object			a <CODE>PdfObject</CODE>
  329            * @return		a <CODE>PdfIndirectObject</CODE>
  330            * @throws IOException
  331            */
  332   
  333           PdfIndirectObject add(PdfObject object) throws IOException {
  334               return add(object, getIndirectReferenceNumber());
  335           }
  336   
  337           PdfIndirectObject add(PdfObject object, boolean inObjStm) throws IOException {
  338               return add(object, getIndirectReferenceNumber(), inObjStm);
  339           }
  340   
  341           /**
  342            * Gets a PdfIndirectReference for an object that will be created in the future.
  343            * @return a PdfIndirectReference
  344            */
  345   
  346           PdfIndirectReference getPdfIndirectReference() {
  347               return new PdfIndirectReference(0, getIndirectReferenceNumber());
  348           }
  349   
  350           int getIndirectReferenceNumber() {
  351               int n = refnum++;
  352               xrefs.add(new PdfCrossReference(n, 0, GENERATION_MAX));
  353               return n;
  354           }
  355   
  356           /**
  357            * Adds a <CODE>PdfObject</CODE> to the body given an already existing
  358            * PdfIndirectReference.
  359            * <P>
  360            * This methods creates a <CODE>PdfIndirectObject</CODE> with the number given by
  361            * <CODE>ref</CODE>, containing the given <CODE>PdfObject</CODE>.
  362            * It also adds a <CODE>PdfCrossReference</CODE> for this object
  363            * to an <CODE>ArrayList</CODE> that will be used to build the
  364            * Cross-reference Table.
  365            *
  366            * @param		object			a <CODE>PdfObject</CODE>
  367            * @param		ref		        a <CODE>PdfIndirectReference</CODE>
  368            * @return		a <CODE>PdfIndirectObject</CODE>
  369            * @throws IOException
  370            */
  371   
  372           PdfIndirectObject add(PdfObject object, PdfIndirectReference ref) throws IOException {
  373               return add(object, ref.getNumber());
  374           }
  375   
  376           PdfIndirectObject add(PdfObject object, PdfIndirectReference ref, boolean inObjStm) throws IOException {
  377               return add(object, ref.getNumber(), inObjStm);
  378           }
  379   
  380           PdfIndirectObject add(PdfObject object, int refNumber) throws IOException {
  381               return add(object, refNumber, true); // to false
  382           }
  383   
  384           PdfIndirectObject add(PdfObject object, int refNumber, boolean inObjStm) throws IOException {
  385               if (inObjStm && object.canBeInObjStm() && writer.isFullCompression()) {
  386                   PdfCrossReference pxref = addToObjStm(object, refNumber);
  387                   PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer);
  388                   if (!xrefs.add(pxref)) {
  389                       xrefs.remove(pxref);
  390                       xrefs.add(pxref);
  391                   }
  392                   return indirect;
  393               }
  394               else {
  395                   PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer);
  396                   PdfCrossReference pxref = new PdfCrossReference(refNumber, position);
  397                   if (!xrefs.add(pxref)) {
  398                       xrefs.remove(pxref);
  399                       xrefs.add(pxref);
  400                   }
  401                   indirect.writeTo(writer.getOs());
  402                   position = writer.getOs().getCounter();
  403                   return indirect;
  404               }
  405           }
  406   
  407           /**
  408            * Returns the offset of the Cross-Reference table.
  409            *
  410            * @return		an offset
  411            */
  412   
  413           int offset() {
  414               return position;
  415           }
  416   
  417           /**
  418            * Returns the total number of objects contained in the CrossReferenceTable of this <CODE>Body</CODE>.
  419            *
  420            * @return	a number of objects
  421            */
  422   
  423           int size() {
  424               return Math.max(((PdfCrossReference)xrefs.last()).getRefnum() + 1, refnum);
  425           }
  426   
  427           /**
  428            * Returns the CrossReferenceTable of the <CODE>Body</CODE>.
  429            * @param os
  430            * @param root
  431            * @param info
  432            * @param encryption
  433            * @param fileID
  434            * @param prevxref
  435            * @throws IOException
  436            */
  437   
  438           void writeCrossReferenceTable(OutputStream os, PdfIndirectReference root, PdfIndirectReference info, PdfIndirectReference encryption, PdfObject fileID, int prevxref) throws IOException {
  439               int refNumber = 0;
  440               if (writer.isFullCompression()) {
  441                   flushObjStm();
  442                   refNumber = getIndirectReferenceNumber();
  443                   xrefs.add(new PdfCrossReference(refNumber, position));
  444               }
  445               PdfCrossReference entry = (PdfCrossReference)xrefs.first();
  446               int first = entry.getRefnum();
  447               int len = 0;
  448               ArrayList sections = new ArrayList();
  449               for (Iterator i = xrefs.iterator(); i.hasNext(); ) {
  450                   entry = (PdfCrossReference)i.next();
  451                   if (first + len == entry.getRefnum())
  452                       ++len;
  453                   else {
  454                       sections.add(new Integer(first));
  455                       sections.add(new Integer(len));
  456                       first = entry.getRefnum();
  457                       len = 1;
  458                   }
  459               }
  460               sections.add(new Integer(first));
  461               sections.add(new Integer(len));
  462               if (writer.isFullCompression()) {
  463                   int mid = 4;
  464                   int mask = 0xff000000;
  465                   for (; mid > 1; --mid) {
  466                       if ((mask & position) != 0)
  467                           break;
  468                       mask >>>= 8;
  469                   }
  470                   ByteBuffer buf = new ByteBuffer();
  471   
  472                   for (Iterator i = xrefs.iterator(); i.hasNext(); ) {
  473                       entry = (PdfCrossReference) i.next();
  474                       entry.toPdf(mid, buf);
  475                   }
  476                   PdfStream xr = new PdfStream(buf.toByteArray());
  477                   buf = null;
  478                   xr.flateCompress(writer.getCompressionLevel());
  479                   xr.put(PdfName.SIZE, new PdfNumber(size()));
  480                   xr.put(PdfName.ROOT, root);
  481                   if (info != null) {
  482                       xr.put(PdfName.INFO, info);
  483                   }
  484                   if (encryption != null)
  485                       xr.put(PdfName.ENCRYPT, encryption);
  486                   if (fileID != null)
  487                       xr.put(PdfName.ID, fileID);
  488                   xr.put(PdfName.W, new PdfArray(new int[]{1, mid, 2}));
  489                   xr.put(PdfName.TYPE, PdfName.XREF);
  490                   PdfArray idx = new PdfArray();
  491                   for (int k = 0; k < sections.size(); ++k)
  492                       idx.add(new PdfNumber(((Integer)sections.get(k)).intValue()));
  493                   xr.put(PdfName.INDEX, idx);
  494                   if (prevxref > 0)
  495                       xr.put(PdfName.PREV, new PdfNumber(prevxref));
  496                   PdfEncryption enc = writer.crypto;
  497                   writer.crypto = null;
  498                   PdfIndirectObject indirect = new PdfIndirectObject(refNumber, xr, writer);
  499                   indirect.writeTo(writer.getOs());
  500                   writer.crypto = enc;
  501               }
  502               else {
  503                   os.write(getISOBytes("xref\n"));
  504                   Iterator i = xrefs.iterator();
  505                   for (int k = 0; k < sections.size(); k += 2) {
  506                       first = ((Integer)sections.get(k)).intValue();
  507                       len = ((Integer)sections.get(k + 1)).intValue();
  508                       os.write(getISOBytes(String.valueOf(first)));
  509                       os.write(getISOBytes(" "));
  510                       os.write(getISOBytes(String.valueOf(len)));
  511                       os.write('\n');
  512                       while (len-- > 0) {
  513                           entry = (PdfCrossReference) i.next();
  514                           entry.toPdf(os);
  515                       }
  516                   }
  517               }
  518           }
  519       }
  520   
  521       /**
  522        * <CODE>PdfTrailer</CODE> is the PDF Trailer object.
  523        * <P>
  524        * This object is described in the 'Portable Document Format Reference Manual version 1.3'
  525        * section 5.16 (page 59-60).
  526        */
  527   
  528       static class PdfTrailer extends PdfDictionary {
  529   
  530           // membervariables
  531   
  532           int offset;
  533   
  534           // constructors
  535   
  536           /**
  537            * Constructs a PDF-Trailer.
  538            *
  539            * @param		size		the number of entries in the <CODE>PdfCrossReferenceTable</CODE>
  540            * @param		offset		offset of the <CODE>PdfCrossReferenceTable</CODE>
  541            * @param		root		an indirect reference to the root of the PDF document
  542            * @param		info		an indirect reference to the info object of the PDF document
  543            * @param encryption
  544            * @param fileID
  545            * @param prevxref
  546            */
  547   
  548           PdfTrailer(int size, int offset, PdfIndirectReference root, PdfIndirectReference info, PdfIndirectReference encryption, PdfObject fileID, int prevxref) {
  549               this.offset = offset;
  550               put(PdfName.SIZE, new PdfNumber(size));
  551               put(PdfName.ROOT, root);
  552               if (info != null) {
  553                   put(PdfName.INFO, info);
  554               }
  555               if (encryption != null)
  556                   put(PdfName.ENCRYPT, encryption);
  557               if (fileID != null)
  558                   put(PdfName.ID, fileID);
  559               if (prevxref > 0)
  560                   put(PdfName.PREV, new PdfNumber(prevxref));
  561           }
  562   
  563           /**
  564            * Returns the PDF representation of this <CODE>PdfObject</CODE>.
  565            * @param writer
  566            * @param os
  567            * @throws IOException
  568            */
  569           public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
  570               os.write(getISOBytes("trailer\n"));
  571               super.toPdf(null, os);
  572               os.write(getISOBytes("\nstartxref\n"));
  573               os.write(getISOBytes(String.valueOf(offset)));
  574               os.write(getISOBytes("\n%%EOF\n"));
  575           }
  576       }
  577   
  578   //	ESSENTIALS
  579   
  580   //	Construct a PdfWriter instance
  581   
  582       /**
  583        * Constructs a <CODE>PdfWriter</CODE>.
  584        */
  585       protected PdfWriter() {
  586       }
  587   
  588       /**
  589        * Constructs a <CODE>PdfWriter</CODE>.
  590        * <P>
  591        * Remark: a PdfWriter can only be constructed by calling the method
  592        * <CODE>getInstance(Document document, OutputStream os)</CODE>.
  593        *
  594        * @param	document	The <CODE>PdfDocument</CODE> that has to be written
  595        * @param	os			The <CODE>OutputStream</CODE> the writer has to write to.
  596        */
  597   
  598       protected PdfWriter(PdfDocument document, OutputStream os) {
  599           super(document, os);
  600           pdf = document;
  601           directContent = new PdfContentByte(this);
  602           directContentUnder = new PdfContentByte(this);
  603       }
  604   
  605       /**
  606        * Use this method to get an instance of the <CODE>PdfWriter</CODE>.
  607        *
  608        * @param	document	The <CODE>Document</CODE> that has to be written
  609        * @param	os	The <CODE>OutputStream</CODE> the writer has to write to.
  610        * @return	a new <CODE>PdfWriter</CODE>
  611        *
  612        * @throws	DocumentException on error
  613        */
  614   
  615       public static PdfWriter getInstance(Document document, OutputStream os)
  616       throws DocumentException {
  617           PdfDocument pdf = new PdfDocument();
  618           document.addDocListener(pdf);
  619           PdfWriter writer = new PdfWriter(pdf, os);
  620           pdf.addWriter(writer);
  621           return writer;
  622       }
  623   
  624       /**
  625        * Use this method to get an instance of the <CODE>PdfWriter</CODE>.
  626        *
  627        * @return a new <CODE>PdfWriter</CODE>
  628        * @param document The <CODE>Document</CODE> that has to be written
  629        * @param os The <CODE>OutputStream</CODE> the writer has to write to.
  630        * @param listener A <CODE>DocListener</CODE> to pass to the PdfDocument.
  631        * @throws DocumentException on error
  632        */
  633   
  634       public static PdfWriter getInstance(Document document, OutputStream os, DocListener listener)
  635       throws DocumentException {
  636           PdfDocument pdf = new PdfDocument();
  637           pdf.addDocListener(listener);
  638           document.addDocListener(pdf);
  639           PdfWriter writer = new PdfWriter(pdf, os);
  640           pdf.addWriter(writer);
  641           return writer;
  642       }
  643   
  644   //	the PdfDocument instance
  645   
  646       /** the pdfdocument object. */
  647       protected PdfDocument pdf;
  648   
  649       /**
  650        * Gets the <CODE>PdfDocument</CODE> associated with this writer.
  651        * @return the <CODE>PdfDocument</CODE>
  652        */
  653   
  654       PdfDocument getPdfDocument() {
  655           return pdf;
  656       }
  657   
  658       /**
  659        * Use this method to get the info dictionary if you want to
  660        * change it directly (add keys and values to the info dictionary).
  661        * @return the info dictionary
  662        */
  663       public PdfDictionary getInfo() {
  664           return pdf.getInfo();
  665       }
  666   
  667       /**
  668        * Use this method to get the current vertical page position.
  669        * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects
  670        *   for elements that do not terminate the lines they've started because those lines will get
  671        *   terminated.
  672        * @return The current vertical page position.
  673        */
  674       public float getVerticalPosition(boolean ensureNewLine) {
  675           return pdf.getVerticalPosition(ensureNewLine);
  676       }
  677   
  678       /**
  679        * Sets the initial leading for the PDF document.
  680        * This has to be done before the document is opened.
  681        * @param	leading	the initial leading
  682        * @since	2.1.6
  683        * @throws	DocumentException	if you try setting the leading after the document was opened.
  684        */
  685       public void setInitialLeading(float leading) throws DocumentException {
  686       	if (open)
  687       		throw new DocumentException("You can't set the initial leading if the document is already open.");
  688       	pdf.setLeading(leading);
  689       }
  690       
  691   //	the PdfDirectContentByte instances
  692   
  693   /*
  694    * You should see Direct Content as a canvas on which you can draw
  695    * graphics and text. One canvas goes on top of the page (getDirectContent),
  696    * the other goes underneath (getDirectContentUnder).
  697    * You can always the same object throughout your document,
  698    * even if you have moved to a new page. Whatever you add on
  699    * the canvas will be displayed on top or under the current page.
  700    */
  701   
  702       /** The direct content in this document. */
  703       protected PdfContentByte directContent;
  704   
  705       /** The direct content under in this document. */
  706       protected PdfContentByte directContentUnder;
  707   
  708       /**
  709        * Use this method to get the direct content for this document.
  710        * There is only one direct content, multiple calls to this method
  711        * will allways retrieve the same object.
  712        * @return the direct content
  713        */
  714   
  715       public PdfContentByte getDirectContent() {
  716           if (!open)
  717               throw new RuntimeException("The document is not open.");
  718           return directContent;
  719       }
  720   
  721       /**
  722        * Use this method to get the direct content under for this document.
  723        * There is only one direct content, multiple calls to this method
  724        * will always retrieve the same object.
  725        * @return the direct content
  726        */
  727   
  728       public PdfContentByte getDirectContentUnder() {
  729           if (!open)
  730               throw new RuntimeException("The document is not open.");
  731           return directContentUnder;
  732       }
  733   
  734       /**
  735        * Resets all the direct contents to empty.
  736        * This happens when a new page is started.
  737        */
  738       void resetContent() {
  739           directContent.reset();
  740           directContentUnder.reset();
  741       }
  742   
  743   //	PDF body
  744   
  745   /*
  746    * A PDF file has 4 parts: a header, a body, a cross-reference table, and a trailer.
  747    * The body contains all the PDF objects that make up the PDF document.
  748    * Each element gets a reference (a set of numbers) and the byte position of
  749    * every object is stored in the cross-reference table.
  750    * Use these methods only if you know what you're doing.
  751    */
  752   
  753       /** body of the PDF document */
  754       protected PdfBody body;
  755   
  756       /**
  757        * Adds the local destinations to the body of the document.
  758        * @param dest the <CODE>HashMap</CODE> containing the destinations
  759        * @throws IOException on error
  760        */
  761   
  762       void addLocalDestinations(TreeMap dest) throws IOException {
  763           for (Iterator i = dest.entrySet().iterator(); i.hasNext();) {
  764               Map.Entry entry = (Map.Entry) i.next();
  765               String name = (String) entry.getKey();
  766               Object obj[] = (Object[]) entry.getValue();
  767               PdfDestination destination = (PdfDestination)obj[2];
  768               if (obj[1] == null)
  769                   obj[1] = getPdfIndirectReference();
  770               if (destination == null)
  771                   addToBody(new PdfString("invalid_" + name), (PdfIndirectReference)obj[1]);
  772               else
  773                   addToBody(destination, (PdfIndirectReference)obj[1]);
  774           }
  775       }
  776   
  777       /**
  778        * Use this method to add a PDF object to the PDF body.
  779        * Use this method only if you know what you're doing!
  780        * @param object
  781        * @return a PdfIndirectObject
  782        * @throws IOException
  783        */
  784       public PdfIndirectObject addToBody(PdfObject object) throws IOException {
  785           PdfIndirectObject iobj = body.add(object);
  786           return iobj;
  787       }
  788   
  789       /**
  790        * Use this method to add a PDF object to the PDF body.
  791        * Use this method only if you know what you're doing!
  792        * @param object
  793        * @param inObjStm
  794        * @return a PdfIndirectObject
  795        * @throws IOException
  796        */
  797       public PdfIndirectObject addToBody(PdfObject object, boolean inObjStm) throws IOException {
  798           PdfIndirectObject iobj = body.add(object, inObjStm);
  799           return iobj;
  800       }
  801   
  802       /**
  803        * Use this method to add a PDF object to the PDF body.
  804        * Use this method only if you know what you're doing!
  805        * @param object
  806        * @param ref
  807        * @return a PdfIndirectObject
  808        * @throws IOException
  809        */
  810       public PdfIndirectObject addToBody(PdfObject object, PdfIndirectReference ref) throws IOException {
  811           PdfIndirectObject iobj = body.add(object, ref);
  812           return iobj;
  813       }
  814   
  815       /**
  816        * Use this method to add a PDF object to the PDF body.
  817        * Use this method only if you know what you're doing!
  818        * @param object
  819        * @param ref
  820        * @param inObjStm
  821        * @return a PdfIndirectObject
  822        * @throws IOException
  823        */
  824       public PdfIndirectObject addToBody(PdfObject object, PdfIndirectReference ref, boolean inObjStm) throws IOException {
  825           PdfIndirectObject iobj = body.add(object, ref, inObjStm);
  826           return iobj;
  827       }
  828   
  829       /**
  830        * Use this method to add a PDF object to the PDF body.
  831        * Use this method only if you know what you're doing!
  832        * @param object
  833        * @param refNumber
  834        * @return a PdfIndirectObject
  835        * @throws IOException
  836        */
  837       public PdfIndirectObject addToBody(PdfObject object, int refNumber) throws IOException {
  838           PdfIndirectObject iobj = body.add(object, refNumber);
  839           return iobj;
  840       }
  841   
  842       /**
  843        * Use this method to add a PDF object to the PDF body.
  844        * Use this method only if you know what you're doing!
  845        * @param object
  846        * @param refNumber
  847        * @param inObjStm
  848        * @return a PdfIndirectObject
  849        * @throws IOException
  850        */
  851       public PdfIndirectObject addToBody(PdfObject object, int refNumber, boolean inObjStm) throws IOException {
  852           PdfIndirectObject iobj = body.add(object, refNumber, inObjStm);
  853           return iobj;
  854       }
  855   
  856       /**
  857        * Use this to get an <CODE>PdfIndirectReference</CODE> for an object that
  858        * will be created in the future.
  859        * Use this method only if you know what you're doing!
  860        * @return the <CODE>PdfIndirectReference</CODE>
  861        */
  862   
  863       public PdfIndirectReference getPdfIndirectReference() {
  864           return body.getPdfIndirectReference();
  865       }
  866   
  867       int getIndirectReferenceNumber() {
  868           return body.getIndirectReferenceNumber();
  869       }
  870   
  871       /**
  872        * Returns the outputStreamCounter.
  873        * @return the outputStreamCounter
  874        */
  875       OutputStreamCounter getOs() {
  876           return os;
  877       }
  878   
  879   
  880   //	PDF Catalog
  881   
  882   /*
  883    * The Catalog is also called the root object of the document.
  884    * Whereas the Cross-Reference maps the objects number with the
  885    * byte offset so that the viewer can find the objects, the
  886    * Catalog tells the viewer the numbers of the objects needed
  887    * to render the document.
  888    */
  889   
  890       protected PdfDictionary getCatalog(PdfIndirectReference rootObj)
  891       {
  892           PdfDictionary catalog = pdf.getCatalog(rootObj);
  893           // [F12] tagged PDF
  894           if (tagged) {
  895               try {
  896                   getStructureTreeRoot().buildTree();
  897               }
  898               catch (Exception e) {
  899                   throw new ExceptionConverter(e);
  900               }
  901               catalog.put(PdfName.STRUCTTREEROOT, structureTreeRoot.getReference());
  902               PdfDictionary mi = new PdfDictionary();
  903               mi.put(PdfName.MARKED, PdfBoolean.PDFTRUE);
  904               if (userProperties)
  905                   mi.put(PdfName.USERPROPERTIES, PdfBoolean.PDFTRUE);
  906               catalog.put(PdfName.MARKINFO, mi);
  907           }
  908           // [F13] OCG
  909           if (!documentOCG.isEmpty()) {
  910           	fillOCProperties(false);
  911           	catalog.put(PdfName.OCPROPERTIES, OCProperties);
  912           }
  913           return catalog;
  914       }
  915   
  916       /** Holds value of property extraCatalog this is used for Output Intents. */
  917       protected PdfDictionary extraCatalog;
  918   
  919       /**
  920        * Sets extra keys to the catalog.
  921        * @return the catalog to change
  922        */
  923       public PdfDictionary getExtraCatalog() {
  924           if (extraCatalog == null)
  925               extraCatalog = new PdfDictionary();
  926           return this.extraCatalog;
  927       }
  928   
  929   //	PdfPages
  930   
  931   /*
  932    * The page root keeps the complete page tree of the document.
  933    * There's an entry in the Catalog that refers to the root
  934    * of the page tree, the page tree contains the references
  935    * to pages and other page trees.
  936    */
  937   
  938       /** The root of the page tree. */
  939       protected PdfPages root = new PdfPages(this);
  940       /** The PdfIndirectReference to the pages. */
  941       protected ArrayList pageReferences = new ArrayList();
  942       /** The current page number. */
  943       protected int currentPageNumber = 1;
  944       /**
  945        * The value of the Tabs entry in the page dictionary.
  946        * @since	2.1.5
  947        */
  948       protected PdfName tabs = null;
  949   
  950       /**
  951        * Use this method to make sure the page tree has a linear structure
  952        * (every leave is attached directly to the root).
  953        * Use this method to allow page reordering with method reorderPages.
  954        */
  955        public void setLinearPageMode() {
  956           root.setLinearMode(null);
  957       }
  958   
  959       /**
  960        * Use this method to reorder the pages in the document.
  961        * A <CODE>null</CODE> argument value only returns the number of pages to process.
  962        * It is advisable to issue a <CODE>Document.newPage()</CODE> before using this method.
  963        * @return the total number of pages
  964        * @param order an array with the new page sequence. It must have the
  965        * same size as the number of pages.
  966        * @throws DocumentException if all the pages are not present in the array
  967        */
  968       public int reorderPages(int order[]) throws DocumentException {
  969           return root.reorderPages(order);
  970       }
  971   
  972       /**
  973        * Use this method to get a reference to a page existing or not.
  974        * If the page does not exist yet the reference will be created
  975        * in advance. If on closing the document, a page number greater
  976        * than the total number of pages was requested, an exception
  977        * is thrown.
  978        * @param page the page number. The first page is 1
  979        * @return the reference to the page
  980        */
  981       public PdfIndirectReference getPageReference(int page) {
  982           --page;
  983           if (page < 0)
  984               throw new IndexOutOfBoundsException("The page numbers start at 1.");
  985           PdfIndirectReference ref;
  986           if (page < pageReferences.size()) {
  987               ref = (PdfIndirectReference)pageReferences.get(page);
  988               if (ref == null) {
  989                   ref = body.getPdfIndirectReference();
  990                   pageReferences.set(page, ref);
  991               }
  992           }
  993           else {
  994               int empty = page - pageReferences.size();
  995               for (int k = 0; k < empty; ++k)
  996                   pageReferences.add(null);
  997               ref = body.getPdfIndirectReference();
  998               pageReferences.add(ref);
  999           }
 1000           return ref;
 1001       }
 1002   
 1003       /**
 1004        * Gets the pagenumber of this document.
 1005        * This number can be different from the real pagenumber,
 1006        * if you have (re)set the page number previously.
 1007        * @return a page number
 1008        */
 1009   
 1010       public int getPageNumber() {
 1011           return pdf.getPageNumber();
 1012       }
 1013   
 1014       PdfIndirectReference getCurrentPage() {
 1015           return getPageReference(currentPageNumber);
 1016       }
 1017   
 1018       public int getCurrentPageNumber() {
 1019           return currentPageNumber;
 1020       }
 1021   
 1022       /**
 1023        * Sets the value for the Tabs entry in the page tree.
 1024        * @param	tabs	Can be PdfName.R, PdfName.C or PdfName.S.
 1025        * Since the Adobe Extensions Level 3, it can also be PdfName.A
 1026        * or PdfName.W
 1027        * @since	2.1.5
 1028        */
 1029       public void setTabs(PdfName tabs) {
 1030       	this.tabs = tabs;
 1031       }
 1032   
 1033       /**
 1034        * Returns the value to be used for the Tabs entry in the page tree.
 1035        * @since	2.1.5
 1036        */
 1037       public PdfName getTabs() {
 1038       	return tabs;
 1039       }
 1040   
 1041       /**
 1042        * Adds some <CODE>PdfContents</CODE> to this Writer.
 1043        * <P>
 1044        * The document has to be open before you can begin to add content
 1045        * to the body of the document.
 1046        *
 1047        * @return a <CODE>PdfIndirectReference</CODE>
 1048        * @param page the <CODE>PdfPage</CODE> to add
 1049        * @param contents the <CODE>PdfContents</CODE> of the page
 1050        * @throws PdfException on error
 1051        */
 1052   
 1053       PdfIndirectReference add(PdfPage page, PdfContents contents) throws PdfException {
 1054           if (!open) {
 1055               throw new PdfException("The document isn't open.");
 1056           }
 1057           PdfIndirectObject object;
 1058           try {
 1059               object = addToBody(contents);
 1060           }
 1061           catch(IOException ioe) {
 1062               throw new ExceptionConverter(ioe);
 1063           }
 1064           page.add(object.getIndirectReference());
 1065           // [U5]
 1066           if (group != null) {
 1067               page.put(PdfName.GROUP, group);
 1068               group = null;
 1069           }
 1070           else if (rgbTransparencyBlending) {
 1071               PdfDictionary pp = new PdfDictionary();
 1072               pp.put(PdfName.TYPE, PdfName.GROUP);
 1073               pp.put(PdfName.S, PdfName.TRANSPARENCY);
 1074               pp.put(PdfName.CS, PdfName.DEVICERGB);
 1075               page.put(PdfName.GROUP, pp);
 1076           }
 1077           root.addPage(page);
 1078           currentPageNumber++;
 1079           return null;
 1080       }
 1081   
 1082   //	page events
 1083   
 1084   /*
 1085    * Page events are specific for iText, not for PDF.
 1086    * Upon specific events (for instance when a page starts
 1087    * or ends), the corresponding method in the page event
 1088    * implementation that is added to the writer is invoked.
 1089    */
 1090   
 1091       /** The <CODE>PdfPageEvent</CODE> for this document. */
 1092       private PdfPageEvent pageEvent;
 1093   
 1094       /**
 1095        * Sets the <CODE>PdfPageEvent</CODE> for this document.
 1096        * @param event the <CODE>PdfPageEvent</CODE> for this document
 1097        */
 1098   
 1099       public void setPageEvent(PdfPageEvent event) {
 1100       	if (event == null) this.pageEvent = null;
 1101       	else if (this.pageEvent == null) this.pageEvent = event;
 1102       	else if (this.pageEvent instanceof PdfPageEventForwarder) ((PdfPageEventForwarder)this.pageEvent).addPageEvent(event);
 1103       	else {
 1104       		PdfPageEventForwarder forward = new PdfPageEventForwarder();
 1105       		forward.addPageEvent(this.pageEvent);
 1106       		forward.addPageEvent(event);
 1107       		this.pageEvent = forward;
 1108       	}
 1109       }
 1110   
 1111       /**
 1112        * Gets the <CODE>PdfPageEvent</CODE> for this document or <CODE>null</CODE>
 1113        * if none is set.
 1114        * @return the <CODE>PdfPageEvent</CODE> for this document or <CODE>null</CODE>
 1115        * if none is set
 1116        */
 1117   
 1118       public PdfPageEvent getPageEvent() {
 1119           return pageEvent;
 1120       }
 1121   
 1122   //	Open and Close methods + method that create the PDF
 1123   
 1124       /** A number referring to the previous Cross-Reference Table. */
 1125       protected int prevxref = 0;
 1126   
 1127       /**
 1128        * Signals that the <CODE>Document</CODE> has been opened and that
 1129        * <CODE>Elements</CODE> can be added.
 1130        * <P>
 1131        * When this method is called, the PDF-document header is
 1132        * written to the outputstream.
 1133        * @see com.lowagie.text.DocWriter#open()
 1134        */
 1135       public void open() {
 1136           super.open();
 1137           try {
 1138           	pdf_version.writeHeader(os);
 1139               body = new PdfBody(this);
 1140               if (pdfxConformance.isPdfX32002()) {
 1141                   PdfDictionary sec = new PdfDictionary();
 1142                   sec.put(PdfName.GAMMA, new PdfArray(new float[]{2.2f,2.2f,2.2f}));
 1143                   sec.put(PdfName.MATRIX, new PdfArray(new float[]{0.4124f,0.2126f,0.0193f,0.3576f,0.7152f,0.1192f,0.1805f,0.0722f,0.9505f}));
 1144                   sec.put(PdfName.WHITEPOINT, new PdfArray(new float[]{0.9505f,1f,1.089f}));
 1145                   PdfArray arr = new PdfArray(PdfName.CALRGB);
 1146                   arr.add(sec);
 1147                   setDefaultColorspace(PdfName.DEFAULTRGB, addToBody(arr).getIndirectReference());
 1148               }
 1149           }
 1150           catch(IOException ioe) {
 1151               throw new ExceptionConverter(ioe);
 1152           }
 1153       }
 1154   
 1155       /**
 1156        * Signals that the <CODE>Document</CODE> was closed and that no other
 1157        * <CODE>Elements</CODE> will be added.
 1158        * <P>
 1159        * The pages-tree is built and written to the outputstream.
 1160        * A Catalog is constructed, as well as an Info-object,
 1161        * the reference table is composed and everything is written
 1162        * to the outputstream embedded in a Trailer.
 1163        * @see com.lowagie.text.DocWriter#close()
 1164        */
 1165       public void close() {
 1166           if (open) {
 1167               if ((currentPageNumber - 1) != pageReferences.size())
 1168                   throw new RuntimeException("The page " + pageReferences.size() +
 1169                   " was requested but the document has only " + (currentPageNumber - 1) + " pages.");
 1170               pdf.close();
 1171               try {
 1172                   addSharedObjectsToBody();
 1173                   // add the root to the body
 1174                   PdfIndirectReference rootRef = root.writePageTree();
 1175                   // make the catalog-object and add it to the body
 1176                   PdfDictionary catalog = getCatalog(rootRef);
 1177                   // [C9] if there is XMP data to add: add it
 1178                   if (xmpMetadata != null) {
 1179                   	PdfStream xmp = new PdfStream(xmpMetadata);
 1180                   	xmp.put(PdfName.TYPE, PdfName.METADATA);
 1181                   	xmp.put(PdfName.SUBTYPE, PdfName.XML);
 1182                       if (crypto != null && !crypto.isMetadataEncrypted()) {
 1183                           PdfArray ar = new PdfArray();
 1184                           ar.add(PdfName.CRYPT);
 1185                           xmp.put(PdfName.FILTER, ar);
 1186                       }
 1187                   	catalog.put(PdfName.METADATA, body.add(xmp).getIndirectReference());
 1188                   }
 1189                   // [C10] make pdfx conformant
 1190                   if (isPdfX()) {
 1191                       pdfxConformance.completeInfoDictionary(getInfo());
 1192                       pdfxConformance.completeExtraCatalog(getExtraCatalog());
 1193                   }
 1194                   // [C11] Output Intents
 1195                   if (extraCatalog != null) {
 1196                       catalog.mergeDifferent(extraCatalog);
 1197                   }
 1198   
 1199                   writeOutlines(catalog, false);
 1200   
 1201                   // add the Catalog to the body
 1202                   PdfIndirectObject indirectCatalog = addToBody(catalog, false);
 1203                   // add the info-object to the body
 1204                   PdfIndirectObject infoObj = addToBody(getInfo(), false);
 1205   
 1206                   // [F1] encryption
 1207                   PdfIndirectReference encryption = null;
 1208                   PdfObject fileID = null;
 1209                   body.flushObjStm();
 1210                   if (crypto != null) {
 1211                       PdfIndirectObject encryptionObject = addToBody(crypto.getEncryptionDictionary(), false);
 1212                       encryption = encryptionObject.getIndirectReference();
 1213                       fileID = crypto.getFileID();
 1214                   }
 1215                   else
 1216                       fileID = PdfEncryption.createInfoId(PdfEncryption.createDocumentId());
 1217   
 1218                   // write the cross-reference table of the body
 1219                   body.writeCrossReferenceTable(os, indirectCatalog.getIndirectReference(),
 1220                       infoObj.getIndirectReference(), encryption,  fileID, prevxref);
 1221   
 1222                   // make the trailer
 1223                   // [F2] full compression
 1224                   if (fullCompression) {
 1225                       os.write(getISOBytes("startxref\n"));
 1226                       os.write(getISOBytes(String.valueOf(body.offset())));
 1227                       os.write(getISOBytes("\n%%EOF\n"));
 1228                   }
 1229                   else {
 1230                       PdfTrailer trailer = new PdfTrailer(body.size(),
 1231                       body.offset(),
 1232                       indirectCatalog.getIndirectReference(),
 1233                       infoObj.getIndirectReference(),
 1234                       encryption,
 1235                       fileID, prevxref);
 1236                       trailer.toPdf(this, os);
 1237                   }
 1238                   super.close();
 1239               }
 1240               catch(IOException ioe) {
 1241                   throw new ExceptionConverter(ioe);
 1242               }
 1243           }
 1244       }
 1245   
 1246       protected void addSharedObjectsToBody() throws IOException {
 1247           // [F3] add the fonts
 1248           for (Iterator it = documentFonts.values().iterator(); it.hasNext();) {
 1249               FontDetails details = (FontDetails)it.next();
 1250               details.writeFont(this);
 1251           }
 1252           // [F4] add the form XObjects
 1253           for (Iterator it = formXObjects.values().iterator(); it.hasNext();) {
 1254               Object objs[] = (Object[])it.next();
 1255               PdfTemplate template = (PdfTemplate)objs[1];
 1256               if (template != null && template.getIndirectReference() instanceof PRIndirectReference)
 1257                   continue;
 1258               if (template != null && template.getType() == PdfTemplate.TYPE_TEMPLATE) {
 1259                   addToBody(template.getFormXObject(compressionLevel), template.getIndirectReference());
 1260               }
 1261           }
 1262           // [F5] add all the dependencies in the imported pages
 1263           for (Iterator it = importedPages.values().iterator(); it.hasNext();) {
 1264               currentPdfReaderInstance = (PdfReaderInstance)it.next();
 1265               currentPdfReaderInstance.writeAllPages();
 1266           }
 1267           currentPdfReaderInstance = null;
 1268           // [F6] add the spotcolors
 1269           for (Iterator it = documentColors.values().iterator(); it.hasNext();) {
 1270               ColorDetails color = (ColorDetails)it.next();
 1271               addToBody(color.getSpotColor(this), color.getIndirectReference());
 1272           }
 1273           // [F7] add the pattern
 1274           for (Iterator it = documentPatterns.keySet().iterator(); it.hasNext();) {
 1275               PdfPatternPainter pat = (PdfPatternPainter)it.next();
 1276               addToBody(pat.getPattern(compressionLevel), pat.getIndirectReference());
 1277           }
 1278           // [F8] add the shading patterns
 1279           for (Iterator it = documentShadingPatterns.keySet().iterator(); it.hasNext();) {
 1280               PdfShadingPattern shadingPattern = (PdfShadingPattern)it.next();
 1281               shadingPattern.addToBody();
 1282           }
 1283           // [F9] add the shadings
 1284           for (Iterator it = documentShadings.keySet().iterator(); it.hasNext();) {
 1285               PdfShading shading = (PdfShading)it.next();
 1286               shading.addToBody();
 1287           }
 1288           // [F10] add the extgstate
 1289           for (Iterator it = documentExtGState.entrySet().iterator(); it.hasNext();) {
 1290               Map.Entry entry = (Map.Entry) it.next();
 1291               PdfDictionary gstate = (PdfDictionary) entry.getKey();
 1292               PdfObject obj[] = (PdfObject[]) entry.getValue();
 1293               addToBody(gstate, (PdfIndirectReference)obj[1]);
 1294           }
 1295           // [F11] add the properties
 1296           for (Iterator it = documentProperties.entrySet().iterator(); it.hasNext();) {
 1297               Map.Entry entry = (Map.Entry) it.next();
 1298               Object prop = entry.getKey();
 1299               PdfObject[] obj = (PdfObject[]) entry.getValue();
 1300               if (prop instanceof PdfLayerMembership){
 1301                   PdfLayerMembership layer = (PdfLayerMembership)prop;
 1302                   addToBody(layer.getPdfObject(), layer.getRef());
 1303               }
 1304               else if ((prop instanceof PdfDictionary) && !(prop instanceof PdfLayer)){
 1305                   addToBody((PdfDictionary)prop, (PdfIndirectReference)obj[1]);
 1306               }
 1307           }
 1308           // [F13] add the OCG layers
 1309           for (Iterator it = documentOCG.iterator(); it.hasNext();) {
 1310               PdfOCG layer = (PdfOCG)it.next();
 1311               addToBody(layer.getPdfObject(), layer.getRef());
 1312           }
 1313       }
 1314   
 1315   // Root data for the PDF document (used when composing the Catalog)
 1316   
 1317   //  [C1] Outlines (bookmarks)
 1318   
 1319        /**
 1320         * Use this method to get the root outline
 1321         * and construct bookmarks.
 1322         * @return the root outline
 1323         */
 1324   
 1325        public PdfOutline getRootOutline() {
 1326            return directContent.getRootOutline();
 1327        }
 1328   
 1329        protected java.util.List newBookmarks;
 1330   
 1331       /**
 1332        * Sets the bookmarks. The list structure is defined in
 1333        * {@link SimpleBookmark}.
 1334        * @param outlines the bookmarks or <CODE>null</CODE> to remove any
 1335        */
 1336       public void setOutlines(java.util.List outlines) {
 1337           newBookmarks = outlines;
 1338       }
 1339   
 1340       protected void writeOutlines(PdfDictionary catalog, boolean namedAsNames) throws IOException {
 1341           if (newBookmarks == null || newBookmarks.isEmpty())
 1342               return;
 1343           PdfDictionary top = new PdfDictionary();
 1344           PdfIndirectReference topRef = getPdfIndirectReference();
 1345           Object kids[] = SimpleBookmark.iterateOutlines(this, topRef, newBookmarks, namedAsNames);
 1346           top.put(PdfName.FIRST, (PdfIndirectReference)kids[0]);
 1347           top.put(PdfName.LAST, (PdfIndirectReference)kids[1]);
 1348           top.put(PdfName.COUNT, new PdfNumber(((Integer)kids[2]).intValue()));
 1349           addToBody(top, topRef);
 1350           catalog.put(PdfName.OUTLINES, topRef);
 1351       }
 1352   
 1353   //	[C2] PdfVersion interface
 1354        /** possible PDF version (header) */
 1355        public static final char VERSION_1_2 = '2';
 1356        /** possible PDF version (header) */
 1357        public static final char VERSION_1_3 = '3';
 1358        /** possible PDF version (header) */
 1359        public static final char VERSION_1_4 = '4';
 1360        /** possible PDF version (header) */
 1361        public static final char VERSION_1_5 = '5';
 1362        /** possible PDF version (header) */
 1363        public static final char VERSION_1_6 = '6';
 1364        /** possible PDF version (header) */
 1365        public static final char VERSION_1_7 = '7';
 1366   
 1367        /** possible PDF version (catalog) */
 1368        public static final PdfName PDF_VERSION_1_2 = new PdfName("1.2");
 1369        /** possible PDF version (catalog) */
 1370        public static final PdfName PDF_VERSION_1_3 = new PdfName("1.3");
 1371        /** possible PDF version (catalog) */
 1372        public static final PdfName PDF_VERSION_1_4 = new PdfName("1.4");
 1373        /** possible PDF version (catalog) */
 1374        public static final PdfName PDF_VERSION_1_5 = new PdfName("1.5");
 1375        /** possible PDF version (catalog) */
 1376        public static final PdfName PDF_VERSION_1_6 = new PdfName("1.6");
 1377        /** possible PDF version (catalog) */
 1378        public static final PdfName PDF_VERSION_1_7 = new PdfName("1.7");
 1379   
 1380       /** Stores the version information for the header and the catalog. */
 1381       protected PdfVersionImp pdf_version = new PdfVersionImp();
 1382   
 1383       /** @see com.lowagie.text.pdf.interfaces.PdfVersion#setPdfVersion(char) */
 1384       public void setPdfVersion(char version) {
 1385           pdf_version.setPdfVersion(version);
 1386       }
 1387   
 1388       /** @see com.lowagie.text.pdf.interfaces.PdfVersion#setAtLeastPdfVersion(char) */
 1389       public void setAtLeastPdfVersion(char version) {
 1390       	pdf_version.setAtLeastPdfVersion(version);
 1391       }
 1392   
 1393   	/** @see com.lowagie.text.pdf.interfaces.PdfVersion#setPdfVersion(com.lowagie.text.pdf.PdfName) */
 1394   	public void setPdfVersion(PdfName version) {
 1395   		pdf_version.setPdfVersion(version);
 1396   	}
 1397   
 1398   	/**
 1399   	 * @see com.lowagie.text.pdf.interfaces.PdfVersion#addDeveloperExtension(com.lowagie.text.pdf.PdfDeveloperExtension)
 1400   	 * @since	2.1.6
 1401   	 */
 1402   	public void addDeveloperExtension(PdfDeveloperExtension de) {
 1403   		pdf_version.addDeveloperExtension(de);
 1404   	}
 1405   	
 1406   	/**
 1407   	 * Returns the version information.
 1408   	 */
 1409   	PdfVersionImp getPdfVersion() {
 1410   		return pdf_version;
 1411   	}
 1412   
 1413   //  [C3] PdfViewerPreferences interface
 1414   
 1415   	// page layout (section 13.1.1 of "iText in Action")
 1416   
 1417   	/** A viewer preference */
 1418   	public static final int PageLayoutSinglePage = 1;
 1419   	/** A viewer preference */
 1420   	public static final int PageLayoutOneColumn = 2;
 1421   	/** A viewer preference */
 1422   	public static final int PageLayoutTwoColumnLeft = 4;
 1423   	/** A viewer preference */
 1424   	public static final int PageLayoutTwoColumnRight = 8;
 1425   	/** A viewer preference */
 1426   	public static final int PageLayoutTwoPageLeft = 16;
 1427   	/** A viewer preference */
 1428   	public static final int PageLayoutTwoPageRight = 32;
 1429   
 1430       // page mode (section 13.1.2 of "iText in Action")
 1431   
 1432       /** A viewer preference */
 1433       public static final int PageModeUseNone = 64;
 1434       /** A viewer preference */
 1435       public static final int PageModeUseOutlines = 128;
 1436       /** A viewer preference */
 1437       public static final int PageModeUseThumbs = 256;
 1438       /** A viewer preference */
 1439       public static final int PageModeFullScreen = 512;
 1440       /** A viewer preference */
 1441       public static final int PageModeUseOC = 1024;
 1442       /** A viewer preference */
 1443       public static final int PageModeUseAttachments = 2048;
 1444   
 1445       // values for setting viewer preferences in iText versions older than 2.x
 1446   
 1447       /** A viewer preference */
 1448       public static final int HideToolbar = 1 << 12;
 1449       /** A viewer preference */
 1450       public static final int HideMenubar = 1 << 13;
 1451       /** A viewer preference */
 1452       public static final int HideWindowUI = 1 << 14;
 1453       /** A viewer preference */
 1454       public static final int FitWindow = 1 << 15;
 1455       /** A viewer preference */
 1456       public static final int CenterWindow = 1 << 16;
 1457       /** A viewer preference */
 1458       public static final int DisplayDocTitle = 1 << 17;
 1459   
 1460       /** A viewer preference */
 1461       public static final int NonFullScreenPageModeUseNone = 1 << 18;
 1462       /** A viewer preference */
 1463       public static final int NonFullScreenPageModeUseOutlines = 1 << 19;
 1464       /** A viewer preference */
 1465       public static final int NonFullScreenPageModeUseThumbs = 1 << 20;
 1466       /** A viewer preference */
 1467       public static final int NonFullScreenPageModeUseOC = 1 << 21;
 1468   
 1469       /** A viewer preference */
 1470       public static final int DirectionL2R = 1 << 22;
 1471       /** A viewer preference */
 1472       public static final int DirectionR2L = 1 << 23;
 1473   
 1474       /** A viewer preference */
 1475       public static final int PrintScalingNone = 1 << 24;
 1476   
 1477       /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#setViewerPreferences(int) */
 1478       public void setViewerPreferences(int preferences) {
 1479           pdf.setViewerPreferences(preferences);
 1480       }
 1481   
 1482       /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#addViewerPreference(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfObject) */
 1483       public void addViewerPreference(PdfName key, PdfObject value) {
 1484       	pdf.addViewerPreference(key, value);
 1485       }
 1486   
 1487   //  [C4] Page labels
 1488   
 1489       /**
 1490        * Use this method to add page labels
 1491        * @param pageLabels the page labels
 1492        */
 1493       public void setPageLabels(PdfPageLabels pageLabels) {
 1494           pdf.setPageLabels(pageLabels);
 1495       }
 1496   
 1497   //  [C5] named objects: named destinations, javascript, embedded files
 1498   
 1499        /**
 1500         * Use this method to add a JavaScript action at the document level.
 1501         * When the document opens, all this JavaScript runs.
 1502         * @param js The JavaScript action
 1503         */
 1504        public void addJavaScript(PdfAction js) {
 1505            pdf.addJavaScript(js);
 1506        }
 1507   
 1508        /**
 1509         * Use this method to add a JavaScript action at the document level.
 1510         * When the document opens, all this JavaScript runs.
 1511         * @param code the JavaScript code
 1512         * @param unicode select JavaScript unicode. Note that the internal
 1513         * Acrobat JavaScript engine does not support unicode,
 1514         * so this may or may not work for you
 1515         */
 1516        public void addJavaScript(String code, boolean unicode) {
 1517            addJavaScript(PdfAction.javaScript(code, this, unicode));
 1518        }
 1519   
 1520        /**
 1521         * Use this method to adds a JavaScript action at the document level.
 1522         * When the document opens, all this JavaScript runs.
 1523         * @param code the JavaScript code
 1524         */
 1525        public void addJavaScript(String code) {
 1526            addJavaScript(code, false);
 1527        }
 1528        /**
 1529         * Use this method to add a JavaScript action at the document level.
 1530         * When the document opens, all this JavaScript runs.
 1531         * @param name	The name of the JS Action in the name tree
 1532         * @param js The JavaScript action
 1533         */
 1534        public void addJavaScript(String name, PdfAction js) {
 1535            pdf.addJavaScript(name, js);
 1536        }
 1537   
 1538        /**
 1539         * Use this method to add a JavaScript action at the document level.
 1540         * When the document opens, all this JavaScript runs.
 1541         * @param name	The name of the JS Action in the name tree
 1542         * @param code the JavaScript code
 1543         * @param unicode select JavaScript unicode. Note that the internal
 1544         * Acrobat JavaScript engine does not support unicode,
 1545         * so this may or may not work for you
 1546         */
 1547        public void addJavaScript(String name, String code, boolean unicode) {
 1548            addJavaScript(name, PdfAction.javaScript(code, this, unicode));
 1549        }
 1550   
 1551        /**
 1552         * Use this method to adds a JavaScript action at the document level.
 1553         * When the document opens, all this JavaScript runs.
 1554         * @param name	The name of the JS Action in the name tree
 1555         * @param code the JavaScript code
 1556         */
 1557        public void addJavaScript(String name, String code) {
 1558            addJavaScript(name, code, false);
 1559        }
 1560   
 1561        /**
 1562         * Use this method to add a file attachment at the document level.
 1563         * @param description the file description
 1564         * @param fileStore an array with the file. If it's <CODE>null</CODE>
 1565         * the file will be read from the disk
 1566         * @param file the path to the file. It will only be used if
 1567         * <CODE>fileStore</CODE> is not <CODE>null</CODE>
 1568         * @param fileDisplay the actual file name stored in the pdf
 1569         * @throws IOException on error
 1570         */
 1571        public void addFileAttachment(String description, byte fileStore[], String file, String fileDisplay) throws IOException {
 1572            addFileAttachment(description, PdfFileSpecification.fileEmbedded(this, file, fileDisplay, fileStore));
 1573        }
 1574   
 1575        /**
 1576         * Use this method to add a file attachment at the document level.
 1577         * @param description the file description
 1578         * @param fs the file specification
 1579         */
 1580        public void addFileAttachment(String description, PdfFileSpecification fs) throws IOException {
 1581            pdf.addFileAttachment(description, fs);
 1582        }
 1583   
 1584        /**
 1585         * Use this method to add a file attachment at the document level.
 1586         * @param fs the file specification
 1587         */
 1588        public void addFileAttachment(PdfFileSpecification fs) throws IOException {
 1589            addFileAttachment(null, fs);
 1590        }
 1591   
 1592   // [C6] Actions (open and additional)
 1593   
 1594        /** action value */
 1595        public static final PdfName DOCUMENT_CLOSE = PdfName.WC;
 1596        /** action value */
 1597        public static final PdfName WILL_SAVE = PdfName.WS;
 1598        /** action value */
 1599        public static final PdfName DID_SAVE = PdfName.DS;
 1600        /** action value */
 1601        public static final PdfName WILL_PRINT = PdfName.WP;
 1602        /** action value */
 1603        public static final PdfName DID_PRINT = PdfName.DP;
 1604   
 1605       /** @see com.lowagie.text.pdf.interfaces.PdfDocumentActions#setOpenAction(java.lang.String) */
 1606       public void setOpenAction(String name) {
 1607            pdf.setOpenAction(name);
 1608        }
 1609   
 1610       /** @see com.lowagie.text.pdf.interfaces.PdfDocumentActions#setOpenAction(com.lowagie.text.pdf.PdfAction) */
 1611       public void setOpenAction(PdfAction action) {
 1612            pdf.setOpenAction(action);
 1613        }
 1614   
 1615       /** @see com.lowagie.text.pdf.interfaces.PdfDocumentActions#setAdditionalAction(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfAction) */
 1616       public void setAdditionalAction(PdfName actionType, PdfAction action) throws DocumentException {
 1617            if (!(actionType.equals(DOCUMENT_CLOSE) ||
 1618            actionType.equals(WILL_SAVE) ||
 1619            actionType.equals(DID_SAVE) ||
 1620            actionType.equals(WILL_PRINT) ||
 1621            actionType.equals(DID_PRINT))) {
 1622                throw new DocumentException("Invalid additional action type: " + actionType.toString());
 1623            }
 1624            pdf.addAdditionalAction(actionType, action);
 1625        }
 1626   
 1627   //  [C7] portable collections
 1628   
 1629       /**
 1630        * Use this method to add the Collection dictionary.
 1631        * @param collection a dictionary of type PdfCollection
 1632        */
 1633       public void setCollection(PdfCollection collection) {
 1634           setAtLeastPdfVersion(VERSION_1_7);
 1635           pdf.setCollection(collection);
 1636       }
 1637   
 1638   //  [C8] AcroForm
 1639   
 1640       /** signature value */
 1641       public static final int SIGNATURE_EXISTS = 1;
 1642       /** signature value */
 1643       public static final int SIGNATURE_APPEND_ONLY = 2;
 1644   
 1645       /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#getAcroForm() */
 1646       public PdfAcroForm getAcroForm() {
 1647           return pdf.getAcroForm();
 1648       }
 1649   
 1650       /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#addAnnotation(com.lowagie.text.pdf.PdfAnnotation) */
 1651       public void addAnnotation(PdfAnnotation annot) {
 1652           pdf.addAnnotation(annot);
 1653       }
 1654   
 1655       void addAnnotation(PdfAnnotation annot, int page) {
 1656           addAnnotation(annot);
 1657       }
 1658   
 1659       /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#addCalculationOrder(com.lowagie.text.pdf.PdfFormField) */
 1660       public void addCalculationOrder(PdfFormField annot) {
 1661           pdf.addCalculationOrder(annot);
 1662       }
 1663   
 1664       /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#setSigFlags(int) */
 1665       public void setSigFlags(int f) {
 1666           pdf.setSigFlags(f);
 1667       }
 1668   
 1669   //  [C9] Metadata
 1670   
 1671       /** XMP Metadata for the document. */
 1672       protected byte[] xmpMetadata = null;
 1673   
 1674       /**
 1675        * Use this method to set the XMP Metadata.
 1676        * @param xmpMetadata The xmpMetadata to set.
 1677        */
 1678       public void setXmpMetadata(byte[] xmpMetadata) {
 1679           this.xmpMetadata = xmpMetadata;
 1680       }
 1681   
 1682       /**
 1683        * Use this method to set the XMP Metadata for each page.
 1684        * @param xmpMetadata The xmpMetadata to set.
 1685        */
 1686       public void setPageXmpMetadata(byte[] xmpMetadata) {
 1687           pdf.setXmpMetadata(xmpMetadata);
 1688       }
 1689   
 1690       /**
 1691        * Use this method to creates XMP Metadata based
 1692        * on the metadata in the PdfDocument.
 1693        */
 1694       public void createXmpMetadata() {
 1695           setXmpMetadata(createXmpMetadataBytes());
 1696       }
 1697   
 1698       /**
 1699        * @return an XmpMetadata byte array
 1700        */
 1701       private byte[] createXmpMetadataBytes() {
 1702           ByteArrayOutputStream baos = new ByteArrayOutputStream();
 1703           try {
 1704               XmpWriter xmp = new XmpWriter(baos, pdf.getInfo(), pdfxConformance.getPDFXConformance());
 1705               xmp.close();
 1706           }
 1707           catch (IOException ioe) {
 1708               ioe.printStackTrace();
 1709           }
 1710           return baos.toByteArray();
 1711       }
 1712   
 1713   //  [C10] PDFX Conformance
 1714       /** A PDF/X level. */
 1715       public static final int PDFXNONE = 0;
 1716       /** A PDF/X level. */
 1717       public static final int PDFX1A2001 = 1;
 1718       /** A PDF/X level. */
 1719       public static final int PDFX32002 = 2;
 1720       /** PDFA-1A level. */
 1721       public static final int PDFA1A = 3;
 1722       /** PDFA-1B level. */
 1723       public static final int PDFA1B = 4;
 1724   
 1725       /** Stores the PDF/X level. */
 1726       private PdfXConformanceImp pdfxConformance = new PdfXConformanceImp();
 1727   
 1728       /** @see com.lowagie.text.pdf.interfaces.PdfXConformance#setPDFXConformance(int) */
 1729       public void setPDFXConformance(int pdfx) {
 1730           if (pdfxConformance.getPDFXConformance() == pdfx)
 1731               return;
 1732           if (pdf.isOpen())
 1733               throw new PdfXConformanceException("PDFX conformance can only be set before opening the document.");
 1734           if (crypto != null)
 1735               throw new PdfXConformanceException("A PDFX conforming document cannot be encrypted.");
 1736           if (pdfx == PDFA1A || pdfx == PDFA1B)
 1737               setPdfVersion(VERSION_1_4);
 1738           else if (pdfx != PDFXNONE)
 1739               setPdfVersion(VERSION_1_3);
 1740           pdfxConformance.setPDFXConformance(pdfx);
 1741       }
 1742   
 1743       /** @see com.lowagie.text.pdf.interfaces.PdfXConformance#getPDFXConformance() */
 1744       public int getPDFXConformance() {
 1745           return pdfxConformance.getPDFXConformance();
 1746       }
 1747   
 1748       /** @see com.lowagie.text.pdf.interfaces.PdfXConformance#isPdfX() */
 1749       public boolean isPdfX() {
 1750           return pdfxConformance.isPdfX();
 1751       }
 1752   
 1753   //  [C11] Output intents
 1754       /**
 1755        * Sets the values of the output intent dictionary. Null values are allowed to
 1756        * suppress any key.
 1757        *
 1758        * @param outputConditionIdentifier a value
 1759        * @param outputCondition           a value, "PDFA/A" to force GTS_PDFA1, otherwise cued by pdfxConformance.
 1760        * @param registryName              a value
 1761        * @param info                      a value
 1762        * @param colorProfile              a value
 1763        * @since 2.1.5
 1764        * @throws IOException on error
 1765        */
 1766       public void setOutputIntents(String outputConditionIdentifier, String outputCondition, String registryName, String info, ICC_Profile colorProfile) throws IOException {
 1767           getExtraCatalog();
 1768           PdfDictionary out = new PdfDictionary(PdfName.OUTPUTINTENT);
 1769           if (outputCondition != null)
 1770               out.put(PdfName.OUTPUTCONDITION, new PdfString(outputCondition, PdfObject.TEXT_UNICODE));
 1771           if (outputConditionIdentifier != null)
 1772               out.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString(outputConditionIdentifier, PdfObject.TEXT_UNICODE));
 1773           if (registryName != null)
 1774               out.put(PdfName.REGISTRYNAME, new PdfString(registryName, PdfObject.TEXT_UNICODE));
 1775           if (info != null)
 1776               out.put(PdfName.INFO, new PdfString(info, PdfObject.TEXT_UNICODE));
 1777           if (colorProfile != null) {
 1778               PdfStream stream = new PdfICCBased(colorProfile, compressionLevel);
 1779               out.put(PdfName.DESTOUTPUTPROFILE, addToBody(stream).getIndirectReference());
 1780           }
 1781   
 1782           PdfName intentSubtype;
 1783           if (pdfxConformance.isPdfA1() || "PDFA/1".equals(outputCondition)) {
 1784               intentSubtype = PdfName.GTS_PDFA1;
 1785           }
 1786           else {
 1787               intentSubtype = PdfName.GTS_PDFX;
 1788           }
 1789   
 1790           out.put(PdfName.S, intentSubtype);
 1791   
 1792           extraCatalog.put(PdfName.OUTPUTINTENTS, new PdfArray(out));
 1793       }
 1794   
 1795      /**
 1796        * Sets the values of the output intent dictionary. Null values are allowed to
 1797        * suppress any key.
 1798        *
 1799        * Prefer the <CODE>ICC_Profile</CODE>-based version of this method.
 1800        * @param outputConditionIdentifier a value
 1801        * @param outputCondition           a value, "PDFA/A" to force GTS_PDFA1, otherwise cued by pdfxConformance.
 1802        * @param registryName              a value
 1803        * @param info                      a value
 1804        * @param destOutputProfile         a value
 1805        * @since 1.x
 1806        *
 1807        * @throws IOException
 1808        */
 1809       public void setOutputIntents(String outputConditionIdentifier, String outputCondition, String registryName, String info, byte destOutputProfile[]) throws IOException {
 1810           ICC_Profile colorProfile = (destOutputProfile == null) ? null : ICC_Profile.getInstance(destOutputProfile);
 1811           setOutputIntents(outputConditionIdentifier, outputCondition, registryName, info, colorProfile);
 1812       }
 1813   
 1814   
 1815       /**
 1816        * Use this method to copy the output intent dictionary
 1817        * from another document to this one.
 1818        * @param reader the other document
 1819        * @param checkExistence <CODE>true</CODE> to just check for the existence of a valid output intent
 1820        * dictionary, <CODE>false</CODE> to insert the dictionary if it exists
 1821        * @throws IOException on error
 1822        * @return <CODE>true</CODE> if the output intent dictionary exists, <CODE>false</CODE>
 1823        * otherwise
 1824        */
 1825       public boolean setOutputIntents(PdfReader reader, boolean checkExistence) throws IOException {
 1826           PdfDictionary catalog = reader.getCatalog();
 1827           PdfArray outs = catalog.getAsArray(PdfName.OUTPUTINTENTS);
 1828           if (outs == null)
 1829               return false;
 1830           if (outs.isEmpty())
 1831               return false;
 1832           PdfDictionary out = outs.getAsDict(0);
 1833           PdfObject obj = PdfReader.getPdfObject(out.get(PdfName.S));
 1834           if (obj == null || !PdfName.GTS_PDFX.equals(obj))
 1835               return false;
 1836           if (checkExistence)
 1837               return true;
 1838           PRStream stream = (PRStream)PdfReader.getPdfObject(out.get(PdfName.DESTOUTPUTPROFILE));
 1839           byte destProfile[] = null;
 1840           if (stream != null) {
 1841               destProfile = PdfReader.getStreamBytes(stream);
 1842           }
 1843           setOutputIntents(getNameString(out, PdfName.OUTPUTCONDITIONIDENTIFIER), getNameString(out, PdfName.OUTPUTCONDITION),
 1844               getNameString(out, PdfName.REGISTRYNAME), getNameString(out, PdfName.INFO), destProfile);
 1845           return true;
 1846       }
 1847   
 1848       private static String getNameString(PdfDictionary dic, PdfName key) {
 1849           PdfObject obj = PdfReader.getPdfObject(dic.get(key));
 1850           if (obj == null || !obj.isString())
 1851               return null;
 1852           return ((PdfString)obj).toUnicodeString();
 1853       }
 1854   
 1855   // PDF Objects that have an impact on the PDF body
 1856   
 1857   //  [F1] PdfEncryptionSettings interface
 1858   
 1859       // types of encryption
 1860   
 1861       /** Type of encryption */
 1862       public static final int STANDARD_ENCRYPTION_40 = 0;
 1863       /** Type of encryption */
 1864       public static final int STANDARD_ENCRYPTION_128 = 1;
 1865       /** Type of encryption */
 1866       public static final int ENCRYPTION_AES_128 = 2;
 1867       /** Mask to separate the encryption type from the encryption mode. */
 1868       static final int ENCRYPTION_MASK = 7;
 1869       /** Add this to the mode to keep the metadata in clear text */
 1870       public static final int DO_NOT_ENCRYPT_METADATA = 8;
 1871       /**
 1872        * Add this to the mode to keep encrypt only the embedded files.
 1873        * @since 2.1.3
 1874        */
 1875       public static final int EMBEDDED_FILES_ONLY = 24;
 1876   
 1877       // permissions
 1878   
 1879       /** The operation permitted when the document is opened with the user password
 1880        *
 1881        * @since 2.0.7
 1882        */
 1883       public static final int ALLOW_PRINTING = 4 + 2048;
 1884   
 1885       /** The operation permitted when the document is opened with the user password
 1886        *
 1887        * @since 2.0.7
 1888        */
 1889       public static final int ALLOW_MODIFY_CONTENTS = 8;
 1890   
 1891       /** The operation permitted when the document is opened with the user password
 1892        *
 1893        * @since 2.0.7
 1894        */
 1895       public static final int ALLOW_COPY = 16;
 1896   
 1897       /** The operation permitted when the document is opened with the user password
 1898        *
 1899        * @since 2.0.7
 1900        */
 1901       public static final int ALLOW_MODIFY_ANNOTATIONS = 32;
 1902   
 1903       /** The operation permitted when the document is opened with the user password
 1904        *
 1905        * @since 2.0.7
 1906        */
 1907       public static final int ALLOW_FILL_IN = 256;
 1908   
 1909       /** The operation permitted when the document is opened with the user password
 1910        *
 1911        * @since 2.0.7
 1912        */
 1913       public static final int ALLOW_SCREENREADERS = 512;
 1914   
 1915       /** The operation permitted when the document is opened with the user password
 1916        *
 1917        * @since 2.0.7
 1918        */
 1919       public static final int ALLOW_ASSEMBLY = 1024;
 1920   
 1921       /** The operation permitted when the document is opened with the user password
 1922        *
 1923        * @since 2.0.7
 1924        */
 1925       public static final int ALLOW_DEGRADED_PRINTING = 4;
 1926   
 1927       /** @deprecated As of iText 2.0.7, use {@link #ALLOW_PRINTING} instead. Scheduled for removal at or after 2.2.0 */
 1928       public static final int AllowPrinting = ALLOW_PRINTING;
 1929       /** @deprecated As of iText 2.0.7, use {@link #ALLOW_MODIFY_CONTENTS} instead. Scheduled for removal at or after 2.2.0 */
 1930       public static final int AllowModifyContents = ALLOW_MODIFY_CONTENTS;
 1931       /** @deprecated As of iText 2.0.7, use {@link #ALLOW_COPY} instead. Scheduled for removal at or after 2.2.0 */
 1932       public static final int AllowCopy = ALLOW_COPY;
 1933       /** @deprecated As of iText 2.0.7, use {@link #ALLOW_MODIFY_ANNOTATIONS} instead. Scheduled for removal at or after 2.2.0 */
 1934       public static final int AllowModifyAnnotations = ALLOW_MODIFY_ANNOTATIONS;
 1935       /** @deprecated As of iText 2.0.7, use {@link #ALLOW_FILL_IN} instead. Scheduled for removal at or after 2.2.0 */
 1936       public static final int AllowFillIn = ALLOW_FILL_IN;
 1937       /** @deprecated As of iText 2.0.7, use {@link #ALLOW_SCREENREADERS} instead. Scheduled for removal at or after 2.2.0 */
 1938       public static final int AllowScreenReaders = ALLOW_SCREENREADERS;
 1939       /** @deprecated As of iText 2.0.7, use {@link #ALLOW_ASSEMBLY} instead. Scheduled for removal at or after 2.2.0 */
 1940       public static final int AllowAssembly = ALLOW_ASSEMBLY;
 1941       /** @deprecated As of iText 2.0.7, use {@link #ALLOW_DEGRADED_PRINTING} instead. Scheduled for removal at or after 2.2.0 */
 1942       public static final int AllowDegradedPrinting = ALLOW_DEGRADED_PRINTING;
 1943   
 1944       // Strength of the encryption (kept for historical reasons)
 1945       /** @deprecated As of iText 2.0.7, use {@link #STANDARD_ENCRYPTION_40} instead. Scheduled for removal at or after 2.2.0 */
 1946       public static final boolean STRENGTH40BITS = false;
 1947       /** @deprecated As of iText 2.0.7, use {@link #STANDARD_ENCRYPTION_128} instead. Scheduled for removal at or after 2.2.0 */
 1948       public static final boolean STRENGTH128BITS = true;
 1949   
 1950       /** Contains the business logic for cryptography. */
 1951       protected PdfEncryption crypto;
 1952       PdfEncryption getEncryption() {
 1953           return crypto;
 1954       }
 1955   
 1956       /** @see com.lowagie.text.pdf.interfaces.PdfEncryptionSettings#setEncryption(byte[], byte[], int, int) */
 1957       public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, int encryptionType) throws DocumentException {
 1958           if (pdf.isOpen())
 1959               throw new DocumentException("Encryption can only be added before opening the document.");
 1960           crypto = new PdfEncryption();
 1961           crypto.setCryptoMode(encryptionType, 0);
 1962           crypto.setupAllKeys(userPassword, ownerPassword, permissions);
 1963       }
 1964   
 1965       /** @see com.lowagie.text.pdf.interfaces.PdfEncryptionSettings#setEncryption(java.security.cert.Certificate[], int[], int) */
 1966       public void setEncryption(Certificate[] certs, int[] permissions, int encryptionType) throws DocumentException {
 1967           if (pdf.isOpen())
 1968               throw new DocumentException("Encryption can only be added before opening the document.");
 1969           crypto = new PdfEncryption();
 1970           if (certs != null) {
 1971               for (int i=0; i < certs.length; i++) {
 1972                   crypto.addRecipient(certs[i], permissions[i]);
 1973               }
 1974           }
 1975           crypto.setCryptoMode(encryptionType, 0);
 1976           crypto.getEncryptionDictionary();
 1977       }
 1978   
 1979       /**
 1980        * Sets the encryption options for this document. The userPassword and the
 1981        *  ownerPassword can be null or have zero length. In this case the ownerPassword
 1982        *  is replaced by a random string. The open permissions for the document can be
 1983        *  AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
 1984        *  AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
 1985        *  The permissions can be combined by ORing them.
 1986        * @param userPassword the user password. Can be null or empty
 1987        * @param ownerPassword the owner password. Can be null or empty
 1988        * @param permissions the user permissions
 1989        * @param strength128Bits <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
 1990        * @throws DocumentException if the document is already open
 1991        * @deprecated As of iText 2.0.3, replaced by (@link #setEncryption(byte[], byte[], int, int)}. Scheduled for removal at or after 2.2.0
 1992        */
 1993       public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits) throws DocumentException {
 1994           setEncryption(userPassword, ownerPassword, permissions, strength128Bits ? STANDARD_ENCRYPTION_128 : STANDARD_ENCRYPTION_40);
 1995       }
 1996   
 1997       /**
 1998        * Sets the encryption options for this document. The userPassword and the
 1999        *  ownerPassword can be null or have zero length. In this case the ownerPassword
 2000        *  is replaced by a random string. The open permissions for the document can be
 2001        *  AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
 2002        *  AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
 2003        *  The permissions can be combined by ORing them.
 2004        * @param strength <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
 2005        * @param userPassword the user password. Can be null or empty
 2006        * @param ownerPassword the owner password. Can be null or empty
 2007        * @param permissions the user permissions
 2008        * @throws DocumentException if the document is already open
 2009        * @deprecated As of iText 2.0.3, replaced by (@link #setEncryption(byte[], byte[], int, int)}. Scheduled for removal at or after 2.2.0
 2010        */
 2011       public void setEncryption(boolean strength, String userPassword, String ownerPassword, int permissions) throws DocumentException {
 2012           setEncryption(getISOBytes(userPassword), getISOBytes(ownerPassword), permissions, strength ? STANDARD_ENCRYPTION_128 : STANDARD_ENCRYPTION_40);
 2013       }
 2014   
 2015       /**
 2016        * Sets the encryption options for this document. The userPassword and the
 2017        *  ownerPassword can be null or have zero length. In this case the ownerPassword
 2018        *  is replaced by a random string. The open permissions for the document can be
 2019        *  AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
 2020        *  AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
 2021        *  The permissions can be combined by ORing them.
 2022        * @param encryptionType the type of encryption. It can be one of STANDARD_ENCRYPTION_40, STANDARD_ENCRYPTION_128 or ENCRYPTION_AES128.
 2023        * Optionally DO_NOT_ENCRYPT_METADATA can be ored to output the metadata in cleartext
 2024        * @param userPassword the user password. Can be null or empty
 2025        * @param ownerPassword the owner password. Can be null or empty
 2026        * @param permissions the user permissions
 2027        * @throws DocumentException if the document is already open
 2028        * @deprecated As of iText 2.0.3, replaced by (@link #setEncryption(byte[], byte[], int, int)}. Scheduled for removal at or after 2.2.0
 2029        */
 2030       public void setEncryption(int encryptionType, String userPassword, String ownerPassword, int permissions) throws DocumentException {
 2031           setEncryption(getISOBytes(userPassword), getISOBytes(ownerPassword), permissions, encryptionType);
 2032       }
 2033   
 2034   //  [F2] compression
 2035   
 2036       /** Holds value of property fullCompression. */
 2037       protected boolean fullCompression = false;
 2038   
 2039       /**
 2040        * Use this method to find out if 1.5 compression is on.
 2041        * @return the 1.5 compression status
 2042        */
 2043       public boolean isFullCompression() {
 2044           return this.fullCompression;
 2045       }
 2046   
 2047       /**
 2048        * Use this method to set the document's compression to the
 2049        * PDF 1.5 mode with object streams and xref streams.
 2050        * It can be set at any time but once set it can't be unset.
 2051        * <p>
 2052        * If set before opening the document it will also set the pdf version to 1.5.
 2053        */
 2054       public void setFullCompression() {
 2055           this.fullCompression = true;
 2056           setAtLeastPdfVersion(VERSION_1_5);
 2057       }
 2058   
 2059       /**
 2060        * The compression level of the content streams.
 2061        * @since 2.1.3
 2062        */
 2063       protected int compressionLevel = PdfStream.DEFAULT_COMPRESSION;
 2064   
 2065       /**
 2066        * Returns the compression level used for streams written by this writer.
 2067        * @return the compression level (0 = best speed, 9 = best compression, -1 is default)
 2068        * @since 2.1.3
 2069        */
 2070       public int getCompressionLevel() {
 2071           return compressionLevel;
 2072       }
 2073   
 2074       /**
 2075        * Sets the compression level to be used for streams written by this writer.
 2076        * @param compressionLevel a value between 0 (best speed) and 9 (best compression)
 2077        * @since 2.1.3
 2078        */
 2079       public void setCompressionLevel(int compressionLevel) {
 2080           if (compressionLevel < PdfStream.NO_COMPRESSION || compressionLevel > PdfStream.BEST_COMPRESSION)
 2081               this.compressionLevel = PdfStream.DEFAULT_COMPRESSION;
 2082           else
 2083               this.compressionLevel = compressionLevel;
 2084       }
 2085   
 2086   //  [F3] adding fonts
 2087   
 2088       /** The fonts of this document */
 2089       protected LinkedHashMap documentFonts = new LinkedHashMap();
 2090   
 2091       /** The font number counter for the fonts in the document. */
 2092       protected int fontNumber = 1;
 2093   
 2094       /**
 2095        * Adds a <CODE>BaseFont</CODE> to the document but not to the page resources.
 2096        * It is used for templates.
 2097        * @param bf the <CODE>BaseFont</CODE> to add
 2098        * @return an <CODE>Object[]</CODE> where position 0 is a <CODE>PdfName</CODE>
 2099        * and position 1 is an <CODE>PdfIndirectReference</CODE>
 2100        */
 2101   
 2102       FontDetails addSimple(BaseFont bf) {
 2103           if (bf.getFontType() == BaseFont.FONT_TYPE_DOCUMENT) {
 2104               return new FontDetails(new PdfName("F" + (fontNumber++)), ((DocumentFont)bf).getIndirectReference(), bf);
 2105           }
 2106           FontDetails ret = (FontDetails)documentFonts.get(bf);
 2107           if (ret == null) {
 2108               PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_FONT, bf);
 2109               ret = new FontDetails(new PdfName("F" + (fontNumber++)), body.getPdfIndirectReference(), bf);
 2110               documentFonts.put(bf, ret);
 2111           }
 2112           return ret;
 2113       }
 2114   
 2115       void eliminateFontSubset(PdfDictionary fonts) {
 2116           for (Iterator it = documentFonts.values().iterator(); it.hasNext();) {
 2117               FontDetails ft = (FontDetails)it.next();
 2118               if (fonts.get(ft.getFontName()) != null)
 2119                   ft.setSubset(false);
 2120           }
 2121       }
 2122   
 2123   //  [F4] adding (and releasing) form XObjects
 2124   
 2125       /** The form XObjects in this document. The key is the xref and the value
 2126           is Object[]{PdfName, template}.*/
 2127       protected HashMap formXObjects = new HashMap();
 2128   
 2129       /** The name counter for the form XObjects name. */
 2130       protected int formXObjectsCounter = 1;
 2131   
 2132       /**
 2133        * Adds a template to the document but not to the page resources.
 2134        * @param template the template to add
 2135        * @param forcedName the template name, rather than a generated one. Can be null
 2136        * @return the <CODE>PdfName</CODE> for this template
 2137        */
 2138   
 2139       PdfName addDirectTemplateSimple(PdfTemplate template, PdfName forcedName) {
 2140           PdfIndirectReference ref = template.getIndirectReference();
 2141           Object obj[] = (Object[])formXObjects.get(ref);
 2142           PdfName name = null;
 2143           try {
 2144               if (obj == null) {
 2145                   if (forcedName == null) {
 2146                       name = new PdfName("Xf" + formXObjectsCounter);
 2147                       ++formXObjectsCounter;
 2148                   }
 2149                   else
 2150                       name = forcedName;
 2151                   if (template.getType() == PdfTemplate.TYPE_IMPORTED) {
 2152                       // If we got here from PdfCopy we'll have to fill importedPages
 2153                       PdfImportedPage ip = (PdfImportedPage)template;
 2154                       PdfReader r = ip.getPdfReaderInstance().getReader();
 2155                       if (!importedPages.containsKey(r)) {
 2156                           importedPages.put(r, ip.getPdfReaderInstance());
 2157                       }
 2158                       template = null;
 2159                   }
 2160                   formXObjects.put(ref, new Object[]{name, template});
 2161               }
 2162               else
 2163                   name = (PdfName)obj[0];
 2164           }
 2165           catch (Exception e) {
 2166               throw new ExceptionConverter(e);
 2167           }
 2168           return name;
 2169       }
 2170   
 2171       /**
 2172        * Use this method to releases the memory used by a template.
 2173        * This method writes the template to the output.
 2174        * The template can still be added to any content
 2175        * but changes to the template itself won't have any effect.
 2176        * @param tp the template to release
 2177        * @throws IOException on error
 2178        */
 2179       public void releaseTemplate(PdfTemplate tp) throws IOException {
 2180           PdfIndirectReference ref = tp.getIndirectReference();
 2181           Object[] objs = (Object[])formXObjects.get(ref);
 2182           if (objs == null || objs[1] == null)
 2183               return;
 2184           PdfTemplate template = (PdfTemplate)objs[1];
 2185           if (template.getIndirectReference() instanceof PRIndirectReference)
 2186               return;
 2187           if (template.getType() == PdfTemplate.TYPE_TEMPLATE) {
 2188               addToBody(template.getFormXObject(compressionLevel), template.getIndirectReference());
 2189               objs[1] = null;
 2190           }
 2191       }
 2192   
 2193   //  [F5] adding pages imported form other PDF documents
 2194   
 2195       protected HashMap importedPages = new HashMap();
 2196   
 2197       /**
 2198        * Use this method to get a page from other PDF document.
 2199        * The page can be used as any other PdfTemplate.
 2200        * Note that calling this method more than once with the same parameters
 2201        * will retrieve the same object.
 2202        * @param reader the PDF document where the page is
 2203        * @param pageNumber the page number. The first page is 1
 2204        * @return the template representing the imported page
 2205        */
 2206       public PdfImportedPage getImportedPage(PdfReader reader, int pageNumber) {
 2207           PdfReaderInstance inst = (PdfReaderInstance)importedPages.get(reader);
 2208           if (inst == null) {
 2209               inst = reader.getPdfReaderInstance(this);
 2210               importedPages.put(reader, inst);
 2211           }
 2212           return inst.getImportedPage(pageNumber);
 2213       }
 2214   
 2215       /**
 2216        * Use this method to writes the reader to the document
 2217        * and free the memory used by it.
 2218        * The main use is when concatenating multiple documents
 2219        * to keep the memory usage restricted to the current
 2220        * appending document.
 2221        * @param reader the <CODE>PdfReader</CODE> to free
 2222        * @throws IOException on error
 2223        */
 2224       public void freeReader(PdfReader reader) throws IOException {
 2225           currentPdfReaderInstance = (PdfReaderInstance)importedPages.get(reader);
 2226           if (currentPdfReaderInstance == null)
 2227               return;
 2228           currentPdfReaderInstance.writeAllPages();
 2229           currentPdfReaderInstance = null;
 2230           importedPages.remove(reader);
 2231       }
 2232   
 2233       /**
 2234        * Use this method to gets the current document size.
 2235        * This size only includes the data already written
 2236        * to the output stream, it does not include templates or fonts.
 2237        * It is useful if used with <CODE>freeReader()</CODE>
 2238        * when concatenating many documents and an idea of
 2239        * the current size is needed.
 2240        * @return the approximate size without fonts or templates
 2241        */
 2242       public int getCurrentDocumentSize() {
 2243           return body.offset() + body.size() * 20 + 0x48;
 2244       }
 2245   
 2246       protected PdfReaderInstance currentPdfReaderInstance;
 2247   
 2248       protected int getNewObjectNumber(PdfReader reader, int number, int generation) {
 2249           return currentPdfReaderInstance.getNewObjectNumber(number, generation);
 2250       }
 2251   
 2252       RandomAccessFileOrArray getReaderFile(PdfReader reader) {
 2253           return currentPdfReaderInstance.getReaderFile();
 2254       }
 2255   
 2256   //  [F6] spot colors
 2257   
 2258       /** The colors of this document */
 2259       protected HashMap documentColors = new HashMap();
 2260   
 2261       /** The color number counter for the colors in the document. */
 2262       protected int colorNumber = 1;
 2263   
 2264       PdfName getColorspaceName() {
 2265           return new PdfName("CS" + (colorNumber++));
 2266       }
 2267   
 2268       /**
 2269        * Adds a <CODE>SpotColor</CODE> to the document but not to the page resources.
 2270        * @param spc the <CODE>SpotColor</CODE> to add
 2271        * @return an <CODE>Object[]</CODE> where position 0 is a <CODE>PdfName</CODE>
 2272        * and position 1 is an <CODE>PdfIndirectReference</CODE>
 2273        */
 2274       ColorDetails addSimple(PdfSpotColor spc) {
 2275           ColorDetails ret = (ColorDetails)documentColors.get(spc);
 2276           if (ret == null) {
 2277               ret = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), spc);
 2278               documentColors.put(spc, ret);
 2279           }
 2280           return ret;
 2281       }
 2282   
 2283   //  [F7] document patterns
 2284   
 2285       /** The patterns of this document */
 2286       protected HashMap documentPatterns = new HashMap();
 2287   
 2288       /** The pattern number counter for the colors in the document. */
 2289       protected int patternNumber = 1;
 2290   
 2291       PdfName addSimplePattern(PdfPatternPainter painter) {
 2292           PdfName name = (PdfName)documentPatterns.get(painter);
 2293           try {
 2294               if ( name == null ) {
 2295                   name = new PdfName("P" + patternNumber);
 2296                   ++patternNumber;
 2297                   documentPatterns.put(painter, name);
 2298               }
 2299           } catch (Exception e) {
 2300               throw new ExceptionConverter(e);
 2301           }
 2302           return name;
 2303       }
 2304   
 2305   //  [F8] shading patterns
 2306   
 2307       protected HashMap documentShadingPatterns = new HashMap();
 2308   
 2309       void addSimpleShadingPattern(PdfShadingPattern shading) {
 2310           if (!documentShadingPatterns.containsKey(shading)) {
 2311               shading.setName(patternNumber);
 2312               ++patternNumber;
 2313               documentShadingPatterns.put(shading, null);
 2314               addSimpleShading(shading.getShading());
 2315           }
 2316       }
 2317   
 2318   //  [F9] document shadings
 2319   
 2320       protected HashMap documentShadings = new HashMap();
 2321   
 2322       void addSimpleShading(PdfShading shading) {
 2323           if (!documentShadings.containsKey(shading)) {
 2324               documentShadings.put(shading, null);
 2325               shading.setName(documentShadings.size());
 2326           }
 2327       }
 2328   
 2329   // [F10] extended graphics state (for instance for transparency)
 2330   
 2331       protected HashMap documentExtGState = new HashMap();
 2332   
 2333       PdfObject[] addSimpleExtGState(PdfDictionary gstate) {
 2334           if (!documentExtGState.containsKey(gstate)) {
 2335               PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_GSTATE, gstate);
 2336               documentExtGState.put(gstate, new PdfObject[]{new PdfName("GS" + (documentExtGState.size() + 1)), getPdfIndirectReference()});
 2337           }
 2338           return (PdfObject[])documentExtGState.get(gstate);
 2339       }
 2340   
 2341   //  [F11] adding properties (OCG, marked content)
 2342   
 2343       protected HashMap documentProperties = new HashMap();
 2344       PdfObject[] addSimpleProperty(Object prop, PdfIndirectReference refi) {
 2345           if (!documentProperties.containsKey(prop)) {
 2346               if (prop instanceof PdfOCG)
 2347               	PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_LAYER, null);
 2348               documentProperties.put(prop, new PdfObject[]{new PdfName("Pr" + (documentProperties.size() + 1)), refi});
 2349           }
 2350           return (PdfObject[])documentProperties.get(prop);
 2351       }
 2352   
 2353       boolean propertyExists(Object prop) {
 2354           return documentProperties.containsKey(prop);
 2355       }
 2356   
 2357   //  [F12] tagged PDF
 2358   
 2359       protected boolean tagged = false;
 2360       protected PdfStructureTreeRoot structureTreeRoot;
 2361   
 2362       /**
 2363        * Mark this document for tagging. It must be called before open.
 2364        */
 2365       public void setTagged() {
 2366           if (open)
 2367               throw new IllegalArgumentException("Tagging must be set before opening the document.");
 2368           tagged = true;
 2369       }
 2370   
 2371       /**
 2372        * Check if the document is marked for tagging.
 2373        * @return <CODE>true</CODE> if the document is marked for tagging
 2374        */
 2375       public boolean isTagged() {
 2376           return tagged;
 2377       }
 2378   
 2379       /**
 2380        * Gets the structure tree root. If the document is not marked for tagging it will return <CODE>null</CODE>.
 2381        * @return the structure tree root
 2382        */
 2383       public PdfStructureTreeRoot getStructureTreeRoot() {
 2384           if (tagged && structureTreeRoot == null)
 2385               structureTreeRoot = new PdfStructureTreeRoot(this);
 2386           return structureTreeRoot;
 2387       }
 2388   
 2389   //  [F13] Optional Content Groups
 2390       /** A hashSet containing all the PdfLayer objects. */
 2391       protected HashSet documentOCG = new HashSet();
 2392       /** An array list used to define the order of an OCG tree. */
 2393       protected ArrayList documentOCGorder = new ArrayList();
 2394       /** The OCProperties in a catalog dictionary. */
 2395       protected PdfOCProperties OCProperties;
 2396       /** The RBGroups array in an OCG dictionary */
 2397       protected PdfArray OCGRadioGroup = new PdfArray();
 2398       /**
 2399        * The locked array in an OCG dictionary
 2400        * @since   2.1.2
 2401        */
 2402       protected PdfArray OCGLocked = new PdfArray();
 2403   
 2404       /**
 2405        * Use this method to get the <B>Optional Content Properties Dictionary</B>.
 2406        * Each call fills the dictionary with the current layer state.
 2407        * It's advisable to only call this method right before close
 2408        * and do any modifications at that time.
 2409        * @return the Optional Content Properties Dictionary
 2410        */
 2411       public PdfOCProperties getOCProperties() {
 2412           fillOCProperties(true);
 2413           return OCProperties;
 2414       }
 2415   
 2416       /**
 2417        * Use this method to set a collection of optional content groups
 2418        * whose states are intended to follow a "radio button" paradigm.
 2419        * That is, the state of at most one optional content group
 2420        * in the array should be ON at a time: if one group is turned
 2421        * ON, all others must be turned OFF.
 2422        * @param group the radio group
 2423        */
 2424       public void addOCGRadioGroup(ArrayList group) {
 2425           PdfArray ar = new PdfArray();
 2426           for (int k = 0; k < group.size(); ++k) {
 2427               PdfLayer layer = (PdfLayer)group.get(k);
 2428               if (layer.getTitle() == null)
 2429                   ar.add(layer.getRef());
 2430           }
 2431           if (ar.size() == 0)
 2432               return;
 2433           OCGRadioGroup.add(ar);
 2434       }
 2435   
 2436       /**
 2437        * Use this method to lock an optional content group.
 2438        * The state of a locked group cannot be changed through the user interface
 2439        * of a viewer application. Producers can use this entry to prevent the visibility
 2440        * of content that depends on these groups from being changed by users.
 2441        * @param layer	the layer that needs to be added to the array of locked OCGs
 2442        * @since	2.1.2
 2443        */
 2444       public void lockLayer(PdfLayer layer) {
 2445           OCGLocked.add(layer.getRef());
 2446       }
 2447   
 2448       private static void getOCGOrder(PdfArray order, PdfLayer layer) {
 2449           if (!layer.isOnPanel())
 2450               return;
 2451           if (layer.getTitle() == null)
 2452               order.add(layer.getRef());
 2453           ArrayList children = layer.getChildren();
 2454           if (children == null)
 2455               return;
 2456           PdfArray kids = new PdfArray();
 2457           if (layer.getTitle() != null)
 2458               kids.add(new PdfString(layer.getTitle(), PdfObject.TEXT_UNICODE));
 2459           for (int k = 0; k < children.size(); ++k) {
 2460               getOCGOrder(kids, (PdfLayer)children.get(k));
 2461           }
 2462           if (kids.size() > 0)
 2463               order.add(kids);
 2464       }
 2465   
 2466       private void addASEvent(PdfName event, PdfName category) {
 2467           PdfArray arr = new PdfArray();
 2468           for (Iterator it = documentOCG.iterator(); it.hasNext();) {
 2469               PdfLayer layer = (PdfLayer)it.next();
 2470               PdfDictionary usage = (PdfDictionary)layer.get(PdfName.USAGE);
 2471               if (usage != null && usage.get(category) != null)
 2472                   arr.add(layer.getRef());
 2473           }
 2474           if (arr.size() == 0)
 2475               return;
 2476           PdfDictionary d = (PdfDictionary)OCProperties.get(PdfName.D);
 2477           PdfArray arras = (PdfArray)d.get(PdfName.AS);
 2478           if (arras == null) {
 2479               arras = new PdfArray();
 2480               d.put(PdfName.AS, arras);
 2481           }
 2482           PdfDictionary as = new PdfDictionary();
 2483           as.put(PdfName.EVENT, event);
 2484           as.put(PdfName.CATEGORY, new PdfArray(category));
 2485           as.put(PdfName.OCGS, arr);
 2486           arras.add(as);
 2487       }
 2488   
 2489       /**
 2490        * @since 2.1.2
 2491        */
 2492       protected void fillOCProperties(boolean erase) {
 2493           if (OCProperties == null)
 2494               OCProperties = new PdfOCProperties();
 2495           if (erase) {
 2496               OCProperties.remove(PdfName.OCGS);
 2497               OCProperties.remove(PdfName.D);
 2498           }
 2499           if (OCProperties.get(PdfName.OCGS) == null) {
 2500               PdfArray gr = new PdfArray();
 2501               for (Iterator it = documentOCG.iterator(); it.hasNext();) {
 2502                   PdfLayer layer = (PdfLayer)it.next();
 2503                   gr.add(layer.getRef());
 2504               }
 2505               OCProperties.put(PdfName.OCGS, gr);
 2506           }
 2507           if (OCProperties.get(PdfName.D) != null)
 2508               return;
 2509           ArrayList docOrder = new ArrayList(documentOCGorder);
 2510           for (Iterator it = docOrder.iterator(); it.hasNext();) {
 2511               PdfLayer layer = (PdfLayer)it.next();
 2512               if (layer.getParent() != null)
 2513                   it.remove();
 2514           }
 2515           PdfArray order = new PdfArray();
 2516           for (Iterator it = docOrder.iterator(); it.hasNext();) {
 2517               PdfLayer layer = (PdfLayer)it.next();
 2518               getOCGOrder(order, layer);
 2519           }
 2520           PdfDictionary d = new PdfDictionary();
 2521           OCProperties.put(PdfName.D, d);
 2522           d.put(PdfName.ORDER, order);
 2523           PdfArray gr = new PdfArray();
 2524           for (Iterator it = documentOCG.iterator(); it.hasNext();) {
 2525               PdfLayer layer = (PdfLayer)it.next();
 2526               if (!layer.isOn())
 2527                   gr.add(layer.getRef());
 2528           }
 2529           if (gr.size() > 0)
 2530               d.put(PdfName.OFF, gr);
 2531           if (OCGRadioGroup.size() > 0)
 2532               d.put(PdfName.RBGROUPS, OCGRadioGroup);
 2533           if (OCGLocked.size() > 0)
 2534               d.put(PdfName.LOCKED, OCGLocked);
 2535           addASEvent(PdfName.VIEW, PdfName.ZOOM);
 2536           addASEvent(PdfName.VIEW, PdfName.VIEW);
 2537           addASEvent(PdfName.PRINT, PdfName.PRINT);
 2538           addASEvent(PdfName.EXPORT, PdfName.EXPORT);
 2539           d.put(PdfName.LISTMODE, PdfName.VISIBLEPAGES);
 2540       }
 2541   
 2542       void registerLayer(PdfOCG layer) {
 2543           PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_LAYER, null);
 2544           if (layer instanceof PdfLayer) {
 2545               PdfLayer la = (PdfLayer)layer;
 2546               if (la.getTitle() == null) {
 2547                   if (!documentOCG.contains(layer)) {
 2548                       documentOCG.add(layer);
 2549                       documentOCGorder.add(layer);
 2550                   }
 2551               }
 2552               else {
 2553                   documentOCGorder.add(layer);
 2554               }
 2555           }
 2556           else
 2557               throw new IllegalArgumentException("Only PdfLayer is accepted.");
 2558       }
 2559   
 2560   //  User methods to change aspects of the page
 2561   
 2562   //  [U1] page size
 2563   
 2564       /**
 2565        * Use this method to get the size of the media box.
 2566        * @return a Rectangle
 2567        */
 2568       public Rectangle getPageSize() {
 2569           return pdf.getPageSize();
 2570       }
 2571   
 2572       /**
 2573        * Use this method to set the crop box.
 2574        * The crop box should not be rotated even if the page is rotated.
 2575        * This change only takes effect in the next page.
 2576        * @param crop the crop box
 2577        */
 2578       public void setCropBoxSize(Rectangle crop) {
 2579           pdf.setCropBoxSize(crop);
 2580       }
 2581   
 2582       /**
 2583        * Use this method to set the page box sizes.
 2584        * Allowed names are: "crop", "trim", "art" and "bleed".
 2585        * @param boxName the box size
 2586        * @param size the size
 2587        */
 2588       public void setBoxSize(String boxName, Rectangle size) {
 2589           pdf.setBoxSize(boxName, size);
 2590       }
 2591   
 2592       /**
 2593        * Use this method to get the size of a trim, art, crop or bleed box,
 2594        * or null if not defined.
 2595        * @param boxName crop, trim, art or bleed
 2596        */
 2597       public Rectangle getBoxSize(String boxName) {
 2598           return pdf.getBoxSize(boxName);
 2599       }
 2600   
 2601   //  [U2] take care of empty pages
 2602   
 2603       /**
 2604        * Use this method to make sure a page is added,
 2605        * even if it's empty. If you use setPageEmpty(false),
 2606        * invoking newPage() after a blank page will add a newPage.
 2607        * @param pageEmpty the state
 2608        */
 2609       public void setPageEmpty(boolean pageEmpty) {
 2610           pdf.setPageEmpty(pageEmpty);
 2611       }
 2612   
 2613   //  [U3] page actions (open and close)
 2614   
 2615       /** action value */
 2616       public static final PdfName PAGE_OPEN = PdfName.O;
 2617       /** action value */
 2618       public static final PdfName PAGE_CLOSE = PdfName.C;
 2619   
 2620       /** @see com.lowagie.text.pdf.interfaces.PdfPageActions#setPageAction(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfAction) */
 2621       public void setPageAction(PdfName actionType, PdfAction action) throws DocumentException {
 2622             if (!actionType.equals(PAGE_OPEN) && !actionType.equals(PAGE_CLOSE))
 2623                 throw new DocumentException("Invalid page additional action type: " + actionType.toString());
 2624             pdf.setPageAction(actionType, action);
 2625         }
 2626   
 2627       /** @see com.lowagie.text.pdf.interfaces.PdfPageActions#setDuration(int) */
 2628       public void setDuration(int seconds) {
 2629            pdf.setDuration(seconds);
 2630        }
 2631   
 2632       /** @see com.lowagie.text.pdf.interfaces.PdfPageActions#setTransition(com.lowagie.text.pdf.PdfTransition) */
 2633       public void setTransition(PdfTransition transition) {
 2634            pdf.setTransition(transition);
 2635        }
 2636   
 2637   //  [U4] Thumbnail image
 2638   
 2639       /**
 2640        * Use this method to set the thumbnail image for the current page.
 2641        * @param image the image
 2642        * @throws PdfException on error
 2643        * @throws DocumentException or error
 2644        */
 2645       public void setThumbnail(Image image) throws PdfException, DocumentException {
 2646           pdf.setThumbnail(image);
 2647       }
 2648   
 2649   //  [U5] Transparency groups
 2650   
 2651       /**
 2652        * A group attributes dictionary specifying the attributes
 2653        * of the page's page group for use in the transparent
 2654        * imaging model
 2655        */
 2656       protected PdfDictionary group;
 2657   
 2658       /**
 2659        * Use this method to get the group dictionary.
 2660        * @return Value of property group.
 2661        */
 2662       public PdfDictionary getGroup() {
 2663           return this.group;
 2664       }
 2665   
 2666       /**
 2667        * Use this method to set the group dictionary.
 2668        * @param group New value of property group.
 2669        */
 2670       public void setGroup(PdfDictionary group) {
 2671           this.group = group;
 2672       }
 2673   
 2674   //  [U6] space char ratio
 2675   
 2676       /** The default space-char ratio. */
 2677       public static final float SPACE_CHAR_RATIO_DEFAULT = 2.5f;
 2678       /** Disable the inter-character spacing. */
 2679       public static final float NO_SPACE_CHAR_RATIO = 10000000f;
 2680   
 2681       /**
 2682        * The ratio between the extra word spacing and the extra character spacing.
 2683        * Extra word spacing will grow <CODE>ratio</CODE> times more than extra character spacing.
 2684        */
 2685       private float spaceCharRatio = SPACE_CHAR_RATIO_DEFAULT;
 2686   
 2687       /**
 2688        * Use this method to gets the space/character extra spacing ratio
 2689        * for fully justified text.
 2690        * @return the space/character extra spacing ratio
 2691        */
 2692       public float getSpaceCharRatio() {
 2693           return spaceCharRatio;
 2694       }
 2695   
 2696       /**
 2697        * Use this method to set the ratio between the extra word spacing and
 2698        * the extra character spacing when the text is fully justified.
 2699        * Extra word spacing will grow <CODE>spaceCharRatio</CODE> times more
 2700        * than extra character spacing. If the ratio is <CODE>PdfWriter.NO_SPACE_CHAR_RATIO</CODE>
 2701        * then the extra character spacing will be zero.
 2702        * @param spaceCharRatio the ratio between the extra word spacing and the extra character spacing
 2703        */
 2704       public void setSpaceCharRatio(float spaceCharRatio) {
 2705           if (spaceCharRatio < 0.001f)
 2706               this.spaceCharRatio = 0.001f;
 2707           else
 2708               this.spaceCharRatio = spaceCharRatio;
 2709       }
 2710   
 2711   //  [U7] run direction (doesn't actually do anything)
 2712   
 2713       /** Use the default run direction. */
 2714       public static final int RUN_DIRECTION_DEFAULT = 0;
 2715       /** Do not use bidirectional reordering. */
 2716       public static final int RUN_DIRECTION_NO_BIDI = 1;
 2717       /** Use bidirectional reordering with left-to-right
 2718        * preferential run direction.
 2719        */
 2720       public static final int RUN_DIRECTION_LTR = 2;
 2721       /** Use bidirectional reordering with right-to-left
 2722        * preferential run direction.
 2723        */
 2724       public static final int RUN_DIRECTION_RTL = 3;
 2725   
 2726       protected int runDirection = RUN_DIRECTION_NO_BIDI;
 2727   
 2728       /**
 2729        * Use this method to set the run direction.
 2730        * This is only used as a placeholder as it does not affect anything.
 2731        * @param runDirection the run direction
 2732        */
 2733       public void setRunDirection(int runDirection) {
 2734           if (runDirection < RUN_DIRECTION_NO_BIDI || runDirection > RUN_DIRECTION_RTL)
 2735               throw new RuntimeException("Invalid run direction: " + runDirection);
 2736           this.runDirection = runDirection;
 2737       }
 2738   
 2739       /**
 2740        * Use this method to set the run direction.
 2741        * @return the run direction
 2742        */
 2743       public int getRunDirection() {
 2744           return runDirection;
 2745       }
 2746   
 2747   //  [U8] user units
 2748   
 2749        protected float userunit = 0f;
 2750       /**
 2751        * Use this method to get the user unit.
 2752        * A user unit is a value that defines the default user space unit.
 2753        * The minimum UserUnit is 1 (1 unit = 1/72 inch).
 2754        * The maximum UserUnit is 75,000.
 2755        * Note that this userunit only works starting with PDF1.6!
 2756        * @return Returns the userunit.
 2757        */
 2758       public float getUserunit() {
 2759           return userunit;
 2760       }
 2761       /**
 2762        * Use this method to set the user unit.
 2763        * A UserUnit is a value that defines the default user space unit.
 2764        * The minimum UserUnit is 1 (1 unit = 1/72 inch).
 2765        * The maximum UserUnit is 75,000.
 2766        * Note that this userunit only works starting with PDF1.6!
 2767        * @param userunit The userunit to set.
 2768        * @throws DocumentException on error
 2769        */
 2770        public void setUserunit(float userunit) throws DocumentException {
 2771    		if (userunit < 1f || userunit > 75000f) throw new DocumentException("UserUnit should be a value between 1 and 75000.");
 2772            this.userunit = userunit;
 2773            setAtLeastPdfVersion(VERSION_1_6);
 2774        }
 2775   
 2776   // Miscellaneous topics
 2777   
 2778   //  [M1] Color settings
 2779   
 2780       protected PdfDictionary defaultColorspace = new PdfDictionary();
 2781       /**
 2782        * Use this method to get the default colorspaces.
 2783        * @return the default colorspaces
 2784        */
 2785       public PdfDictionary getDefaultColorspace() {
 2786           return defaultColorspace;
 2787       }
 2788   
 2789       /**
 2790        * Use this method to sets the default colorspace that will be applied
 2791        * to all the document. The colorspace is only applied if another colorspace
 2792        * with the same name is not present in the content.
 2793        * <p>
 2794        * The colorspace is applied immediately when creating templates and
 2795        * at the page end for the main document content.
 2796        * @param key the name of the colorspace. It can be <CODE>PdfName.DEFAULTGRAY</CODE>, <CODE>PdfName.DEFAULTRGB</CODE>
 2797        * or <CODE>PdfName.DEFAULTCMYK</CODE>
 2798        * @param cs the colorspace. A <CODE>null</CODE> or <CODE>PdfNull</CODE> removes any colorspace with the same name
 2799        */
 2800       public void setDefaultColorspace(PdfName key, PdfObject cs) {
 2801           if (cs == null || cs.isNull())
 2802               defaultColorspace.remove(key);
 2803           defaultColorspace.put(key, cs);
 2804       }
 2805   
 2806   //  [M2] spot patterns
 2807   
 2808       protected HashMap documentSpotPatterns = new HashMap();
 2809       protected ColorDetails patternColorspaceRGB;
 2810       protected ColorDetails patternColorspaceGRAY;
 2811       protected ColorDetails patternColorspaceCMYK;
 2812   
 2813       ColorDetails addSimplePatternColorspace(Color color) {
 2814           int type = ExtendedColor.getType(color);
 2815           if (type == ExtendedColor.TYPE_PATTERN || type == ExtendedColor.TYPE_SHADING)
 2816               throw new RuntimeException("An uncolored tile pattern can not have another pattern or shading as color.");
 2817           try {
 2818               switch (type) {
 2819                   case ExtendedColor.TYPE_RGB:
 2820                       if (patternColorspaceRGB == null) {
 2821                           patternColorspaceRGB = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
 2822                           PdfArray array = new PdfArray(PdfName.PATTERN);
 2823                           array.add(PdfName.DEVICERGB);
 2824                           addToBody(array, patternColorspaceRGB.getIndirectReference());
 2825                       }
 2826                       return patternColorspaceRGB;
 2827                   case ExtendedColor.TYPE_CMYK:
 2828                       if (patternColorspaceCMYK == null) {
 2829                           patternColorspaceCMYK = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
 2830                           PdfArray array = new PdfArray(PdfName.PATTERN);
 2831                           array.add(PdfName.DEVICECMYK);
 2832                           addToBody(array, patternColorspaceCMYK.getIndirectReference());
 2833                       }
 2834                       return patternColorspaceCMYK;
 2835                   case ExtendedColor.TYPE_GRAY:
 2836                       if (patternColorspaceGRAY == null) {
 2837                           patternColorspaceGRAY = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
 2838                           PdfArray array = new PdfArray(PdfName.PATTERN);
 2839                           array.add(PdfName.DEVICEGRAY);
 2840                           addToBody(array, patternColorspaceGRAY.getIndirectReference());
 2841                       }
 2842                       return patternColorspaceGRAY;
 2843                   case ExtendedColor.TYPE_SEPARATION: {
 2844                       ColorDetails details = addSimple(((SpotColor)color).getPdfSpotColor());
 2845                       ColorDetails patternDetails = (ColorDetails)documentSpotPatterns.get(details);
 2846                       if (patternDetails == null) {
 2847                           patternDetails = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
 2848                           PdfArray array = new PdfArray(PdfName.PATTERN);
 2849                           array.add(details.getIndirectReference());
 2850                           addToBody(array, patternDetails.getIndirectReference());
 2851                           documentSpotPatterns.put(details, patternDetails);
 2852                       }
 2853                       return patternDetails;
 2854                   }
 2855                   default:
 2856                       throw new RuntimeException("Invalid color type in PdfWriter.addSimplePatternColorspace().");
 2857               }
 2858           }
 2859           catch (Exception e) {
 2860               throw new RuntimeException(e.getMessage());
 2861           }
 2862       }
 2863   
 2864   //  [M3] Images
 2865   
 2866       /**
 2867        * Use this method to get the strictImageSequence status.
 2868        * @return value of property strictImageSequence
 2869        */
 2870       public boolean isStrictImageSequence() {
 2871           return pdf.isStrictImageSequence();
 2872       }
 2873   
 2874       /**
 2875        * Use this method to set the image sequence, so that it follows
 2876        * the text in strict order (or not).
 2877        * @param strictImageSequence new value of property strictImageSequence
 2878        *
 2879        */
 2880       public void setStrictImageSequence(boolean strictImageSequence) {
 2881           pdf.setStrictImageSequence(strictImageSequence);
 2882       }
 2883   
 2884       /**
 2885        * Use this method to clear text wrapping around images (if applicable).
 2886        * @throws DocumentException
 2887        */
 2888       public void clearTextWrap() throws DocumentException {
 2889           pdf.clearTextWrap();
 2890       }
 2891   
 2892       /** Dictionary, containing all the images of the PDF document */
 2893       protected PdfDictionary imageDictionary = new PdfDictionary();
 2894   
 2895       /** This is the list with all the images in the document. */
 2896       private HashMap images = new HashMap();
 2897   
 2898       /**
 2899        * Use this method to adds an image to the document
 2900        * but not to the page resources. It is used with
 2901        * templates and <CODE>Document.add(Image)</CODE>.
 2902        * Use this method only if you know what you're doing!
 2903        * @param image the <CODE>Image</CODE> to add
 2904        * @return the name of the image added
 2905        * @throws PdfException on error
 2906        * @throws DocumentException on error
 2907        */
 2908       public PdfName addDirectImageSimple(Image image) throws PdfException, DocumentException {
 2909           return addDirectImageSimple(image, null);
 2910       }
 2911   
 2912       /**
 2913        * Adds an image to the document but not to the page resources.
 2914        * It is used with templates and <CODE>Document.add(Image)</CODE>.
 2915        * Use this method only if you know what you're doing!
 2916        * @param image the <CODE>Image</CODE> to add
 2917        * @param fixedRef the reference to used. It may be <CODE>null</CODE>,
 2918        * a <CODE>PdfIndirectReference</CODE> or a <CODE>PRIndirectReference</CODE>.
 2919        * @return the name of the image added
 2920        * @throws PdfException on error
 2921        * @throws DocumentException on error
 2922        */
 2923       public PdfName addDirectImageSimple(Image image, PdfIndirectReference fixedRef) throws PdfException, DocumentException {
 2924           PdfName name;
 2925           // if the images is already added, just retrieve the name
 2926           if (images.containsKey(image.getMySerialId())) {
 2927               name = (PdfName) images.get(image.getMySerialId());
 2928           }
 2929           // if it's a new image, add it to the document
 2930           else {
 2931               if (image.isImgTemplate()) {
 2932                   name = new PdfName("img" + images.size());
 2933                   if(image instanceof ImgWMF){
 2934                       try {
 2935                           ImgWMF wmf = (ImgWMF)image;
 2936                           wmf.readWMF(PdfTemplate.createTemplate(this, 0, 0));
 2937                       }
 2938                       catch (Exception e) {
 2939                           throw new DocumentException(e);
 2940                       }
 2941                   }
 2942               }
 2943               else {
 2944                   PdfIndirectReference dref = image.getDirectReference();
 2945                   if (dref != null) {
 2946                       PdfName rname = new PdfName("img" + images.size());
 2947                       images.put(image.getMySerialId(), rname);
 2948                       imageDictionary.put(rname, dref);
 2949                       return rname;
 2950                   }
 2951                   Image maskImage = image.getImageMask();
 2952                   PdfIndirectReference maskRef = null;
 2953                   if (maskImage != null) {
 2954                       PdfName mname = (PdfName)images.get(maskImage.getMySerialId());
 2955                       maskRef = getImageReference(mname);
 2956                   }
 2957                   PdfImage i = new PdfImage(image, "img" + images.size(), maskRef);
 2958                   if (image instanceof ImgJBIG2) {
 2959                       byte[] globals = ((ImgJBIG2) image).getGlobalBytes();
 2960                       if (globals != null) {
 2961                           PdfDictionary decodeparms = new PdfDictionary();
 2962                           decodeparms.put(PdfName.JBIG2GLOBALS, getReferenceJBIG2Globals(globals));
 2963                           i.put(PdfName.DECODEPARMS, decodeparms);
 2964                       }
 2965                   }
 2966                   if (image.hasICCProfile()) {
 2967                       PdfICCBased icc = new PdfICCBased(image.getICCProfile(), image.getCompressionLevel());
 2968                       PdfIndirectReference iccRef = add(icc);
 2969                       PdfArray iccArray = new PdfArray();
 2970                       iccArray.add(PdfName.ICCBASED);
 2971                       iccArray.add(iccRef);
 2972                       PdfArray colorspace = i.getAsArray(PdfName.COLORSPACE);
 2973                       if (colorspace != null) {
 2974                           if (colorspace.size() > 1 && PdfName.INDEXED.equals(colorspace.getPdfObject(0)))
 2975                               colorspace.set(1, iccArray);
 2976                           else
 2977                               i.put(PdfName.COLORSPACE, iccArray);
 2978                       }
 2979                       else
 2980                           i.put(PdfName.COLORSPACE, iccArray);
 2981                   }
 2982                   add(i, fixedRef);
 2983                   name = i.name();
 2984               }
 2985               images.put(image.getMySerialId(), name);
 2986           }
 2987           return name;
 2988       }
 2989   
 2990       /**
 2991        * Writes a <CODE>PdfImage</CODE> to the outputstream.
 2992        *
 2993        * @param pdfImage the image to be added
 2994        * @return a <CODE>PdfIndirectReference</CODE> to the encapsulated image
 2995        * @throws PdfException when a document isn't open yet, or has been closed
 2996        */
 2997   
 2998       PdfIndirectReference add(PdfImage pdfImage, PdfIndirectReference fixedRef) throws PdfException {
 2999           if (! imageDictionary.contains(pdfImage.name())) {
 3000               PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_IMAGE, pdfImage);
 3001               if (fixedRef instanceof PRIndirectReference) {
 3002                   PRIndirectReference r2 = (PRIndirectReference)fixedRef;
 3003                   fixedRef = new PdfIndirectReference(0, getNewObjectNumber(r2.getReader(), r2.getNumber(), r2.getGeneration()));
 3004               }
 3005               try {
 3006                   if (fixedRef == null)
 3007                       fixedRef = addToBody(pdfImage).getIndirectReference();
 3008                   else
 3009                       addToBody(pdfImage, fixedRef);
 3010               }
 3011               catch(IOException ioe) {
 3012                   throw new ExceptionConverter(ioe);
 3013               }
 3014               imageDictionary.put(pdfImage.name(), fixedRef);
 3015               return fixedRef;
 3016           }
 3017           return (PdfIndirectReference) imageDictionary.get(pdfImage.name());
 3018       }
 3019   
 3020       /**
 3021        * return the <CODE>PdfIndirectReference</CODE> to the image with a given name.
 3022        *
 3023        * @param name the name of the image
 3024        * @return a <CODE>PdfIndirectReference</CODE>
 3025        */
 3026   
 3027       PdfIndirectReference getImageReference(PdfName name) {
 3028           return (PdfIndirectReference) imageDictionary.get(name);
 3029       }
 3030   
 3031       protected PdfIndirectReference add(PdfICCBased icc) {
 3032           PdfIndirectObject object;
 3033           try {
 3034               object = addToBody(icc);
 3035           }
 3036           catch(IOException ioe) {
 3037               throw new ExceptionConverter(ioe);
 3038           }
 3039           return object.getIndirectReference();
 3040       }
 3041   
 3042       /**
 3043        * A HashSet with Stream objects containing JBIG2 Globals
 3044        * @since 2.1.5
 3045        */
 3046       protected HashMap JBIG2Globals = new HashMap();
 3047       /**
 3048        * Gets an indirect reference to a JBIG2 Globals stream.
 3049        * Adds the stream if it hasn't already been added to the writer.
 3050   	 * @param	content a byte array that may already been added to the writer inside a stream object.
 3051        * @since  2.1.5
 3052        */
 3053       protected PdfIndirectReference getReferenceJBIG2Globals(byte[] content) {
 3054           if (content == null) return null;
 3055           PdfStream stream;
 3056           for (Iterator i = JBIG2Globals.keySet().iterator(); i.hasNext(); ) {
 3057               stream = (PdfStream) i.next();
 3058               if (Arrays.equals(content, stream.getBytes())) {
 3059                   return (PdfIndirectReference) JBIG2Globals.get(stream);
 3060               }
 3061           }
 3062           stream = new PdfStream(content);
 3063           PdfIndirectObject ref;
 3064           try {
 3065               ref = addToBody(stream);
 3066           } catch (IOException e) {
 3067               return null;
 3068           }
 3069           JBIG2Globals.put(stream, ref.getIndirectReference());
 3070           return ref.getIndirectReference();
 3071       }
 3072   
 3073   //  [M4] Old table functionality; do we still need it?
 3074   
 3075       /**
 3076        * Checks if a <CODE>Table</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
 3077        *
 3078        * @param   table   the table that has to be checked
 3079        * @param   margin  a certain margin
 3080        * @return  <CODE>true</CODE> if the <CODE>Table</CODE> fits the page, <CODE>false</CODE> otherwise.
 3081        */
 3082   
 3083       public boolean fitsPage(Table table, float margin) {
 3084           return pdf.bottom(table) > pdf.indentBottom() + margin;
 3085       }
 3086   
 3087       /**
 3088        * Checks if a <CODE>Table</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
 3089        *
 3090        * @param   table  the table that has to be checked
 3091        * @return  <CODE>true</CODE> if the <CODE>Table</CODE> fits the page, <CODE>false</CODE> otherwise.
 3092        */
 3093   
 3094       public boolean fitsPage(Table table) {
 3095           return fitsPage(table, 0);
 3096       }
 3097   //  [F12] tagged PDF
 3098       /**
 3099        * A flag indicating the presence of structure elements that contain user properties attributes.
 3100        */
 3101       private boolean userProperties;
 3102   
 3103       /**
 3104        * Gets the flag indicating the presence of structure elements that contain user properties attributes.
 3105        * @return the user properties flag
 3106        */
 3107       public boolean isUserProperties() {
 3108           return this.userProperties;
 3109       }
 3110   
 3111       /**
 3112        * Sets the flag indicating the presence of structure elements that contain user properties attributes.
 3113        * @param userProperties the user properties flag
 3114        */
 3115       public void setUserProperties(boolean userProperties) {
 3116           this.userProperties = userProperties;
 3117       }
 3118   
 3119       /**
 3120        * Holds value of property RGBTranparency.
 3121        */
 3122       private boolean rgbTransparencyBlending;
 3123   
 3124       /**
 3125        * Gets the transparency blending colorspace.
 3126        * @return <code>true</code> if the transparency blending colorspace is RGB, <code>false</code>
 3127        * if it is the default blending colorspace
 3128        * @since 2.1.0
 3129        */
 3130       public boolean isRgbTransparencyBlending() {
 3131           return this.rgbTransparencyBlending;
 3132       }
 3133   
 3134       /**
 3135        * Sets the transparency blending colorspace to RGB. The default blending colorspace is
 3136        * CMYK and will result in faded colors in the screen and in printing. Calling this method
 3137        * will return the RGB colors to what is expected. The RGB blending will be applied to all subsequent pages
 3138        * until other value is set.
 3139        * Note that this is a generic solution that may not work in all cases.
 3140        * @param rgbTransparencyBlending <code>true</code> to set the transparency blending colorspace to RGB, <code>false</code>
 3141        * to use the default blending colorspace
 3142        * @since 2.1.0
 3143        */
 3144       public void setRgbTransparencyBlending(boolean rgbTransparencyBlending) {
 3145           this.rgbTransparencyBlending = rgbTransparencyBlending;
 3146       }
 3147   }

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