KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > PdfWriter


1 /*
2  * $Id: PdfWriter.java 2765 2007-05-20 11:11:16Z blowagie $
3  *
4  * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
5  *
6  * The contents of this file are subject to the Mozilla Public License Version 1.1
7  * (the "License"); you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the License.
13  *
14  * The Original Code is 'iText, a free JAVA-PDF library'.
15  *
16  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18  * All Rights Reserved.
19  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21  *
22  * Contributor(s): all the names of the contributors are added in the source code
23  * where applicable.
24  *
25  * Alternatively, the contents of this file may be used under the terms of the
26  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27  * provisions of LGPL are applicable instead of those above. If you wish to
28  * allow use of your version of this file only under the terms of the LGPL
29  * License and not to allow others to use your version of this file under
30  * the MPL, indicate your decision by deleting the provisions above and
31  * replace them with the notice and other provisions required by the LGPL.
32  * If you do not delete the provisions above, a recipient may use your version
33  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34  *
35  * This library is free software; you can redistribute it and/or modify it
36  * under the terms of the MPL as stated above or under the terms of the GNU
37  * Library General Public License as published by the Free Software Foundation;
38  * either version 2 of the License, or any later version.
39  *
40  * This library is distributed in the hope that it will be useful, but WITHOUT
41  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43  * details.
44  *
45  * If you didn't download this code from the following link, you should check if
46  * you aren't using an obsolete version:
47  * http://www.lowagie.com/iText/
48  */

49
50 package com.lowagie.text.pdf;
51
52 import java.awt.Color JavaDoc;
53 import java.io.ByteArrayOutputStream JavaDoc;
54 import java.io.IOException JavaDoc;
55 import java.io.OutputStream JavaDoc;
56 import java.util.ArrayList JavaDoc;
57 import java.util.HashMap JavaDoc;
58 import java.util.HashSet JavaDoc;
59 import java.util.Iterator JavaDoc;
60 import java.util.Map JavaDoc;
61 import java.util.TreeMap JavaDoc;
62 import java.util.TreeSet JavaDoc;
63 import java.security.cert.Certificate JavaDoc;
64
65 import com.lowagie.text.DocListener;
66 import com.lowagie.text.DocWriter;
67 import com.lowagie.text.Document;
68 import com.lowagie.text.DocumentException;
69 import com.lowagie.text.ExceptionConverter;
70 import com.lowagie.text.Image;
71 import com.lowagie.text.ImgWMF;
72 import com.lowagie.text.Rectangle;
73 import com.lowagie.text.Table;
74 import com.lowagie.text.pdf.collection.PdfCollection;
75 import com.lowagie.text.pdf.events.PdfPageEventForwarder;
76 import com.lowagie.text.pdf.interfaces.PdfAnnotations;
77 import com.lowagie.text.pdf.interfaces.PdfDocumentActions;
78 import com.lowagie.text.pdf.interfaces.PdfEncryptionSettings;
79 import com.lowagie.text.pdf.interfaces.PdfPageActions;
80 import com.lowagie.text.pdf.interfaces.PdfVersion;
81 import com.lowagie.text.pdf.interfaces.PdfViewerPreferences;
82 import com.lowagie.text.pdf.interfaces.PdfXConformance;
83 import com.lowagie.text.pdf.interfaces.PdfRunDirection;
84 import com.lowagie.text.pdf.internal.PdfVersionImp;
85 import com.lowagie.text.pdf.internal.PdfXConformanceImp;
86 import com.lowagie.text.xml.xmp.XmpWriter;
87
88 /**
89  * A <CODE>DocWriter</CODE> class for PDF.
90  * <P>
91  * When this <CODE>PdfWriter</CODE> is added
92  * to a certain <CODE>PdfDocument</CODE>, the PDF representation of every Element
93  * added to this Document will be written to the outputstream.</P>
94  */

95
96 public class PdfWriter extends DocWriter implements
97     PdfViewerPreferences,
98     PdfEncryptionSettings,
99     PdfVersion,
100     PdfDocumentActions,
101     PdfPageActions,
102     PdfXConformance,
103     PdfRunDirection,
104     PdfAnnotations {
105     
106 // INNER CLASSES
107

108     /**
109      * This class generates the structure of a PDF document.
110      * <P>
111      * This class covers the third section of Chapter 5 in the 'Portable Document Format
112      * Reference Manual version 1.3' (page 55-60). It contains the body of a PDF document
113      * (section 5.14) and it can also generate a Cross-reference Table (section 5.15).
114      *
115      * @see PdfWriter
116      * @see PdfObject
117      * @see PdfIndirectObject
118      */

119     
120     public static class PdfBody {
121         
122         // inner classes
123

124         /**
125          * <CODE>PdfCrossReference</CODE> is an entry in the PDF Cross-Reference table.
126          */

127         
128         static class PdfCrossReference implements Comparable JavaDoc {
129             
130             // membervariables
131
private int type;
132             
133             /** Byte offset in the PDF file. */
134             private int offset;
135             
136             private int refnum;
137             /** generation of the object. */
138             private int generation;
139             
140             // constructors
141
/**
142              * Constructs a cross-reference element for a PdfIndirectObject.
143              * @param refnum
144              * @param offset byte offset of the object
145              * @param generation generationnumber of the object
146              */

147             
148             PdfCrossReference(int refnum, int offset, int generation) {
149                 type = 0;
150                 this.offset = offset;
151                 this.refnum = refnum;
152                 this.generation = generation;
153             }
154             
155             /**
156              * Constructs a cross-reference element for a PdfIndirectObject.
157              * @param refnum
158              * @param offset byte offset of the object
159              */

160             
161             PdfCrossReference(int refnum, int offset) {
162                 type = 1;
163                 this.offset = offset;
164                 this.refnum = refnum;
165                 this.generation = 0;
166             }
167             
168             PdfCrossReference(int type, int refnum, int offset, int generation) {
169                 this.type = type;
170                 this.offset = offset;
171                 this.refnum = refnum;
172                 this.generation = generation;
173             }
174             
175             int getRefnum() {
176                 return refnum;
177             }
178             
179             /**
180              * Returns the PDF representation of this <CODE>PdfObject</CODE>.
181              * @param os
182              * @throws IOException
183              */

184             
185             public void toPdf(OutputStream JavaDoc os) throws IOException JavaDoc {
186                 StringBuffer JavaDoc off = new StringBuffer JavaDoc("0000000000").append(offset);
187                 off.delete(0, off.length() - 10);
188                 StringBuffer JavaDoc gen = new StringBuffer JavaDoc("00000").append(generation);
189                 gen.delete(0, gen.length() - 5);
190
191                 off.append(' ').append(gen).append(generation == 65535 ? " f \n" : " n \n");
192                 os.write(getISOBytes(off.toString()));
193             }
194             
195             /**
196              * Writes PDF syntax to the OutputStream
197              * @param midSize
198              * @param os
199              * @throws IOException
200              */

201             public void toPdf(int midSize, OutputStream JavaDoc os) throws IOException JavaDoc {
202                 os.write((byte)type);
203                 while (--midSize >= 0)
204                     os.write((byte)((offset >>> (8 * midSize)) & 0xff));
205                 os.write((byte)((generation >>> 8) & 0xff));
206                 os.write((byte)(generation & 0xff));
207             }
208             
209             /**
210              * @see java.lang.Comparable#compareTo(java.lang.Object)
211              */

212             public int compareTo(Object JavaDoc o) {
213                 PdfCrossReference other = (PdfCrossReference)o;
214                 return (refnum < other.refnum ? -1 : (refnum==other.refnum ? 0 : 1));
215             }
216             
217             /**
218              * @see java.lang.Object#equals(java.lang.Object)
219              */

220             public boolean equals(Object JavaDoc obj) {
221                 if (obj instanceof PdfCrossReference) {
222                     PdfCrossReference other = (PdfCrossReference)obj;
223                     return (refnum == other.refnum);
224                 }
225                 else
226                     return false;
227             }
228
229             /**
230              * @see java.lang.Object#hashCode()
231              */

232             public int hashCode() {
233                 return refnum;
234             }
235
236         }
237
238         private static final int OBJSINSTREAM = 200;
239         
240         // membervariables
241

242         /** array containing the cross-reference table of the normal objects. */
243         private TreeSet JavaDoc xrefs;
244         private int refnum;
245         /** the current byteposition in the body. */
246         private int position;
247         private PdfWriter writer;
248         private ByteBuffer index;
249         private ByteBuffer streamObjects;
250         private int currentObjNum;
251         private int numObj = 0;
252         
253         // constructors
254

255         /**
256          * Constructs a new <CODE>PdfBody</CODE>.
257          * @param writer
258          */

259         PdfBody(PdfWriter writer) {
260             xrefs = new TreeSet JavaDoc();
261             xrefs.add(new PdfCrossReference(0, 0, 65535));
262             position = writer.getOs().getCounter();
263             refnum = 1;
264             this.writer = writer;
265         }
266         
267         // methods
268

269         void setRefnum(int refnum) {
270             this.refnum = refnum;
271         }
272         
273         private PdfWriter.PdfBody.PdfCrossReference addToObjStm(PdfObject obj, int nObj) throws IOException JavaDoc {
274             if (numObj >= OBJSINSTREAM)
275                 flushObjStm();
276             if (index == null) {
277                 index = new ByteBuffer();
278                 streamObjects = new ByteBuffer();
279                 currentObjNum = getIndirectReferenceNumber();
280                 numObj = 0;
281             }
282             int p = streamObjects.size();
283             int idx = numObj++;
284             PdfEncryption enc = writer.crypto;
285             writer.crypto = null;
286             obj.toPdf(writer, streamObjects);
287             writer.crypto = enc;
288             streamObjects.append(' ');
289             index.append(nObj).append(' ').append(p).append(' ');
290             return new PdfWriter.PdfBody.PdfCrossReference(2, nObj, currentObjNum, idx);
291         }
292         
293         private void flushObjStm() throws IOException JavaDoc {
294             if (numObj == 0)
295                 return;
296             int first = index.size();
297             index.append(streamObjects);
298             PdfStream stream = new PdfStream(index.toByteArray());
299             stream.flateCompress();
300             stream.put(PdfName.TYPE, PdfName.OBJSTM);
301             stream.put(PdfName.N, new PdfNumber(numObj));
302             stream.put(PdfName.FIRST, new PdfNumber(first));
303             add(stream, currentObjNum);
304             index = null;
305             streamObjects = null;
306             numObj = 0;
307         }
308         
309         /**
310          * Adds a <CODE>PdfObject</CODE> to the body.
311          * <P>
312          * This methods creates a <CODE>PdfIndirectObject</CODE> with a
313          * certain number, containing the given <CODE>PdfObject</CODE>.
314          * It also adds a <CODE>PdfCrossReference</CODE> for this object
315          * to an <CODE>ArrayList</CODE> that will be used to build the
316          * Cross-reference Table.
317          *
318          * @param object a <CODE>PdfObject</CODE>
319          * @return a <CODE>PdfIndirectObject</CODE>
320          * @throws IOException
321          */

322         
323         PdfIndirectObject add(PdfObject object) throws IOException JavaDoc {
324             return add(object, getIndirectReferenceNumber());
325         }
326         
327         PdfIndirectObject add(PdfObject object, boolean inObjStm) throws IOException JavaDoc {
328             return add(object, getIndirectReferenceNumber(), inObjStm);
329         }
330         
331         /**
332          * Gets a PdfIndirectReference for an object that will be created in the future.
333          * @return a PdfIndirectReference
334          */

335         
336         PdfIndirectReference getPdfIndirectReference() {
337             return new PdfIndirectReference(0, getIndirectReferenceNumber());
338         }
339         
340         int getIndirectReferenceNumber() {
341             int n = refnum++;
342             xrefs.add(new PdfCrossReference(n, 0, 65536));
343             return n;
344         }
345         
346         /**
347          * Adds a <CODE>PdfObject</CODE> to the body given an already existing
348          * PdfIndirectReference.
349          * <P>
350          * This methods creates a <CODE>PdfIndirectObject</CODE> with the number given by
351          * <CODE>ref</CODE>, containing the given <CODE>PdfObject</CODE>.
352          * It also adds a <CODE>PdfCrossReference</CODE> for this object
353          * to an <CODE>ArrayList</CODE> that will be used to build the
354          * Cross-reference Table.
355          *
356          * @param object a <CODE>PdfObject</CODE>
357          * @param ref a <CODE>PdfIndirectReference</CODE>
358          * @return a <CODE>PdfIndirectObject</CODE>
359          * @throws IOException
360          */

361         
362         PdfIndirectObject add(PdfObject object, PdfIndirectReference ref) throws IOException JavaDoc {
363             return add(object, ref.getNumber());
364         }
365         
366         PdfIndirectObject add(PdfObject object, PdfIndirectReference ref, boolean inObjStm) throws IOException JavaDoc {
367             return add(object, ref.getNumber(), inObjStm);
368         }
369         
370         PdfIndirectObject add(PdfObject object, int refNumber) throws IOException JavaDoc {
371             return add(object, refNumber, true); // to false
372
}
373         
374         PdfIndirectObject add(PdfObject object, int refNumber, boolean inObjStm) throws IOException JavaDoc {
375             if (inObjStm && object.canBeInObjStm() && writer.isFullCompression()) {
376                 PdfCrossReference pxref = addToObjStm(object, refNumber);
377                 PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer);
378                 if (!xrefs.add(pxref)) {
379                     xrefs.remove(pxref);
380                     xrefs.add(pxref);
381                 }
382                 return indirect;
383             }
384             else {
385                 PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer);
386                 PdfCrossReference pxref = new PdfCrossReference(refNumber, position);
387                 if (!xrefs.add(pxref)) {
388                     xrefs.remove(pxref);
389                     xrefs.add(pxref);
390                 }
391                 indirect.writeTo(writer.getOs());
392                 position = writer.getOs().getCounter();
393                 return indirect;
394             }
395         }
396         
397         /**
398          * Returns the offset of the Cross-Reference table.
399          *
400          * @return an offset
401          */

