Save This Page
Home » iText-src-2.1.3 » com.lowagie » text » pdf » [javadoc | source]
    1   /*
    2    * $Id: PdfWriter.java,v 1.96 2005/05/04 14:32:39 blowagie Exp $
    3    * $Name:  $
    4    *
    5    * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
    6    *
    7    * The contents of this file are subject to the Mozilla Public License Version 1.1
    8    * (the "License"); you may not use this file except in compliance with the License.
    9    * You may obtain a copy of the License at http://www.mozilla.org/MPL/
   10    *
   11    * Software distributed under the License is distributed on an "AS IS" basis,
   12    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   13    * for the specific language governing rights and limitations under the License.
   14    *
   15    * The Original Code is 'iText, a free JAVA-PDF library'.
   16    *
   17    * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
   18    * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
   19    * All Rights Reserved.
   20    * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
   21    * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
   22    *
   23    * Contributor(s): all the names of the contributors are added in the source code
   24    * where applicable.
   25    *
   26    * Alternatively, the contents of this file may be used under the terms of the
   27    * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
   28    * provisions of LGPL are applicable instead of those above.  If you wish to
   29    * allow use of your version of this file only under the terms of the LGPL
   30    * License and not to allow others to use your version of this file under
   31    * the MPL, indicate your decision by deleting the provisions above and
   32    * replace them with the notice and other provisions required by the LGPL.
   33    * If you do not delete the provisions above, a recipient may use your version
   34    * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
   35    *
   36    * This library is free software; you can redistribute it and/or modify it
   37    * under the terms of the MPL as stated above or under the terms of the GNU
   38    * Library General Public License as published by the Free Software Foundation;
   39    * either version 2 of the License, or any later version.
   40    *
   41    * This library is distributed in the hope that it will be useful, but WITHOUT
   42    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   43    * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
   44    * details.
   45    *
   46    * If you didn't download this code from the following link, you should check if
   47    * you aren't using an obsolete version:
   48    * http://www.lowagie.com/iText/
   49    */
   50   
   51   package com.lowagie.text.pdf;
   52   
   53   import java.awt.Color;
   54   import java.io.IOException;
   55   import java.io.OutputStream;
   56   import java.util.ArrayList;
   57   import java.util.HashMap;
   58   import java.util.Iterator;
   59   import java.util.TreeMap;
   60   import java.util.TreeSet;
   61   import java.util.HashSet;
   62   
   63   import com.lowagie.text.DocListener;
   64   import com.lowagie.text.DocWriter;
   65   import com.lowagie.text.Document;
   66   import com.lowagie.text.DocumentException;
   67   import com.lowagie.text.ExceptionConverter;
   68   import com.lowagie.text.Image;
   69   import com.lowagie.text.ImgWMF;
   70   import com.lowagie.text.Rectangle;
   71   import com.lowagie.text.Table;
   72   import com.lowagie.text.ImgPostscript;
   73   
   74   /**
   75    * A <CODE>DocWriter</CODE> class for PDF.
   76    * <P>
   77    * When this <CODE>PdfWriter</CODE> is added
   78    * to a certain <CODE>PdfDocument</CODE>, the PDF representation of every Element
   79    * added to this Document will be written to the outputstream.</P>
   80    */
   81   
   82   public class PdfWriter extends DocWriter {
   83       
   84       // inner classes
   85       
   86       /**
   87        * This class generates the structure of a PDF document.
   88        * <P>
   89        * This class covers the third section of Chapter 5 in the 'Portable Document Format
   90        * Reference Manual version 1.3' (page 55-60). It contains the body of a PDF document
   91        * (section 5.14) and it can also generate a Cross-reference Table (section 5.15).
   92        *
   93        * @see		PdfWriter
   94        * @see		PdfObject
   95        * @see		PdfIndirectObject
   96        */
   97       
   98       public static class PdfBody {
   99           
  100           // inner classes
  101           
  102           /**
  103            * <CODE>PdfCrossReference</CODE> is an entry in the PDF Cross-Reference table.
  104            */
  105           
  106           static class PdfCrossReference implements Comparable {
  107               
  108               // membervariables
  109               private int type;
  110               
  111               /**	Byte offset in the PDF file. */
  112               private int offset;
  113               
  114               private int refnum;
  115               /**	generation of the object. */
  116               private int generation;
  117               
  118               // constructors
  119               /**
  120                * Constructs a cross-reference element for a PdfIndirectObject.
  121                * @param refnum
  122                * @param	offset		byte offset of the object
  123                * @param	generation	generationnumber of the object
  124                */
  125               
  126               PdfCrossReference(int refnum, int offset, int generation) {
  127                   type = 0;
  128                   this.offset = offset;
  129                   this.refnum = refnum;
  130                   this.generation = generation;
  131               }
  132               
  133               /**
  134                * Constructs a cross-reference element for a PdfIndirectObject.
  135                * @param refnum
  136                * @param	offset		byte offset of the object
  137                */
  138               
  139               PdfCrossReference(int refnum, int offset) {
  140                   type = 1;
  141                   this.offset = offset;
  142                   this.refnum = refnum;
  143                   this.generation = 0;
  144               }
  145               
  146               PdfCrossReference(int type, int refnum, int offset, int generation) {
  147                   this.type = type;
  148                   this.offset = offset;
  149                   this.refnum = refnum;
  150                   this.generation = generation;
  151               }
  152               
  153               int getRefnum() {
  154                   return refnum;
  155               }
  156               
  157               /**
  158                * Returns the PDF representation of this <CODE>PdfObject</CODE>.
  159                * @param os
  160                * @throws IOException
  161                */
  162               
  163               public void toPdf(OutputStream os) throws IOException {
  164                   // This code makes it more difficult to port the lib to JDK1.1.x:
  165                   // StringBuffer off = new StringBuffer("0000000000").append(offset);
  166                   // off.delete(0, off.length() - 10);
  167                   // StringBuffer gen = new StringBuffer("00000").append(generation);
  168                   // gen.delete(0, gen.length() - 5);
  169                   // so it was changed into this:
  170                   String s = "0000000000" + offset;
  171                   StringBuffer off = new StringBuffer(s.substring(s.length() - 10));
  172                   s = "00000" + generation;
  173                   String gen = s.substring(s.length() - 5);
  174                   if (generation == 65535) {
  175                       os.write(getISOBytes(off.append(' ').append(gen).append(" f \n").toString()));
  176                   }
  177                   else
  178                       os.write(getISOBytes(off.append(' ').append(gen).append(" n \n").toString()));
  179               }
  180               
  181               /**
  182                * Writes PDF syntax to the OutputStream
  183                * @param midSize
  184                * @param os
  185                * @throws IOException
  186                */
  187               public void toPdf(int midSize, OutputStream os) throws IOException {
  188                   os.write((byte)type);
  189                   while (--midSize >= 0)
  190                       os.write((byte)((offset >>> (8 * midSize)) & 0xff));
  191                   os.write((byte)((generation >>> 8) & 0xff));
  192                   os.write((byte)(generation & 0xff));
  193               }
  194               
  195               /**
  196                * @see java.lang.Comparable#compareTo(java.lang.Object)
  197                */
  198               public int compareTo(Object o) {
  199                   PdfCrossReference other = (PdfCrossReference)o;
  200                   return (refnum < other.refnum ? -1 : (refnum==other.refnum ? 0 : 1));
  201               }
  202               
  203               /**
  204                * @see java.lang.Object#equals(java.lang.Object)
  205                */
  206               public boolean equals(Object obj) {
  207                   if (obj instanceof PdfCrossReference) {
  208                       PdfCrossReference other = (PdfCrossReference)obj;
  209                       return (refnum == other.refnum);
  210                   }
  211                   else
  212                       return false;
  213               }
  214               
  215           }
  216           
  217           // membervariables
  218           
  219           /** array containing the cross-reference table of the normal objects. */
  220           private TreeSet xrefs;
  221           private int refnum;
  222           /** the current byteposition in the body. */
  223           private int position;
  224           private PdfWriter writer;
  225           // constructors
  226           
  227           /**
  228            * Constructs a new <CODE>PdfBody</CODE>.
  229            * @param writer
  230            */
  231           PdfBody(PdfWriter writer) {
  232               xrefs = new TreeSet();
  233               xrefs.add(new PdfCrossReference(0, 0, 65535));
  234               position = writer.getOs().getCounter();
  235               refnum = 1;
  236               this.writer = writer;
  237           }
  238           
  239           void setRefnum(int refnum) {
  240               this.refnum = refnum;
  241           }
  242           
  243           // methods
  244           
  245           private static final int OBJSINSTREAM = 200;
  246           
  247           private ByteBuffer index;
  248           private ByteBuffer streamObjects;
  249           private int currentObjNum;
  250           private int numObj = 0;
  251           
  252           private PdfWriter.PdfBody.PdfCrossReference addToObjStm(PdfObject obj, int nObj) throws IOException {
  253               if (numObj >= OBJSINSTREAM)
  254                   flushObjStm();
  255               if (index == null) {
  256                   index = new ByteBuffer();
  257                   streamObjects = new ByteBuffer();
  258                   currentObjNum = getIndirectReferenceNumber();
  259                   numObj = 0;
  260               }
  261               int p = streamObjects.size();
  262               int idx = numObj++;
  263               PdfEncryption enc = writer.crypto;
  264               writer.crypto = null;
  265               obj.toPdf(writer, streamObjects);
  266               writer.crypto = enc;
  267               streamObjects.append(' ');
  268               index.append(nObj).append(' ').append(p).append(' ');
  269               return new PdfWriter.PdfBody.PdfCrossReference(2, nObj, currentObjNum, idx);
  270           }
  271           
  272           private void flushObjStm() throws IOException {
  273               if (numObj == 0)
  274                   return;
  275               int first = index.size();
  276               index.append(streamObjects);
  277               PdfStream stream = new PdfStream(index.toByteArray());
  278               stream.flateCompress();
  279               stream.put(PdfName.TYPE, PdfName.OBJSTM);
  280               stream.put(PdfName.N, new PdfNumber(numObj));
  281               stream.put(PdfName.FIRST, new PdfNumber(first));
  282               add(stream, currentObjNum);
  283               index = null;
  284               streamObjects = null;
  285               numObj = 0;
  286           }
  287           
  288           /**
  289            * Adds a <CODE>PdfObject</CODE> to the body.
  290            * <P>
  291            * This methods creates a <CODE>PdfIndirectObject</CODE> with a
  292            * certain number, containing the given <CODE>PdfObject</CODE>.
  293            * It also adds a <CODE>PdfCrossReference</CODE> for this object
  294            * to an <CODE>ArrayList</CODE> that will be used to build the
  295            * Cross-reference Table.
  296            *
  297            * @param		object			a <CODE>PdfObject</CODE>
  298            * @return		a <CODE>PdfIndirectObject</CODE>
  299            * @throws IOException
  300            */
  301           
  302           PdfIndirectObject add(PdfObject object) throws IOException {
  303               return add(object, getIndirectReferenceNumber());
  304           }
  305           
  306           PdfIndirectObject add(PdfObject object, boolean inObjStm) throws IOException {
  307               return add(object, getIndirectReferenceNumber(), inObjStm);
  308           }
  309           
  310           /**
  311            * Gets a PdfIndirectReference for an object that will be created in the future.
  312            * @return a PdfIndirectReference
  313            */
  314           
  315           PdfIndirectReference getPdfIndirectReference() {
  316               return new PdfIndirectReference(0, getIndirectReferenceNumber());
  317           }
  318           
  319           int getIndirectReferenceNumber() {
  320               int n = refnum++;
  321               xrefs.add(new PdfCrossReference(n, 0, 65536));
  322               return n;
  323           }
  324           
  325           /**
  326            * Adds a <CODE>PdfObject</CODE> to the body given an already existing
  327            * PdfIndirectReference.
  328            * <P>
  329            * This methods creates a <CODE>PdfIndirectObject</CODE> with the number given by
  330            * <CODE>ref</CODE>, containing the given <CODE>PdfObject</CODE>.
  331            * It also adds a <CODE>PdfCrossReference</CODE> for this object
  332            * to an <CODE>ArrayList</CODE> that will be used to build the
  333            * Cross-reference Table.
  334            *
  335            * @param		object			a <CODE>PdfObject</CODE>
  336            * @param		ref		        a <CODE>PdfIndirectReference</CODE>
  337            * @return		a <CODE>PdfIndirectObject</CODE>
  338            * @throws IOException
  339            */
  340           
  341           PdfIndirectObject add(PdfObject object, PdfIndirectReference ref) throws IOException {
  342               return add(object, ref.getNumber());
  343           }
  344           
  345           PdfIndirectObject add(PdfObject object, PdfIndirectReference ref, boolean inObjStm) throws IOException {
  346               return add(object, ref.getNumber(), inObjStm);
  347           }
  348           
  349           PdfIndirectObject add(PdfObject object, int refNumber) throws IOException {
  350               return add(object, refNumber, true); // to false
  351           }
  352           
  353           PdfIndirectObject add(PdfObject object, int refNumber, boolean inObjStm) throws IOException {
  354               if (inObjStm && object.canBeInObjStm() && writer.isFullCompression()) {
  355                   PdfCrossReference pxref = addToObjStm(object, refNumber);
  356                   PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer);
  357                   if (!xrefs.add(pxref)) {
  358                       xrefs.remove(pxref);
  359                       xrefs.add(pxref);
  360                   }
  361                   return indirect;
  362               }
  363               else {
  364                   PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer);
  365                   PdfCrossReference pxref = new PdfCrossReference(refNumber, position);
  366                   if (!xrefs.add(pxref)) {
  367                       xrefs.remove(pxref);
  368                       xrefs.add(pxref);
  369                   }
  370                   indirect.writeTo(writer.getOs());
  371                   position = writer.getOs().getCounter();
  372                   return indirect;
  373               }
  374           }
  375           
  376           /**
  377            * Adds a <CODE>PdfResources</CODE> object to the body.
  378            *
  379            * @param		object			the <CODE>PdfResources</CODE>
  380            * @return		a <CODE>PdfIndirectObject</CODE>
  381            */
  382           
  383   //        PdfIndirectObject add(PdfResources object) {
  384   //            return add(object);
  385   //        }
  386           
  387           /**
  388            * Adds a <CODE>PdfPages</CODE> object to the body.
  389            *
  390            * @param		object			the root of the document
  391            * @return		a <CODE>PdfIndirectObject</CODE>
  392            */
  393           
  394   //        PdfIndirectObject add(PdfPages object) throws IOException {
  395   //            PdfIndirectObject indirect = new PdfIndirectObject(PdfWriter.ROOT, object, writer);
  396   //            rootOffset = position;
  397   //            indirect.writeTo(writer.getOs());
  398   //            position = writer.getOs().getCounter();
  399   //            return indirect;
  400   //        }
  401           
  402           /**
  403            * Returns the offset of the Cross-Reference table.
  404            *
  405            * @return		an offset
  406            */
  407           
  408           int offset() {
  409               return position;
  410           }
  411           
  412           /**
  413            * Returns the total number of objects contained in the CrossReferenceTable of this <CODE>Body</CODE>.
  414            *
  415            * @return	a number of objects
  416            */
  417           
  418           int size() {
  419               return Math.max(((PdfCrossReference)xrefs.last()).getRefnum() + 1, refnum);
  420           }
  421           
  422           /**
  423            * Returns the CrossReferenceTable of the <CODE>Body</CODE>.
  424            * @param os
  425            * @param root
  426            * @param info
  427            * @param encryption
  428            * @param fileID
  429            * @param prevxref
  430            * @throws IOException
  431            */
  432           
  433           void writeCrossReferenceTable(OutputStream os, PdfIndirectReference root, PdfIndirectReference info, PdfIndirectReference encryption, PdfObject fileID, int prevxref) throws IOException {
  434               int refNumber = 0;
  435               if (writer.isFullCompression()) {
  436                   flushObjStm();
  437                   refNumber = getIndirectReferenceNumber();
  438                   xrefs.add(new PdfCrossReference(refNumber, position));
  439               }
  440               PdfCrossReference entry = (PdfCrossReference)xrefs.first();
  441               int first = entry.getRefnum();
  442               int len = 0;
  443               ArrayList sections = new ArrayList();
  444               for (Iterator i = xrefs.iterator(); i.hasNext(); ) {
  445                   entry = (PdfCrossReference)i.next();
  446                   if (first + len == entry.getRefnum())
  447                       ++len;
  448                   else {
  449                       sections.add(new Integer(first));
  450                       sections.add(new Integer(len));
  451                       first = entry.getRefnum();
  452                       len = 1;
  453                   }
  454               }
  455               sections.add(new Integer(first));
  456               sections.add(new Integer(len));
  457               if (writer.isFullCompression()) {
  458                   int mid = 4;
  459                   int mask = 0xff000000;
  460                   for (; mid > 1; --mid) {
  461                       if ((mask & position) != 0)
  462                           break;
  463                       mask >>>= 8;
  464                   }
  465                   ByteBuffer buf = new ByteBuffer();
  466                   
  467                   for (Iterator i = xrefs.iterator(); i.hasNext(); ) {
  468                       entry = (PdfCrossReference) i.next();
  469                       entry.toPdf(mid, buf);
  470                   }
  471                   PdfStream xr = new PdfStream(buf.toByteArray());
  472                   buf = null;
  473                   xr.flateCompress();
  474                   xr.put(PdfName.SIZE, new PdfNumber(size()));
  475                   xr.put(PdfName.ROOT, root);
  476                   if (info != null) {
  477                       xr.put(PdfName.INFO, info);
  478                   }
  479                   if (encryption != null)
  480                       xr.put(PdfName.ENCRYPT, encryption);
  481                   if (fileID != null)
  482                       xr.put(PdfName.ID, fileID);
  483                   xr.put(PdfName.W, new PdfArray(new int[]{1, mid, 2}));
  484                   xr.put(PdfName.TYPE, PdfName.XREF);
  485                   PdfArray idx = new PdfArray();
  486                   for (int k = 0; k < sections.size(); ++k)
  487                       idx.add(new PdfNumber(((Integer)sections.get(k)).intValue()));
  488                   xr.put(PdfName.INDEX, idx);
  489                   if (prevxref > 0)
  490                       xr.put(PdfName.PREV, new PdfNumber(prevxref));
  491                   PdfEncryption enc = writer.crypto;
  492                   writer.crypto = null;
  493                   PdfIndirectObject indirect = new PdfIndirectObject(refNumber, xr, writer);
  494                   indirect.writeTo(writer.getOs());
  495                   writer.crypto = enc;
  496               }
  497               else {
  498                   os.write(getISOBytes("xref\n"));
  499                   Iterator i = xrefs.iterator();
  500                   for (int k = 0; k < sections.size(); k += 2) {
  501                       first = ((Integer)sections.get(k)).intValue();
  502                       len = ((Integer)sections.get(k + 1)).intValue();
  503                       os.write(getISOBytes(String.valueOf(first)));
  504                       os.write(getISOBytes(" "));
  505                       os.write(getISOBytes(String.valueOf(len)));
  506                       os.write('\n');
  507                       while (len-- > 0) {
  508                           entry = (PdfCrossReference) i.next();
  509                           entry.toPdf(os);
  510                       }
  511                   }
  512               }
  513           }
  514       }
  515       
  516       /**
  517        * <CODE>PdfTrailer</CODE> is the PDF Trailer object.
  518        * <P>
  519        * This object is described in the 'Portable Document Format Reference Manual version 1.3'
  520        * section 5.16 (page 59-60).
  521        */
  522       
  523       static class PdfTrailer extends PdfDictionary {
  524           
  525           // membervariables
  526           
  527           int offset;
  528           
  529           // constructors
  530           
  531           /**
  532            * Constructs a PDF-Trailer.
  533            *
  534            * @param		size		the number of entries in the <CODE>PdfCrossReferenceTable</CODE>
  535            * @param		offset		offset of the <CODE>PdfCrossReferenceTable</CODE>
  536            * @param		root		an indirect reference to the root of the PDF document
  537            * @param		info		an indirect reference to the info object of the PDF document
  538            * @param encryption
  539            * @param fileID
  540            * @param prevxref
  541            */
  542           
  543           PdfTrailer(int size, int offset, PdfIndirectReference root, PdfIndirectReference info, PdfIndirectReference encryption, PdfObject fileID, int prevxref) {
  544               this.offset = offset;
  545               put(PdfName.SIZE, new PdfNumber(size));
  546               put(PdfName.ROOT, root);
  547               if (info != null) {
  548                   put(PdfName.INFO, info);
  549               }
  550               if (encryption != null)
  551                   put(PdfName.ENCRYPT, encryption);
  552               if (fileID != null)
  553                   put(PdfName.ID, fileID);
  554               if (prevxref > 0)
  555                   put(PdfName.PREV, new PdfNumber(prevxref));
  556           }
  557           
  558           /**
  559            * Returns the PDF representation of this <CODE>PdfObject</CODE>.
  560            * @param writer
  561            * @param os
  562            * @throws IOException
  563            */
  564           public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
  565               os.write(getISOBytes("trailer\n"));
  566               super.toPdf(null, os);
  567               os.write(getISOBytes("\nstartxref\n"));
  568               os.write(getISOBytes(String.valueOf(offset)));
  569               os.write(getISOBytes("\n%%EOF\n"));
  570           }
  571       }
  572       // static membervariables
  573       
  574       /** A viewer preference */
  575       public static final int PageLayoutSinglePage = 1;
  576       /** A viewer preference */
  577       public static final int PageLayoutOneColumn = 2;
  578       /** A viewer preference */
  579       public static final int PageLayoutTwoColumnLeft = 4;
  580       /** A viewer preference */
  581       public static final int PageLayoutTwoColumnRight = 8;
  582       
  583       /** A viewer preference */
  584       public static final int PageModeUseNone = 16;
  585       /** A viewer preference */
  586       public static final int PageModeUseOutlines = 32;
  587       /** A viewer preference */
  588       public static final int PageModeUseThumbs = 64;
  589       /** A viewer preference */
  590       public static final int PageModeFullScreen = 128;
  591       /** A viewer preference */
  592       public static final int PageModeUseOC = 1 << 20;
  593       
  594       /** A viewer preference */
  595       public static final int HideToolbar = 256;
  596       /** A viewer preference */
  597       public static final int HideMenubar = 512;
  598       /** A viewer preference */
  599       public static final int HideWindowUI = 1024;
  600       /** A viewer preference */
  601       public static final int FitWindow = 2048;
  602       /** A viewer preference */
  603       public static final int CenterWindow = 4096;
  604       
  605       /** A viewer preference */
  606       public static final int NonFullScreenPageModeUseNone = 8192;
  607       /** A viewer preference */
  608       public static final int NonFullScreenPageModeUseOutlines = 16384;
  609       /** A viewer preference */
  610       public static final int NonFullScreenPageModeUseThumbs = 32768;
  611       /** A viewer preference */
  612       public static final int NonFullScreenPageModeUseOC = 1 << 19;
  613       
  614       /** A viewer preference */
  615       public static final int DirectionL2R = 1 << 16;
  616       /** A viewer preference */
  617       public static final int DirectionR2L = 1 << 17;
  618       /** A viewer preference */
  619       public static final int DisplayDocTitle = 1 << 18;
  620       /** A viewer preference */
  621       public static final int PrintScalingNone = 1 << 20;
  622       /** The mask to decide if a ViewerPreferences dictionary is needed */
  623       static final int ViewerPreferencesMask = 0xffff00;
  624       /** The operation permitted when the document is opened with the user password */
  625       public static final int AllowPrinting = 4 + 2048;
  626       /** The operation permitted when the document is opened with the user password */
  627       public static final int AllowModifyContents = 8;
  628       /** The operation permitted when the document is opened with the user password */
  629       public static final int AllowCopy = 16;
  630       /** The operation permitted when the document is opened with the user password */
  631       public static final int AllowModifyAnnotations = 32;
  632       /** The operation permitted when the document is opened with the user password */
  633       public static final int AllowFillIn = 256;
  634       /** The operation permitted when the document is opened with the user password */
  635       public static final int AllowScreenReaders = 512;
  636       /** The operation permitted when the document is opened with the user password */
  637       public static final int AllowAssembly = 1024;
  638       /** The operation permitted when the document is opened with the user password */
  639       public static final int AllowDegradedPrinting = 4;
  640       /** Type of encryption */
  641       public static final boolean STRENGTH40BITS = false;
  642       /** Type of encryption */
  643       public static final boolean STRENGTH128BITS = true;
  644       /** action value */
  645       public static final PdfName DOCUMENT_CLOSE = PdfName.WC;
  646       /** action value */
  647       public static final PdfName WILL_SAVE = PdfName.WS;
  648       /** action value */
  649       public static final PdfName DID_SAVE = PdfName.DS;
  650       /** action value */
  651       public static final PdfName WILL_PRINT = PdfName.WP;
  652       /** action value */
  653       public static final PdfName DID_PRINT = PdfName.DP;
  654       /** action value */
  655       public static final PdfName PAGE_OPEN = PdfName.O;
  656       /** action value */
  657       public static final PdfName PAGE_CLOSE = PdfName.C;
  658   
  659       /** signature value */
  660       public static final int SIGNATURE_EXISTS = 1;
  661       /** signature value */
  662       public static final int SIGNATURE_APPEND_ONLY = 2;
  663       
  664       /** possible PDF version */
  665       public static final char VERSION_1_2 = '2';
  666       /** possible PDF version */
  667       public static final char VERSION_1_3 = '3';
  668       /** possible PDF version */
  669       public static final char VERSION_1_4 = '4';
  670       /** possible PDF version */
  671       public static final char VERSION_1_5 = '5';
  672       /** possible PDF version */
  673       public static final char VERSION_1_6 = '6';
  674       
  675       private static final int VPOINT = 7;
  676       /** this is the header of a PDF document */
  677       protected byte[] HEADER = getISOBytes("%PDF-1.4\n%\u00e2\u00e3\u00cf\u00d3\n");
  678   
  679       protected int prevxref = 0;
  680       
  681       protected PdfPages root = new PdfPages(this);
  682       
  683       /** Dictionary, containing all the images of the PDF document */
  684       protected PdfDictionary imageDictionary = new PdfDictionary();
  685       
  686       /** This is the list with all the images in the document. */
  687       private HashMap images = new HashMap();
  688       
  689       /** The form XObjects in this document. The key is the xref and the value
  690           is Object[]{PdfName, template}.*/
  691       protected HashMap formXObjects = new HashMap();
  692       
  693       /** The name counter for the form XObjects name. */
  694       protected int formXObjectsCounter = 1;
  695       
  696       /** The font number counter for the fonts in the document. */
  697       protected int fontNumber = 1;
  698       
  699       /** The color number counter for the colors in the document. */
  700       protected int colorNumber = 1;
  701       
  702       /** The patten number counter for the colors in the document. */
  703       protected int patternNumber = 1;
  704       
  705       /** The direct content in this document. */
  706       protected PdfContentByte directContent;
  707       
  708       /** The direct content under in this document. */
  709       protected PdfContentByte directContentUnder;
  710       
  711       /** The fonts of this document */
  712       protected HashMap documentFonts = new HashMap();
  713       
  714       /** The colors of this document */
  715       protected HashMap documentColors = new HashMap();
  716       
  717       /** The patterns of this document */
  718       protected HashMap documentPatterns = new HashMap();
  719       
  720       protected HashMap documentShadings = new HashMap();
  721       
  722       protected HashMap documentShadingPatterns = new HashMap();
  723       
  724       protected ColorDetails patternColorspaceRGB;
  725       protected ColorDetails patternColorspaceGRAY;
  726       protected ColorDetails patternColorspaceCMYK;
  727       protected HashMap documentSpotPatterns = new HashMap();
  728       
  729       protected HashMap documentExtGState = new HashMap();
  730       
  731       protected HashMap documentLayers = new HashMap();
  732       protected HashSet documentOCG = new HashSet();
  733       protected ArrayList documentOCGorder = new ArrayList();
  734       protected PdfOCProperties OCProperties;
  735       protected PdfArray OCGRadioGroup = new PdfArray();
  736       
  737       protected PdfDictionary defaultColorspace = new PdfDictionary();
  738       
  739       /** PDF/X value */
  740       public static final int PDFXNONE = 0;
  741       /** PDF/X value */
  742       public static final int PDFX1A2001 = 1;
  743       /** PDF/X value */
  744       public static final int PDFX32002 = 2;
  745   
  746       private int pdfxConformance = PDFXNONE;
  747       
  748       static final int PDFXKEY_COLOR = 1;
  749       static final int PDFXKEY_CMYK = 2;
  750       static final int PDFXKEY_RGB = 3;
  751       static final int PDFXKEY_FONT = 4;
  752       static final int PDFXKEY_IMAGE = 5;
  753       static final int PDFXKEY_GSTATE = 6;
  754       static final int PDFXKEY_LAYER = 7;
  755       
  756       // membervariables
  757       
  758       /** body of the PDF document */
  759       protected PdfBody body;
  760       
  761       /** the pdfdocument object. */
  762       protected PdfDocument pdf;
  763       
  764       /** The <CODE>PdfPageEvent</CODE> for this document. */
  765       private PdfPageEvent pageEvent;
  766       
  767       protected PdfEncryption crypto;
  768       
  769       protected HashMap importedPages = new HashMap();
  770       
  771       protected PdfReaderInstance currentPdfReaderInstance;
  772       
  773       /** The PdfIndirectReference to the pages. */
  774       protected ArrayList pageReferences = new ArrayList();
  775       
  776       protected int currentPageNumber = 1;
  777       
  778       protected PdfDictionary group;
  779       
  780       /** The default space-char ratio. */    
  781       public static final float SPACE_CHAR_RATIO_DEFAULT = 2.5f;
  782       /** Disable the inter-character spacing. */    
  783       public static final float NO_SPACE_CHAR_RATIO = 10000000f;
  784       
  785       /** Use the default run direction. */    
  786       public static final int RUN_DIRECTION_DEFAULT = 0;
  787       /** Do not use bidirectional reordering. */    
  788       public static final int RUN_DIRECTION_NO_BIDI = 1;
  789       /** Use bidirectional reordering with left-to-right
  790        * preferential run direction.
  791        */    
  792       public static final int RUN_DIRECTION_LTR = 2;
  793       /** Use bidirectional reordering with right-to-left
  794        * preferential run direction.
  795        */    
  796       public static final int RUN_DIRECTION_RTL = 3;
  797       protected int runDirection = RUN_DIRECTION_NO_BIDI;
  798       /**
  799        * The ratio between the extra word spacing and the extra character spacing.
  800        * Extra word spacing will grow <CODE>ratio</CODE> times more than extra character spacing.
  801        */
  802       private float spaceCharRatio = SPACE_CHAR_RATIO_DEFAULT;
  803       
  804       /** Holds value of property extraCatalog. */
  805       private PdfDictionary extraCatalog;
  806       
  807       /**
  808        * Holds value of property fullCompression.
  809        */
  810       protected boolean fullCompression = false;
  811           
  812       // constructor
  813       
  814       protected PdfWriter() {
  815       }
  816       
  817       /**
  818        * Constructs a <CODE>PdfWriter</CODE>.
  819        * <P>
  820        * Remark: a PdfWriter can only be constructed by calling the method
  821        * <CODE>getInstance(Document document, OutputStream os)</CODE>.
  822        *
  823        * @param	document	The <CODE>PdfDocument</CODE> that has to be written
  824        * @param	os			The <CODE>OutputStream</CODE> the writer has to write to.
  825        */
  826       
  827       protected PdfWriter(PdfDocument document, OutputStream os) {
  828           super(document, os);
  829           pdf = document;
  830           directContent = new PdfContentByte(this);
  831           directContentUnder = new PdfContentByte(this);
  832       }
  833       
  834       // get an instance of the PdfWriter
  835       
  836       /**
  837        * Gets an instance of the <CODE>PdfWriter</CODE>.
  838        *
  839        * @param	document	The <CODE>Document</CODE> that has to be written
  840        * @param	os	The <CODE>OutputStream</CODE> the writer has to write to.
  841        * @return	a new <CODE>PdfWriter</CODE>
  842        *
  843        * @throws	DocumentException on error
  844        */
  845       
  846       public static PdfWriter getInstance(Document document, OutputStream os)
  847       throws DocumentException {
  848           PdfDocument pdf = new PdfDocument();
  849           document.addDocListener(pdf);
  850           PdfWriter writer = new PdfWriter(pdf, os);
  851           pdf.addWriter(writer);
  852           return writer;
  853       }
  854       
  855       /** Gets an instance of the <CODE>PdfWriter</CODE>.
  856        *
  857        * @return a new <CODE>PdfWriter</CODE>
  858        * @param document The <CODE>Document</CODE> that has to be written
  859        * @param os The <CODE>OutputStream</CODE> the writer has to write to.
  860        * @param listener A <CODE>DocListener</CODE> to pass to the PdfDocument.
  861        * @throws DocumentException on error
  862        */
  863       
  864       public static PdfWriter getInstance(Document document, OutputStream os, DocListener listener)
  865       throws DocumentException {
  866           PdfDocument pdf = new PdfDocument();
  867           pdf.addDocListener(listener);
  868           document.addDocListener(pdf);
  869           PdfWriter writer = new PdfWriter(pdf, os);
  870           pdf.addWriter(writer);
  871           return writer;
  872       }
  873       
  874       // methods to write objects to the outputstream
  875       
  876       /**
  877        * Adds some <CODE>PdfContents</CODE> to this Writer.
  878        * <P>
  879        * The document has to be open before you can begin to add content
  880        * to the body of the document.
  881        *
  882        * @return a <CODE>PdfIndirectReference</CODE>
  883        * @param page the <CODE>PdfPage</CODE> to add
  884        * @param contents the <CODE>PdfContents</CODE> of the page
  885        * @throws PdfException on error
  886        */
  887       
  888       PdfIndirectReference add(PdfPage page, PdfContents contents) throws PdfException {
  889           if (!open) {
  890               throw new PdfException("The document isn't open.");
  891           }
  892           PdfIndirectObject object;
  893           try {
  894               object = addToBody(contents);
  895           }
  896           catch(IOException ioe) {
  897               throw new ExceptionConverter(ioe);
  898           }
  899           page.add(object.getIndirectReference());
  900           if (group != null) {
  901               page.put(PdfName.GROUP, group);
  902               group = null;
  903           }
  904           root.addPage(page);
  905           currentPageNumber++;
  906           return null;
  907       }
  908       
  909       /** Adds an image to the document but not to the page resources. It is used with
  910        * templates and <CODE>Document.add(Image)</CODE>.
  911        * @param image the <CODE>Image</CODE> to add
  912        * @return the name of the image added
  913        * @throws PdfException on error
  914        * @throws DocumentException on error
  915        */
  916       PdfName addDirectImageSimple(Image image) throws PdfException, DocumentException {
  917           PdfName name;
  918           // if the images is already added, just retrieve the name
  919           if (images.containsKey(image.getMySerialId())) {
  920               name = (PdfName) images.get(image.getMySerialId());
  921           }
  922           // if it's a new image, add it to the document
  923           else {
  924               if (image.isImgTemplate()) {
  925                   name = new PdfName("img" + images.size());
  926                   if (image.templateData() == null) {
  927                       if(image instanceof ImgWMF){
  928                           try {
  929                               ImgWMF wmf = (ImgWMF)image;
  930                               wmf.readWMF(getDirectContent().createTemplate(0, 0));
  931                           }
  932                           catch (Exception e) {
  933                               throw new DocumentException(e);
  934                           }
  935                       }else{
  936                           try {
  937                               ((ImgPostscript)image).readPostscript(getDirectContent().createTemplate(0, 0));
  938                           }
  939                           catch (Exception e) {
  940                               throw new DocumentException(e);
  941                           }
  942                           
  943                       }
  944                   }
  945               }
  946               else {
  947                   Image maskImage = image.getImageMask();
  948                   PdfIndirectReference maskRef = null;
  949                   if (maskImage != null) {
  950                       PdfName mname = (PdfName)images.get(maskImage.getMySerialId());
  951                       maskRef = getImageReference(mname);
  952                   }
  953                   PdfImage i = new PdfImage(image, "img" + images.size(), maskRef);
  954                   if (image.hasICCProfile()) {
  955                       PdfICCBased icc = new PdfICCBased(image.getICCProfile());
  956                       PdfIndirectReference iccRef = add(icc);
  957                       PdfArray iccArray = new PdfArray();
  958                       iccArray.add(PdfName.ICCBASED);
  959                       iccArray.add(iccRef);
  960                       PdfObject colorspace = i.get(PdfName.COLORSPACE);
  961                       if (colorspace != null && colorspace.isArray()) {
  962                           ArrayList ar = ((PdfArray)colorspace).getArrayList();
  963                           if (ar.size() > 1 && PdfName.INDEXED.equals(ar.get(0)))
  964                               ar.set(1, iccArray);
  965                           else
  966                               i.put(PdfName.COLORSPACE, iccArray);
  967                       }
  968                       else
  969                           i.put(PdfName.COLORSPACE, iccArray);
  970                   }
  971                   add(i);
  972                   name = i.name();
  973               }
  974               images.put(image.getMySerialId(), name);
  975           }
  976           return name;
  977       }
  978   
  979       /**
  980        * Writes a <CODE>PdfImage</CODE> to the outputstream.
  981        *
  982        * @param pdfImage the image to be added
  983        * @return a <CODE>PdfIndirectReference</CODE> to the encapsulated image
  984        * @throws PdfException when a document isn't open yet, or has been closed
  985        */
  986       
  987       PdfIndirectReference add(PdfImage pdfImage) throws PdfException {
  988           if (! imageDictionary.contains(pdfImage.name())) {
  989               checkPDFXConformance(this, PDFXKEY_IMAGE, pdfImage);
  990               PdfIndirectObject object;
  991               try {
  992                   object = addToBody(pdfImage);
  993               }
  994               catch(IOException ioe) {
  995                   throw new ExceptionConverter(ioe);
  996               }
  997               imageDictionary.put(pdfImage.name(), object.getIndirectReference());
  998               return object.getIndirectReference();
  999           }
 1000           return (PdfIndirectReference) imageDictionary.get(pdfImage.name());
 1001       }
 1002       
 1003       protected PdfIndirectReference add(PdfICCBased icc) throws PdfException {
 1004           PdfIndirectObject object;
 1005           try {
 1006               object = addToBody(icc);
 1007           }
 1008           catch(IOException ioe) {
 1009               throw new ExceptionConverter(ioe);
 1010           }
 1011           return object.getIndirectReference();
 1012       }
 1013       
 1014       /**
 1015        * return the <CODE>PdfIndirectReference</CODE> to the image with a given name.
 1016        *
 1017        * @param name the name of the image
 1018        * @return a <CODE>PdfIndirectReference</CODE>
 1019        */
 1020       
 1021       PdfIndirectReference getImageReference(PdfName name) {
 1022           return (PdfIndirectReference) imageDictionary.get(name);
 1023       }
 1024       
 1025       // methods to open and close the writer
 1026       
 1027       /**
 1028        * Signals that the <CODE>Document</CODE> has been opened and that
 1029        * <CODE>Elements</CODE> can be added.
 1030        * <P>
 1031        * When this method is called, the PDF-document header is
 1032        * written to the outputstream.
 1033        */
 1034       
 1035       public void open() {
 1036           super.open();
 1037           try {
 1038               os.write(HEADER);
 1039               body = new PdfBody(this);
 1040               if (pdfxConformance == PDFX32002) {
 1041                   PdfDictionary sec = new PdfDictionary();
 1042                   sec.put(PdfName.GAMMA, new PdfArray(new float[]{2.2f,2.2f,2.2f}));
 1043                   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}));
 1044                   sec.put(PdfName.WHITEPOINT, new PdfArray(new float[]{0.9505f,1f,1.089f}));
 1045                   PdfArray arr = new PdfArray(PdfName.CALRGB);
 1046                   arr.add(sec);
 1047                   setDefaultColorspace(PdfName.DEFAULTRGB, addToBody(arr).getIndirectReference());
 1048               }
 1049           }
 1050           catch(IOException ioe) {
 1051               throw new ExceptionConverter(ioe);
 1052           }
 1053       }
 1054       
 1055       private static void getOCGOrder(PdfArray order, PdfLayer layer) {
 1056           if (!layer.isOnPanel())
 1057               return;
 1058           if (layer.getTitle() == null)
 1059               order.add(layer.getRef());
 1060           ArrayList children = layer.getChildren();
 1061           if (children == null)
 1062               return;
 1063           PdfArray kids = new PdfArray();
 1064           if (layer.getTitle() != null)
 1065               kids.add(new PdfString(layer.getTitle(), PdfObject.TEXT_UNICODE));
 1066           for (int k = 0; k < children.size(); ++k) {
 1067               getOCGOrder(kids, (PdfLayer)children.get(k));
 1068           }
 1069           if (kids.size() > 0)
 1070               order.add(kids);
 1071       }
 1072       
 1073       private void addASEvent(PdfName event, PdfName category) {
 1074           PdfArray arr = new PdfArray();
 1075           for (Iterator it = documentOCG.iterator(); it.hasNext();) {
 1076               PdfLayer layer = (PdfLayer)it.next();
 1077               PdfDictionary usage = (PdfDictionary)layer.get(PdfName.USAGE);
 1078               if (usage != null && usage.get(category) != null)
 1079                   arr.add(layer.getRef());
 1080           }
 1081           if (arr.size() == 0)
 1082               return;
 1083           PdfDictionary d = (PdfDictionary)OCProperties.get(PdfName.D);
 1084           PdfArray arras = (PdfArray)d.get(PdfName.AS);
 1085           if (arras == null) {
 1086               arras = new PdfArray();
 1087               d.put(PdfName.AS, arras);
 1088           }
 1089           PdfDictionary as = new PdfDictionary();
 1090           as.put(PdfName.EVENT, event);
 1091           as.put(PdfName.CATEGORY, new PdfArray(category));
 1092           as.put(PdfName.OCGS, arr);
 1093           arras.add(as);
 1094       }
 1095       
 1096       private void fillOCProperties(boolean erase) {
 1097           if (OCProperties == null)
 1098               OCProperties = new PdfOCProperties();
 1099           if (erase) {
 1100               OCProperties.remove(PdfName.OCGS);
 1101               OCProperties.remove(PdfName.D);
 1102           }
 1103           if (OCProperties.get(PdfName.OCGS) == null) {
 1104               PdfArray gr = new PdfArray();
 1105               for (Iterator it = documentOCG.iterator(); it.hasNext();) {
 1106                   PdfLayer layer = (PdfLayer)it.next();
 1107                   gr.add(layer.getRef());
 1108               }
 1109               OCProperties.put(PdfName.OCGS, gr);
 1110           }
 1111           if (OCProperties.get(PdfName.D) != null)
 1112               return;
 1113           ArrayList docOrder = new ArrayList(documentOCGorder);
 1114           for (Iterator it = docOrder.iterator(); it.hasNext();) {
 1115               PdfLayer layer = (PdfLayer)it.next();
 1116               if (layer.getParent() != null)
 1117                   it.remove();
 1118           }
 1119           PdfArray order = new PdfArray();
 1120           for (Iterator it = docOrder.iterator(); it.hasNext();) {
 1121               PdfLayer layer = (PdfLayer)it.next();
 1122               getOCGOrder(order, layer);
 1123           }
 1124           PdfDictionary d = new PdfDictionary();
 1125           OCProperties.put(PdfName.D, d);
 1126           d.put(PdfName.ORDER, order);
 1127           PdfArray gr = new PdfArray();
 1128           for (Iterator it = documentOCG.iterator(); it.hasNext();) {
 1129               PdfLayer layer = (PdfLayer)it.next();
 1130               if (!layer.isOn())
 1131                   gr.add(layer.getRef());
 1132           }
 1133           if (gr.size() > 0)
 1134               d.put(PdfName.OFF, gr);
 1135           if (OCGRadioGroup.size() > 0)
 1136               d.put(PdfName.RBGROUPS, OCGRadioGroup);
 1137           addASEvent(PdfName.VIEW, PdfName.ZOOM);
 1138           addASEvent(PdfName.VIEW, PdfName.VIEW);
 1139           addASEvent(PdfName.PRINT, PdfName.PRINT);
 1140           addASEvent(PdfName.EXPORT, PdfName.EXPORT);
 1141           d.put(PdfName.LISTMODE, PdfName.VISIBLEPAGES);
 1142       }
 1143       
 1144       protected PdfDictionary getCatalog(PdfIndirectReference rootObj)
 1145       {
 1146           PdfDictionary catalog = ((PdfDocument)document).getCatalog(rootObj);
 1147           if (documentOCG.size() == 0)
 1148               return catalog;
 1149           fillOCProperties(false);
 1150           catalog.put(PdfName.OCPROPERTIES, OCProperties);
 1151           return catalog;
 1152       }
 1153   
 1154       protected void addSharedObjectsToBody() throws IOException {
 1155           // add the fonts
 1156           for (Iterator it = documentFonts.values().iterator(); it.hasNext();) {
 1157               FontDetails details = (FontDetails)it.next();
 1158               details.writeFont(this);
 1159           }
 1160           // add the form XObjects
 1161           for (Iterator it = formXObjects.values().iterator(); it.hasNext();) {
 1162               Object objs[] = (Object[])it.next();
 1163               PdfTemplate template = (PdfTemplate)objs[1];
 1164               if (template != null && template.getIndirectReference() instanceof PRIndirectReference)
 1165                   continue;
 1166               if (template != null && template.getType() == PdfTemplate.TYPE_TEMPLATE) {
 1167                   PdfIndirectObject obj = addToBody(template.getFormXObject(), template.getIndirectReference());
 1168               }
 1169           }
 1170           // add all the dependencies in the imported pages
 1171           for (Iterator it = importedPages.values().iterator(); it.hasNext();) {
 1172               currentPdfReaderInstance = (PdfReaderInstance)it.next();
 1173               currentPdfReaderInstance.writeAllPages();
 1174           }
 1175           currentPdfReaderInstance = null;
 1176           // add the color
 1177           for (Iterator it = documentColors.values().iterator(); it.hasNext();) {
 1178               ColorDetails color = (ColorDetails)it.next();
 1179               PdfIndirectObject cobj = addToBody(color.getSpotColor(this), color.getIndirectReference());
 1180           }
 1181           // add the pattern
 1182           for (Iterator it = documentPatterns.keySet().iterator(); it.hasNext();) {
 1183               PdfPatternPainter pat = (PdfPatternPainter)it.next();
 1184               PdfIndirectObject pobj = addToBody(pat.getPattern(), pat.getIndirectReference());
 1185           }
 1186           // add the shading patterns
 1187           for (Iterator it = documentShadingPatterns.keySet().iterator(); it.hasNext();) {
 1188               PdfShadingPattern shadingPattern = (PdfShadingPattern)it.next();
 1189               shadingPattern.addToBody();
 1190           }
 1191           // add the shadings
 1192           for (Iterator it = documentShadings.keySet().iterator(); it.hasNext();) {
 1193               PdfShading shading = (PdfShading)it.next();
 1194               shading.addToBody();
 1195           }
 1196           // add the extgstate
 1197           for (Iterator it = documentExtGState.keySet().iterator(); it.hasNext();) {
 1198               PdfDictionary gstate = (PdfDictionary)it.next();
 1199               PdfObject obj[] = (PdfObject[])documentExtGState.get(gstate);
 1200               addToBody(gstate, (PdfIndirectReference)obj[1]);
 1201           }
 1202           // add the layers
 1203           for (Iterator it = documentLayers.keySet().iterator(); it.hasNext();) {
 1204               PdfOCG layer = (PdfOCG)it.next();
 1205               if (layer instanceof PdfLayerMembership)
 1206                   addToBody(layer.getPdfObject(), layer.getRef());
 1207           }
 1208           for (Iterator it = documentOCG.iterator(); it.hasNext();) {
 1209               PdfOCG layer = (PdfOCG)it.next();
 1210               addToBody(layer.getPdfObject(), layer.getRef());
 1211           }
 1212       }
 1213       
 1214       /**
 1215        * Signals that the <CODE>Document</CODE> was closed and that no other
 1216        * <CODE>Elements</CODE> will be added.
 1217        * <P>
 1218        * The pages-tree is built and written to the outputstream.
 1219        * A Catalog is constructed, as well as an Info-object,
 1220        * the referencetable is composed and everything is written
 1221        * to the outputstream embedded in a Trailer.
 1222        */
 1223       
 1224       public synchronized void close() {
 1225           if (open) {
 1226               if ((currentPageNumber - 1) != pageReferences.size())
 1227                   throw new RuntimeException("The page " + pageReferences.size() +
 1228                   " was requested but the document has only " + (currentPageNumber - 1) + " pages.");
 1229               pdf.close();
 1230               try {
 1231                   addSharedObjectsToBody();
 1232                   // add the root to the body
 1233                   PdfIndirectReference rootRef = root.writePageTree();
 1234                   // make the catalog-object and add it to the body
 1235                   PdfDictionary catalog = getCatalog(rootRef);
 1236                   // make pdfx conformant
 1237                   PdfDictionary info = getInfo();
 1238                   if (pdfxConformance != PDFXNONE) {
 1239                       if (info.get(PdfName.GTS_PDFXVERSION) == null) {
 1240                           if (pdfxConformance == PDFX1A2001) {
 1241                               info.put(PdfName.GTS_PDFXVERSION, new PdfString("PDF/X-1:2001"));
 1242                               info.put(new PdfName("GTS_PDFXConformance"), new PdfString("PDF/X-1a:2001"));
 1243                           }
 1244                           else if (pdfxConformance == PDFX32002)
 1245                               info.put(PdfName.GTS_PDFXVERSION, new PdfString("PDF/X-3:2002"));
 1246                       }
 1247                       if (info.get(PdfName.TITLE) == null) {
 1248                           info.put(PdfName.TITLE, new PdfString("Pdf document"));
 1249                       }
 1250                       if (info.get(PdfName.CREATOR) == null) {
 1251                           info.put(PdfName.CREATOR, new PdfString("Unknown"));
 1252                       }
 1253                       if (info.get(PdfName.TRAPPED) == null) {
 1254                           info.put(PdfName.TRAPPED, new PdfName("False"));
 1255                       }
 1256                       getExtraCatalog();
 1257                       if (extraCatalog.get(PdfName.OUTPUTINTENTS) == null) {
 1258                           PdfDictionary out = new PdfDictionary(PdfName.OUTPUTINTENT);
 1259                           out.put(PdfName.OUTPUTCONDITION, new PdfString("SWOP CGATS TR 001-1995"));
 1260                           out.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString("CGATS TR 001"));
 1261                           out.put(PdfName.REGISTRYNAME, new PdfString("http://www.color.org"));
 1262                           out.put(PdfName.INFO, new PdfString(""));
 1263                           out.put(PdfName.S, PdfName.GTS_PDFX);
 1264                           extraCatalog.put(PdfName.OUTPUTINTENTS, new PdfArray(out));
 1265                       }
 1266                   }
 1267                   if (extraCatalog != null) {
 1268                       catalog.mergeDifferent(extraCatalog);
 1269                   }
 1270                   PdfIndirectObject indirectCatalog = addToBody(catalog, false);
 1271                   // add the info-object to the body
 1272                   PdfIndirectObject infoObj = addToBody(info, false);
 1273                   PdfIndirectReference encryption = null;
 1274                   PdfObject fileID = null;
 1275                   body.flushObjStm();
 1276                   if (crypto != null) {
 1277                       PdfIndirectObject encryptionObject = addToBody(crypto.getEncryptionDictionary(), false);
 1278                       encryption = encryptionObject.getIndirectReference();
 1279                       fileID = crypto.getFileID();
 1280                   }
 1281                   else
 1282                       fileID = PdfEncryption.createInfoId(PdfEncryption.createDocumentId());
 1283                   
 1284                   // write the cross-reference table of the body
 1285                   body.writeCrossReferenceTable(os, indirectCatalog.getIndirectReference(),
 1286                       infoObj.getIndirectReference(), encryption,  fileID, prevxref);
 1287   
 1288                   // make the trailer
 1289                   if (fullCompression) {
 1290                       os.write(getISOBytes("startxref\n"));
 1291                       os.write(getISOBytes(String.valueOf(body.offset())));
 1292                       os.write(getISOBytes("\n%%EOF\n"));
 1293                   }
 1294                   else {
 1295                       PdfTrailer trailer = new PdfTrailer(body.size(),
 1296                       body.offset(),
 1297                       indirectCatalog.getIndirectReference(),
 1298                       infoObj.getIndirectReference(),
 1299                       encryption,
 1300                       fileID, prevxref);
 1301                       trailer.toPdf(this, os);
 1302                   }
 1303                   super.close();
 1304               }
 1305               catch(IOException ioe) {
 1306                   throw new ExceptionConverter(ioe);
 1307               }
 1308           }
 1309       }
 1310       
 1311       // methods
 1312       
 1313       /**
 1314        * Sometimes it is necessary to know where the just added <CODE>Table</CODE> ends.
 1315        *
 1316        * For instance to avoid to add another table in a page that is ending up, because
 1317        * the new table will be probably splitted just after the header (it is an
 1318        * unpleasant effect, isn't it?).
 1319        *
 1320        * Added on September 8th, 2001
 1321        * by Francesco De Milato
 1322        * francesco.demilato@tiscalinet.it
 1323        * @param table the <CODE>Table</CODE>
 1324        * @return the bottom height of the just added table
 1325        */
 1326       
 1327       public float getTableBottom(Table table) {
 1328           return pdf.bottom(table) - pdf.indentBottom();
 1329       }
 1330       
 1331       /**
 1332   	 * Gets a pre-rendered table.
 1333   	 * (Contributed by dperezcar@fcc.es) 
 1334   	 * @param table		Contains the table definition.  Its contents are deleted, after being pre-rendered.
 1335        * @return a PdfTable
 1336   	 */
 1337   	
 1338   	public PdfTable getPdfTable(Table table) {
 1339   		return pdf.getPdfTable(table, true);
 1340   	}
 1341   
 1342   	/**
 1343   	 * Row additions to the original {@link Table} used to build the {@link PdfTable} are processed and pre-rendered,
 1344   	 * and then the contents are deleted. 
 1345   	 * If the pre-rendered table doesn't fit, then it is fully rendered and its data discarded.  
 1346   	 * There shouldn't be any column change in the underlying {@link Table} object.
 1347   	 * (Contributed by dperezcar@fcc.es) 
 1348   	 *
 1349   	 * @param	table		The pre-rendered table obtained from {@link #getPdfTable(Table)} 
 1350   	 * @return	true if the table is rendered and emptied.
 1351   	 * @throws DocumentException
 1352   	 * @see #getPdfTable(Table)
 1353   	 */
 1354   	
 1355   	public boolean breakTableIfDoesntFit(PdfTable table) throws DocumentException {
 1356   		return pdf.breakTableIfDoesntFit(table);
 1357   	}
 1358       
 1359       /**
 1360        * Checks if a <CODE>Table</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
 1361        *
 1362        * @param	table	the table that has to be checked
 1363        * @param	margin	a certain margin
 1364        * @return	<CODE>true</CODE> if the <CODE>Table</CODE> fits the page, <CODE>false</CODE> otherwise.
 1365        */
 1366       
 1367       public boolean fitsPage(Table table, float margin) {
 1368           return pdf.bottom(table) > pdf.indentBottom() + margin;
 1369       }
 1370       
 1371       /**
 1372        * Checks if a <CODE>Table</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
 1373        *
 1374        * @param	table	the table that has to be checked
 1375        * @return	<CODE>true</CODE> if the <CODE>Table</CODE> fits the page, <CODE>false</CODE> otherwise.
 1376        */
 1377       
 1378       public boolean fitsPage(Table table) {
 1379           return fitsPage(table, 0);
 1380       }
 1381       
 1382       /**
 1383        * Checks if a <CODE>PdfPTable</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
 1384        *
 1385        * @param	table	the table that has to be checked
 1386        * @param	margin	a certain margin
 1387        * @return	<CODE>true</CODE> if the <CODE>PdfPTable</CODE> fits the page, <CODE>false</CODE> otherwise.
 1388        */
 1389       public boolean fitsPage(PdfPTable table, float margin) {
 1390           return pdf.fitsPage(table, margin);
 1391       }
 1392       
 1393       /**
 1394        * Checks if a <CODE>PdfPTable</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
 1395        *
 1396        * @param	table	the table that has to be checked
 1397        * @return	<CODE>true</CODE> if the <CODE>PdfPTable</CODE> fits the page, <CODE>false</CODE> otherwise.
 1398        */
 1399       public boolean fitsPage(PdfPTable table) {
 1400           return pdf.fitsPage(table, 0);
 1401       }
 1402       
 1403       /**
 1404        * Gets the current vertical page position.
 1405        * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects 
 1406        *   for elements that do not terminate the lines they've started because those lines will get
 1407        *   terminated. 
 1408        * @return The current vertical page position.
 1409        */
 1410       public float getVerticalPosition(boolean ensureNewLine) {
 1411           return pdf.getVerticalPosition(ensureNewLine);
 1412       }
 1413       
 1414       /**
 1415        * Checks if writing is paused.
 1416        *
 1417        * @return		<CODE>true</CODE> if writing temporarely has to be paused, <CODE>false</CODE> otherwise.
 1418        */
 1419       
 1420       boolean isPaused() {
 1421           return pause;
 1422       }
 1423       
 1424       /**
 1425        * Gets the direct content for this document. There is only one direct content,
 1426        * multiple calls to this method will allways retrieve the same.
 1427        * @return the direct content
 1428        */
 1429       
 1430       public PdfContentByte getDirectContent() {
 1431           if (!open)
 1432               throw new RuntimeException("The document is not open.");
 1433           return directContent;
 1434       }
 1435       
 1436       /**
 1437        * Gets the direct content under for this document. There is only one direct content,
 1438        * multiple calls to this method will allways retrieve the same.
 1439        * @return the direct content
 1440        */
 1441       
 1442       public PdfContentByte getDirectContentUnder() {
 1443           if (!open)
 1444               throw new RuntimeException("The document is not open.");
 1445           return directContentUnder;
 1446       }
 1447       
 1448       /**
 1449        * Resets all the direct contents to empty. This happens when a new page is started.
 1450        */
 1451       
 1452       void resetContent() {
 1453           directContent.reset();
 1454           directContentUnder.reset();
 1455       }
 1456       
 1457       /** Gets the AcroForm object.
 1458        * @return the <CODE>PdfAcroForm</CODE>
 1459        */
 1460       
 1461       public PdfAcroForm getAcroForm() {
 1462           return pdf.getAcroForm();
 1463       }
 1464       
 1465       /** Gets the root outline.
 1466        * @return the root outline
 1467        */
 1468       
 1469       public PdfOutline getRootOutline() {
 1470           return directContent.getRootOutline();
 1471       }
 1472       
 1473       /**
 1474        * Returns the outputStreamCounter.
 1475        * @return the outputStreamCounter
 1476        */
 1477       public OutputStreamCounter getOs() {
 1478           return os;
 1479       }
 1480           
 1481       /**
 1482        * Adds a <CODE>BaseFont</CODE> to the document but not to the page resources.
 1483        * It is used for templates.
 1484        * @param bf the <CODE>BaseFont</CODE> to add
 1485        * @return an <CODE>Object[]</CODE> where position 0 is a <CODE>PdfName</CODE>
 1486        * and position 1 is an <CODE>PdfIndirectReference</CODE>
 1487        */
 1488       
 1489       FontDetails addSimple(BaseFont bf) {
 1490           if (bf.getFontType() == BaseFont.FONT_TYPE_DOCUMENT) {
 1491               return new FontDetails(new PdfName("F" + (fontNumber++)), ((DocumentFont)bf).getIndirectReference(), bf);
 1492           }
 1493           FontDetails ret = (FontDetails)documentFonts.get(bf);
 1494           if (ret == null) {
 1495               checkPDFXConformance(this, PDFXKEY_FONT, bf);
 1496               ret = new FontDetails(new PdfName("F" + (fontNumber++)), body.getPdfIndirectReference(), bf);
 1497               documentFonts.put(bf, ret);
 1498           }
 1499           return ret;
 1500       }
 1501       
 1502       void eliminateFontSubset(PdfDictionary fonts) {
 1503           for (Iterator it = documentFonts.values().iterator(); it.hasNext();) {
 1504               FontDetails ft = (FontDetails)it.next();
 1505               if (fonts.get(ft.getFontName()) != null)
 1506                   ft.setSubset(false);
 1507           }
 1508       }
 1509       
 1510       /**
 1511        * Adds a <CODE>SpotColor</CODE> to the document but not to the page resources.
 1512        * @param spc the <CODE>SpotColor</CODE> to add
 1513        * @return an <CODE>Object[]</CODE> where position 0 is a <CODE>PdfName</CODE>
 1514        * and position 1 is an <CODE>PdfIndirectReference</CODE>
 1515        */
 1516       
 1517       ColorDetails addSimple(PdfSpotColor spc) {
 1518           ColorDetails ret = (ColorDetails)documentColors.get(spc);
 1519           if (ret == null) {
 1520               ret = new ColorDetails(new PdfName("CS" + (colorNumber++)), body.getPdfIndirectReference(), spc);
 1521               documentColors.put(spc, ret);
 1522           }
 1523           return ret;
 1524       }
 1525       
 1526       ColorDetails addSimplePatternColorspace(Color color) {
 1527           int type = ExtendedColor.getType(color);
 1528           if (type == ExtendedColor.TYPE_PATTERN || type == ExtendedColor.TYPE_SHADING)
 1529               throw new RuntimeException("An uncolored tile pattern can not have another pattern or shading as color.");
 1530           try {
 1531               switch (type) {
 1532                   case ExtendedColor.TYPE_RGB:
 1533                       if (patternColorspaceRGB == null) {
 1534                           patternColorspaceRGB = new ColorDetails(new PdfName("CS" + (colorNumber++)), body.getPdfIndirectReference(), null);
 1535                           PdfArray array = new PdfArray(PdfName.PATTERN);
 1536                           array.add(PdfName.DEVICERGB);
 1537                           PdfIndirectObject cobj = addToBody(array, patternColorspaceRGB.getIndirectReference());
 1538                       }
 1539                       return patternColorspaceRGB;
 1540                   case ExtendedColor.TYPE_CMYK:
 1541                       if (patternColorspaceCMYK == null) {
 1542                           patternColorspaceCMYK = new ColorDetails(new PdfName("CS" + (colorNumber++)), body.getPdfIndirectReference(), null);
 1543                           PdfArray array = new PdfArray(PdfName.PATTERN);
 1544                           array.add(PdfName.DEVICECMYK);
 1545                           PdfIndirectObject cobj = addToBody(array, patternColorspaceCMYK.getIndirectReference());
 1546                       }
 1547                       return patternColorspaceCMYK;
 1548                   case ExtendedColor.TYPE_GRAY:
 1549                       if (patternColorspaceGRAY == null) {
 1550                           patternColorspaceGRAY = new ColorDetails(new PdfName("CS" + (colorNumber++)), body.getPdfIndirectReference(), null);
 1551                           PdfArray array = new PdfArray(PdfName.PATTERN);
 1552                           array.add(PdfName.DEVICEGRAY);
 1553                           PdfIndirectObject cobj = addToBody(array, patternColorspaceGRAY.getIndirectReference());
 1554                       }
 1555                       return patternColorspaceGRAY;
 1556                   case ExtendedColor.TYPE_SEPARATION: {
 1557                       ColorDetails details = addSimple(((SpotColor)color).getPdfSpotColor());
 1558                       ColorDetails patternDetails = (ColorDetails)documentSpotPatterns.get(details);
 1559                       if (patternDetails == null) {
 1560                           patternDetails = new ColorDetails(new PdfName("CS" + (colorNumber++)), body.getPdfIndirectReference(), null);
 1561                           PdfArray array = new PdfArray(PdfName.PATTERN);
 1562                           array.add(details.getIndirectReference());
 1563                           PdfIndirectObject cobj = addToBody(array, patternDetails.getIndirectReference());
 1564                           documentSpotPatterns.put(details, patternDetails);
 1565                       }
 1566                       return patternDetails;
 1567                   }
 1568                   default:
 1569                       throw new RuntimeException("Invalid color type in PdfWriter.addSimplePatternColorspace().");
 1570               }
 1571           }
 1572           catch (Exception e) {
 1573               throw new RuntimeException(e.getMessage());
 1574           }
 1575       }
 1576       
 1577       void addSimpleShadingPattern(PdfShadingPattern shading) {
 1578           if (!documentShadingPatterns.containsKey(shading)) {
 1579               shading.setName(patternNumber);
 1580               ++patternNumber;
 1581               documentShadingPatterns.put(shading, null);
 1582               addSimpleShading(shading.getShading());
 1583           }
 1584       }
 1585       
 1586       void addSimpleShading(PdfShading shading) {
 1587           if (!documentShadings.containsKey(shading)) {
 1588               documentShadings.put(shading, null);
 1589               shading.setName(documentShadings.size());
 1590           }
 1591       }
 1592       
 1593       PdfObject[] addSimpleExtGState(PdfDictionary gstate) {
 1594           if (!documentExtGState.containsKey(gstate)) {
 1595               checkPDFXConformance(this, PDFXKEY_GSTATE, gstate);
 1596               documentExtGState.put(gstate, new PdfObject[]{new PdfName("GS" + (documentExtGState.size() + 1)), getPdfIndirectReference()});
 1597           }
 1598           return (PdfObject[])documentExtGState.get(gstate);
 1599       }
 1600       
 1601       void registerLayer(PdfOCG layer) {
 1602           checkPDFXConformance(this, PDFXKEY_LAYER, null);
 1603           if (layer instanceof PdfLayer) {
 1604               PdfLayer la = (PdfLayer)layer;
 1605               if (la.getTitle() == null) {
 1606                   if (!documentOCG.contains(layer)) {
 1607                       documentOCG.add(layer);
 1608                       documentOCGorder.add(layer);
 1609                   }
 1610               }
 1611               else {
 1612                   documentOCGorder.add(layer);
 1613               }
 1614           }
 1615           else
 1616               throw new IllegalArgumentException("Only PdfLayer is accepted.");
 1617       }
 1618       
 1619       PdfName addSimpleLayer(PdfOCG layer) {
 1620           if (!documentLayers.containsKey(layer)) {
 1621               checkPDFXConformance(this, PDFXKEY_LAYER, null);
 1622               documentLayers.put(layer, new PdfName("OC" + (documentLayers.size() + 1)));
 1623           }
 1624           return (PdfName)documentLayers.get(layer);
 1625       }
 1626       
 1627       /**
 1628        * Gets the <CODE>PdfDocument</CODE> associated with this writer.
 1629        * @return the <CODE>PdfDocument</CODE>
 1630        */
 1631       
 1632       PdfDocument getPdfDocument() {
 1633           return pdf;
 1634       }
 1635       
 1636       /**
 1637        * Gets a <CODE>PdfIndirectReference</CODE> for an object that
 1638        * will be created in the future.
 1639        * @return the <CODE>PdfIndirectReference</CODE>
 1640        */
 1641       
 1642       public PdfIndirectReference getPdfIndirectReference() {
 1643           return body.getPdfIndirectReference();
 1644       }
 1645       
 1646       int getIndirectReferenceNumber() {
 1647           return body.getIndirectReferenceNumber();
 1648       }
 1649       
 1650       PdfName addSimplePattern(PdfPatternPainter painter) {
 1651           PdfName name = (PdfName)documentPatterns.get(painter);
 1652           try {
 1653               if ( name == null ) {
 1654                   name = new PdfName("P" + patternNumber);
 1655                   ++patternNumber;
 1656                   documentPatterns.put(painter, name);
 1657               }
 1658           } catch (Exception e) {
 1659               throw new ExceptionConverter(e);
 1660           }
 1661           return name;
 1662       }
 1663       
 1664       /**
 1665        * Adds a template to the document but not to the page resources.
 1666        * @param template the template to add
 1667        * @param forcedName the template name, rather than a generated one. Can be null
 1668        * @return the <CODE>PdfName</CODE> for this template
 1669        */
 1670       
 1671       PdfName addDirectTemplateSimple(PdfTemplate template, PdfName forcedName) {
 1672           PdfIndirectReference ref = template.getIndirectReference();
 1673           Object obj[] = (Object[])formXObjects.get(ref);
 1674           PdfName name = null;
 1675           try {
 1676               if (obj == null) {
 1677                   if (forcedName == null) {
 1678                       name = new PdfName("Xf" + formXObjectsCounter);
 1679                       ++formXObjectsCounter;
 1680                   }
 1681                   else
 1682                       name = forcedName;
 1683                   if (template.getType() == PdfTemplate.TYPE_IMPORTED)
 1684                       template = null;
 1685                   formXObjects.put(ref, new Object[]{name, template});
 1686               }
 1687               else
 1688                   name = (PdfName)obj[0];
 1689           }
 1690           catch (Exception e) {
 1691               throw new ExceptionConverter(e);
 1692           }
 1693           return name;
 1694       }
 1695       
 1696       /**
 1697        * Sets the <CODE>PdfPageEvent</CODE> for this document.
 1698        * @param pageEvent the <CODE>PdfPageEvent</CODE> for this document
 1699        */
 1700       
 1701       public void setPageEvent(PdfPageEvent pageEvent) {
 1702           this.pageEvent = pageEvent;
 1703       }
 1704       
 1705       /**
 1706        * Gets the <CODE>PdfPageEvent</CODE> for this document or <CODE>null</CODE>
 1707        * if none is set.
 1708        * @return the <CODE>PdfPageEvent</CODE> for this document or <CODE>null</CODE>
 1709        * if none is set
 1710        */
 1711       
 1712       public PdfPageEvent getPageEvent() {
 1713           return pageEvent;
 1714       }
 1715       
 1716       /**
 1717        * Adds the local destinations to the body of the document.
 1718        * @param dest the <CODE>HashMap</CODE> containing the destinations
 1719        * @throws IOException on error
 1720        */
 1721       
 1722       void addLocalDestinations(TreeMap dest) throws IOException {
 1723           for (Iterator i = dest.keySet().iterator(); i.hasNext();) {
 1724               String name = (String)i.next();
 1725               Object obj[] = (Object[])dest.get(name);
 1726               PdfDestination destination = (PdfDestination)obj[2];
 1727               if (destination == null)
 1728                   throw new RuntimeException("The name '" + name + "' has no local destination.");
 1729               if (obj[1] == null)
 1730                   obj[1] = getPdfIndirectReference();
 1731               PdfIndirectObject iob = addToBody(destination, (PdfIndirectReference)obj[1]);
 1732           }
 1733       }
 1734       
 1735       /**
 1736        * Gets the current pagenumber of this document.
 1737        *
 1738        * @return a page number
 1739        */
 1740       
 1741       public int getPageNumber() {
 1742           return pdf.getPageNumber();
 1743       }
 1744       
 1745       /**
 1746        * Sets the viewer preferences by ORing some constants.
 1747        * <p>
 1748        * <ul>
 1749        * <li>The page layout to be used when the document is opened (choose one).
 1750        *   <ul>
 1751        *   <li><b>PageLayoutSinglePage</b> - Display one page at a time. (default)
 1752        *   <li><b>PageLayoutOneColumn</b> - Display the pages in one column.
 1753        *   <li><b>PageLayoutTwoColumnLeft</b> - Display the pages in two columns, with
 1754        *       oddnumbered pages on the left.
 1755        *   <li><b>PageLayoutTwoColumnRight</b> - Display the pages in two columns, with
 1756        *       oddnumbered pages on the right.
 1757        *   </ul>
 1758        * <li>The page mode how the document should be displayed
 1759        *     when opened (choose one).
 1760        *   <ul>
 1761        *   <li><b>PageModeUseNone</b> - Neither document outline nor thumbnail images visible. (default)
 1762        *   <li><b>PageModeUseOutlines</b> - Document outline visible.
 1763        *   <li><b>PageModeUseThumbs</b> - Thumbnail images visible.
 1764        *   <li><b>PageModeFullScreen</b> - Full-screen mode, with no menu bar, window
 1765        *       controls, or any other window visible.
 1766        *   <li><b>PageModeUseOC</b> - Optional content group panel visible
 1767        *   </ul>
 1768        * <li><b>HideToolbar</b> - A flag specifying whether to hide the viewer application's tool
 1769        *     bars when the document is active.
 1770        * <li><b>HideMenubar</b> - A flag specifying whether to hide the viewer application's
 1771        *     menu bar when the document is active.
 1772        * <li><b>HideWindowUI</b> - A flag specifying whether to hide user interface elements in
 1773        *     the document's window (such as scroll bars and navigation controls),
 1774        *     leaving only the document's contents displayed.
 1775        * <li><b>FitWindow</b> - A flag specifying whether to resize the document's window to
 1776        *     fit the size of the first displayed page.
 1777        * <li><b>CenterWindow</b> - A flag specifying whether to position the document's window
 1778        *     in the center of the screen.
 1779        * <li><b>DisplayDocTitle</b> - A flag specifying whether to display the document's title
 1780        *     in the top bar.
 1781        * <li>The predominant reading order for text. This entry has no direct effect on the
 1782        *     document's contents or page numbering, but can be used to determine the relative
 1783        *     positioning of pages when displayed side by side or printed <i>n-up</i> (choose one).
 1784        *   <ul>
 1785        *   <li><b>DirectionL2R</b> - Left to right
 1786        *   <li><b>DirectionR2L</b> - Right to left (including vertical writing systems such as
 1787        *       Chinese, Japanese, and Korean)
 1788        *   </ul>
 1789        * <li>The document's page mode, specifying how to display the
 1790        *     document on exiting full-screen mode. It is meaningful only
 1791        *     if the page mode is <b>PageModeFullScreen</b> (choose one).
 1792        *   <ul>
 1793        *   <li><b>NonFullScreenPageModeUseNone</b> - Neither document outline nor thumbnail images
 1794        *       visible
 1795        *   <li><b>NonFullScreenPageModeUseOutlines</b> - Document outline visible
 1796        *   <li><b>NonFullScreenPageModeUseThumbs</b> - Thumbnail images visible
 1797        *   <li><b>NonFullScreenPageModeUseOC</b> - Optional content group panel visible
 1798        *   </ul>
 1799        * <li><b>PrintScalingNone</b> - Indicates that the print dialog should reflect no page scaling.
 1800        * </ul>
 1801        * @param preferences the viewer preferences
 1802        */
 1803       
 1804       public void setViewerPreferences(int preferences) {
 1805           pdf.setViewerPreferences(preferences);
 1806       }
 1807       
 1808       /** Sets the encryption options for this document. The userPassword and the
 1809        *  ownerPassword can be null or have zero length. In this case the ownerPassword
 1810        *  is replaced by a random string. The open permissions for the document can be
 1811        *  AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
 1812        *  AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
 1813        *  The permissions can be combined by ORing them.
 1814        * @param userPassword the user password. Can be null or empty
 1815        * @param ownerPassword the owner password. Can be null or empty
 1816        * @param permissions the user permissions
 1817        * @param strength128Bits <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
 1818        * @throws DocumentException if the document is already open
 1819        */
 1820       public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits) throws DocumentException {
 1821           if (pdf.isOpen())
 1822               throw new DocumentException("Encryption can only be added before opening the document.");
 1823           crypto = new PdfEncryption();
 1824           crypto.setupAllKeys(userPassword, ownerPassword, permissions, strength128Bits);
 1825       }
 1826       
 1827       /**
 1828        * Sets the encryption options for this document. The userPassword and the
 1829        *  ownerPassword can be null or have zero length. In this case the ownerPassword
 1830        *  is replaced by a random string. The open permissions for the document can be
 1831        *  AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
 1832        *  AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
 1833        *  The permissions can be combined by ORing them.
 1834        * @param strength <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
 1835        * @param userPassword the user password. Can be null or empty
 1836        * @param ownerPassword the owner password. Can be null or empty
 1837        * @param permissions the user permissions
 1838        * @throws DocumentException if the document is already open
 1839        */
 1840       public void setEncryption(boolean strength, String userPassword, String ownerPassword, int permissions) throws DocumentException {
 1841           setEncryption(getISOBytes(userPassword), getISOBytes(ownerPassword), permissions, strength);
 1842       }
 1843       
 1844       /**
 1845        * Adds an object to the PDF body.
 1846        * @param object
 1847        * @return a PdfIndirectObject
 1848        * @throws IOException
 1849        */
 1850       public PdfIndirectObject addToBody(PdfObject object) throws IOException {
 1851           PdfIndirectObject iobj = body.add(object);
 1852           return iobj;
 1853       }
 1854       
 1855       /**
 1856        * Adds an object to the PDF body.
 1857        * @param object
 1858        * @param inObjStm
 1859        * @return a PdfIndirectObject
 1860        * @throws IOException
 1861        */
 1862       public PdfIndirectObject addToBody(PdfObject object, boolean inObjStm) throws IOException {
 1863           PdfIndirectObject iobj = body.add(object, inObjStm);
 1864           return iobj;
 1865       }
 1866       
 1867       /**
 1868        * Adds an object to the PDF body.
 1869        * @param object
 1870        * @param ref
 1871        * @return a PdfIndirectObject
 1872        * @throws IOException
 1873        */
 1874       public PdfIndirectObject addToBody(PdfObject object, PdfIndirectReference ref) throws IOException {
 1875           PdfIndirectObject iobj = body.add(object, ref);
 1876           return iobj;
 1877       }
 1878       
 1879       /**
 1880        * Adds an object to the PDF body.
 1881        * @param object
 1882        * @param ref
 1883        * @param inObjStm
 1884        * @return a PdfIndirectObject
 1885        * @throws IOException
 1886        */
 1887       public PdfIndirectObject addToBody(PdfObject object, PdfIndirectReference ref, boolean inObjStm) throws IOException {
 1888           PdfIndirectObject iobj = body.add(object, ref, inObjStm);
 1889           return iobj;
 1890       }
 1891       
 1892       /**
 1893        * Adds an object to the PDF body.
 1894        * @param object
 1895        * @param refNumber
 1896        * @return a PdfIndirectObject
 1897        * @throws IOException
 1898        */
 1899       public PdfIndirectObject addToBody(PdfObject object, int refNumber) throws IOException {
 1900           PdfIndirectObject iobj = body.add(object, refNumber);
 1901           return iobj;
 1902       }
 1903       
 1904       /**
 1905        * Adds an object to the PDF body.
 1906        * @param object
 1907        * @param refNumber
 1908        * @param inObjStm
 1909        * @return a PdfIndirectObject
 1910        * @throws IOException
 1911        */
 1912       public PdfIndirectObject addToBody(PdfObject object, int refNumber, boolean inObjStm) throws IOException {
 1913           PdfIndirectObject iobj = body.add(object, refNumber, inObjStm);
 1914           return iobj;
 1915       }
 1916       
 1917       /** When the document opens it will jump to the destination with
 1918        * this name.
 1919        * @param name the name of the destination to jump to
 1920        */
 1921       public void setOpenAction(String name) {
 1922           pdf.setOpenAction(name);
 1923       }
 1924       
 1925       /** Additional-actions defining the actions to be taken in
 1926        * response to various trigger events affecting the document
 1927        * as a whole. The actions types allowed are: <CODE>DOCUMENT_CLOSE</CODE>,
 1928        * <CODE>WILL_SAVE</CODE>, <CODE>DID_SAVE</CODE>, <CODE>WILL_PRINT</CODE>
 1929        * and <CODE>DID_PRINT</CODE>.
 1930        *
 1931        * @param actionType the action type
 1932        * @param action the action to execute in response to the trigger
 1933        * @throws PdfException on invalid action type
 1934        */
 1935       public void setAdditionalAction(PdfName actionType, PdfAction action) throws PdfException {
 1936           if (!(actionType.equals(DOCUMENT_CLOSE) ||
 1937           actionType.equals(WILL_SAVE) ||
 1938           actionType.equals(DID_SAVE) ||
 1939           actionType.equals(WILL_PRINT) ||
 1940           actionType.equals(DID_PRINT))) {
 1941               throw new PdfException("Invalid additional action type: " + actionType.toString());
 1942           }
 1943           pdf.addAdditionalAction(actionType, action);
 1944       }
 1945       
 1946       /** When the document opens this <CODE>action</CODE> will be
 1947        * invoked.
 1948        * @param action the action to be invoked
 1949        */
 1950       public void setOpenAction(PdfAction action) {
 1951           pdf.setOpenAction(action);
 1952       }
 1953       
 1954       /** Sets the page labels
 1955        * @param pageLabels the page labels
 1956        */
 1957       public void setPageLabels(PdfPageLabels pageLabels) {
 1958           pdf.setPageLabels(pageLabels);
 1959       }
 1960       
 1961       PdfEncryption getEncryption() {
 1962           return crypto;
 1963       }
 1964       
 1965       RandomAccessFileOrArray getReaderFile(PdfReader reader) {
 1966           return currentPdfReaderInstance.getReaderFile();
 1967       }
 1968       
 1969       protected int getNewObjectNumber(PdfReader reader, int number, int generation) {
 1970           return currentPdfReaderInstance.getNewObjectNumber(number, generation);
 1971       }
 1972       
 1973       /** Gets a page from other PDF document. The page can be used as
 1974        * any other PdfTemplate. Note that calling this method more than
 1975        * once with the same parameters will retrieve the same object.
 1976        * @param reader the PDF document where the page is
 1977        * @param pageNumber the page number. The first page is 1
 1978        * @return the template representing the imported page
 1979        */
 1980       public PdfImportedPage getImportedPage(PdfReader reader, int pageNumber) {
 1981           PdfReaderInstance inst = (PdfReaderInstance)importedPages.get(reader);
 1982           if (inst == null) {
 1983               inst = reader.getPdfReaderInstance(this);
 1984               importedPages.put(reader, inst);
 1985           }
 1986           return inst.getImportedPage(pageNumber);
 1987       }
 1988       
 1989       /** Adds a JavaScript action at the document level. When the document
 1990        * opens all this JavaScript runs.
 1991        * @param js The JavaScrip action
 1992        */
 1993       public void addJavaScript(PdfAction js) {
 1994           pdf.addJavaScript(js);
 1995       }
 1996       
 1997       /** Adds a JavaScript action at the document level. When the document
 1998        * opens all this JavaScript runs.
 1999        * @param code the JavaScript code
 2000        * @param unicode select JavaScript unicode. Note that the internal
 2001        * Acrobat JavaScript engine does not support unicode,
 2002        * so this may or may not work for you
 2003        */
 2004       public void addJavaScript(String code, boolean unicode) {
 2005           addJavaScript(PdfAction.javaScript(code, this, unicode));
 2006       }
 2007       
 2008       /** Adds a JavaScript action at the document level. When the document
 2009        * opens all this JavaScript runs.
 2010        * @param code the JavaScript code
 2011        */
 2012       public void addJavaScript(String code) {
 2013           addJavaScript(code, false);
 2014       }
 2015       
 2016       /** Sets the crop box. The crop box should not be rotated even if the
 2017        * page is rotated. This change only takes effect in the next
 2018        * page.
 2019        * @param crop the crop box
 2020        */
 2021       public void setCropBoxSize(Rectangle crop) {
 2022           pdf.setCropBoxSize(crop);
 2023       }
 2024       
 2025       /** Gets a reference to a page existing or not. If the page does not exist
 2026        * yet the reference will be created in advance. If on closing the document, a
 2027        * page number greater than the total number of pages was requested, an
 2028        * exception is thrown.
 2029        * @param page the page number. The first page is 1
 2030        * @return the reference to the page
 2031        */
 2032       public PdfIndirectReference getPageReference(int page) {
 2033           --page;
 2034           if (page < 0)
 2035               throw new IndexOutOfBoundsException("The page numbers start at 1.");
 2036           PdfIndirectReference ref;
 2037           if (page < pageReferences.size()) {
 2038               ref = (PdfIndirectReference)pageReferences.get(page);
 2039               if (ref == null) {
 2040                   ref = body.getPdfIndirectReference();
 2041                   pageReferences.set(page, ref);
 2042               }
 2043           }
 2044           else {
 2045               int empty = page - pageReferences.size();
 2046               for (int k = 0; k < empty; ++k)
 2047                   pageReferences.add(null);
 2048               ref = body.getPdfIndirectReference();
 2049               pageReferences.add(ref);
 2050           }
 2051           return ref;
 2052       }
 2053       
 2054       PdfIndirectReference getCurrentPage() {
 2055           return getPageReference(currentPageNumber);
 2056       }
 2057       
 2058       int getCurrentPageNumber() {
 2059           return currentPageNumber;
 2060       }
 2061       
 2062       /** Adds the <CODE>PdfAnnotation</CODE> to the calculation order
 2063        * array.
 2064        * @param annot the <CODE>PdfAnnotation</CODE> to be added
 2065        */
 2066       public void addCalculationOrder(PdfFormField annot) {
 2067           pdf.addCalculationOrder(annot);
 2068       }
 2069       
 2070       /** Set the signature flags.
 2071        * @param f the flags. This flags are ORed with current ones
 2072        */
 2073       public void setSigFlags(int f) {
 2074           pdf.setSigFlags(f);
 2075       }
 2076       
 2077       /** Adds a <CODE>PdfAnnotation</CODE> or a <CODE>PdfFormField</CODE>
 2078        * to the document. Only the top parent of a <CODE>PdfFormField</CODE>
 2079        * needs to be added.
 2080        * @param annot the <CODE>PdfAnnotation</CODE> or the <CODE>PdfFormField</CODE> to add
 2081        */
 2082       public void addAnnotation(PdfAnnotation annot) {
 2083           pdf.addAnnotation(annot);
 2084       }
 2085       
 2086       void addAnnotation(PdfAnnotation annot, int page) {
 2087           addAnnotation(annot);
 2088       }
 2089       
 2090       /** Sets the PDF version. Must be used right before the document
 2091        * is opened. Valid options are VERSION_1_2, VERSION_1_3,
 2092        * VERSION_1_4, VERSION_1_5 and VERSION_1_6. VERSION_1_4 is the default.
 2093        * @param version the version number
 2094        */
 2095       public void setPdfVersion(char version) {
 2096           if (HEADER.length > VPOINT)
 2097               HEADER[VPOINT] = (byte)version;
 2098       }
 2099       
 2100       /** Reorder the pages in the document. A <CODE>null</CODE> argument value
 2101        * only returns the number of pages to process. It is
 2102        * advisable to issue a <CODE>Document.newPage()</CODE>
 2103        * before using this method.
 2104        * @return the total number of pages
 2105        * @param order an array with the new page sequence. It must have the
 2106        * same size as the number of pages.
 2107        * @throws DocumentException if all the pages are not present in the array
 2108        */
 2109       public int reorderPages(int order[]) throws DocumentException {
 2110           return root.reorderPages(order);
 2111       }
 2112       
 2113       /** Gets the space/character extra spacing ratio for
 2114        * fully justified text.
 2115        * @return the space/character extra spacing ratio
 2116        */
 2117       public float getSpaceCharRatio() {
 2118           return spaceCharRatio;
 2119       }
 2120       
 2121       /** Sets the ratio between the extra word spacing and the extra character spacing
 2122        * when the text is fully justified.
 2123        * Extra word spacing will grow <CODE>spaceCharRatio</CODE> times more than extra character spacing.
 2124        * If the ratio is <CODE>PdfWriter.NO_SPACE_CHAR_RATIO</CODE> then the extra character spacing
 2125        * will be zero.
 2126        * @param spaceCharRatio the ratio between the extra word spacing and the extra character spacing
 2127        */
 2128       public void setSpaceCharRatio(float spaceCharRatio) {
 2129           if (spaceCharRatio < 0.001f)
 2130               this.spaceCharRatio = 0.001f;
 2131           else
 2132               this.spaceCharRatio = spaceCharRatio;
 2133       }
 2134       
 2135       /** Sets the run direction. This is only used as a placeholder
 2136        * as it does not affect anything.
 2137        * @param runDirection the run direction
 2138        */    
 2139       public void setRunDirection(int runDirection) {
 2140           if (runDirection < RUN_DIRECTION_NO_BIDI || runDirection > RUN_DIRECTION_RTL)
 2141               throw new RuntimeException("Invalid run direction: " + runDirection);
 2142           this.runDirection = runDirection;
 2143       }
 2144       
 2145       /** Gets the run direction.
 2146        * @return the run direction
 2147        */    
 2148       public int getRunDirection() {
 2149           return runDirection;
 2150       }
 2151   
 2152       /**
 2153        * Sets the display duration for the page (for presentations)
 2154        * @param seconds   the number of seconds to display the page
 2155        */
 2156       public void setDuration(int seconds) {
 2157           pdf.setDuration(seconds);
 2158       }
 2159       
 2160       /**
 2161        * Sets the transition for the page
 2162        * @param transition   the Transition object
 2163        */
 2164       public void setTransition(PdfTransition transition) {
 2165           pdf.setTransition(transition);
 2166       }
 2167       
 2168       /** Writes the reader to the document and frees the memory used by it.
 2169        * The main use is when concatenating multiple documents to keep the
 2170        * memory usage restricted to the current appending document.
 2171        * @param reader the <CODE>PdfReader</CODE> to free
 2172        * @throws IOException on error
 2173        */    
 2174       public void freeReader(PdfReader reader) throws IOException {
 2175           currentPdfReaderInstance = (PdfReaderInstance)importedPages.get(reader);
 2176           if (currentPdfReaderInstance == null)
 2177               return;
 2178           currentPdfReaderInstance.writeAllPages();
 2179           currentPdfReaderInstance = null;
 2180           importedPages.remove(reader);
 2181       }
 2182       
 2183       /** Sets the open and close page additional action.
 2184        * @param actionType the action type. It can be <CODE>PdfWriter.PAGE_OPEN</CODE>
 2185        * or <CODE>PdfWriter.PAGE_CLOSE</CODE>
 2186        * @param action the action to perform
 2187        * @throws PdfException if the action type is invalid
 2188        */    
 2189       public void setPageAction(PdfName actionType, PdfAction action) throws PdfException {
 2190           if (!actionType.equals(PAGE_OPEN) && !actionType.equals(PAGE_CLOSE))
 2191               throw new PdfException("Invalid page additional action type: " + actionType.toString());
 2192           pdf.setPageAction(actionType, action);
 2193       }
 2194       
 2195       /** Gets the current document size. This size only includes
 2196        * the data already writen to the output stream, it does not
 2197        * include templates or fonts. It is usefull if used with
 2198        * <CODE>freeReader()</CODE> when concatenating many documents
 2199        * and an idea of the current size is needed.
 2200        * @return the approximate size without fonts or templates
 2201        */    
 2202       public int getCurrentDocumentSize() {
 2203           return body.offset() + body.size() * 20 + 0x48;
 2204       }
 2205       
 2206       /** Getter for property strictImageSequence.
 2207        * @return value of property strictImageSequence
 2208        *
 2209        */
 2210       public boolean isStrictImageSequence() {
 2211           return pdf.isStrictImageSequence();
 2212       }
 2213       
 2214       /** Sets the image sequence to follow the text in strict order.
 2215        * @param strictImageSequence new value of property strictImageSequence
 2216        *
 2217        */
 2218       public void setStrictImageSequence(boolean strictImageSequence) {
 2219           pdf.setStrictImageSequence(strictImageSequence);
 2220       }
 2221       
 2222       /**
 2223        * If you use setPageEmpty(false), invoking newPage() after a blank page will add a newPage.
 2224        * @param pageEmpty the state
 2225        */
 2226       public void setPageEmpty(boolean pageEmpty) {
 2227           pdf.setPageEmpty(pageEmpty);
 2228       }
 2229   
 2230       /** Gets the info dictionary for changing.
 2231        * @return the info dictionary
 2232        */    
 2233       public PdfDictionary getInfo() {
 2234           return ((PdfDocument)document).getInfo();
 2235       }
 2236       
 2237       /**
 2238        * Sets extra keys to the catalog.
 2239        * @return the catalog to change
 2240        */    
 2241       public PdfDictionary getExtraCatalog() {
 2242           if (extraCatalog == null)
 2243               extraCatalog = new PdfDictionary();
 2244           return this.extraCatalog;
 2245       }
 2246       
 2247       /**
 2248        * Sets the document in a suitable way to do page reordering.
 2249        */    
 2250        public void setLinearPageMode() {
 2251           root.setLinearMode(null);
 2252       }
 2253       
 2254       /** Getter for property group.
 2255        * @return Value of property group.
 2256        *
 2257        */
 2258       public PdfDictionary getGroup() {
 2259           return this.group;
 2260       }
 2261       
 2262       /** Setter for property group.
 2263        * @param group New value of property group.
 2264        *
 2265        */
 2266       public void setGroup(PdfDictionary group) {
 2267           this.group = group;
 2268       }
 2269       
 2270       /**
 2271        * Sets the PDFX conformance level. Allowed values are PDFX1A2001 and PDFX32002. It
 2272        * must be called before opening the document.
 2273        * @param pdfxConformance the conformance level
 2274        */    
 2275       public void setPDFXConformance(int pdfxConformance) {
 2276           if (this.pdfxConformance == pdfxConformance)
 2277               return;
 2278           if (pdf.isOpen())
 2279               throw new PdfXConformanceException("PDFX conformance can only be set before opening the document.");
 2280           if (crypto != null)
 2281               throw new PdfXConformanceException("A PDFX conforming document cannot be encrypted.");
 2282           if (pdfxConformance != PDFXNONE)
 2283               setPdfVersion(VERSION_1_3);
 2284           this.pdfxConformance = pdfxConformance;
 2285       }
 2286    
 2287       /**
 2288        * Gets the PDFX conformance level.
 2289        * @return the PDFX conformance level
 2290        */    
 2291       public int getPDFXConformance() {
 2292           return pdfxConformance;
 2293       }
 2294       
 2295       static void checkPDFXConformance(PdfWriter writer, int key, Object obj1) {
 2296           if (writer == null || writer.pdfxConformance == PDFXNONE)
 2297               return;
 2298           int conf = writer.pdfxConformance;
 2299           switch (key) {
 2300               case PDFXKEY_COLOR:
 2301                   switch (conf) {
 2302                       case PDFX1A2001:
 2303                           if (obj1 instanceof ExtendedColor) {
 2304                               ExtendedColor ec = (ExtendedColor)obj1;
 2305                               switch (ec.getType()) {
 2306                                   case ExtendedColor.TYPE_CMYK:
 2307                                   case ExtendedColor.TYPE_GRAY:
 2308                                       return;
 2309                                   case ExtendedColor.TYPE_RGB:
 2310                                       throw new PdfXConformanceException("Colorspace RGB is not allowed.");
 2311                                   case ExtendedColor.TYPE_SEPARATION:
 2312                                       SpotColor sc = (SpotColor)ec;
 2313                                       checkPDFXConformance(writer, PDFXKEY_COLOR, sc.getPdfSpotColor().getAlternativeCS());
 2314                                       break;
 2315                                   case ExtendedColor.TYPE_SHADING:
 2316                                       ShadingColor xc = (ShadingColor)ec;
 2317                                       checkPDFXConformance(writer, PDFXKEY_COLOR, xc.getPdfShadingPattern().getShading().getColorSpace());
 2318                                       break;
 2319                                   case ExtendedColor.TYPE_PATTERN:
 2320                                       PatternColor pc = (PatternColor)ec;
 2321                                       checkPDFXConformance(writer, PDFXKEY_COLOR, pc.getPainter().getDefaultColor());
 2322                                       break;
 2323                               }
 2324                           }
 2325                           else if (obj1 instanceof Color)
 2326                               throw new PdfXConformanceException("Colorspace RGB is not allowed.");
 2327                           break;
 2328                   }
 2329                   break;
 2330               case PDFXKEY_CMYK:
 2331                   break;
 2332               case PDFXKEY_RGB:
 2333                   if (conf == PDFX1A2001)
 2334                       throw new PdfXConformanceException("Colorspace RGB is not allowed.");
 2335                   break;
 2336               case PDFXKEY_FONT:
 2337                   if (!((BaseFont)obj1).isEmbedded())
 2338                       throw new PdfXConformanceException("All the fonts must be embedded.");
 2339                   break;
 2340               case PDFXKEY_IMAGE:
 2341                   PdfImage image = (PdfImage)obj1;
 2342                   if (image.get(PdfName.SMASK) != null)
 2343                       throw new PdfXConformanceException("The /SMask key is not allowed in images.");
 2344                   switch (conf) {
 2345                       case PDFX1A2001:
 2346                           PdfObject cs = image.get(PdfName.COLORSPACE);
 2347                           if (cs == null)
 2348                               return;
 2349                           if (cs.isName()) {
 2350                               if (PdfName.DEVICERGB.equals(cs))
 2351                                   throw new PdfXConformanceException("Colorspace RGB is not allowed.");
 2352                           }
 2353                           else if (cs.isArray()) {
 2354                               if (PdfName.CALRGB.equals((PdfObject)((PdfArray)cs).getArrayList().get(0)))
 2355                                   throw new PdfXConformanceException("Colorspace CalRGB is not allowed.");
 2356                           }
 2357                           break;
 2358                   }
 2359                   break;
 2360               case PDFXKEY_GSTATE:
 2361                   PdfDictionary gs = (PdfDictionary)obj1;
 2362                   PdfObject obj = gs.get(PdfName.BM);
 2363                   if (obj != null && !PdfGState.BM_NORMAL.equals(obj) && !PdfGState.BM_COMPATIBLE.equals(obj))
 2364                       throw new PdfXConformanceException("Blend mode " + obj.toString() + " not allowed.");
 2365                   obj = gs.get(PdfName.CA);
 2366                   double v = 0.0;
 2367                   if (obj != null && (v = ((PdfNumber)obj).doubleValue()) != 1.0)
 2368                       throw new PdfXConformanceException("Transparency is not allowed: /CA = " + v);
 2369                   obj = gs.get(PdfName.ca);
 2370                   v = 0.0;
 2371                   if (obj != null && (v = ((PdfNumber)obj).doubleValue()) != 1.0)
 2372                       throw new PdfXConformanceException("Transparency is not allowed: /ca = " + v);
 2373                   break;
 2374               case PDFXKEY_LAYER:
 2375                   throw new PdfXConformanceException("Layers are not allowed.");
 2376           }
 2377       }
 2378       
 2379       /**
 2380        * Sets the values of the output intent dictionary. Null values are allowed to
 2381        * suppress any key.
 2382        * @param outputConditionIdentifier a value
 2383        * @param outputCondition a value
 2384        * @param registryName a value
 2385        * @param info a value
 2386        * @param destOutputProfile a value
 2387        * @throws IOException on error
 2388        */    
 2389       public void setOutputIntents(String outputConditionIdentifier, String outputCondition, String registryName, String info, byte destOutputProfile[]) throws IOException {
 2390           getExtraCatalog();
 2391           PdfDictionary out = new PdfDictionary(PdfName.OUTPUTINTENT);
 2392           if (outputCondition != null)
 2393               out.put(PdfName.OUTPUTCONDITION, new PdfString(outputCondition, PdfObject.TEXT_UNICODE));
 2394           if (outputConditionIdentifier != null)
 2395               out.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString(outputConditionIdentifier, PdfObject.TEXT_UNICODE));
 2396           if (registryName != null)
 2397               out.put(PdfName.REGISTRYNAME, new PdfString(registryName, PdfObject.TEXT_UNICODE));
 2398           if (info != null)
 2399               out.put(PdfName.INFO, new PdfString(registryName, PdfObject.TEXT_UNICODE));
 2400           if (destOutputProfile != null) {
 2401               PdfStream stream = new PdfStream(destOutputProfile);
 2402               stream.flateCompress();
 2403               out.put(PdfName.DESTOUTPUTPROFILE, addToBody(stream).getIndirectReference());
 2404           }
 2405           out.put(PdfName.S, PdfName.GTS_PDFX);
 2406           extraCatalog.put(PdfName.OUTPUTINTENTS, new PdfArray(out));
 2407       }
 2408       
 2409       private static String getNameString(PdfDictionary dic, PdfName key) {
 2410           PdfObject obj = PdfReader.getPdfObject(dic.get(key));
 2411           if (obj == null || !obj.isString())
 2412               return null;
 2413           return ((PdfString)obj).toUnicodeString();
 2414       }
 2415       
 2416       /**
 2417        * Copies the output intent dictionary from other document to this one.
 2418        * @param reader the other document
 2419        * @param checkExistence <CODE>true</CODE> to just check for the existence of a valid output intent
 2420        * dictionary, <CODE>false</CODE> to insert the dictionary if it exists
 2421        * @throws IOException on error
 2422        * @return <CODE>true</CODE> if the output intent dictionary exists, <CODE>false</CODE>
 2423        * otherwise
 2424        */    
 2425       public boolean setOutputIntents(PdfReader reader, boolean checkExistence) throws IOException {
 2426           PdfDictionary catalog = reader.getCatalog();
 2427           PdfArray outs = (PdfArray)PdfReader.getPdfObject(catalog.get(PdfName.OUTPUTINTENTS));
 2428           if (outs == null)
 2429               return false;
 2430           ArrayList arr = outs.getArrayList();
 2431           if (arr.size() == 0)
 2432               return false;
 2433           PdfDictionary out = (PdfDictionary)PdfReader.getPdfObject((PdfObject)arr.get(0));
 2434           PdfObject obj = PdfReader.getPdfObject(out.get(PdfName.S));
 2435           if (obj == null || !PdfName.GTS_PDFX.equals(obj))
 2436               return false;
 2437           if (checkExistence)
 2438               return true;
 2439           PRStream stream = (PRStream)PdfReader.getPdfObject(out.get(PdfName.DESTOUTPUTPROFILE));
 2440           byte destProfile[] = null;
 2441           if (stream != null) {
 2442               destProfile = PdfReader.getStreamBytes(stream);
 2443           }
 2444           setOutputIntents(getNameString(out, PdfName.OUTPUTCONDITIONIDENTIFIER), getNameString(out, PdfName.OUTPUTCONDITION),
 2445               getNameString(out, PdfName.REGISTRYNAME), getNameString(out, PdfName.INFO), destProfile);
 2446           return true;
 2447       }
 2448       
 2449       /**
 2450        * Sets the page box sizes. Allowed names are: "crop", "trim", "art" and "bleed".
 2451        * @param boxName the box size
 2452        * @param size the size
 2453        */    
 2454       public void setBoxSize(String boxName, Rectangle size) {
 2455           pdf.setBoxSize(boxName, size);
 2456       }
 2457       
 2458       /**
 2459        * Gets the default colorspaces.
 2460        * @return the default colorspaces
 2461        */    
 2462       public PdfDictionary getDefaultColorspace() {
 2463           return defaultColorspace;
 2464       }
 2465   
 2466       /**
 2467        * Sets the default colorspace that will be applied to all the document.
 2468        * The colorspace is only applied if another colorspace with the same name
 2469        * is not present in the content.
 2470        * <p>
 2471        * The colorspace is applied immediately when creating templates and at the page
 2472        * end for the main document content.
 2473        * @param key the name of the colorspace. It can be <CODE>PdfName.DEFAULTGRAY</CODE>, <CODE>PdfName.DEFAULTRGB</CODE>
 2474        * or <CODE>PdfName.DEFAULTCMYK</CODE>
 2475        * @param cs the colorspace. A <CODE>null</CODE> or <CODE>PdfNull</CODE> removes any colorspace with the same name
 2476        */    
 2477       public void setDefaultColorspace(PdfName key, PdfObject cs) {
 2478           if (cs == null || cs.isNull())
 2479               defaultColorspace.remove(key);
 2480           defaultColorspace.put(key, cs);
 2481       }
 2482   
 2483       /**
 2484        * Gets the 1.5 compression status.
 2485        * @return <code>true</code> if the 1.5 compression is on
 2486        */
 2487       public boolean isFullCompression() {
 2488           return this.fullCompression;
 2489       }
 2490       
 2491       /**
 2492        * Sets the document's compression to the new 1.5 mode with object streams and xref
 2493        * streams. It can be set at any time but once set it can't be unset.
 2494        * <p>
 2495        * If set before opening the document it will also set the pdf version to 1.5.
 2496        */
 2497       public void setFullCompression() {
 2498           this.fullCompression = true;
 2499           setPdfVersion(VERSION_1_5);
 2500       }
 2501       
 2502       /**
 2503        * Gets the <B>Optional Content Properties Dictionary</B>. Each call fills the dictionary with the current layer
 2504        * state. It's advisable to only call this method right before close and do any modifications
 2505        * at that time.
 2506        * @return the Optional Content Properties Dictionary
 2507        */    
 2508       public PdfOCProperties getOCProperties() {
 2509           fillOCProperties(true);
 2510           return OCProperties;
 2511       }
 2512       
 2513       /**
 2514        * Sets a collection of optional content groups whose states are intended to follow
 2515        * a "radio button" paradigm. That is, the state of at most one optional
 2516        * content group in the array should be ON at a time: if one group is turned
 2517        * ON, all others must be turned OFF.
 2518        * @param group the radio group
 2519        */    
 2520       public void addOCGRadioGroup(ArrayList group) {
 2521           PdfArray ar = new PdfArray();
 2522           for (int k = 0; k < group.size(); ++k) {
 2523               PdfLayer layer = (PdfLayer)group.get(k);
 2524               if (layer.getTitle() == null)
 2525                   ar.add(layer.getRef());
 2526           }
 2527           if (ar.size() == 0)
 2528               return;
 2529           OCGRadioGroup.add(ar);
 2530       }
 2531       
 2532       /**
 2533        * Sets the the thumbnail image for the current page.
 2534        * @param image the image
 2535        * @throws PdfException on error
 2536        * @throws DocumentException or error
 2537        */    
 2538       public void setThumbnail(Image image) throws PdfException, DocumentException {
 2539           pdf.setThumbnail(image);
 2540       }
 2541   
 2542   }

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