402         
403         int offset() {
404             return position;
405         }
406         
407         /**
408          * Returns the total number of objects contained in the CrossReferenceTable of this <CODE>Body</CODE>.
409          *
410          * @return a number of objects
411          */

412         
413         int size() {
414             return Math.max(((PdfCrossReference)xrefs.last()).getRefnum() + 1, refnum);
415         }
416         
417         /**
418          * Returns the CrossReferenceTable of the <CODE>Body</CODE>.
419          * @param os
420          * @param root
421          * @param info
422          * @param encryption
423          * @param fileID
424          * @param prevxref
425          * @throws IOException
426          */

427         
428         void writeCrossReferenceTable(OutputStream JavaDoc os, PdfIndirectReference root, PdfIndirectReference info, PdfIndirectReference encryption, PdfObject fileID, int prevxref) throws IOException JavaDoc {
429             int refNumber = 0;
430             if (writer.isFullCompression()) {
431                 flushObjStm();
432                 refNumber = getIndirectReferenceNumber();
433                 xrefs.add(new PdfCrossReference(refNumber, position));
434             }
435             PdfCrossReference entry = (PdfCrossReference)xrefs.first();
436             int first = entry.getRefnum();
437             int len = 0;
438             ArrayList JavaDoc sections = new ArrayList JavaDoc();
439             for (Iterator JavaDoc i = xrefs.iterator(); i.hasNext(); ) {
440                 entry = (PdfCrossReference)i.next();
441                 if (first + len == entry.getRefnum())
442                     ++len;
443                 else {
444                     sections.add(new Integer JavaDoc(first));
445                     sections.add(new Integer JavaDoc(len));
446                     first = entry.getRefnum();
447                     len = 1;
448                 }
449             }
450             sections.add(new Integer JavaDoc(first));
451             sections.add(new Integer JavaDoc(len));
452             if (writer.isFullCompression()) {
453                 int mid = 4;
454                 int mask = 0xff000000;
455                 for (; mid > 1; --mid) {
456                     if ((mask & position) != 0)
457                         break;
458                     mask >>>= 8;
459                 }
460                 ByteBuffer buf = new ByteBuffer();
461                 
462                 for (Iterator JavaDoc i = xrefs.iterator(); i.hasNext(); ) {
463                     entry = (PdfCrossReference) i.next();
464                     entry.toPdf(mid, buf);
465                 }
466                 PdfStream xr = new PdfStream(buf.toByteArray());
467                 buf = null;
468                 xr.flateCompress();
469                 xr.put(PdfName.SIZE, new PdfNumber(size()));
470                 xr.put(PdfName.ROOT, root);
471                 if (info != null) {
472                     xr.put(PdfName.INFO, info);
473                 }
474                 if (encryption != null)
475                     xr.put(PdfName.ENCRYPT, encryption);
476                 if (fileID != null)
477                     xr.put(PdfName.ID, fileID);
478                 xr.put(PdfName.W, new PdfArray(new int[]{1, mid, 2}));
479                 xr.put(PdfName.TYPE, PdfName.XREF);
480                 PdfArray idx = new PdfArray();
481                 for (int k = 0; k < sections.size(); ++k)
482                     idx.add(new PdfNumber(((Integer JavaDoc)sections.get(k)).intValue()));
483                 xr.put(PdfName.INDEX, idx);
484                 if (prevxref > 0)
485                     xr.put(PdfName.PREV, new PdfNumber(prevxref));
486                 PdfEncryption enc = writer.crypto;
487                 writer.crypto = null;
488                 PdfIndirectObject indirect = new PdfIndirectObject(refNumber, xr, writer);
489                 indirect.writeTo(writer.getOs());
490                 writer.crypto = enc;
491             }
492             else {
493                 os.write(getISOBytes("xref\n"));
494                 Iterator JavaDoc i = xrefs.iterator();
495                 for (int k = 0; k < sections.size(); k += 2) {
496                     first = ((Integer JavaDoc)sections.get(k)).intValue();
497                     len = ((Integer JavaDoc)sections.get(k + 1)).intValue();
498                     os.write(getISOBytes(String.valueOf(first)));
499                     os.write(getISOBytes(" "));
500                     os.write(getISOBytes(String.valueOf(len)));
501                     os.write('\n');
502                     while (len-- > 0) {
503                         entry = (PdfCrossReference) i.next();
504                         entry.toPdf(os);
505                     }
506                 }
507             }
508         }
509     }
510     
511     /**
512      * <CODE>PdfTrailer</CODE> is the PDF Trailer object.
513      * <P>
514      * This object is described in the 'Portable Document Format Reference Manual version 1.3'
515      * section 5.16 (page 59-60).
516      */

517     
518     static class PdfTrailer extends PdfDictionary {
519         
520         // membervariables
521

522         int offset;
523         
524         // constructors
525

526         /**
527          * Constructs a PDF-Trailer.
528          *
529          * @param size the number of entries in the <CODE>PdfCrossReferenceTable</CODE>
530          * @param offset offset of the <CODE>PdfCrossReferenceTable</CODE>
531          * @param root an indirect reference to the root of the PDF document
532          * @param info an indirect reference to the info object of the PDF document
533          * @param encryption
534          * @param fileID
535          * @param prevxref
536          */

537         
538         PdfTrailer(int size, int offset, PdfIndirectReference root, PdfIndirectReference info, PdfIndirectReference encryption, PdfObject fileID, int prevxref) {
539             this.offset = offset;
540             put(PdfName.SIZE, new PdfNumber(size));
541             put(PdfName.ROOT, root);
542             if (info != null) {
543                 put(PdfName.INFO, info);
544             }
545             if (encryption != null)
546                 put(PdfName.ENCRYPT, encryption);
547             if (fileID != null)
548                 put(PdfName.ID, fileID);
549             if (prevxref > 0)
550                 put(PdfName.PREV, new PdfNumber(prevxref));
551         }
552         
553         /**
554          * Returns the PDF representation of this <CODE>PdfObject</CODE>.
555          * @param writer
556          * @param os
557          * @throws IOException
558          */

559         public void toPdf(PdfWriter writer, OutputStream JavaDoc os) throws IOException JavaDoc {
560             os.write(getISOBytes("trailer\n"));
561             super.toPdf(null, os);
562             os.write(getISOBytes("\nstartxref\n"));
563             os.write(getISOBytes(String.valueOf(offset)));
564             os.write(getISOBytes("\n%%EOF\n"));
565         }
566     }
567
568 // ESSENTIALS
569

570 // Construct a PdfWriter instance
571

572     /**
573      * Constructs a <CODE>PdfWriter</CODE>.
574      */

575     protected PdfWriter() {
576     }
577     
578     /**
579      * Constructs a <CODE>PdfWriter</CODE>.
580      * <P>
581      * Remark: a PdfWriter can only be constructed by calling the method
582      * <CODE>getInstance(Document document, OutputStream os)</CODE>.
583      *
584      * @param document The <CODE>PdfDocument</CODE> that has to be written
585      * @param os The <CODE>OutputStream</CODE> the writer has to write to.
586      */

587     
588     protected PdfWriter(PdfDocument document, OutputStream JavaDoc os) {
589         super(document, os);
590         pdf = document;
591         directContent = new PdfContentByte(this);
592         directContentUnder = new PdfContentByte(this);
593     }
594     
595     /**
596      * Use this method to get an instance of the <CODE>PdfWriter</CODE>.
597      *
598      * @param document The <CODE>Document</CODE> that has to be written
599      * @param os The <CODE>OutputStream</CODE> the writer has to write to.
600      * @return a new <CODE>PdfWriter</CODE>
601      *
602      * @throws DocumentException on error
603      */

604     
605     public static PdfWriter getInstance(Document document, OutputStream JavaDoc os)
606     throws DocumentException {
607         PdfDocument pdf = new PdfDocument();
608         document.addDocListener(pdf);
609         PdfWriter writer = new PdfWriter(pdf, os);
610         pdf.addWriter(writer);
611         return writer;
612     }
613     
614     /**
615      * Use this method to get an instance of the <CODE>PdfWriter</CODE>.
616      *
617      * @return a new <CODE>PdfWriter</CODE>
618      * @param document The <CODE>Document</CODE> that has to be written
619      * @param os The <CODE>OutputStream</CODE> the writer has to write to.
620      * @param listener A <CODE>DocListener</CODE> to pass to the PdfDocument.
621      * @throws DocumentException on error
622      */

623     
624     public static PdfWriter getInstance(Document document, OutputStream JavaDoc os, DocListener listener)
625     throws DocumentException {
626         PdfDocument pdf = new PdfDocument();
627         pdf.addDocListener(listener);
628         document.addDocListener(pdf);
629         PdfWriter writer = new PdfWriter(pdf, os);
630         pdf.addWriter(writer);
631         return writer;
632     }
633
634 // the PdfDocument instance
635

636     /** the pdfdocument object. */
637     protected PdfDocument pdf;
638     
639     /**
640      * Gets the <CODE>PdfDocument</CODE> associated with this writer.
641      * @return the <CODE>PdfDocument</CODE>
642      */

643     
644     PdfDocument getPdfDocument() {
645         return pdf;
646     }
647
648     /**
649      * Use this method to get the info dictionary if you want to
650      * change it directly (add keys and values to the info dictionary).
651      * @return the info dictionary
652      */

653     public PdfDictionary getInfo() {
654         return pdf.getInfo();
655     }
656
657     /**
658      * Use this method to get the current vertical page position.
659      * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects
660      * for elements that do not terminate the lines they've started because those lines will get
661      * terminated.
662      * @return The current vertical page position.
663      */

664     public float getVerticalPosition(boolean ensureNewLine) {
665         return pdf.getVerticalPosition(ensureNewLine);
666     }
667     
668 // the PdfDirectContentByte instances
669

670 /*
671  * You should see Direct Content as a canvas on which you can draw
672  * graphics and text. One canvas goes on top of the page (getDirectContent),
673  * the other goes underneath (getDirectContentUnder).
674  * You can always the same object throughout your document,
675  * even if you have moved to a new page. Whatever you add on
676  * the canvas will be displayed on top or under the current page.
677  */

678     
679     /** The direct content in this document. */
680     protected PdfContentByte directContent;
681     
682     /** The direct content under in this document. */
683     protected PdfContentByte directContentUnder;
684     
685     /**
686      * Use this method to get the direct content for this document.
687      * There is only one direct content, multiple calls to this method
688      * will allways retrieve the same object.
689      * @return the direct content
690      */

691     
692     public PdfContentByte getDirectContent() {
693         if (!open)
694             throw new RuntimeException JavaDoc("The document is not open.");
695         return directContent;
696     }
697     
698     /**
699      * Use this method to get the direct content under for this document.
700      * There is only one direct content, multiple calls to this method
701      * will allways retrieve the same object.
702      * @return the direct content
703      */

704     
705     public PdfContentByte getDirectContentUnder() {
706         if (!open)
707             throw new RuntimeException JavaDoc("The document is not open.");
708         return directContentUnder;
709     }
710     
711     /**
712      * Resets all the direct contents to empty.
713      * This happens when a new page is started.
714      */

715     void resetContent() {
716         directContent.reset();
717         directContentUnder.reset();
718     }
719     
720 // PDF body
721

722 /*
723  * A PDF file has 4 parts: a header, a body, a cross-reference table, and a trailer.
724  * The body contains all the PDF objects that make up the PDF document.
725  * Each element gets a reference (a set of numbers) and the byte position of
726  * every object is stored in the cross-reference table.
727  * Use these methods only if you know what you're doing.
728  */

729     
730     /** body of the PDF document */
731     protected PdfBody body;
732     
733     /**
734      * Adds the local destinations to the body of the document.
735      * @param dest the <CODE>HashMap</CODE> containing the destinations
736      * @throws IOException on error
737      */

738     
739     void addLocalDestinations(TreeMap JavaDoc dest) throws IOException JavaDoc {
740         for (Iterator JavaDoc i = dest.entrySet().iterator(); i.hasNext();) {
741             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
742             String JavaDoc name = (String JavaDoc) entry.getKey();
743             Object JavaDoc obj[] = (Object JavaDoc[]) entry.getValue();
744             PdfDestination destination = (PdfDestination)obj[2];
745             if (destination == null)
746                 throw new RuntimeException JavaDoc("The name '" + name + "' has no local destination.");
747             if (obj[1] == null)
748                 obj[1] = getPdfIndirectReference();
749             addToBody(destination, (PdfIndirectReference)obj[1]);
750         }
751     }
752     
753     /**
754      * Use this method to add a PDF object to the PDF body.
755      * Use this method only if you know what you're doing!
756      * @param object
757      * @return a PdfIndirectObject
758      * @throws IOException
759      */

760     public PdfIndirectObject addToBody(PdfObject object) throws IOException JavaDoc {
761         PdfIndirectObject iobj = body.add(object);
762         return iobj;
763     }
764     
765     /**
766      * Use this method to add a PDF object to the PDF body.
767      * Use this method only if you know what you're doing!
768      * @param object
769      * @param inObjStm
770      * @return a PdfIndirectObject
771      * @throws IOException
772      */

773     public PdfIndirectObject addToBody(PdfObject object, boolean inObjStm) throws IOException JavaDoc {
774         PdfIndirectObject iobj = body.add(object, inObjStm);
775         return iobj;
776     }
777     
778     /**
779      * Use this method to add a PDF object to the PDF body.
780      * Use this method only if you know what you're doing!
781      * @param object
782      * @param ref
783      * @return a PdfIndirectObject
784      * @throws IOException
785      */

786     public PdfIndirectObject addToBody(PdfObject object, PdfIndirectReference ref) throws IOException JavaDoc {
787         PdfIndirectObject iobj = body.add(object, ref);
788         return iobj;
789     }
790     
791     /**
792      * Use this method to add a PDF object to the PDF body.
793      * Use this method only if you know what you're doing!
794      * @param object
795      * @param ref
796      * @param inObjStm
797      * @return a PdfIndirectObject
798      * @throws IOException
799      */

800     public PdfIndirectObject addToBody(PdfObject object, PdfIndirectReference ref, boolean inObjStm) throws IOException JavaDoc {
801         PdfIndirectObject iobj = body.add(object, ref, inObjStm);
802         return iobj;
803     }
804     
805     /**
806      * Use this method to add a PDF object to the PDF body.
807      * Use this method only if you know what you're doing!
808      * @param object
809      * @param refNumber
810      * @return a PdfIndirectObject
811      * @throws IOException
812      */

813     public PdfIndirectObject addToBody(PdfObject object, int refNumber) throws IOException JavaDoc {
814         PdfIndirectObject iobj = body.add(object, refNumber);
815         return iobj;
816     }
817     
818     /**
819      * Use this method to add a PDF object to the PDF body.
820      * Use this method only if you know what you're doing!
821      * @param object
822      * @param refNumber
823      * @param inObjStm
824      * @return a PdfIndirectObject
825      * @throws IOException
826      */

827     public PdfIndirectObject addToBody(PdfObject object, int refNumber, boolean inObjStm) throws IOException JavaDoc {
828         PdfIndirectObject iobj = body.add(object, refNumber, inObjStm);
829         return iobj;
830     }
831     
832     /**
833      * Use this to get an <CODE>PdfIndirectReference</CODE> for an object that
834      * will be created in the future.
835      * Use this method only if you know what you're doing!
836      * @return the <CODE>PdfIndirectReference</CODE>
837      */

838     
839     public PdfIndirectReference getPdfIndirectReference() {
840         return body.getPdfIndirectReference();
841     }
842     
843     int getIndirectReferenceNumber() {
844         return body.getIndirectReferenceNumber();
845     }
846     
847     /**
848      * Returns the outputStreamCounter.
849      * @return the outputStreamCounter
850      */

851     OutputStreamCounter getOs() {
852         return os;
853     }
854
855
856 // PDF Catalog
857

858 /*
859  * The Catalog is also called the root object of the document.
860  * Whereas the Cross-Reference maps the objects number with the
861  * byte offset so that the viewer can find the objects, the
862  * Catalog tells the viewer the numbers of the objects needed
863  * to render the document.
864  */

865     
866     protected PdfDictionary getCatalog(PdfIndirectReference rootObj)
867     {
868         PdfDictionary catalog = pdf.getCatalog(rootObj);
869         // [F12] tagged PDF
870
if (tagged) {
871             try {
872                 getStructureTreeRoot().buildTree();
873             }
874             catch (Exception JavaDoc e) {
875                 throw new ExceptionConverter(e);
876             }
877             catalog.put(PdfName.STRUCTTREEROOT, structureTreeRoot.getReference());
878             PdfDictionary mi = new PdfDictionary();
879             mi.put(PdfName.MARKED, PdfBoolean.PDFTRUE);
880             if (userProperties)
881                 mi.put(PdfName.USERPROPERTIES, PdfBoolean.PDFTRUE);
882             catalog.put(PdfName.MARKINFO, mi);
883         }
884         // [F13] OCG
885
if (!documentOCG.isEmpty()) {
886             fillOCProperties(false);
887             catalog.put(PdfName.OCPROPERTIES, OCProperties);
888         }
889         return catalog;
890     }
891     
892     /** Holds value of property extraCatalog this is used for Output Intents. */
893     protected PdfDictionary extraCatalog;
894     
895     /**
896      * Sets extra keys to the catalog.
897      * @return the catalog to change
898      */

899     public PdfDictionary getExtraCatalog() {
900         if (extraCatalog == null)
901             extraCatalog = new PdfDictionary();
902         return this.extraCatalog;
903     }
904     
905 // PdfPages
906

907 /*
908  * The page root keeps the complete page tree of the document.
909  * There's an entry in the Catalog that refers to the root
910  * of the page tree, the page tree contains the references
911  * to pages and other page trees.
912  */

913     
914     /** The root of the page tree. */
915     protected PdfPages root = new PdfPages(this);
916     /** The PdfIndirectReference to the pages. */
917     protected ArrayList JavaDoc pageReferences = new ArrayList JavaDoc();
918     /** The current page number. */
919     protected int currentPageNumber = 1;
920
921     /**
922      * Use this method to make sure the page tree has a lineair structure
923      * (every leave is attached directly to the root).
924      * Use this method to allow page reordering with method reorderPages.
925      */

926      public void setLinearPageMode() {
927         root.setLinearMode(null);
928     }
929      
930     /**
931      * Use this method to reorder the pages in the document.
932      * A <CODE>null</CODE> argument value only returns the number of pages to process.
933      * It is advisable to issue a <CODE>Document.newPage()</CODE> before using this method.
934      * @return the total number of pages
935      * @param order an array with the new page sequence. It must have the
936      * same size as the number of pages.
937      * @throws DocumentException if all the pages are not present in the array
938      */

939     public int reorderPages(int order[]) throws DocumentException {
940         return root.reorderPages(order);
941     }
942     
943     /**
944      * Use this method to get a reference to a page existing or not.
945      * If the page does not exist yet the reference will be created
946      * in advance. If on closing the document, a page number greater
947      * than the total number of pages was requested, an exception
948      * is thrown.
949      * @param page the page number. The first page is 1
950      * @return the reference to the page
951      */

952     public PdfIndirectReference getPageReference(int page) {
953         --page;
954         if (page < 0)
955             throw new IndexOutOfBoundsException JavaDoc("The page numbers start at 1.");
956         PdfIndirectReference ref;
957         if (page < pageReferences.size()) {
958             ref = (PdfIndirectReference)pageReferences.get(page);
959             if (ref == null) {
960                 ref = body.getPdfIndirectReference();
961                 pageReferences.set(page, ref);
962             }
963         }
964         else {
965             int empty = page - pageReferences.size();
966             for (int k = 0; k < empty; ++k)
967                 pageReferences.add(null);
968             ref = body.getPdfIndirectReference();
969             pageReferences.add(ref);
970         }
971         return ref;
972     }
973     
974     /**
975      * Gets the pagenumber of this document.
976      * This number can be different from the real pagenumber,
977      * if you have (re)set the page number previously.
978      * @return a page number
979      */

980     
981     public int getPageNumber() {
982         return pdf.getPageNumber();
983     }
984     
985     PdfIndirectReference getCurrentPage() {
986         return getPageReference(currentPageNumber);
987     }
988     
989     public int getCurrentPageNumber() {
990         return currentPageNumber;
991     }
992     
993     /**
994      * Adds some <CODE>PdfContents</CODE> to this Writer.
995      * <P>
996      * The document has to be open before you can begin to add content
997      * to the body of the document.
998      *
999      * @return a <CODE>PdfIndirectReference</CODE>
1000     * @param page the <CODE>PdfPage</CODE> to add
1001     * @param contents the <CODE>PdfContents</CODE> of the page
1002     * @throws PdfException on error
1003     */

1004    
1005    PdfIndirectReference add(PdfPage page, PdfContents contents) throws PdfException {
1006        if (!open) {
1007            throw new PdfException("The document isn't open.");
1008        }
1009        PdfIndirectObject object;
1010        try {
1011            object = addToBody(contents);
1012        }
1013        catch(IOException JavaDoc ioe) {
1014            throw new ExceptionConverter(ioe);
1015        }
1016        page.add(object.getIndirectReference());
1017        // [U5]
1018
if (group != null) {
1019            page.put(PdfName.GROUP, group);
1020            group = null;
1021        }
1022        root.addPage(page);
1023        currentPageNumber++;
1024        return null;
1025    }
1026    
1027// page events
1028

1029/*
1030 * Page events are specific for iText, not for PDF.
1031 * Upon specific events (for instance when a page starts
1032 * or ends), the corresponing method in the page event
1033 * implementation that is added to the writer is invoked.
1034 */

1035    
1036    /** The <CODE>PdfPageEvent</CODE> for this document. */
1037    private PdfPageEvent pageEvent;
1038    
1039    /**
1040     * Sets the <CODE>PdfPageEvent</CODE> for this document.
1041     * @param event the <CODE>PdfPageEvent</CODE> for this document
1042     */

1043    
1044    public void setPageEvent(PdfPageEvent event) {
1045        if (event == null) this.pageEvent = null;
1046        else if (this.pageEvent == null) this.pageEvent = event;
1047        else if (this.pageEvent instanceof PdfPageEventForwarder) ((PdfPageEventForwarder)this.pageEvent).addPageEvent(event);
1048        else {
1049            PdfPageEventForwarder forward = new PdfPageEventForwarder();
1050            forward.addPageEvent(this.pageEvent);
1051            forward.addPageEvent(event);
1052            this.pageEvent = forward;
1053        }
1054    }
1055    
1056    /**
1057     * Gets the <CODE>PdfPageEvent</CODE> for this document or <CODE>null</CODE>
1058     * if none is set.
1059     * @return the <CODE>PdfPageEvent</CODE> for this document or <CODE>null</CODE>
1060     * if none is set
1061     */

1062    
1063    public PdfPageEvent getPageEvent() {
1064        return pageEvent;
1065    }
1066    
1067// Open en Close method + method that create the PDF
1068

1069    /** A number refering to the previous Cross-Reference Table. */
1070    protected int prevxref = 0;
1071    
1072    /**
1073     * Signals that the <CODE>Document</CODE> has been opened and that
1074     * <CODE>Elements</CODE> can be added.
1075     * <P>
1076     * When this method is called, the PDF-document header is
1077     * written to the outputstream.
1078     * @see com.lowagie.text.DocWriter#open()
1079     */

1080    public void open() {
1081        super.open();
1082        try {
1083            pdf_version.writeHeader(os);
1084            body = new PdfBody(this);
1085            if (pdfxConformance.isPdfX32002()) {
1086                PdfDictionary sec = new PdfDictionary();
1087                sec.put(PdfName.GAMMA, new PdfArray(new float[]{2.2f,2.2f,2.2f}));
1088                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}));
1089                sec.put(PdfName.WHITEPOINT, new PdfArray(new float[]{0.9505f,1f,1.089f}));
1090                PdfArray arr = new PdfArray(PdfName.CALRGB);
1091                arr.add(sec);
1092                setDefaultColorspace(PdfName.DEFAULTRGB, addToBody(arr).getIndirectReference());
1093            }
1094        }
1095        catch(IOException JavaDoc ioe) {
1096            throw new ExceptionConverter(ioe);
1097        }
1098    }
1099    
1100    /**
1101     * Signals that the <CODE>Document</CODE> was closed and that no other
1102     * <CODE>Elements</CODE> will be added.
1103     * <P>
1104     * The pages-tree is built and written to the outputstream.
1105     * A Catalog is constructed, as well as an Info-object,
1106     * the referencetable is composed and everything is written
1107     * to the outputstream embedded in a Trailer.
1108     * @see com.lowagie.text.DocWriter#close()
1109     */

1110    public synchronized void close() {
1111        if (open) {
1112            if ((currentPageNumber - 1) != pageReferences.size())
1113                throw new RuntimeException JavaDoc("The page " + pageReferences.size() +
1114                " was requested but the document has only " + (currentPageNumber - 1) + " pages.");
1115            pdf.close();
1116            try {
1117                addSharedObjectsToBody();
1118                // add the root to the body
1119
PdfIndirectReference rootRef = root.writePageTree();
1120                // make the catalog-object and add it to the body
1121
PdfDictionary catalog = getCatalog(rootRef);
1122                // [C9] if there is XMP data to add: add it
1123
if (xmpMetadata != null) {
1124                    PdfStream xmp = new PdfStream(xmpMetadata);
1125                    xmp.put(PdfName.TYPE, PdfName.METADATA);
1126                    xmp.put(PdfName.SUBTYPE, PdfName.XML);
1127                    if (crypto != null && !crypto.isMetadataEncrypted()) {
1128                        PdfArray ar = new PdfArray();
1129                        ar.add(PdfName.CRYPT);
1130                        xmp.put(PdfName.FILTER, ar);
1131                    }
1132                    catalog.put(PdfName.METADATA, body.add(xmp).getIndirectReference());
1133                }
1134                // [C10] make pdfx conformant
1135
if (isPdfX()) {
1136                    pdfxConformance.completeInfoDictionary(getInfo());
1137                    pdfxConformance.completeExtraCatalog(getExtraCatalog());
1138                }
1139                // [C11] Output Intents
1140
if (extraCatalog != null) {
1141                    catalog.mergeDifferent(extraCatalog);
1142                }
1143                
1144                // add the Catalog to the body
1145
PdfIndirectObject indirectCatalog = addToBody(catalog, false);
1146                // add the info-object to the body
1147
PdfIndirectObject infoObj = addToBody(getInfo(), false);
1148                
1149                // [F1] encryption
1150
PdfIndirectReference encryption = null;
1151                PdfObject fileID = null;
1152                body.flushObjStm();
1153                if (crypto != null) {
1154                    PdfIndirectObject encryptionObject = addToBody(crypto.getEncryptionDictionary(), false);
1155                    encryption = encryptionObject.getIndirectReference();
1156                    fileID = crypto.getFileID();
1157                }
1158                else
1159                    fileID = PdfEncryption.createInfoId(PdfEncryption.createDocumentId());
1160                
1161                // write the cross-reference table of the body
1162
body.writeCrossReferenceTable(os, indirectCatalog.getIndirectReference(),
1163                    infoObj.getIndirectReference(), encryption, fileID, prevxref);
1164
1165                // make the trailer
1166
// [F2] full compression
1167
if (fullCompression) {
1168                    os.write(getISOBytes("startxref\n"));
1169                    os.write(getISOBytes(String.valueOf(body.offset())));
1170                    os.write(getISOBytes("\n%%EOF\n"));
1171                }
1172                else {
1173                    PdfTrailer trailer = new PdfTrailer(body.size(),
1174                    body.offset(),
1175                    indirectCatalog.getIndirectReference(),
1176                    infoObj.getIndirectReference(),
1177                    encryption,
1178                    fileID, prevxref);
1179                    trailer.toPdf(this, os);
1180                }
1181                super.close();
1182            }
1183            catch(IOException JavaDoc ioe) {
1184                throw new ExceptionConverter(ioe);
1185            }
1186        }
1187    }
1188    
1189    protected void addSharedObjectsToBody() throws IOException JavaDoc {
1190        // [F3] add the fonts
1191
for (Iterator JavaDoc it = documentFonts.values().iterator(); it.hasNext();) {
1192            FontDetails details = (FontDetails)it.next();
1193            details.writeFont(this);
1194        }
1195        // [F4] add the form XObjects
1196
for (Iterator JavaDoc it = formXObjects.values().iterator(); it.hasNext();) {
1197            Object JavaDoc objs[] = (Object JavaDoc[])it.next();
1198            PdfTemplate template = (PdfTemplate)objs[1];
1199            if (template != null && template.getIndirectReference() instanceof PRIndirectReference)
1200                continue;
1201            if (template != null && template.getType() == PdfTemplate.TYPE_TEMPLATE) {
1202                addToBody(template.getFormXObject(), template.getIndirectReference());
1203            }
1204        }
1205        // [F5] add all the dependencies in the imported pages
1206
for (Iterator JavaDoc it = importedPages.values().iterator(); it.hasNext();) {
1207            currentPdfReaderInstance = (PdfReaderInstance)it.next();
1208            currentPdfReaderInstance.writeAllPages();
1209        }
1210        currentPdfReaderInstance = null;
1211        // [F6] add the spotcolors
1212
for (Iterator JavaDoc it = documentColors.values().iterator(); it.hasNext();) {
1213            ColorDetails color = (ColorDetails)it.next();
1214            addToBody(color.getSpotColor(this), color.getIndirectReference());
1215        }
1216        // [F7] add the pattern
1217
for (Iterator JavaDoc it = documentPatterns.keySet().iterator(); it.hasNext();) {
1218            PdfPatternPainter pat = (PdfPatternPainter)it.next();
1219            addToBody(pat.getPattern(), pat.getIndirectReference());
1220        }
1221        // [F8] add the shading patterns
1222
for (Iterator JavaDoc it = documentShadingPatterns.keySet().iterator(); it.hasNext();) {
1223            PdfShadingPattern shadingPattern = (PdfShadingPattern)it.next();
1224            shadingPattern.addToBody();
1225        }
1226        // [F9] add the shadings
1227
for (Iterator JavaDoc it = documentShadings.keySet().iterator(); it.hasNext();) {
1228            PdfShading shading = (PdfShading)it.next();
1229            shading.addToBody();
1230        }
1231        // [F10] add the extgstate
1232
for (Iterator JavaDoc it = documentExtGState.entrySet().iterator(); it.hasNext();) {
1233            Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
1234            PdfDictionary gstate = (PdfDictionary) entry.getKey();
1235            PdfObject obj[] = (PdfObject[]) entry.getValue();
1236            addToBody(gstate, (PdfIndirectReference)obj[1]);
1237        }
1238        // [F11] add the properties
1239
for (Iterator JavaDoc it = documentProperties.entrySet().iterator(); it.hasNext();) {
1240            Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
1241            Object JavaDoc prop = entry.getKey();
1242            PdfObject[] obj = (PdfObject[]) entry.getValue();
1243            if (prop instanceof PdfLayerMembership){
1244                PdfLayerMembership layer = (PdfLayerMembership)prop;
1245                addToBody(layer.getPdfObject(), layer.getRef());
1246            }
1247            else if ((prop instanceof PdfDictionary) && !(prop instanceof PdfLayer)){
1248                addToBody((PdfDictionary)prop, (PdfIndirectReference)obj[1]);
1249            }
1250        }
1251        // [F13] add the OCG layers
1252
for (Iterator JavaDoc it = documentOCG.iterator(); it.hasNext();) {
1253            PdfOCG layer = (PdfOCG)it.next();
1254            addToBody(layer.getPdfObject(), layer.getRef());
1255        }
1256    }
1257     
1258// Root data for the PDF document (used when composing the Catalog)
1259

1260// [C1] Outlines (bookmarks)
1261

1262     /**
1263      * Use this method to get the root outline
1264      * and construct bookmarks.
1265      * @return the root outline
1266      */

1267     
1268     public PdfOutline getRootOutline() {
1269         return directContent.getRootOutline();
1270     }
1271     
1272// [C2] PdfVersion interface
1273
/** possible PDF version (header) */
1274     public static final char VERSION_1_2 = '2';
1275     /** possible PDF version (header) */
1276     public static final char VERSION_1_3 = '3';
1277     /** possible PDF version (header) */
1278     public static final char VERSION_1_4 = '4';
1279     /** possible PDF version (header) */
1280     public static final char VERSION_1_5 = '5';
1281     /** possible PDF version (header) */
1282     public static final char VERSION_1_6 = '6';
1283     /** possible PDF version (header) */
1284     public static final char VERSION_1_7 = '7';
1285     
1286     /** possible PDF version (catalog) */
1287     public static final PdfName PDF_VERSION_1_2 = new PdfName("1.2");
1288     /** possible PDF version (catalog) */
1289     public static final PdfName PDF_VERSION_1_3 = new PdfName("1.3");
1290     /** possible PDF version (catalog) */
1291     public static final PdfName PDF_VERSION_1_4 = new PdfName("1.4");
1292     /** possible PDF version (catalog) */
1293     public static final PdfName PDF_VERSION_1_5 = new PdfName("1.5");
1294     /** possible PDF version (catalog) */
1295     public static final PdfName PDF_VERSION_1_6 = new PdfName("1.6");
1296     /** possible PDF version (catalog) */
1297     public static final PdfName PDF_VERSION_1_7 = new PdfName("1.7");
1298
1299    /** Stores the version information for the header and the catalog. */
1300    protected PdfVersionImp pdf_version = new PdfVersionImp();
1301    
1302    /** @see com.lowagie.text.pdf.interfaces.PdfVersion#setPdfVersion(char) */
1303    public void setPdfVersion(char version) {
1304        pdf_version.setPdfVersion(version);
1305    }
1306    
1307    /** @see com.lowagie.text.pdf.interfaces.PdfVersion#setAtLeastPdfVersion(char) */
1308    public void setAtLeastPdfVersion(char version) {
1309        pdf_version.setAtLeastPdfVersion(version);
1310    }
1311
1312    /** @see com.lowagie.text.pdf.interfaces.PdfVersion#setPdfVersion(com.lowagie.text.pdf.PdfName) */
1313    public void setPdfVersion(PdfName version) {
1314        pdf_version.setPdfVersion(version);
1315    }
1316    
1317    /**
1318     * Returns the version information.
1319     */

1320    PdfVersionImp getPdfVersion() {
1321        return pdf_version;
1322    }
1323    
1324// [C3] PdfViewerPreferences interface
1325

1326    // page layout (section 13.1.1 of "iText in Action")
1327

1328    /** A viewer preference */
1329    public static final int PageLayoutSinglePage = 1;
1330    /** A viewer preference */
1331    public static final int PageLayoutOneColumn = 2;
1332    /** A viewer preference */
1333    public static final int PageLayoutTwoColumnLeft = 4;
1334    /** A viewer preference */
1335    public static final int PageLayoutTwoColumnRight = 8;
1336    /** A viewer preference */
1337    public static final int PageLayoutTwoPageLeft = 16;
1338    /** A viewer preference */
1339    public static final int PageLayoutTwoPageRight = 32;
1340
1341    // page mode (section 13.1.2 of "iText in Action")
1342

1343    /** A viewer preference */
1344    public static final int PageModeUseNone = 64;
1345    /** A viewer preference */
1346    public static final int PageModeUseOutlines = 128;
1347    /** A viewer preference */
1348    public static final int PageModeUseThumbs = 256;
1349    /** A viewer preference */
1350    public static final int PageModeFullScreen = 512;
1351    /** A viewer preference */
1352    public static final int PageModeUseOC = 1024;
1353    /** A viewer preference */
1354    public static final int PageModeUseAttachments = 2048;
1355    
1356    // values for setting viewer preferences in iText versions older than 2.x
1357

1358    /** A viewer preference */
1359    public static final int HideToolbar = 1 << 12;
1360    /** A viewer preference */
1361    public static final int HideMenubar = 1 << 13;
1362    /** A viewer preference */
1363    public static final int HideWindowUI = 1 << 14;
1364    /** A viewer preference */
1365    public static final int FitWindow = 1 << 15;
1366    /** A viewer preference */
1367    public static final int CenterWindow = 1 << 16;
1368    /** A viewer preference */
1369    public static final int DisplayDocTitle = 1 << 17;
1370
1371    /** A viewer preference */
1372    public static final int NonFullScreenPageModeUseNone = 1 << 18;
1373    /** A viewer preference */
1374    public static final int NonFullScreenPageModeUseOutlines = 1 << 19;
1375    /** A viewer preference */
1376    public static final int NonFullScreenPageModeUseThumbs = 1 << 20;
1377    /** A viewer preference */
1378    public static final int NonFullScreenPageModeUseOC = 1 << 21;
1379
1380    /** A viewer preference */
1381    public static final int DirectionL2R = 1 << 22;
1382    /** A viewer preference */
1383    public static final int DirectionR2L = 1 << 23;
1384
1385    /** A viewer preference */
1386    public static final int PrintScalingNone = 1 << 24;
1387    
1388    /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#setViewerPreferences(int) */
1389    public void setViewerPreferences(int preferences) {
1390        pdf.setViewerPreferences(preferences);
1391    }
1392
1393    /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#addViewerPreference(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfObject) */
1394    public void addViewerPreference(PdfName key, PdfObject value) {
1395        pdf.addViewerPreference(key, value);
1396    }
1397 
1398// [C4] Page labels
1399

1400    /**
1401     * Use this method to add page labels
1402     * @param pageLabels the page labels
1403     */

1404    public void setPageLabels(PdfPageLabels pageLabels) {
1405        pdf.setPageLabels(pageLabels);
1406    }
1407     
1408// [C5] named objects: named destinations, javascript, embedded files
1409

1410     /**
1411      * Use this method to add a JavaScript action at the document level.
1412      * When the document opens, all this JavaScript runs.
1413      * @param js The JavaScrip action
1414      */

1415     public void addJavaScript(PdfAction js) {
1416         pdf.addJavaScript(js);
1417     }
1418     
1419     /**
1420      * Use this method to add a JavaScript action at the document level.
1421      * When the document opens, all this JavaScript runs.
1422      * @param code the JavaScript code
1423      * @param unicode select JavaScript unicode. Note that the internal
1424      * Acrobat JavaScript engine does not support unicode,
1425      * so this may or may not work for you
1426      */

1427     public void addJavaScript(String JavaDoc code, boolean unicode) {
1428         addJavaScript(PdfAction.javaScript(code, this, unicode));
1429     }
1430     
1431     /**
1432      * Use this method to adds a JavaScript action at the document level.
1433      * When the document opens, all this JavaScript runs.
1434      * @param code the JavaScript code
1435      */

1436     public void addJavaScript(String JavaDoc code) {
1437         addJavaScript(code, false);
1438     }
1439     
1440     /**
1441      * Use this method to add a file attachment at the document level.
1442      * @param description the file description
1443      * @param fileStore an array with the file. If it's <CODE>null</CODE>
1444      * the file will be read from the disk
1445      * @param file the path to the file. It will only be used if
1446      * <CODE>fileStore</CODE> is not <CODE>null</CODE>
1447      * @param fileDisplay the actual file name stored in the pdf
1448      * @throws IOException on error
1449      */

1450     public void addFileAttachment(String JavaDoc description, byte fileStore[], String JavaDoc file, String JavaDoc fileDisplay) throws IOException JavaDoc {
1451         addFileAttachment(description, PdfFileSpecification.fileEmbedded(this, file, fileDisplay, fileStore));
1452     }
1453
1454     /**
1455      * Use this method to add a file attachment at the document level.
1456      * @param description the file description
1457      * @param fs the file specification
1458      */

1459     public void addFileAttachment(String JavaDoc description, PdfFileSpecification fs) throws IOException JavaDoc {
1460         pdf.addFileAttachment(description, fs);
1461     }
1462     
1463     /**
1464      * Use this method to add a file attachment at the document level.
1465      * @param fs the file specification
1466      */

1467     public void addFileAttachment(PdfFileSpecification fs) throws IOException JavaDoc {
1468         addFileAttachment(null, fs);
1469     }
1470     
1471// [C6] Actions (open and additional)
1472

1473     /** action value */
1474     public static final PdfName DOCUMENT_CLOSE = PdfName.WC;
1475     /** action value */
1476     public static final PdfName WILL_SAVE = PdfName.WS;
1477     /** action value */
1478     public static final PdfName DID_SAVE = PdfName.DS;
1479     /** action value */
1480     public static final PdfName WILL_PRINT = PdfName.WP;
1481     /** action value */
1482     public static final PdfName DID_PRINT = PdfName.DP;
1483     
1484    /** @see com.lowagie.text.pdf.interfaces.PdfDocumentActions#setOpenAction(java.lang.String) */
1485    public void setOpenAction(String JavaDoc name) {
1486         pdf.setOpenAction(name);
1487     }
1488
1489    /** @see com.lowagie.text.pdf.interfaces.PdfDocumentActions#setOpenAction(com.lowagie.text.pdf.PdfAction) */
1490    public void setOpenAction(PdfAction action) {
1491         pdf.setOpenAction(action);
1492     }
1493     
1494    /** @see com.lowagie.text.pdf.interfaces.PdfDocumentActions#setAdditionalAction(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfAction) */
1495    public void setAdditionalAction(PdfName actionType, PdfAction action) throws DocumentException {
1496         if (!(actionType.equals(DOCUMENT_CLOSE) ||
1497         actionType.equals(WILL_SAVE) ||
1498         actionType.equals(DID_SAVE) ||
1499         actionType.equals(WILL_PRINT) ||
1500         actionType.equals(DID_PRINT))) {
1501             throw new DocumentException("Invalid additional action type: " + actionType.toString());
1502         }
1503         pdf.addAdditionalAction(actionType, action);
1504     }
1505    
1506// [C7] portable collections
1507

1508    /**
1509     * Use this method to add the Collection dictionary.
1510     * @param collection a dictionary of type PdfCollection
1511     */

1512    public void setCollection(PdfCollection collection) {
1513        setAtLeastPdfVersion(VERSION_1_7);
1514        pdf.setCollection(collection);
1515    }
1516    
1517// [C8] AcroForm
1518

1519    /** signature value */
1520    public static final int SIGNATURE_EXISTS = 1;
1521    /** signature value */
1522    public static final int SIGNATURE_APPEND_ONLY = 2;
1523    
1524    /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#getAcroForm() */
1525    public PdfAcroForm getAcroForm() {
1526        return pdf.getAcroForm();
1527    }
1528    
1529    /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#addAnnotation(com.lowagie.text.pdf.PdfAnnotation) */
1530    public void addAnnotation(PdfAnnotation annot) {
1531        pdf.addAnnotation(annot);
1532    }
1533    
1534    void addAnnotation(PdfAnnotation annot, int page) {
1535        addAnnotation(annot);
1536    }
1537    
1538    /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#addCalculationOrder(com.lowagie.text.pdf.PdfFormField) */
1539    public void addCalculationOrder(PdfFormField annot) {
1540        pdf.addCalculationOrder(annot);
1541    }
1542    
1543    /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#setSigFlags(int) */
1544    public void setSigFlags(int f) {
1545        pdf.setSigFlags(f);
1546    }
1547
1548// [C9] Metadata
1549

1550    /** XMP Metadata for the document. */
1551    protected byte[] xmpMetadata = null;
1552    
1553    /**
1554     * Use this method to set the XMP Metadata.
1555     * @param xmpMetadata The xmpMetadata to set.
1556     */

1557    public void setXmpMetadata(byte[] xmpMetadata) {
1558        this.xmpMetadata = xmpMetadata;
1559    }
1560    
1561    /**
1562     * Use this method to creates XMP Metadata based
1563     * on the metadata in the PdfDocument.
1564     */

1565    public void createXmpMetadata() {
1566        setXmpMetadata(createXmpMetadataBytes());
1567    }
1568    
1569    /**
1570     * @return an XmpMetadata byte array
1571     */

1572    private byte[] createXmpMetadataBytes() {
1573        ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
1574        try {
1575            XmpWriter xmp = new XmpWriter(baos, pdf.getInfo());
1576            xmp.close();
1577        }
1578        catch(IOException JavaDoc ioe) {
1579            ioe.printStackTrace();
1580        }
1581        return baos.toByteArray();
1582    }
1583    
1584// [C10] PDFX Conformance
1585
/** A PDF/X level. */
1586    public static final int PDFXNONE = 0;
1587    /** A PDF/X level. */
1588    public static final int PDFX1A2001 = 1;
1589    /** A PDF/X level. */
1590    public static final int PDFX32002 = 2;
1591    
1592    /** Stores the PDF/X level. */
1593    private PdfXConformanceImp pdfxConformance = new PdfXConformanceImp();
1594    
1595    /** @see com.lowagie.text.pdf.interfaces.PdfXConformance#setPDFXConformance(int) */
1596    public void setPDFXConformance(int pdfx) {
1597        if (pdfxConformance.getPDFXConformance() == pdfx)
1598            return;
1599        if (pdf.isOpen())
1600            throw new PdfXConformanceException("PDFX conformance can only be set before opening the document.");
1601        if (crypto != null)
1602            throw new PdfXConformanceException("A PDFX conforming document cannot be encrypted.");
1603        if (pdfx!= PDFXNONE)
1604            setPdfVersion(VERSION_1_3);
1605        pdfxConformance.setPDFXConformance(pdfx);
1606    }
1607 
1608    /** @see com.lowagie.text.pdf.interfaces.PdfXConformance#getPDFXConformance() */
1609    public int getPDFXConformance() {
1610        return pdfxConformance.getPDFXConformance();
1611    }
1612    
1613    /** @see com.lowagie.text.pdf.interfaces.PdfXConformance#isPdfX() */
1614    public boolean isPdfX() {
1615        return pdfxConformance.isPdfX();
1616    }
1617     
1618// [C11] Output intents
1619

1620    /**
1621     * Use this method to set the values of the output intent dictionary.
1622     * Null values are allowed to suppress any key.
1623     * @param outputConditionIdentifier a value
1624     * @param outputCondition a value
1625     * @param registryName a value
1626     * @param info a value
1627     * @param destOutputProfile a value
1628     * @throws IOException on error
1629     */

1630    public void setOutputIntents(String JavaDoc outputConditionIdentifier, String JavaDoc outputCondition, String JavaDoc registryName, String JavaDoc info, byte destOutputProfile[]) throws IOException JavaDoc {
1631        getExtraCatalog();
1632        PdfDictionary out = new PdfDictionary(PdfName.OUTPUTINTENT);
1633        if (outputCondition != null)
1634            out.put(PdfName.OUTPUTCONDITION, new PdfString(outputCondition, PdfObject.TEXT_UNICODE));
1635        if (outputConditionIdentifier != null)
1636            out.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString(outputConditionIdentifier, PdfObject.TEXT_UNICODE));
1637        if (registryName != null)
1638            out.put(PdfName.REGISTRYNAME, new PdfString(registryName, PdfObject.TEXT_UNICODE));
1639        if (info != null)
1640            out.put(PdfName.INFO, new PdfString(info, PdfObject.TEXT_UNICODE));
1641        if (destOutputProfile != null) {
1642            PdfStream stream = new PdfStream(destOutputProfile);
1643            stream.flateCompress();
1644            out.put(PdfName.DESTOUTPUTPROFILE, addToBody(stream).getIndirectReference());
1645        }
1646        out.put(PdfName.S, PdfName.GTS_PDFX);
1647        extraCatalog.put(PdfName.OUTPUTINTENTS, new PdfArray(out));
1648    }
1649    
1650    /**
1651     * Use this method to copy the output intent dictionary
1652     * from another document to this one.
1653     * @param reader the other document
1654     * @param checkExistence <CODE>true</CODE> to just check for the existence of a valid output intent
1655     * dictionary, <CODE>false</CODE> to insert the dictionary if it exists
1656     * @throws IOException on error
1657     * @return <CODE>true</CODE> if the output intent dictionary exists, <CODE>false</CODE>
1658     * otherwise
1659     */

1660    public boolean setOutputIntents(PdfReader reader, boolean checkExistence) throws IOException JavaDoc {
1661        PdfDictionary catalog = reader.getCatalog();
1662        PdfArray outs = (PdfArray)PdfReader.getPdfObject(catalog.get(PdfName.OUTPUTINTENTS));
1663        if (outs == null)
1664            return false;
1665        ArrayList JavaDoc arr = outs.getArrayList();
1666        if (arr.isEmpty())
1667            return false;
1668        PdfDictionary out = (PdfDictionary)PdfReader.getPdfObject((PdfObject)arr.get(0));
1669        PdfObject obj = PdfReader.getPdfObject(out.get(PdfName.S));
1670        if (obj == null || !PdfName.GTS_PDFX.equals(obj))
1671            return false;
1672        if (checkExistence)
1673            return true;
1674        PRStream stream = (PRStream)PdfReader.getPdfObject(out.get(PdfName.DESTOUTPUTPROFILE));
1675        byte destProfile[] = null;
1676        if (stream != null) {
1677            destProfile = PdfReader.getStreamBytes(stream);
1678        }
1679        setOutputIntents(getNameString(out, PdfName.OUTPUTCONDITIONIDENTIFIER), getNameString(out, PdfName.OUTPUTCONDITION),
1680            getNameString(out, PdfName.REGISTRYNAME), getNameString(out, PdfName.INFO), destProfile);
1681        return true;
1682    }
1683
1684    private static String JavaDoc getNameString(PdfDictionary dic, PdfName key) {
1685        PdfObject obj = PdfReader.getPdfObject(dic.get(key));
1686        if (obj == null || !obj.isString())
1687            return null;
1688        return ((PdfString)obj).toUnicodeString();
1689    }
1690    
1691// PDF Objects that have an impact on the PDF body
1692

1693// [F1] PdfEncryptionSettings interface
1694

1695    // types of encryption
1696

1697    /** Type of encryption */
1698    public static final int STANDARD_ENCRYPTION_40 = 0;
1699    /** Type of encryption */
1700    public static final int STANDARD_ENCRYPTION_128 = 1;
1701    /** Type of encryption */
1702    public static final int ENCRYPTION_AES_128 = 2;
1703    /** Mask to separate the encryption type from the encryption mode. */
1704    static final int ENCRYPTION_MASK = 7;
1705    /** Add this to the mode to keep the metadata in clear text */
1706    public static final int DO_NOT_ENCRYPT_METADATA = 8;
1707    
1708    // permissions
1709

1710    /** The operation permitted when the document is opened with the user password */
1711    public static final int AllowPrinting = 4 + 2048;
1712    /** The operation permitted when the document is opened with the user password */
1713    public static final int AllowModifyContents = 8;
1714    /** The operation permitted when the document is opened with the user password */
1715    public static final int AllowCopy = 16;
1716    /** The operation permitted when the document is opened with the user password */
1717    public static final int AllowModifyAnnotations = 32;
1718    /** The operation permitted when the document is opened with the user password */
1719    public static final int AllowFillIn = 256;
1720    /** The operation permitted when the document is opened with the user password */
1721    public static final int AllowScreenReaders = 512;
1722    /** The operation permitted when the document is opened with the user password */
1723    public static final int AllowAssembly = 1024;
1724    /** The operation permitted when the document is opened with the user password */
1725    public static final int AllowDegradedPrinting = 4;
1726    
1727    // Strength of the encryption (kept for historical reasons)
1728
/** Type of standard encryption strength*/
1729    public static final boolean STRENGTH40BITS = false;
1730    /** Type of standard encryption strength */
1731    public static final boolean STRENGTH128BITS = true;
1732    
1733    /** Contains the business logic for cryptography. */
1734    protected PdfEncryption crypto;
1735    PdfEncryption getEncryption() {
1736        return crypto;
1737    }
1738    
1739    /** @see com.lowagie.text.pdf.interfaces.PdfEncryptionSettings#setEncryption(byte[], byte[], int, int) */
1740    public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, int encryptionType) throws DocumentException {
1741        if (pdf.isOpen())
1742            throw new DocumentException("Encryption can only be added before opening the document.");
1743        crypto = new PdfEncryption();
1744        crypto.setCryptoMode(encryptionType, 0);
1745        crypto.setupAllKeys(userPassword, ownerPassword, permissions);
1746    }
1747    
1748    /** @see com.lowagie.text.pdf.interfaces.PdfEncryptionSettings#setEncryption(java.security.cert.Certificate[], int[], int) */
1749    public void setEncryption(Certificate JavaDoc[] certs, int[] permissions, int encryptionType) throws DocumentException {
1750        if (pdf.isOpen())
1751            throw new DocumentException("Encryption can only be added before opening the document.");
1752        crypto = new PdfEncryption();
1753        if (certs != null) {
1754            for (int i=0; i < certs.length; i++) {
1755                crypto.addRecipient(certs[i], permissions[i]);
1756            }
1757        }
1758        crypto.setCryptoMode(encryptionType, 0);
1759        crypto.getEncryptionDictionary();
1760    }
1761    
1762    /**
1763     * Sets the encryption options for this document. The userPassword and the
1764     * ownerPassword can be null or have zero length. In this case the ownerPassword
1765     * is replaced by a random string. The open permissions for the document can be
1766     * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
1767     * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
1768     * The permissions can be combined by ORing them.
1769     * @param userPassword the user password. Can be null or empty
1770     * @param ownerPassword the owner password. Can be null or empty
1771     * @param permissions the user permissions
1772     * @param strength128Bits <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
1773     * @throws DocumentException if the document is already open
1774     * @deprecated use the methods described in the PdfEncryptionSettings interface
1775     */

1776    public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits) throws DocumentException {
1777        setEncryption(userPassword, ownerPassword, permissions, strength128Bits ? STANDARD_ENCRYPTION_128 : STANDARD_ENCRYPTION_40);
1778    }
1779    
1780    /**
1781     * Sets the encryption options for this document. The userPassword and the
1782     * ownerPassword can be null or have zero length. In this case the ownerPassword
1783     * is replaced by a random string. The open permissions for the document can be
1784     * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
1785     * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
1786     * The permissions can be combined by ORing them.
1787     * @param strength <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
1788     * @param userPassword the user password. Can be null or empty
1789     * @param ownerPassword the owner password. Can be null or empty
1790     * @param permissions the user permissions
1791     * @throws DocumentException if the document is already open
1792     * @deprecated use the methods described in the PdfEncryptionSettings interface
1793     */

1794    public void setEncryption(boolean strength, String JavaDoc userPassword, String JavaDoc ownerPassword, int permissions) throws DocumentException {
1795        setEncryption(getISOBytes(userPassword), getISOBytes(ownerPassword), permissions, strength ? STANDARD_ENCRYPTION_128 : STANDARD_ENCRYPTION_40);
1796    }
1797    
1798    /**
1799     * Sets the encryption options for this document. The userPassword and the
1800     * ownerPassword can be null or have zero length. In this case the ownerPassword
1801     * is replaced by a random string. The open permissions for the document can be
1802     * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
1803     * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
1804     * The permissions can be combined by ORing them.
1805     * @param encryptionType the type of encryption. It can be one of STANDARD_ENCRYPTION_40, STANDARD_ENCRYPTION_128 or ENCRYPTION_AES128.
1806     * Optionally DO_NOT_ENCRYPT_METADATA can be ored to output the metadata in cleartext
1807     * @param userPassword the user password. Can be null or empty
1808     * @param ownerPassword the owner password. Can be null or empty
1809     * @param permissions the user permissions
1810     * @throws DocumentException if the document is already open
1811     * @deprecated use the methods described in the PdfEncryptionSettings interface
1812     */

1813    public void setEncryption(int encryptionType, String JavaDoc userPassword, String JavaDoc ownerPassword, int permissions) throws DocumentException {
1814        setEncryption(getISOBytes(userPassword), getISOBytes(ownerPassword), permissions, encryptionType);
1815    }
1816    
1817// [F2] compression
1818

1819    /** Holds value of property fullCompression. */
1820    protected boolean fullCompression = false;
1821
1822    /**
1823     * Use this method to find out if 1.5 compression is on.
1824     * @return the 1.5 compression status
1825     */

1826    public boolean isFullCompression() {
1827        return this.fullCompression;
1828    }
1829    
1830    /**
1831     * Use this method to set the document's compression to the
1832     * PDF 1.5 mode with object streams and xref streams.
1833     * It can be set at any time but once set it can't be unset.
1834     * <p>
1835     * If set before opening the document it will also set the pdf version to 1.5.
1836     */

1837    public void setFullCompression() {
1838        this.fullCompression = true;
1839        setAtLeastPdfVersion(VERSION_1_5);
1840    }
1841    
1842// [F3] adding fonts
1843

1844    /** The fonts of this document */
1845    protected HashMap JavaDoc documentFonts = new HashMap JavaDoc();
1846    
1847    /** The font number counter for the fonts in the document. */
1848    protected int fontNumber = 1;
1849    
1850    /**
1851     * Adds a <CODE>BaseFont</CODE> to the document but not to the page resources.
1852     * It is used for templates.
1853     * @param bf the <CODE>BaseFont</CODE> to add
1854     * @return an <CODE>Object[]</CODE> where position 0 is a <CODE>PdfName</CODE>
1855     * and position 1 is an <CODE>PdfIndirectReference</CODE>
1856     */

1857    
1858    FontDetails addSimple(BaseFont bf) {
1859        if (bf.getFontType() == BaseFont.FONT_TYPE_DOCUMENT) {
1860            return new FontDetails(new PdfName("F" + (fontNumber++)), ((DocumentFont)bf).getIndirectReference(), bf);
1861        }
1862        FontDetails ret = (FontDetails)documentFonts.get(bf);
1863        if (ret == null) {
1864            PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_FONT, bf);
1865            ret = new FontDetails(new PdfName("F" + (fontNumber++)), body.getPdfIndirectReference(), bf);
1866            documentFonts.put(bf, ret);
1867        }
1868        return ret;
1869    }
1870    
1871    void eliminateFontSubset(PdfDictionary fonts) {
1872        for (Iterator JavaDoc it = documentFonts.values().iterator(); it.hasNext();) {
1873            FontDetails ft = (FontDetails)it.next();
1874            if (fonts.get(ft.getFontName()) != null)
1875                ft.setSubset(false);
1876        }
1877    }
1878    
1879// [F4] adding (and releasing) form XObjects
1880

1881    /** The form XObjects in this document. The key is the xref and the value
1882        is Object[]{PdfName, template}.*/

1883    protected HashMap JavaDoc formXObjects = new HashMap JavaDoc();
1884    
1885    /** The name counter for the form XObjects name. */
1886    protected int formXObjectsCounter = 1;
1887    
1888    /**
1889     * Adds a template to the document but not to the page resources.
1890     * @param template the template to add
1891     * @param forcedName the template name, rather than a generated one. Can be null
1892     * @return the <CODE>PdfName</CODE> for this template
1893     */

1894    
1895    PdfName addDirectTemplateSimple(PdfTemplate template, PdfName forcedName) {
1896        PdfIndirectReference ref = template.getIndirectReference();
1897        Object JavaDoc obj[] = (Object JavaDoc[])formXObjects.get(ref);
1898        PdfName name = null;
1899        try {
1900            if (obj == null) {
1901                if (forcedName == null) {
1902                    name = new PdfName("Xf" + formXObjectsCounter);
1903                    ++formXObjectsCounter;
1904                }
1905                else
1906                    name = forcedName;
1907                if (template.getType() == PdfTemplate.TYPE_IMPORTED)
1908                    template = null;
1909                formXObjects.put(ref, new Object JavaDoc[]{name, template});
1910            }
1911            else
1912                name = (PdfName)obj[0];
1913        }
1914        catch (Exception JavaDoc e) {
1915            throw new ExceptionConverter(e);
1916        }
1917        return name;
1918    }
1919    
1920    /**
1921     * Use this method to releases the memory used by a template.
1922     * This method writes the template to the output.
1923     * The template can still be added to any content
1924     * but changes to the template itself won't have any effect.
1925     * @param tp the template to release
1926     * @throws IOException on error
1927     */

1928    public void releaseTemplate(PdfTemplate tp) throws IOException JavaDoc {
1929        PdfIndirectReference ref = tp.getIndirectReference();
1930        Object JavaDoc[] objs = (Object JavaDoc[])formXObjects.get(ref);
1931        if (objs == null || objs[1] == null)
1932            return;
1933        PdfTemplate template = (PdfTemplate)objs[1];
1934        if (template.getIndirectReference() instanceof PRIndirectReference)
1935            return;
1936        if (template.getType() == PdfTemplate.TYPE_TEMPLATE) {
1937            addToBody(template.getFormXObject(), template.getIndirectReference());
1938            objs[1] = null;
1939        }
1940    }
1941    
1942// [F5] adding pages imported form other PDF documents
1943

1944    protected HashMap JavaDoc importedPages = new HashMap JavaDoc();
1945    
1946    /**
1947     * Use this method to get a page from other PDF document.
1948     * The page can be used as any other PdfTemplate.
1949     * Note that calling this method more than once with the same parameters
1950     * will retrieve the same object.
1951     * @param reader the PDF document where the page is
1952     * @param pageNumber the page number. The first page is 1
1953     * @return the template representing the imported page
1954     */

1955    public PdfImportedPage getImportedPage(PdfReader reader, int pageNumber) {
1956        PdfReaderInstance inst = (PdfReaderInstance)importedPages.get(reader);
1957        if (inst == null) {
1958            inst = reader.getPdfReaderInstance(this);
1959            importedPages.put(reader, inst);
1960        }
1961        return inst.getImportedPage(pageNumber);
1962    }
1963    
1964    /**
1965     * Use this method to writes the reader to the document
1966     * and free the memory used by it.
1967     * The main use is when concatenating multiple documents
1968     * to keep the memory usage restricted to the current
1969     * appending document.
1970     * @param reader the <CODE>PdfReader</CODE> to free
1971     * @throws IOException on error
1972     */

1973    public void freeReader(PdfReader reader) throws IOException JavaDoc {
1974        currentPdfReaderInstance = (PdfReaderInstance)importedPages.get(reader);
1975        if (currentPdfReaderInstance == null)
1976            return;
1977        currentPdfReaderInstance.writeAllPages();
1978        currentPdfReaderInstance = null;
1979        importedPages.remove(reader);
1980    }
1981    
1982    /**
1983     * Use this method to gets the current document size.
1984     * This size only includes the data already writen
1985     * to the output stream, it does not include templates or fonts.
1986     * It is usefull if used with <CODE>freeReader()</CODE>
1987     * when concatenating many documents and an idea of
1988     * the current size is needed.
1989     * @return the approximate size without fonts or templates
1990     */

1991    public int getCurrentDocumentSize() {
1992        return body.offset() + body.size() * 20 + 0x48;
1993    }
1994    
1995    protected PdfReaderInstance currentPdfReaderInstance;
1996    
1997    protected int getNewObjectNumber(PdfReader reader, int number, int generation) {
1998        return currentPdfReaderInstance.getNewObjectNumber(number, generation);
1999    }
2000
2001    RandomAccessFileOrArray getReaderFile(PdfReader reader) {
2002        return currentPdfReaderInstance.getReaderFile();
2003    }
2004
2005// [F6] spot colors
2006

2007    /** The colors of this document */
2008    protected HashMap JavaDoc documentColors = new HashMap JavaDoc();
2009    
2010    /** The color number counter for the colors in the document. */
2011    protected int colorNumber = 1;
2012    
2013    PdfName getColorspaceName() {
2014        return new PdfName("CS" + (colorNumber++));
2015    }
2016    
2017    /**
2018     * Adds a <CODE>SpotColor</CODE> to the document but not to the page resources.
2019     * @param spc the <CODE>SpotColor</CODE> to add
2020     * @return an <CODE>Object[]</CODE> where position 0 is a <CODE>PdfName</CODE>
2021     * and position 1 is an <CODE>PdfIndirectReference</CODE>
2022     */

2023    ColorDetails addSimple(PdfSpotColor spc) {
2024        ColorDetails ret = (ColorDetails)documentColors.get(spc);
2025        if (ret == null) {
2026            ret = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), spc);
2027            documentColors.put(spc, ret);
2028        }
2029        return ret;
2030    }
2031
2032// [F7] document patterns
2033

2034    /** The patterns of this document */
2035    protected HashMap JavaDoc documentPatterns = new HashMap JavaDoc();
2036    
2037    /** The patten number counter for the colors in the document. */
2038    protected int patternNumber = 1;
2039    
2040    PdfName addSimplePattern(PdfPatternPainter painter) {
2041        PdfName name = (PdfName)documentPatterns.get(painter);
2042        try {
2043            if ( name == null ) {
2044                name = new PdfName("P" + patternNumber);
2045                ++patternNumber;
2046                documentPatterns.put(painter, name);
2047            }
2048        } catch (Exception JavaDoc e) {
2049            throw new ExceptionConverter(e);
2050        }
2051        return name;
2052    }
2053    
2054// [F8] shading patterns
2055

2056    protected HashMap JavaDoc documentShadingPatterns = new HashMap JavaDoc();
2057    
2058    void addSimpleShadingPattern(PdfShadingPattern shading) {
2059        if (!documentShadingPatterns.containsKey(shading)) {
2060            shading.setName(patternNumber);
2061            ++patternNumber;
2062            documentShadingPatterns.put(shading, null);
2063            addSimpleShading(shading.getShading());
2064        }
2065    }
2066
2067// [F9] document shadings
2068

2069    protected HashMap JavaDoc documentShadings = new HashMap JavaDoc();
2070    
2071    void addSimpleShading(PdfShading shading) {
2072        if (!documentShadings.containsKey(shading)) {
2073            documentShadings.put(shading, null);
2074            shading.setName(documentShadings.size());
2075        }
2076    }
2077    
2078// [F10] extended graphics state (for instance for transparency)
2079

2080    protected HashMap JavaDoc documentExtGState = new HashMap JavaDoc();
2081    
2082    PdfObject[] addSimpleExtGState(PdfDictionary gstate) {
2083        if (!documentExtGState.containsKey(gstate)) {
2084            PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_GSTATE, gstate);
2085            documentExtGState.put(gstate, new PdfObject[]{new PdfName("GS" + (documentExtGState.size() + 1)), getPdfIndirectReference()});
2086        }
2087        return (PdfObject[])documentExtGState.get(gstate);
2088    }
2089
2090// [F11] adding properties (OCG, marked content)
2091

2092    protected HashMap JavaDoc documentProperties = new HashMap JavaDoc();
2093    PdfObject[] addSimpleProperty(Object JavaDoc prop, PdfIndirectReference refi) {
2094        if (!documentProperties.containsKey(prop)) {
2095            if (prop instanceof PdfOCG)
2096                PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_LAYER, null);
2097            documentProperties.put(prop, new PdfObject[]{new PdfName("Pr" + (documentProperties.size() + 1)), refi});
2098        }
2099        return (PdfObject[])documentProperties.get(prop);
2100    }
2101
2102    boolean propertyExists(Object JavaDoc prop) {
2103        return documentProperties.containsKey(prop);
2104    }
2105
2106// [F12] tagged PDF
2107

2108    protected boolean tagged = false;
2109    protected PdfStructureTreeRoot structureTreeRoot;
2110    
2111    /**
2112     * Mark this document for tagging. It must be called before open.
2113     */

2114    public void setTagged() {
2115        if (open)
2116            throw new IllegalArgumentException JavaDoc("Tagging must be set before opening the document.");
2117        tagged = true;
2118    }
2119    
2120    /**
2121     * Check if the document is marked for tagging.
2122     * @return <CODE>true</CODE> if the document is marked for tagging
2123     */

2124    public boolean isTagged() {
2125        return tagged;
2126    }
2127    
2128    /**
2129     * Gets the structure tree root. If the document is not marked for tagging it will return <CODE>null</CODE>.
2130     * @return the structure tree root
2131     */

2132    public PdfStructureTreeRoot getStructureTreeRoot() {
2133        if (tagged && structureTreeRoot == null)
2134            structureTreeRoot = new PdfStructureTreeRoot(this);
2135        return structureTreeRoot;
2136    }
2137    
2138// [F13] Optional Content Groups
2139
protected HashSet JavaDoc documentOCG = new HashSet JavaDoc();
2140    protected ArrayList JavaDoc documentOCGorder = new ArrayList JavaDoc();
2141    protected PdfOCProperties OCProperties;
2142    protected PdfArray OCGRadioGroup = new PdfArray();
2143    
2144    /**
2145     * Use this method to get the <B>Optional Content Properties Dictionary</B>.
2146     * Each call fills the dictionary with the current layer state.
2147     * It's advisable to only call this method right before close
2148     * and do any modifications at that time.
2149     * @return the Optional Content Properties Dictionary
2150     */

2151    public PdfOCProperties getOCProperties() {
2152        fillOCProperties(true);
2153        return OCProperties;
2154    }
2155    
2156    /**
2157     * Use this method to set a collection of optional content groups
2158     * whose states are intended to follow a "radio button" paradigm.
2159     * That is, the state of at most one optional content group
2160     * in the array should be ON at a time: if one group is turned
2161     * ON, all others must be turned OFF.
2162     * @param group the radio group
2163     */

2164    public void addOCGRadioGroup(ArrayList JavaDoc group) {
2165        PdfArray ar = new PdfArray();
2166        for (int k = 0; k < group.size(); ++k) {
2167            PdfLayer layer = (PdfLayer)group.get(k);
2168            if (layer.getTitle() == null)
2169                ar.add(layer.getRef());
2170        }
2171        if (ar.size() == 0)
2172            return;
2173        OCGRadioGroup.add(ar);
2174    }
2175    
2176    private static void getOCGOrder(PdfArray order, PdfLayer layer) {
2177        if (!layer.isOnPanel())
2178            return;
2179        if (layer.getTitle() == null)
2180            order.add(layer.getRef());
2181        ArrayList JavaDoc children = layer.getChildren();
2182        if (children == null)
2183            return;
2184        PdfArray kids = new PdfArray();
2185        if (layer.getTitle() != null)
2186            kids.add(new PdfString(layer.getTitle(), PdfObject.TEXT_UNICODE));
2187        for (int k = 0; k < children.size(); ++k) {
2188            getOCGOrder(kids, (PdfLayer)children.get(k));
2189        }
2190        if (kids.size() > 0)
2191            order.add(kids);
2192    }
2193    
2194    private void addASEvent(PdfName event, PdfName category) {
2195        PdfArray arr = new PdfArray();
2196        for (Iterator JavaDoc it = documentOCG.iterator(); it.hasNext();) {
2197            PdfLayer layer = (PdfLayer)it.next();
2198            PdfDictionary usage = (PdfDictionary)layer.get(PdfName.USAGE);
2199            if (usage != null && usage.get(category) != null)
2200                arr.add(layer.getRef());
2201        }
2202        if (arr.size() == 0)
2203            return;
2204        PdfDictionary d = (PdfDictionary)OCProperties.get(PdfName.D);
2205        PdfArray arras = (PdfArray)d.get(PdfName.AS);
2206        if (arras == null) {
2207            arras = new PdfArray();
2208            d.put(PdfName.AS, arras);
2209        }
2210        PdfDictionary as = new PdfDictionary();
2211        as.put(PdfName.EVENT, event);
2212        as.put(PdfName.CATEGORY, new PdfArray(category));
2213        as.put(PdfName.OCGS, arr);
2214        arras.add(as);
2215    }
2216    
2217    private void fillOCProperties(boolean erase) {
2218        if (OCProperties == null)
2219            OCProperties = new PdfOCProperties();
2220        if (erase) {
2221            OCProperties.remove(PdfName.OCGS);
2222            OCProperties.remove(PdfName.D);
2223        }
2224        if (OCProperties.get(PdfName.OCGS) == null) {
2225            PdfArray gr = new PdfArray();
2226            for (Iterator JavaDoc it = documentOCG.iterator(); it.hasNext();) {
2227                PdfLayer layer = (PdfLayer)it.next();
2228                gr.add(layer.getRef());
2229            }
2230            OCProperties.put(PdfName.OCGS, gr);
2231        }
2232        if (OCProperties.get(PdfName.D) != null)
2233            return;
2234        ArrayList JavaDoc docOrder = new ArrayList JavaDoc(documentOCGorder);
2235        for (Iterator JavaDoc it = docOrder.iterator(); it.hasNext();) {
2236            PdfLayer layer = (PdfLayer)it.next();
2237            if (layer.getParent() != null)
2238                it.remove();
2239        }
2240        PdfArray order = new PdfArray();
2241        for (Iterator JavaDoc it = docOrder.iterator(); it.hasNext();) {
2242            PdfLayer layer = (PdfLayer)it.next();
2243            getOCGOrder(order, layer);
2244        }
2245        PdfDictionary d = new PdfDictionary();
2246        OCProperties.put(PdfName.D, d);
2247        d.put(PdfName.ORDER, order);
2248        PdfArray gr = new PdfArray();
2249        for (Iterator JavaDoc it = documentOCG.iterator(); it.hasNext();) {
2250            PdfLayer layer = (PdfLayer)it.next();
2251            if (!layer.isOn())
2252                gr.add(layer.getRef());
2253        }
2254        if (gr.size() > 0)
2255            d.put(PdfName.OFF, gr);
2256        if (OCGRadioGroup.size() > 0)
2257            d.put(PdfName.RBGROUPS, OCGRadioGroup);
2258        addASEvent(PdfName.VIEW, PdfName.ZOOM);
2259        addASEvent(PdfName.VIEW, PdfName.VIEW);
2260        addASEvent(PdfName.PRINT, PdfName.PRINT);
2261        addASEvent(PdfName.EXPORT, PdfName.EXPORT);
2262        d.put(PdfName.LISTMODE, PdfName.VISIBLEPAGES);
2263    }
2264    
2265    void registerLayer(PdfOCG layer) {
2266        PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_LAYER, null);
2267        if (layer instanceof PdfLayer) {
2268            PdfLayer la = (PdfLayer)layer;
2269            if (la.getTitle() == null) {
2270                if (!documentOCG.contains(layer)) {
2271                    documentOCG.add(layer);
2272                    documentOCGorder.add(layer);
2273                }
2274            }
2275            else {
2276                documentOCGorder.add(layer);
2277            }
2278        }
2279        else
2280            throw new IllegalArgumentException JavaDoc("Only PdfLayer is accepted.");
2281    }
2282
2283// User methods to change aspects of the page
2284

2285// [U1] page size
2286

2287    /**
2288     * Use this method to get the size of the media box.
2289     * @return a Rectangle
2290     */

2291    public Rectangle getPageSize() {
2292        return pdf.getPageSize();
2293    }
2294    
2295    /**
2296     * Use this method to set the crop box.
2297     * The crop box should not be rotated even if the page is rotated.
2298     * This change only takes effect in the next page.
2299     * @param crop the crop box
2300     */

2301    public void setCropBoxSize(Rectangle crop) {
2302        pdf.setCropBoxSize(crop);
2303    }
2304    
2305    /**
2306     * Use this method to set the page box sizes.
2307     * Allowed names are: "crop", "trim", "art" and "bleed".
2308     * @param boxName the box size
2309     * @param size the size
2310     */

2311    public void setBoxSize(String JavaDoc boxName, Rectangle size) {
2312        pdf.setBoxSize(boxName, size);
2313    }
2314
2315    /**
2316     * Use this method to get the size of a trim, art, crop or bleed box,
2317     * or null if not defined.
2318     * @param boxName crop, trim, art or bleed
2319     */

2320    public Rectangle getBoxSize(String JavaDoc boxName) {
2321        return pdf.getBoxSize(boxName);
2322    }
2323    
2324// [U2] take care of empty pages
2325

2326    /**
2327     * Use this method to make sure a page is added,
2328     * even if it's empty. If you use setPageEmpty(false),
2329     * invoking newPage() after a blank page will add a newPage.
2330     * @param pageEmpty the state
2331     */

2332    public void setPageEmpty(boolean pageEmpty) {
2333        pdf.setPageEmpty(pageEmpty);
2334    }
2335    
2336// [U3] page actions (open and close)
2337

2338    /** action value */
2339    public static final PdfName PAGE_OPEN = PdfName.O;
2340    /** action value */
2341    public static final PdfName PAGE_CLOSE = PdfName.C;
2342     
2343    /** @see com.lowagie.text.pdf.interfaces.PdfPageActions#setPageAction(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfAction) */
2344    public void setPageAction(PdfName actionType, PdfAction action) throws DocumentException {
2345          if (!actionType.equals(PAGE_OPEN) && !actionType.equals(PAGE_CLOSE))
2346              throw new DocumentException("Invalid page additional action type: " + actionType.toString());
2347          pdf.setPageAction(actionType, action);
2348      }
2349     
2350    /** @see com.lowagie.text.pdf.interfaces.PdfPageActions#setDuration(int) */
2351    public void setDuration(int seconds) {
2352         pdf.setDuration(seconds);
2353     }
2354     
2355    /** @see com.lowagie.text.pdf.interfaces.PdfPageActions#setTransition(com.lowagie.text.pdf.PdfTransition) */
2356    public void setTransition(PdfTransition transition) {
2357         pdf.setTransition(transition);
2358     }
2359    
2360// [U4] Thumbnail image
2361

2362    /**
2363     * Use this method to set the thumbnail image for the current page.
2364     * @param image the image
2365     * @throws PdfException on error
2366     * @throws DocumentException or error
2367     */

2368    public void setThumbnail(Image image) throws PdfException, DocumentException {
2369        pdf.setThumbnail(image);
2370    }
2371
2372// [U5] Transparency groups
2373

2374    /**
2375     * A group attributes dictionary specifying the attributes
2376     * of the page's page group for use in the transparent
2377     * imaging model
2378     */

2379    protected PdfDictionary group;
2380    
2381    /**
2382     * Use this method to get the group dictionary.
2383     * @return Value of property group.
2384     */

2385    public PdfDictionary getGroup() {
2386        return this.group;
2387    }
2388    
2389    /**
2390     * Use this method to set the group dictionary.
2391     * @param group New value of property group.
2392     */

2393    public void setGroup(PdfDictionary group) {
2394        this.group = group;
2395    }
2396    
2397// [U6] space char ratio
2398

2399    /** The default space-char ratio. */
2400    public static final float SPACE_CHAR_RATIO_DEFAULT = 2.5f;
2401    /** Disable the inter-character spacing. */
2402    public static final float NO_SPACE_CHAR_RATIO = 10000000f;
2403
2404    /**
2405     * The ratio between the extra word spacing and the extra character spacing.
2406     * Extra word spacing will grow <CODE>ratio</CODE> times more than extra character spacing.
2407     */

2408    private float spaceCharRatio = SPACE_CHAR_RATIO_DEFAULT;
2409    
2410    /**
2411     * Use this method to gets the space/character extra spacing ratio
2412     * for fully justified text.
2413     * @return the space/character extra spacing ratio
2414     */

2415    public float getSpaceCharRatio() {
2416        return spaceCharRatio;
2417    }
2418    
2419    /**
2420     * Use this method to set the ratio between the extra word spacing and
2421     * the extra character spacing when the text is fully justified.
2422     * Extra word spacing will grow <CODE>spaceCharRatio</CODE> times more
2423     * than extra character spacing. If the ratio is <CODE>PdfWriter.NO_SPACE_CHAR_RATIO</CODE>
2424     * then the extra character spacing will be zero.
2425     * @param spaceCharRatio the ratio between the extra word spacing and the extra character spacing
2426     */

2427    public void setSpaceCharRatio(float spaceCharRatio) {
2428        if (spaceCharRatio < 0.001f)
2429            this.spaceCharRatio = 0.001f;
2430        else
2431            this.spaceCharRatio = spaceCharRatio;
2432    }
2433
2434// [U7] run direction (doesn't actually do anything)
2435

2436    /** Use the default run direction. */
2437    public static final int RUN_DIRECTION_DEFAULT = 0;
2438    /** Do not use bidirectional reordering. */
2439    public static final int RUN_DIRECTION_NO_BIDI = 1;
2440    /** Use bidirectional reordering with left-to-right
2441     * preferential run direction.
2442     */

2443    public static final int RUN_DIRECTION_LTR = 2;
2444    /** Use bidirectional reordering with right-to-left
2445     * preferential run direction.
2446     */

2447    public static final int RUN_DIRECTION_RTL = 3;
2448    
2449    protected int runDirection = RUN_DIRECTION_NO_BIDI;
2450    
2451    /**
2452     * Use this method to set the run direction.
2453     * This is only used as a placeholder as it does not affect anything.
2454     * @param runDirection the run direction
2455     */

2456    public void setRunDirection(int runDirection) {
2457        if (runDirection < RUN_DIRECTION_NO_BIDI || runDirection > RUN_DIRECTION_RTL)
2458            throw new RuntimeException JavaDoc("Invalid run direction: " + runDirection);
2459        this.runDirection = runDirection;
2460    }
2461    
2462    /**
2463     * Use this method to set the run direction.
2464     * @return the run direction
2465     */

2466    public int getRunDirection() {
2467        return runDirection;
2468    }
2469     
2470// [U8] user units
2471

2472     protected float userunit = 0f;
2473    /**
2474     * Use this method to get the user unit.
2475     * A user unit is a value that defines the default user space unit.
2476     * The minimum UserUnit is 1 (1 unit = 1/72 inch).
2477     * The maximum UserUnit is 75,000.
2478     * Note that this userunit only works starting with PDF1.6!
2479     * @return Returns the userunit.
2480     */

2481    public float getUserunit() {
2482        return userunit;
2483    }
2484    /**
2485     * Use this method to set the user unit.
2486     * A UserUnit is a value that defines the default user space unit.
2487     * The minimum UserUnit is 1 (1 unit = 1/72 inch).
2488     * The maximum UserUnit is 75,000.
2489     * Note that this userunit only works starting with PDF1.6!
2490     * @param userunit The userunit to set.
2491     * @throws DocumentException on error
2492     */

2493    public void setUserunit(float userunit) throws DocumentException {
2494        if (userunit < 1f || userunit > 75000f) throw new DocumentException("UserUnit should be a value between 1 and 75000.");
2495        this.userunit = userunit;
2496         setAtLeastPdfVersion(VERSION_1_6);
2497    }
2498    
2499// Miscellaneous topics
2500

2501// [M1] Color settings
2502

2503    protected PdfDictionary defaultColorspace = new PdfDictionary();
2504    /**
2505     * Use this method to get the default colorspaces.
2506     * @return the default colorspaces
2507     */

2508    public PdfDictionary getDefaultColorspace() {
2509        return defaultColorspace;
2510    }
2511
2512    /**
2513     * Use this method to sets the default colorspace that will be applied
2514     * to all the document. The colorspace is only applied if another colorspace
2515     * with the same name is not present in the content.
2516     * <p>
2517     * The colorspace is applied immediately when creating templates and
2518     * at the page end for the main document content.
2519     * @param key the name of the colorspace. It can be <CODE>PdfName.DEFAULTGRAY</CODE>, <CODE>PdfName.DEFAULTRGB</CODE>
2520     * or <CODE>PdfName.DEFAULTCMYK</CODE>
2521     * @param cs the colorspace. A <CODE>null</CODE> or <CODE>PdfNull</CODE> removes any colorspace with the same name
2522     */

2523    public void setDefaultColorspace(PdfName key, PdfObject cs) {
2524        if (cs == null || cs.isNull())
2525            defaultColorspace.remove(key);
2526        defaultColorspace.put(key, cs);
2527    }
2528    
2529// [M2] spot patterns
2530

2531    protected HashMap JavaDoc documentSpotPatterns = new HashMap JavaDoc();
2532    protected ColorDetails patternColorspaceRGB;
2533    protected ColorDetails patternColorspaceGRAY;
2534    protected ColorDetails patternColorspaceCMYK;
2535   
2536    ColorDetails addSimplePatternColorspace(Color JavaDoc color) {
2537        int type = ExtendedColor.getType(color);
2538        if (type == ExtendedColor.TYPE_PATTERN || type == ExtendedColor.TYPE_SHADING)
2539            throw new RuntimeException JavaDoc("An uncolored tile pattern can not have another pattern or shading as color.");
2540        try {
2541            switch (type) {
2542                case ExtendedColor.TYPE_RGB:
2543                    if (patternColorspaceRGB == null) {
2544                        patternColorspaceRGB = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
2545                        PdfArray array = new PdfArray(PdfName.PATTERN);
2546                        array.add(PdfName.DEVICERGB);
2547                        addToBody(array, patternColorspaceRGB.getIndirectReference());
2548                    }
2549                    return patternColorspaceRGB;
2550                case ExtendedColor.TYPE_CMYK:
2551                    if (patternColorspaceCMYK == null) {
2552                        patternColorspaceCMYK = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
2553                        PdfArray array = new PdfArray(PdfName.PATTERN);
2554                        array.add(PdfName.DEVICECMYK);
2555                        addToBody(array, patternColorspaceCMYK.getIndirectReference());
2556                    }
2557                    return patternColorspaceCMYK;
2558                case ExtendedColor.TYPE_GRAY:
2559                    if (patternColorspaceGRAY == null) {
2560                        patternColorspaceGRAY = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
2561                        PdfArray array = new PdfArray(PdfName.PATTERN);
2562                        array.add(PdfName.DEVICEGRAY);
2563                        addToBody(array, patternColorspaceGRAY.getIndirectReference());
2564                    }
2565                    return patternColorspaceGRAY;
2566                case ExtendedColor.TYPE_SEPARATION: {
2567                    ColorDetails details = addSimple(((SpotColor)color).getPdfSpotColor());
2568                    ColorDetails patternDetails = (ColorDetails)documentSpotPatterns.get(details);
2569                    if (patternDetails == null) {
2570                        patternDetails = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
2571                        PdfArray array = new PdfArray(PdfName.PATTERN);
2572                        array.add(details.getIndirectReference());
2573                        addToBody(array, patternDetails.getIndirectReference());
2574                        documentSpotPatterns.put(details, patternDetails);
2575                    }
2576                    return patternDetails;
2577                }
2578                default:
2579                    throw new RuntimeException JavaDoc("Invalid color type in PdfWriter.addSimplePatternColorspace().");
2580            }
2581        }
2582        catch (Exception JavaDoc e) {
2583            throw new RuntimeException JavaDoc(e.getMessage());
2584        }
2585    }
2586    
2587// [M3] Images
2588

2589    /**
2590     * Use this method to get the strictImageSequence status.
2591     * @return value of property strictImageSequence
2592     */

2593    public boolean isStrictImageSequence() {
2594        return pdf.isStrictImageSequence();
2595    }
2596    
2597    /**
2598     * Use this method to set the image sequence, so that it follows
2599     * the text in strict order (or not).
2600     * @param strictImageSequence new value of property strictImageSequence
2601     *
2602     */

2603    public void setStrictImageSequence(boolean strictImageSequence) {
2604        pdf.setStrictImageSequence(strictImageSequence);
2605    }
2606    
2607    /**
2608     * Use this method to clear text wrapping around images (if applicable).
2609     * @throws DocumentException
2610     */

2611    public void clearTextWrap() throws DocumentException {
2612        pdf.clearTextWrap();
2613    }
2614    
2615    /** Dictionary, containing all the images of the PDF document */
2616    protected PdfDictionary imageDictionary = new PdfDictionary();
2617    
2618    /** This is the list with all the images in the document. */
2619    private HashMap JavaDoc images = new HashMap JavaDoc();
2620    
2621    /**
2622     * Use this method to adds an image to the document
2623     * but not to the page resources. It is used with
2624     * templates and <CODE>Document.add(Image)</CODE>.
2625     * Use this method only if you know what you're doing!
2626     * @param image the <CODE>Image</CODE> to add
2627     * @return the name of the image added
2628     * @throws PdfException on error
2629     * @throws DocumentException on error
2630     */

2631    public PdfName addDirectImageSimple(Image image) throws PdfException, DocumentException {
2632        return addDirectImageSimple(image, null);
2633    }
2634    
2635    /**
2636     * Adds an image to the document but not to the page resources.
2637     * It is used with templates and <CODE>Document.add(Image)</CODE>.
2638     * Use this method only if you know what you're doing!
2639     * @param image the <CODE>Image</CODE> to add
2640     * @param fixedRef the reference to used. It may be <CODE>null</CODE>,
2641     * a <CODE>PdfIndirectReference</CODE> or a <CODE>PRIndirectReference</CODE>.
2642     * @return the name of the image added
2643     * @throws PdfException on error
2644     * @throws DocumentException on error
2645     */

2646    public PdfName addDirectImageSimple(Image image, PdfIndirectReference fixedRef) throws PdfException, DocumentException {
2647        PdfName name;
2648        // if the images is already added, just retrieve the name
2649
if (images.containsKey(image.getMySerialId())) {
2650            name = (PdfName) images.get(image.getMySerialId());
2651        }
2652        // if it's a new image, add it to the document
2653
else {
2654            if (image.isImgTemplate()) {
2655                name = new PdfName("img" + images.size());
2656                if (image.getTemplateData() == null) {
2657                    if(image instanceof ImgWMF){
2658                        try {
2659                            ImgWMF wmf = (ImgWMF)image;
2660                            wmf.readWMF(PdfTemplate.createTemplate(this, 0, 0));
2661                        }
2662                        catch (Exception JavaDoc e) {
2663                            throw new DocumentException(e);
2664                        }
2665                    }
2666                }
2667            }
2668            else {
2669                PdfIndirectReference dref = image.getDirectReference();
2670                if (dref != null) {
2671                    PdfName rname = new PdfName("img" + images.size());
2672                    images.put(image.getMySerialId(), rname);
2673                    imageDictionary.put(rname, dref);
2674                    return rname;
2675                }
2676                Image maskImage = image.getImageMask();
2677                PdfIndirectReference maskRef = null;
2678                if (maskImage != null) {
2679                    PdfName mname = (PdfName)images.get(maskImage.getMySerialId());
2680                    maskRef = getImageReference(mname);
2681                }
2682                PdfImage i = new PdfImage(image, "img" + images.size(), maskRef);
2683                if (image.hasICCProfile()) {
2684                    PdfICCBased icc = new PdfICCBased(image.getICCProfile());
2685                    PdfIndirectReference iccRef = add(icc);
2686                    PdfArray iccArray = new PdfArray();
2687                    iccArray.add(PdfName.ICCBASED);
2688                    iccArray.add(iccRef);
2689                    PdfObject colorspace = i.get(PdfName.COLORSPACE);
2690                    if (colorspace != null && colorspace.isArray()) {
2691                        ArrayList JavaDoc ar = ((PdfArray)colorspace).getArrayList();
2692                        if (ar.size() > 1 && PdfName.INDEXED.equals(ar.get(0)))
2693                            ar.set(1, iccArray);
2694                        else
2695                            i.put(PdfName.COLORSPACE, iccArray);
2696                    }
2697                    else
2698                        i.put(PdfName.COLORSPACE, iccArray);
2699                }
2700                add(i, fixedRef);
2701                name = i.name();
2702            }
2703            images.put(image.getMySerialId(), name);
2704        }
2705        return name;
2706    }
2707
2708    /**
2709     * Writes a <CODE>PdfImage</CODE> to the outputstream.
2710     *
2711     * @param pdfImage the image to be added
2712     * @return a <CODE>PdfIndirectReference</CODE> to the encapsulated image
2713     * @throws PdfException when a document isn't open yet, or has been closed
2714     */

2715    
2716    PdfIndirectReference add(PdfImage pdfImage, PdfIndirectReference fixedRef) throws PdfException {
2717        if (! imageDictionary.contains(pdfImage.name())) {
2718            PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_IMAGE, pdfImage);
2719            if (fixedRef instanceof PRIndirectReference) {
2720                PRIndirectReference r2 = (PRIndirectReference)fixedRef;
2721                fixedRef = new PdfIndirectReference(0, getNewObjectNumber(r2.getReader(), r2.getNumber(), r2.getGeneration()));
2722            }
2723            try {
2724                if (fixedRef == null)
2725                    fixedRef = addToBody(pdfImage).getIndirectReference();
2726                else
2727                    addToBody(pdfImage, fixedRef);
2728            }
2729            catch(IOException JavaDoc ioe) {
2730                throw new ExceptionConverter(ioe);
2731            }
2732            imageDictionary.put(pdfImage.name(), fixedRef);
2733            return fixedRef;
2734        }
2735        return (PdfIndirectReference) imageDictionary.get(pdfImage.name());
2736    }
2737    
2738    /**
2739     * return the <CODE>PdfIndirectReference</CODE> to the image with a given name.
2740     *
2741     * @param name the name of the image
2742     * @return a <CODE>PdfIndirectReference</CODE>
2743     */

2744    
2745    PdfIndirectReference getImageReference(PdfName name) {
2746        return (PdfIndirectReference) imageDictionary.get(name);
2747    }
2748    
2749    protected PdfIndirectReference add(PdfICCBased icc) {
2750        PdfIndirectObject object;
2751        try {
2752            object = addToBody(icc);
2753        }
2754        catch(IOException JavaDoc ioe) {
2755            throw new ExceptionConverter(ioe);
2756        }
2757        return object.getIndirectReference();
2758    }
2759    
2760// [M4] Old table functionality; do we still need it?
2761

2762    /**
2763     * Sometimes it is necessary to know where the just added <CODE>Table</CODE> ends.
2764     *
2765     * For instance to avoid to add another table in a page that is ending up, because
2766     * the new table will be probably splitted just after the header (it is an
2767     * unpleasant effect, isn't it?).
2768     *
2769     * Added on September 8th, 2001
2770     * by Francesco De Milato
2771     * francesco.demilato@tiscalinet.it
2772     * @param table the <CODE>Table</CODE>
2773     * @return the bottom height of the just added table
2774     * @deprecated this method will probably disappear in one of the next releases
2775     */

2776    
2777    public float getTableBottom(Table table) {
2778        return pdf.bottom(table) - pdf.indentBottom();
2779    }
2780    
2781    /**
2782     * Gets a pre-rendered table.
2783     * (Contributed by dperezcar@fcc.es)
2784     * @param table Contains the table definition. Its contents are deleted, after being pre-rendered.
2785     * @return a PdfTable
2786     * @deprecated this method will probably disappear in one of the next releases
2787     */

2788    
2789    public PdfTable getPdfTable(Table table) {
2790        return pdf.getPdfTable(table, true);
2791    }
2792
2793    /**
2794     * Row additions to the original {@link Table} used to build the {@link PdfTable} are processed and pre-rendered,
2795     * and then the contents are deleted.
2796     * If the pre-rendered table doesn't fit, then it is fully rendered and its data discarded.
2797     * There shouldn't be any column change in the underlying {@link Table} object.
2798     * (Contributed by dperezcar@fcc.es)
2799     *
2800     * @param table The pre-rendered table obtained from {@link #getPdfTable(Table)}
2801     * @return true if the table is rendered and emptied.
2802     * @throws DocumentException
2803     * @see #getPdfTable(Table)
2804     * @deprecated this method will probably disappear in one of the next releases
2805     */

2806    
2807    public boolean breakTableIfDoesntFit(PdfTable table) throws DocumentException {
2808        return pdf.breakTableIfDoesntFit(table);
2809    }
2810    
2811    /**
2812     * Checks if a <CODE>Table</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
2813     *
2814     * @param table the table that has to be checked
2815     * @param margin a certain margin
2816     * @return <CODE>true</CODE> if the <CODE>Table</CODE> fits the page, <CODE>false</CODE> otherwise.
2817     * @deprecated this method will probably disappear in one of the next releases
2818     */

2819    
2820    public boolean fitsPage(Table table, float margin) {
2821        return pdf.bottom(table) > pdf.indentBottom() + margin;
2822    }
2823    
2824    /**
2825     * Checks if a <CODE>Table</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
2826     *
2827     * @param table the table that has to be checked
2828     * @return <CODE>true</CODE> if the <CODE>Table</CODE> fits the page, <CODE>false</CODE> otherwise.
2829     * @deprecated this method will probably disappear in one of the next releases
2830     */

2831    
2832    public boolean fitsPage(Table table) {
2833        return fitsPage(table, 0);
2834    }
2835    
2836    /**
2837     * Checks if a <CODE>PdfPTable</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
2838     *
2839     * @param table the table that has to be checked
2840     * @param margin a certain margin
2841     * @return <CODE>true</CODE> if the <CODE>PdfPTable</CODE> fits the page, <CODE>false</CODE> otherwise.
2842     * @deprecated this method will probably disappear in one of the next releases
2843     */

2844    public boolean fitsPage(PdfPTable table, float margin) {
2845        return pdf.fitsPage(table, margin);
2846    }
2847    
2848    /**
2849     * Checks if a <CODE>PdfPTable</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
2850     *
2851     * @param table the table that has to be checked
2852     * @return <CODE>true</CODE> if the <CODE>PdfPTable</CODE> fits the page, <CODE>false</CODE> otherwise.
2853     * @deprecated this method will probably disappear in one of the next releases
2854     */

2855    public boolean fitsPage(PdfPTable table) {
2856        return pdf.fitsPage(table, 0);
2857    }
2858
2859    /**
2860     * A flag indicating the presence of structure elements that contain user properties attributes.
2861     */

2862    private boolean userProperties;
2863
2864    /**
2865     * Gets the flag indicating the presence of structure elements that contain user properties attributes.
2866     * @return the user properties flag
2867     */

2868    public boolean isUserProperties() {
2869        return this.userProperties;
2870    }
2871
2872    /**
2873     * Sets the flag indicating the presence of structure elements that contain user properties attributes.
2874     * @param userProperties the user properties flag
2875     */

2876    public void setUserProperties(boolean userProperties) {
2877        this.userProperties = userProperties;
2878    }
2879}
2880
Popular Tags