0001: /*
0002: * $Id: PdfWriter.java 2904 2007-08-30 17:18:07Z psoares33 $
0003: *
0004: * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version 1.1
0007: * (the "License"); you may not use this file except in compliance with the License.
0008: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
0009: *
0010: * Software distributed under the License is distributed on an "AS IS" basis,
0011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0012: * for the specific language governing rights and limitations under the License.
0013: *
0014: * The Original Code is 'iText, a free JAVA-PDF library'.
0015: *
0016: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
0017: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
0018: * All Rights Reserved.
0019: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
0020: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
0021: *
0022: * Contributor(s): all the names of the contributors are added in the source code
0023: * where applicable.
0024: *
0025: * Alternatively, the contents of this file may be used under the terms of the
0026: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
0027: * provisions of LGPL are applicable instead of those above. If you wish to
0028: * allow use of your version of this file only under the terms of the LGPL
0029: * License and not to allow others to use your version of this file under
0030: * the MPL, indicate your decision by deleting the provisions above and
0031: * replace them with the notice and other provisions required by the LGPL.
0032: * If you do not delete the provisions above, a recipient may use your version
0033: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
0034: *
0035: * This library is free software; you can redistribute it and/or modify it
0036: * under the terms of the MPL as stated above or under the terms of the GNU
0037: * Library General Public License as published by the Free Software Foundation;
0038: * either version 2 of the License, or any later version.
0039: *
0040: * This library is distributed in the hope that it will be useful, but WITHOUT
0041: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
0042: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
0043: * details.
0044: *
0045: * If you didn't download this code from the following link, you should check if
0046: * you aren't using an obsolete version:
0047: * http://www.lowagie.com/iText/
0048: */
0049:
0050: package com.lowagie.text.pdf;
0051:
0052: import java.awt.Color;
0053: import java.io.ByteArrayOutputStream;
0054: import java.io.IOException;
0055: import java.io.OutputStream;
0056: import java.util.ArrayList;
0057: import java.util.HashMap;
0058: import java.util.HashSet;
0059: import java.util.Iterator;
0060: import java.util.Map;
0061: import java.util.TreeMap;
0062: import java.util.TreeSet;
0063: import java.security.cert.Certificate;
0064:
0065: import com.lowagie.text.DocListener;
0066: import com.lowagie.text.DocWriter;
0067: import com.lowagie.text.Document;
0068: import com.lowagie.text.DocumentException;
0069: import com.lowagie.text.ExceptionConverter;
0070: import com.lowagie.text.Image;
0071: import com.lowagie.text.ImgWMF;
0072: import com.lowagie.text.Rectangle;
0073: import com.lowagie.text.Table;
0074: import com.lowagie.text.pdf.collection.PdfCollection;
0075: import com.lowagie.text.pdf.events.PdfPageEventForwarder;
0076: import com.lowagie.text.pdf.interfaces.PdfAnnotations;
0077: import com.lowagie.text.pdf.interfaces.PdfDocumentActions;
0078: import com.lowagie.text.pdf.interfaces.PdfEncryptionSettings;
0079: import com.lowagie.text.pdf.interfaces.PdfPageActions;
0080: import com.lowagie.text.pdf.interfaces.PdfVersion;
0081: import com.lowagie.text.pdf.interfaces.PdfViewerPreferences;
0082: import com.lowagie.text.pdf.interfaces.PdfXConformance;
0083: import com.lowagie.text.pdf.interfaces.PdfRunDirection;
0084: import com.lowagie.text.pdf.internal.PdfVersionImp;
0085: import com.lowagie.text.pdf.internal.PdfXConformanceImp;
0086: import com.lowagie.text.xml.xmp.XmpWriter;
0087:
0088: /**
0089: * A <CODE>DocWriter</CODE> class for PDF.
0090: * <P>
0091: * When this <CODE>PdfWriter</CODE> is added
0092: * to a certain <CODE>PdfDocument</CODE>, the PDF representation of every Element
0093: * added to this Document will be written to the outputstream.</P>
0094: */
0095:
0096: public class PdfWriter extends DocWriter implements
0097: PdfViewerPreferences, PdfEncryptionSettings, PdfVersion,
0098: PdfDocumentActions, PdfPageActions, PdfXConformance,
0099: PdfRunDirection, PdfAnnotations {
0100:
0101: // INNER CLASSES
0102:
0103: /**
0104: * This class generates the structure of a PDF document.
0105: * <P>
0106: * This class covers the third section of Chapter 5 in the 'Portable Document Format
0107: * Reference Manual version 1.3' (page 55-60). It contains the body of a PDF document
0108: * (section 5.14) and it can also generate a Cross-reference Table (section 5.15).
0109: *
0110: * @see PdfWriter
0111: * @see PdfObject
0112: * @see PdfIndirectObject
0113: */
0114:
0115: public static class PdfBody {
0116:
0117: // inner classes
0118:
0119: /**
0120: * <CODE>PdfCrossReference</CODE> is an entry in the PDF Cross-Reference table.
0121: */
0122:
0123: static class PdfCrossReference implements Comparable {
0124:
0125: // membervariables
0126: private int type;
0127:
0128: /** Byte offset in the PDF file. */
0129: private int offset;
0130:
0131: private int refnum;
0132: /** generation of the object. */
0133: private int generation;
0134:
0135: // constructors
0136: /**
0137: * Constructs a cross-reference element for a PdfIndirectObject.
0138: * @param refnum
0139: * @param offset byte offset of the object
0140: * @param generation generationnumber of the object
0141: */
0142:
0143: PdfCrossReference(int refnum, int offset, int generation) {
0144: type = 0;
0145: this .offset = offset;
0146: this .refnum = refnum;
0147: this .generation = generation;
0148: }
0149:
0150: /**
0151: * Constructs a cross-reference element for a PdfIndirectObject.
0152: * @param refnum
0153: * @param offset byte offset of the object
0154: */
0155:
0156: PdfCrossReference(int refnum, int offset) {
0157: type = 1;
0158: this .offset = offset;
0159: this .refnum = refnum;
0160: this .generation = 0;
0161: }
0162:
0163: PdfCrossReference(int type, int refnum, int offset,
0164: int generation) {
0165: this .type = type;
0166: this .offset = offset;
0167: this .refnum = refnum;
0168: this .generation = generation;
0169: }
0170:
0171: int getRefnum() {
0172: return refnum;
0173: }
0174:
0175: /**
0176: * Returns the PDF representation of this <CODE>PdfObject</CODE>.
0177: * @param os
0178: * @throws IOException
0179: */
0180:
0181: public void toPdf(OutputStream os) throws IOException {
0182: StringBuffer off = new StringBuffer("0000000000")
0183: .append(offset);
0184: off.delete(0, off.length() - 10);
0185: StringBuffer gen = new StringBuffer("00000")
0186: .append(generation);
0187: gen.delete(0, gen.length() - 5);
0188:
0189: off.append(' ').append(gen).append(
0190: generation == 65535 ? " f \n" : " n \n");
0191: os.write(getISOBytes(off.toString()));
0192: }
0193:
0194: /**
0195: * Writes PDF syntax to the OutputStream
0196: * @param midSize
0197: * @param os
0198: * @throws IOException
0199: */
0200: public void toPdf(int midSize, OutputStream os)
0201: throws IOException {
0202: os.write((byte) type);
0203: while (--midSize >= 0)
0204: os
0205: .write((byte) ((offset >>> (8 * midSize)) & 0xff));
0206: os.write((byte) ((generation >>> 8) & 0xff));
0207: os.write((byte) (generation & 0xff));
0208: }
0209:
0210: /**
0211: * @see java.lang.Comparable#compareTo(java.lang.Object)
0212: */
0213: public int compareTo(Object o) {
0214: PdfCrossReference other = (PdfCrossReference) o;
0215: return (refnum < other.refnum ? -1
0216: : (refnum == other.refnum ? 0 : 1));
0217: }
0218:
0219: /**
0220: * @see java.lang.Object#equals(java.lang.Object)
0221: */
0222: public boolean equals(Object obj) {
0223: if (obj instanceof PdfCrossReference) {
0224: PdfCrossReference other = (PdfCrossReference) obj;
0225: return (refnum == other.refnum);
0226: } else
0227: return false;
0228: }
0229:
0230: /**
0231: * @see java.lang.Object#hashCode()
0232: */
0233: public int hashCode() {
0234: return refnum;
0235: }
0236:
0237: }
0238:
0239: private static final int OBJSINSTREAM = 200;
0240:
0241: // membervariables
0242:
0243: /** array containing the cross-reference table of the normal objects. */
0244: private TreeSet xrefs;
0245: private int refnum;
0246: /** the current byteposition in the body. */
0247: private int position;
0248: private PdfWriter writer;
0249: private ByteBuffer index;
0250: private ByteBuffer streamObjects;
0251: private int currentObjNum;
0252: private int numObj = 0;
0253:
0254: // constructors
0255:
0256: /**
0257: * Constructs a new <CODE>PdfBody</CODE>.
0258: * @param writer
0259: */
0260: PdfBody(PdfWriter writer) {
0261: xrefs = new TreeSet();
0262: xrefs.add(new PdfCrossReference(0, 0, 65535));
0263: position = writer.getOs().getCounter();
0264: refnum = 1;
0265: this .writer = writer;
0266: }
0267:
0268: // methods
0269:
0270: void setRefnum(int refnum) {
0271: this .refnum = refnum;
0272: }
0273:
0274: private PdfWriter.PdfBody.PdfCrossReference addToObjStm(
0275: PdfObject obj, int nObj) throws IOException {
0276: if (numObj >= OBJSINSTREAM)
0277: flushObjStm();
0278: if (index == null) {
0279: index = new ByteBuffer();
0280: streamObjects = new ByteBuffer();
0281: currentObjNum = getIndirectReferenceNumber();
0282: numObj = 0;
0283: }
0284: int p = streamObjects.size();
0285: int idx = numObj++;
0286: PdfEncryption enc = writer.crypto;
0287: writer.crypto = null;
0288: obj.toPdf(writer, streamObjects);
0289: writer.crypto = enc;
0290: streamObjects.append(' ');
0291: index.append(nObj).append(' ').append(p).append(' ');
0292: return new PdfWriter.PdfBody.PdfCrossReference(2, nObj,
0293: currentObjNum, idx);
0294: }
0295:
0296: private void flushObjStm() throws IOException {
0297: if (numObj == 0)
0298: return;
0299: int first = index.size();
0300: index.append(streamObjects);
0301: PdfStream stream = new PdfStream(index.toByteArray());
0302: stream.flateCompress();
0303: stream.put(PdfName.TYPE, PdfName.OBJSTM);
0304: stream.put(PdfName.N, new PdfNumber(numObj));
0305: stream.put(PdfName.FIRST, new PdfNumber(first));
0306: add(stream, currentObjNum);
0307: index = null;
0308: streamObjects = null;
0309: numObj = 0;
0310: }
0311:
0312: /**
0313: * Adds a <CODE>PdfObject</CODE> to the body.
0314: * <P>
0315: * This methods creates a <CODE>PdfIndirectObject</CODE> with a
0316: * certain number, containing the given <CODE>PdfObject</CODE>.
0317: * It also adds a <CODE>PdfCrossReference</CODE> for this object
0318: * to an <CODE>ArrayList</CODE> that will be used to build the
0319: * Cross-reference Table.
0320: *
0321: * @param object a <CODE>PdfObject</CODE>
0322: * @return a <CODE>PdfIndirectObject</CODE>
0323: * @throws IOException
0324: */
0325:
0326: PdfIndirectObject add(PdfObject object) throws IOException {
0327: return add(object, getIndirectReferenceNumber());
0328: }
0329:
0330: PdfIndirectObject add(PdfObject object, boolean inObjStm)
0331: throws IOException {
0332: return add(object, getIndirectReferenceNumber(), inObjStm);
0333: }
0334:
0335: /**
0336: * Gets a PdfIndirectReference for an object that will be created in the future.
0337: * @return a PdfIndirectReference
0338: */
0339:
0340: PdfIndirectReference getPdfIndirectReference() {
0341: return new PdfIndirectReference(0,
0342: getIndirectReferenceNumber());
0343: }
0344:
0345: int getIndirectReferenceNumber() {
0346: int n = refnum++;
0347: xrefs.add(new PdfCrossReference(n, 0, 65536));
0348: return n;
0349: }
0350:
0351: /**
0352: * Adds a <CODE>PdfObject</CODE> to the body given an already existing
0353: * PdfIndirectReference.
0354: * <P>
0355: * This methods creates a <CODE>PdfIndirectObject</CODE> with the number given by
0356: * <CODE>ref</CODE>, containing the given <CODE>PdfObject</CODE>.
0357: * It also adds a <CODE>PdfCrossReference</CODE> for this object
0358: * to an <CODE>ArrayList</CODE> that will be used to build the
0359: * Cross-reference Table.
0360: *
0361: * @param object a <CODE>PdfObject</CODE>
0362: * @param ref a <CODE>PdfIndirectReference</CODE>
0363: * @return a <CODE>PdfIndirectObject</CODE>
0364: * @throws IOException
0365: */
0366:
0367: PdfIndirectObject add(PdfObject object, PdfIndirectReference ref)
0368: throws IOException {
0369: return add(object, ref.getNumber());
0370: }
0371:
0372: PdfIndirectObject add(PdfObject object,
0373: PdfIndirectReference ref, boolean inObjStm)
0374: throws IOException {
0375: return add(object, ref.getNumber(), inObjStm);
0376: }
0377:
0378: PdfIndirectObject add(PdfObject object, int refNumber)
0379: throws IOException {
0380: return add(object, refNumber, true); // to false
0381: }
0382:
0383: PdfIndirectObject add(PdfObject object, int refNumber,
0384: boolean inObjStm) throws IOException {
0385: if (inObjStm && object.canBeInObjStm()
0386: && writer.isFullCompression()) {
0387: PdfCrossReference pxref = addToObjStm(object, refNumber);
0388: PdfIndirectObject indirect = new PdfIndirectObject(
0389: refNumber, object, writer);
0390: if (!xrefs.add(pxref)) {
0391: xrefs.remove(pxref);
0392: xrefs.add(pxref);
0393: }
0394: return indirect;
0395: } else {
0396: PdfIndirectObject indirect = new PdfIndirectObject(
0397: refNumber, object, writer);
0398: PdfCrossReference pxref = new PdfCrossReference(
0399: refNumber, position);
0400: if (!xrefs.add(pxref)) {
0401: xrefs.remove(pxref);
0402: xrefs.add(pxref);
0403: }
0404: indirect.writeTo(writer.getOs());
0405: position = writer.getOs().getCounter();
0406: return indirect;
0407: }
0408: }
0409:
0410: /**
0411: * Returns the offset of the Cross-Reference table.
0412: *
0413: * @return an offset
0414: */
0415:
0416: int offset() {
0417: return position;
0418: }
0419:
0420: /**
0421: * Returns the total number of objects contained in the CrossReferenceTable of this <CODE>Body</CODE>.
0422: *
0423: * @return a number of objects
0424: */
0425:
0426: int size() {
0427: return Math.max(((PdfCrossReference) xrefs.last())
0428: .getRefnum() + 1, refnum);
0429: }
0430:
0431: /**
0432: * Returns the CrossReferenceTable of the <CODE>Body</CODE>.
0433: * @param os
0434: * @param root
0435: * @param info
0436: * @param encryption
0437: * @param fileID
0438: * @param prevxref
0439: * @throws IOException
0440: */
0441:
0442: void writeCrossReferenceTable(OutputStream os,
0443: PdfIndirectReference root, PdfIndirectReference info,
0444: PdfIndirectReference encryption, PdfObject fileID,
0445: int prevxref) throws IOException {
0446: int refNumber = 0;
0447: if (writer.isFullCompression()) {
0448: flushObjStm();
0449: refNumber = getIndirectReferenceNumber();
0450: xrefs.add(new PdfCrossReference(refNumber, position));
0451: }
0452: PdfCrossReference entry = (PdfCrossReference) xrefs.first();
0453: int first = entry.getRefnum();
0454: int len = 0;
0455: ArrayList sections = new ArrayList();
0456: for (Iterator i = xrefs.iterator(); i.hasNext();) {
0457: entry = (PdfCrossReference) i.next();
0458: if (first + len == entry.getRefnum())
0459: ++len;
0460: else {
0461: sections.add(new Integer(first));
0462: sections.add(new Integer(len));
0463: first = entry.getRefnum();
0464: len = 1;
0465: }
0466: }
0467: sections.add(new Integer(first));
0468: sections.add(new Integer(len));
0469: if (writer.isFullCompression()) {
0470: int mid = 4;
0471: int mask = 0xff000000;
0472: for (; mid > 1; --mid) {
0473: if ((mask & position) != 0)
0474: break;
0475: mask >>>= 8;
0476: }
0477: ByteBuffer buf = new ByteBuffer();
0478:
0479: for (Iterator i = xrefs.iterator(); i.hasNext();) {
0480: entry = (PdfCrossReference) i.next();
0481: entry.toPdf(mid, buf);
0482: }
0483: PdfStream xr = new PdfStream(buf.toByteArray());
0484: buf = null;
0485: xr.flateCompress();
0486: xr.put(PdfName.SIZE, new PdfNumber(size()));
0487: xr.put(PdfName.ROOT, root);
0488: if (info != null) {
0489: xr.put(PdfName.INFO, info);
0490: }
0491: if (encryption != null)
0492: xr.put(PdfName.ENCRYPT, encryption);
0493: if (fileID != null)
0494: xr.put(PdfName.ID, fileID);
0495: xr
0496: .put(PdfName.W, new PdfArray(new int[] { 1,
0497: mid, 2 }));
0498: xr.put(PdfName.TYPE, PdfName.XREF);
0499: PdfArray idx = new PdfArray();
0500: for (int k = 0; k < sections.size(); ++k)
0501: idx.add(new PdfNumber(((Integer) sections.get(k))
0502: .intValue()));
0503: xr.put(PdfName.INDEX, idx);
0504: if (prevxref > 0)
0505: xr.put(PdfName.PREV, new PdfNumber(prevxref));
0506: PdfEncryption enc = writer.crypto;
0507: writer.crypto = null;
0508: PdfIndirectObject indirect = new PdfIndirectObject(
0509: refNumber, xr, writer);
0510: indirect.writeTo(writer.getOs());
0511: writer.crypto = enc;
0512: } else {
0513: os.write(getISOBytes("xref\n"));
0514: Iterator i = xrefs.iterator();
0515: for (int k = 0; k < sections.size(); k += 2) {
0516: first = ((Integer) sections.get(k)).intValue();
0517: len = ((Integer) sections.get(k + 1)).intValue();
0518: os.write(getISOBytes(String.valueOf(first)));
0519: os.write(getISOBytes(" "));
0520: os.write(getISOBytes(String.valueOf(len)));
0521: os.write('\n');
0522: while (len-- > 0) {
0523: entry = (PdfCrossReference) i.next();
0524: entry.toPdf(os);
0525: }
0526: }
0527: }
0528: }
0529: }
0530:
0531: /**
0532: * <CODE>PdfTrailer</CODE> is the PDF Trailer object.
0533: * <P>
0534: * This object is described in the 'Portable Document Format Reference Manual version 1.3'
0535: * section 5.16 (page 59-60).
0536: */
0537:
0538: static class PdfTrailer extends PdfDictionary {
0539:
0540: // membervariables
0541:
0542: int offset;
0543:
0544: // constructors
0545:
0546: /**
0547: * Constructs a PDF-Trailer.
0548: *
0549: * @param size the number of entries in the <CODE>PdfCrossReferenceTable</CODE>
0550: * @param offset offset of the <CODE>PdfCrossReferenceTable</CODE>
0551: * @param root an indirect reference to the root of the PDF document
0552: * @param info an indirect reference to the info object of the PDF document
0553: * @param encryption
0554: * @param fileID
0555: * @param prevxref
0556: */
0557:
0558: PdfTrailer(int size, int offset, PdfIndirectReference root,
0559: PdfIndirectReference info,
0560: PdfIndirectReference encryption, PdfObject fileID,
0561: int prevxref) {
0562: this .offset = offset;
0563: put(PdfName.SIZE, new PdfNumber(size));
0564: put(PdfName.ROOT, root);
0565: if (info != null) {
0566: put(PdfName.INFO, info);
0567: }
0568: if (encryption != null)
0569: put(PdfName.ENCRYPT, encryption);
0570: if (fileID != null)
0571: put(PdfName.ID, fileID);
0572: if (prevxref > 0)
0573: put(PdfName.PREV, new PdfNumber(prevxref));
0574: }
0575:
0576: /**
0577: * Returns the PDF representation of this <CODE>PdfObject</CODE>.
0578: * @param writer
0579: * @param os
0580: * @throws IOException
0581: */
0582: public void toPdf(PdfWriter writer, OutputStream os)
0583: throws IOException {
0584: os.write(getISOBytes("trailer\n"));
0585: super .toPdf(null, os);
0586: os.write(getISOBytes("\nstartxref\n"));
0587: os.write(getISOBytes(String.valueOf(offset)));
0588: os.write(getISOBytes("\n%%EOF\n"));
0589: }
0590: }
0591:
0592: // ESSENTIALS
0593:
0594: // Construct a PdfWriter instance
0595:
0596: /**
0597: * Constructs a <CODE>PdfWriter</CODE>.
0598: */
0599: protected PdfWriter() {
0600: }
0601:
0602: /**
0603: * Constructs a <CODE>PdfWriter</CODE>.
0604: * <P>
0605: * Remark: a PdfWriter can only be constructed by calling the method
0606: * <CODE>getInstance(Document document, OutputStream os)</CODE>.
0607: *
0608: * @param document The <CODE>PdfDocument</CODE> that has to be written
0609: * @param os The <CODE>OutputStream</CODE> the writer has to write to.
0610: */
0611:
0612: protected PdfWriter(PdfDocument document, OutputStream os) {
0613: super (document, os);
0614: pdf = document;
0615: directContent = new PdfContentByte(this );
0616: directContentUnder = new PdfContentByte(this );
0617: }
0618:
0619: /**
0620: * Use this method to get an instance of the <CODE>PdfWriter</CODE>.
0621: *
0622: * @param document The <CODE>Document</CODE> that has to be written
0623: * @param os The <CODE>OutputStream</CODE> the writer has to write to.
0624: * @return a new <CODE>PdfWriter</CODE>
0625: *
0626: * @throws DocumentException on error
0627: */
0628:
0629: public static PdfWriter getInstance(Document document,
0630: OutputStream os) throws DocumentException {
0631: PdfDocument pdf = new PdfDocument();
0632: document.addDocListener(pdf);
0633: PdfWriter writer = new PdfWriter(pdf, os);
0634: pdf.addWriter(writer);
0635: return writer;
0636: }
0637:
0638: /**
0639: * Use this method to get an instance of the <CODE>PdfWriter</CODE>.
0640: *
0641: * @return a new <CODE>PdfWriter</CODE>
0642: * @param document The <CODE>Document</CODE> that has to be written
0643: * @param os The <CODE>OutputStream</CODE> the writer has to write to.
0644: * @param listener A <CODE>DocListener</CODE> to pass to the PdfDocument.
0645: * @throws DocumentException on error
0646: */
0647:
0648: public static PdfWriter getInstance(Document document,
0649: OutputStream os, DocListener listener)
0650: throws DocumentException {
0651: PdfDocument pdf = new PdfDocument();
0652: pdf.addDocListener(listener);
0653: document.addDocListener(pdf);
0654: PdfWriter writer = new PdfWriter(pdf, os);
0655: pdf.addWriter(writer);
0656: return writer;
0657: }
0658:
0659: // the PdfDocument instance
0660:
0661: /** the pdfdocument object. */
0662: protected PdfDocument pdf;
0663:
0664: /**
0665: * Gets the <CODE>PdfDocument</CODE> associated with this writer.
0666: * @return the <CODE>PdfDocument</CODE>
0667: */
0668:
0669: PdfDocument getPdfDocument() {
0670: return pdf;
0671: }
0672:
0673: /**
0674: * Use this method to get the info dictionary if you want to
0675: * change it directly (add keys and values to the info dictionary).
0676: * @return the info dictionary
0677: */
0678: public PdfDictionary getInfo() {
0679: return pdf.getInfo();
0680: }
0681:
0682: /**
0683: * Use this method to get the current vertical page position.
0684: * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects
0685: * for elements that do not terminate the lines they've started because those lines will get
0686: * terminated.
0687: * @return The current vertical page position.
0688: */
0689: public float getVerticalPosition(boolean ensureNewLine) {
0690: return pdf.getVerticalPosition(ensureNewLine);
0691: }
0692:
0693: // the PdfDirectContentByte instances
0694:
0695: /*
0696: * You should see Direct Content as a canvas on which you can draw
0697: * graphics and text. One canvas goes on top of the page (getDirectContent),
0698: * the other goes underneath (getDirectContentUnder).
0699: * You can always the same object throughout your document,
0700: * even if you have moved to a new page. Whatever you add on
0701: * the canvas will be displayed on top or under the current page.
0702: */
0703:
0704: /** The direct content in this document. */
0705: protected PdfContentByte directContent;
0706:
0707: /** The direct content under in this document. */
0708: protected PdfContentByte directContentUnder;
0709:
0710: /**
0711: * Use this method to get the direct content for this document.
0712: * There is only one direct content, multiple calls to this method
0713: * will allways retrieve the same object.
0714: * @return the direct content
0715: */
0716:
0717: public PdfContentByte getDirectContent() {
0718: if (!open)
0719: throw new RuntimeException("The document is not open.");
0720: return directContent;
0721: }
0722:
0723: /**
0724: * Use this method to get the direct content under for this document.
0725: * There is only one direct content, multiple calls to this method
0726: * will allways retrieve the same object.
0727: * @return the direct content
0728: */
0729:
0730: public PdfContentByte getDirectContentUnder() {
0731: if (!open)
0732: throw new RuntimeException("The document is not open.");
0733: return directContentUnder;
0734: }
0735:
0736: /**
0737: * Resets all the direct contents to empty.
0738: * This happens when a new page is started.
0739: */
0740: void resetContent() {
0741: directContent.reset();
0742: directContentUnder.reset();
0743: }
0744:
0745: // PDF body
0746:
0747: /*
0748: * A PDF file has 4 parts: a header, a body, a cross-reference table, and a trailer.
0749: * The body contains all the PDF objects that make up the PDF document.
0750: * Each element gets a reference (a set of numbers) and the byte position of
0751: * every object is stored in the cross-reference table.
0752: * Use these methods only if you know what you're doing.
0753: */
0754:
0755: /** body of the PDF document */
0756: protected PdfBody body;
0757:
0758: /**
0759: * Adds the local destinations to the body of the document.
0760: * @param dest the <CODE>HashMap</CODE> containing the destinations
0761: * @throws IOException on error
0762: */
0763:
0764: void addLocalDestinations(TreeMap dest) throws IOException {
0765: for (Iterator i = dest.entrySet().iterator(); i.hasNext();) {
0766: Map.Entry entry = (Map.Entry) i.next();
0767: String name = (String) entry.getKey();
0768: Object obj[] = (Object[]) entry.getValue();
0769: PdfDestination destination = (PdfDestination) obj[2];
0770: if (destination == null)
0771: throw new RuntimeException("The name '" + name
0772: + "' has no local destination.");
0773: if (obj[1] == null)
0774: obj[1] = getPdfIndirectReference();
0775: addToBody(destination, (PdfIndirectReference) obj[1]);
0776: }
0777: }
0778:
0779: /**
0780: * Use this method to add a PDF object to the PDF body.
0781: * Use this method only if you know what you're doing!
0782: * @param object
0783: * @return a PdfIndirectObject
0784: * @throws IOException
0785: */
0786: public PdfIndirectObject addToBody(PdfObject object)
0787: throws IOException {
0788: PdfIndirectObject iobj = body.add(object);
0789: return iobj;
0790: }
0791:
0792: /**
0793: * Use this method to add a PDF object to the PDF body.
0794: * Use this method only if you know what you're doing!
0795: * @param object
0796: * @param inObjStm
0797: * @return a PdfIndirectObject
0798: * @throws IOException
0799: */
0800: public PdfIndirectObject addToBody(PdfObject object,
0801: boolean inObjStm) throws IOException {
0802: PdfIndirectObject iobj = body.add(object, inObjStm);
0803: return iobj;
0804: }
0805:
0806: /**
0807: * Use this method to add a PDF object to the PDF body.
0808: * Use this method only if you know what you're doing!
0809: * @param object
0810: * @param ref
0811: * @return a PdfIndirectObject
0812: * @throws IOException
0813: */
0814: public PdfIndirectObject addToBody(PdfObject object,
0815: PdfIndirectReference ref) throws IOException {
0816: PdfIndirectObject iobj = body.add(object, ref);
0817: return iobj;
0818: }
0819:
0820: /**
0821: * Use this method to add a PDF object to the PDF body.
0822: * Use this method only if you know what you're doing!
0823: * @param object
0824: * @param ref
0825: * @param inObjStm
0826: * @return a PdfIndirectObject
0827: * @throws IOException
0828: */
0829: public PdfIndirectObject addToBody(PdfObject object,
0830: PdfIndirectReference ref, boolean inObjStm)
0831: throws IOException {
0832: PdfIndirectObject iobj = body.add(object, ref, inObjStm);
0833: return iobj;
0834: }
0835:
0836: /**
0837: * Use this method to add a PDF object to the PDF body.
0838: * Use this method only if you know what you're doing!
0839: * @param object
0840: * @param refNumber
0841: * @return a PdfIndirectObject
0842: * @throws IOException
0843: */
0844: public PdfIndirectObject addToBody(PdfObject object, int refNumber)
0845: throws IOException {
0846: PdfIndirectObject iobj = body.add(object, refNumber);
0847: return iobj;
0848: }
0849:
0850: /**
0851: * Use this method to add a PDF object to the PDF body.
0852: * Use this method only if you know what you're doing!
0853: * @param object
0854: * @param refNumber
0855: * @param inObjStm
0856: * @return a PdfIndirectObject
0857: * @throws IOException
0858: */
0859: public PdfIndirectObject addToBody(PdfObject object, int refNumber,
0860: boolean inObjStm) throws IOException {
0861: PdfIndirectObject iobj = body.add(object, refNumber, inObjStm);
0862: return iobj;
0863: }
0864:
0865: /**
0866: * Use this to get an <CODE>PdfIndirectReference</CODE> for an object that
0867: * will be created in the future.
0868: * Use this method only if you know what you're doing!
0869: * @return the <CODE>PdfIndirectReference</CODE>
0870: */
0871:
0872: public PdfIndirectReference getPdfIndirectReference() {
0873: return body.getPdfIndirectReference();
0874: }
0875:
0876: int getIndirectReferenceNumber() {
0877: return body.getIndirectReferenceNumber();
0878: }
0879:
0880: /**
0881: * Returns the outputStreamCounter.
0882: * @return the outputStreamCounter
0883: */
0884: OutputStreamCounter getOs() {
0885: return os;
0886: }
0887:
0888: // PDF Catalog
0889:
0890: /*
0891: * The Catalog is also called the root object of the document.
0892: * Whereas the Cross-Reference maps the objects number with the
0893: * byte offset so that the viewer can find the objects, the
0894: * Catalog tells the viewer the numbers of the objects needed
0895: * to render the document.
0896: */
0897:
0898: protected PdfDictionary getCatalog(PdfIndirectReference rootObj) {
0899: PdfDictionary catalog = pdf.getCatalog(rootObj);
0900: // [F12] tagged PDF
0901: if (tagged) {
0902: try {
0903: getStructureTreeRoot().buildTree();
0904: } catch (Exception e) {
0905: throw new ExceptionConverter(e);
0906: }
0907: catalog.put(PdfName.STRUCTTREEROOT, structureTreeRoot
0908: .getReference());
0909: PdfDictionary mi = new PdfDictionary();
0910: mi.put(PdfName.MARKED, PdfBoolean.PDFTRUE);
0911: if (userProperties)
0912: mi.put(PdfName.USERPROPERTIES, PdfBoolean.PDFTRUE);
0913: catalog.put(PdfName.MARKINFO, mi);
0914: }
0915: // [F13] OCG
0916: if (!documentOCG.isEmpty()) {
0917: fillOCProperties(false);
0918: catalog.put(PdfName.OCPROPERTIES, OCProperties);
0919: }
0920: return catalog;
0921: }
0922:
0923: /** Holds value of property extraCatalog this is used for Output Intents. */
0924: protected PdfDictionary extraCatalog;
0925:
0926: /**
0927: * Sets extra keys to the catalog.
0928: * @return the catalog to change
0929: */
0930: public PdfDictionary getExtraCatalog() {
0931: if (extraCatalog == null)
0932: extraCatalog = new PdfDictionary();
0933: return this .extraCatalog;
0934: }
0935:
0936: // PdfPages
0937:
0938: /*
0939: * The page root keeps the complete page tree of the document.
0940: * There's an entry in the Catalog that refers to the root
0941: * of the page tree, the page tree contains the references
0942: * to pages and other page trees.
0943: */
0944:
0945: /** The root of the page tree. */
0946: protected PdfPages root = new PdfPages(this );
0947: /** The PdfIndirectReference to the pages. */
0948: protected ArrayList pageReferences = new ArrayList();
0949: /** The current page number. */
0950: protected int currentPageNumber = 1;
0951:
0952: /**
0953: * Use this method to make sure the page tree has a lineair structure
0954: * (every leave is attached directly to the root).
0955: * Use this method to allow page reordering with method reorderPages.
0956: */
0957: public void setLinearPageMode() {
0958: root.setLinearMode(null);
0959: }
0960:
0961: /**
0962: * Use this method to reorder the pages in the document.
0963: * A <CODE>null</CODE> argument value only returns the number of pages to process.
0964: * It is advisable to issue a <CODE>Document.newPage()</CODE> before using this method.
0965: * @return the total number of pages
0966: * @param order an array with the new page sequence. It must have the
0967: * same size as the number of pages.
0968: * @throws DocumentException if all the pages are not present in the array
0969: */
0970: public int reorderPages(int order[]) throws DocumentException {
0971: return root.reorderPages(order);
0972: }
0973:
0974: /**
0975: * Use this method to get a reference to a page existing or not.
0976: * If the page does not exist yet the reference will be created
0977: * in advance. If on closing the document, a page number greater
0978: * than the total number of pages was requested, an exception
0979: * is thrown.
0980: * @param page the page number. The first page is 1
0981: * @return the reference to the page
0982: */
0983: public PdfIndirectReference getPageReference(int page) {
0984: --page;
0985: if (page < 0)
0986: throw new IndexOutOfBoundsException(
0987: "The page numbers start at 1.");
0988: PdfIndirectReference ref;
0989: if (page < pageReferences.size()) {
0990: ref = (PdfIndirectReference) pageReferences.get(page);
0991: if (ref == null) {
0992: ref = body.getPdfIndirectReference();
0993: pageReferences.set(page, ref);
0994: }
0995: } else {
0996: int empty = page - pageReferences.size();
0997: for (int k = 0; k < empty; ++k)
0998: pageReferences.add(null);
0999: ref = body.getPdfIndirectReference();
1000: pageReferences.add(ref);
1001: }
1002: return ref;
1003: }
1004:
1005: /**
1006: * Gets the pagenumber of this document.
1007: * This number can be different from the real pagenumber,
1008: * if you have (re)set the page number previously.
1009: * @return a page number
1010: */
1011:
1012: public int getPageNumber() {
1013: return pdf.getPageNumber();
1014: }
1015:
1016: PdfIndirectReference getCurrentPage() {
1017: return getPageReference(currentPageNumber);
1018: }
1019:
1020: public int getCurrentPageNumber() {
1021: return currentPageNumber;
1022: }
1023:
1024: /**
1025: * Adds some <CODE>PdfContents</CODE> to this Writer.
1026: * <P>
1027: * The document has to be open before you can begin to add content
1028: * to the body of the document.
1029: *
1030: * @return a <CODE>PdfIndirectReference</CODE>
1031: * @param page the <CODE>PdfPage</CODE> to add
1032: * @param contents the <CODE>PdfContents</CODE> of the page
1033: * @throws PdfException on error
1034: */
1035:
1036: PdfIndirectReference add(PdfPage page, PdfContents contents)
1037: throws PdfException {
1038: if (!open) {
1039: throw new PdfException("The document isn't open.");
1040: }
1041: PdfIndirectObject object;
1042: try {
1043: object = addToBody(contents);
1044: } catch (IOException ioe) {
1045: throw new ExceptionConverter(ioe);
1046: }
1047: page.add(object.getIndirectReference());
1048: // [U5]
1049: if (group != null) {
1050: page.put(PdfName.GROUP, group);
1051: group = null;
1052: }
1053: root.addPage(page);
1054: currentPageNumber++;
1055: return null;
1056: }
1057:
1058: // page events
1059:
1060: /*
1061: * Page events are specific for iText, not for PDF.
1062: * Upon specific events (for instance when a page starts
1063: * or ends), the corresponing method in the page event
1064: * implementation that is added to the writer is invoked.
1065: */
1066:
1067: /** The <CODE>PdfPageEvent</CODE> for this document. */
1068: private PdfPageEvent pageEvent;
1069:
1070: /**
1071: * Sets the <CODE>PdfPageEvent</CODE> for this document.
1072: * @param event the <CODE>PdfPageEvent</CODE> for this document
1073: */
1074:
1075: public void setPageEvent(PdfPageEvent event) {
1076: if (event == null)
1077: this .pageEvent = null;
1078: else if (this .pageEvent == null)
1079: this .pageEvent = event;
1080: else if (this .pageEvent instanceof PdfPageEventForwarder)
1081: ((PdfPageEventForwarder) this .pageEvent)
1082: .addPageEvent(event);
1083: else {
1084: PdfPageEventForwarder forward = new PdfPageEventForwarder();
1085: forward.addPageEvent(this .pageEvent);
1086: forward.addPageEvent(event);
1087: this .pageEvent = forward;
1088: }
1089: }
1090:
1091: /**
1092: * Gets the <CODE>PdfPageEvent</CODE> for this document or <CODE>null</CODE>
1093: * if none is set.
1094: * @return the <CODE>PdfPageEvent</CODE> for this document or <CODE>null</CODE>
1095: * if none is set
1096: */
1097:
1098: public PdfPageEvent getPageEvent() {
1099: return pageEvent;
1100: }
1101:
1102: // Open en Close method + method that create the PDF
1103:
1104: /** A number refering to the previous Cross-Reference Table. */
1105: protected int prevxref = 0;
1106:
1107: /**
1108: * Signals that the <CODE>Document</CODE> has been opened and that
1109: * <CODE>Elements</CODE> can be added.
1110: * <P>
1111: * When this method is called, the PDF-document header is
1112: * written to the outputstream.
1113: * @see com.lowagie.text.DocWriter#open()
1114: */
1115: public void open() {
1116: super .open();
1117: try {
1118: pdf_version.writeHeader(os);
1119: body = new PdfBody(this );
1120: if (pdfxConformance.isPdfX32002()) {
1121: PdfDictionary sec = new PdfDictionary();
1122: sec.put(PdfName.GAMMA, new PdfArray(new float[] { 2.2f,
1123: 2.2f, 2.2f }));
1124: sec.put(PdfName.MATRIX, new PdfArray(new float[] {
1125: 0.4124f, 0.2126f, 0.0193f, 0.3576f, 0.7152f,
1126: 0.1192f, 0.1805f, 0.0722f, 0.9505f }));
1127: sec.put(PdfName.WHITEPOINT, new PdfArray(new float[] {
1128: 0.9505f, 1f, 1.089f }));
1129: PdfArray arr = new PdfArray(PdfName.CALRGB);
1130: arr.add(sec);
1131: setDefaultColorspace(PdfName.DEFAULTRGB, addToBody(arr)
1132: .getIndirectReference());
1133: }
1134: } catch (IOException ioe) {
1135: throw new ExceptionConverter(ioe);
1136: }
1137: }
1138:
1139: /**
1140: * Signals that the <CODE>Document</CODE> was closed and that no other
1141: * <CODE>Elements</CODE> will be added.
1142: * <P>
1143: * The pages-tree is built and written to the outputstream.
1144: * A Catalog is constructed, as well as an Info-object,
1145: * the referencetable is composed and everything is written
1146: * to the outputstream embedded in a Trailer.
1147: * @see com.lowagie.text.DocWriter#close()
1148: */
1149: public void close() {
1150: if (open) {
1151: if ((currentPageNumber - 1) != pageReferences.size())
1152: throw new RuntimeException("The page "
1153: + pageReferences.size()
1154: + " was requested but the document has only "
1155: + (currentPageNumber - 1) + " pages.");
1156: pdf.close();
1157: try {
1158: addSharedObjectsToBody();
1159: // add the root to the body
1160: PdfIndirectReference rootRef = root.writePageTree();
1161: // make the catalog-object and add it to the body
1162: PdfDictionary catalog = getCatalog(rootRef);
1163: // [C9] if there is XMP data to add: add it
1164: if (xmpMetadata != null) {
1165: PdfStream xmp = new PdfStream(xmpMetadata);
1166: xmp.put(PdfName.TYPE, PdfName.METADATA);
1167: xmp.put(PdfName.SUBTYPE, PdfName.XML);
1168: if (crypto != null && !crypto.isMetadataEncrypted()) {
1169: PdfArray ar = new PdfArray();
1170: ar.add(PdfName.CRYPT);
1171: xmp.put(PdfName.FILTER, ar);
1172: }
1173: catalog.put(PdfName.METADATA, body.add(xmp)
1174: .getIndirectReference());
1175: }
1176: // [C10] make pdfx conformant
1177: if (isPdfX()) {
1178: pdfxConformance.completeInfoDictionary(getInfo());
1179: pdfxConformance
1180: .completeExtraCatalog(getExtraCatalog());
1181: }
1182: // [C11] Output Intents
1183: if (extraCatalog != null) {
1184: catalog.mergeDifferent(extraCatalog);
1185: }
1186:
1187: writeOutlines(catalog, false);
1188:
1189: // add the Catalog to the body
1190: PdfIndirectObject indirectCatalog = addToBody(catalog,
1191: false);
1192: // add the info-object to the body
1193: PdfIndirectObject infoObj = addToBody(getInfo(), false);
1194:
1195: // [F1] encryption
1196: PdfIndirectReference encryption = null;
1197: PdfObject fileID = null;
1198: body.flushObjStm();
1199: if (crypto != null) {
1200: PdfIndirectObject encryptionObject = addToBody(
1201: crypto.getEncryptionDictionary(), false);
1202: encryption = encryptionObject
1203: .getIndirectReference();
1204: fileID = crypto.getFileID();
1205: } else
1206: fileID = PdfEncryption.createInfoId(PdfEncryption
1207: .createDocumentId());
1208:
1209: // write the cross-reference table of the body
1210: body.writeCrossReferenceTable(os, indirectCatalog
1211: .getIndirectReference(), infoObj
1212: .getIndirectReference(), encryption, fileID,
1213: prevxref);
1214:
1215: // make the trailer
1216: // [F2] full compression
1217: if (fullCompression) {
1218: os.write(getISOBytes("startxref\n"));
1219: os
1220: .write(getISOBytes(String.valueOf(body
1221: .offset())));
1222: os.write(getISOBytes("\n%%EOF\n"));
1223: } else {
1224: PdfTrailer trailer = new PdfTrailer(body.size(),
1225: body.offset(), indirectCatalog
1226: .getIndirectReference(), infoObj
1227: .getIndirectReference(),
1228: encryption, fileID, prevxref);
1229: trailer.toPdf(this , os);
1230: }
1231: super .close();
1232: } catch (IOException ioe) {
1233: throw new ExceptionConverter(ioe);
1234: }
1235: }
1236: }
1237:
1238: protected void addSharedObjectsToBody() throws IOException {
1239: // [F3] add the fonts
1240: for (Iterator it = documentFonts.values().iterator(); it
1241: .hasNext();) {
1242: FontDetails details = (FontDetails) it.next();
1243: details.writeFont(this );
1244: }
1245: // [F4] add the form XObjects
1246: for (Iterator it = formXObjects.values().iterator(); it
1247: .hasNext();) {
1248: Object objs[] = (Object[]) it.next();
1249: PdfTemplate template = (PdfTemplate) objs[1];
1250: if (template != null
1251: && template.getIndirectReference() instanceof PRIndirectReference)
1252: continue;
1253: if (template != null
1254: && template.getType() == PdfTemplate.TYPE_TEMPLATE) {
1255: addToBody(template.getFormXObject(), template
1256: .getIndirectReference());
1257: }
1258: }
1259: // [F5] add all the dependencies in the imported pages
1260: for (Iterator it = importedPages.values().iterator(); it
1261: .hasNext();) {
1262: currentPdfReaderInstance = (PdfReaderInstance) it.next();
1263: currentPdfReaderInstance.writeAllPages();
1264: }
1265: currentPdfReaderInstance = null;
1266: // [F6] add the spotcolors
1267: for (Iterator it = documentColors.values().iterator(); it
1268: .hasNext();) {
1269: ColorDetails color = (ColorDetails) it.next();
1270: addToBody(color.getSpotColor(this ), color
1271: .getIndirectReference());
1272: }
1273: // [F7] add the pattern
1274: for (Iterator it = documentPatterns.keySet().iterator(); it
1275: .hasNext();) {
1276: PdfPatternPainter pat = (PdfPatternPainter) it.next();
1277: addToBody(pat.getPattern(), pat.getIndirectReference());
1278: }
1279: // [F8] add the shading patterns
1280: for (Iterator it = documentShadingPatterns.keySet().iterator(); it
1281: .hasNext();) {
1282: PdfShadingPattern shadingPattern = (PdfShadingPattern) it
1283: .next();
1284: shadingPattern.addToBody();
1285: }
1286: // [F9] add the shadings
1287: for (Iterator it = documentShadings.keySet().iterator(); it
1288: .hasNext();) {
1289: PdfShading shading = (PdfShading) it.next();
1290: shading.addToBody();
1291: }
1292: // [F10] add the extgstate
1293: for (Iterator it = documentExtGState.entrySet().iterator(); it
1294: .hasNext();) {
1295: Map.Entry entry = (Map.Entry) it.next();
1296: PdfDictionary gstate = (PdfDictionary) entry.getKey();
1297: PdfObject obj[] = (PdfObject[]) entry.getValue();
1298: addToBody(gstate, (PdfIndirectReference) obj[1]);
1299: }
1300: // [F11] add the properties
1301: for (Iterator it = documentProperties.entrySet().iterator(); it
1302: .hasNext();) {
1303: Map.Entry entry = (Map.Entry) it.next();
1304: Object prop = entry.getKey();
1305: PdfObject[] obj = (PdfObject[]) entry.getValue();
1306: if (prop instanceof PdfLayerMembership) {
1307: PdfLayerMembership layer = (PdfLayerMembership) prop;
1308: addToBody(layer.getPdfObject(), layer.getRef());
1309: } else if ((prop instanceof PdfDictionary)
1310: && !(prop instanceof PdfLayer)) {
1311: addToBody((PdfDictionary) prop,
1312: (PdfIndirectReference) obj[1]);
1313: }
1314: }
1315: // [F13] add the OCG layers
1316: for (Iterator it = documentOCG.iterator(); it.hasNext();) {
1317: PdfOCG layer = (PdfOCG) it.next();
1318: addToBody(layer.getPdfObject(), layer.getRef());
1319: }
1320: }
1321:
1322: // Root data for the PDF document (used when composing the Catalog)
1323:
1324: // [C1] Outlines (bookmarks)
1325:
1326: /**
1327: * Use this method to get the root outline
1328: * and construct bookmarks.
1329: * @return the root outline
1330: */
1331:
1332: public PdfOutline getRootOutline() {
1333: return directContent.getRootOutline();
1334: }
1335:
1336: protected java.util.List newBookmarks;
1337:
1338: /**
1339: * Sets the bookmarks. The list structure is defined in
1340: * {@link SimpleBookmark}.
1341: * @param outlines the bookmarks or <CODE>null</CODE> to remove any
1342: */
1343: public void setOutlines(java.util.List outlines) {
1344: newBookmarks = outlines;
1345: }
1346:
1347: protected void writeOutlines(PdfDictionary catalog,
1348: boolean namedAsNames) throws IOException {
1349: if (newBookmarks == null || newBookmarks.isEmpty())
1350: return;
1351: PdfDictionary top = new PdfDictionary();
1352: PdfIndirectReference topRef = getPdfIndirectReference();
1353: Object kids[] = SimpleBookmark.iterateOutlines(this , topRef,
1354: newBookmarks, namedAsNames);
1355: top.put(PdfName.FIRST, (PdfIndirectReference) kids[0]);
1356: top.put(PdfName.LAST, (PdfIndirectReference) kids[1]);
1357: top.put(PdfName.COUNT, new PdfNumber(((Integer) kids[2])
1358: .intValue()));
1359: addToBody(top, topRef);
1360: catalog.put(PdfName.OUTLINES, topRef);
1361: }
1362:
1363: // [C2] PdfVersion interface
1364: /** possible PDF version (header) */
1365: public static final char VERSION_1_2 = '2';
1366: /** possible PDF version (header) */
1367: public static final char VERSION_1_3 = '3';
1368: /** possible PDF version (header) */
1369: public static final char VERSION_1_4 = '4';
1370: /** possible PDF version (header) */
1371: public static final char VERSION_1_5 = '5';
1372: /** possible PDF version (header) */
1373: public static final char VERSION_1_6 = '6';
1374: /** possible PDF version (header) */
1375: public static final char VERSION_1_7 = '7';
1376:
1377: /** possible PDF version (catalog) */
1378: public static final PdfName PDF_VERSION_1_2 = new PdfName("1.2");
1379: /** possible PDF version (catalog) */
1380: public static final PdfName PDF_VERSION_1_3 = new PdfName("1.3");
1381: /** possible PDF version (catalog) */
1382: public static final PdfName PDF_VERSION_1_4 = new PdfName("1.4");
1383: /** possible PDF version (catalog) */
1384: public static final PdfName PDF_VERSION_1_5 = new PdfName("1.5");
1385: /** possible PDF version (catalog) */
1386: public static final PdfName PDF_VERSION_1_6 = new PdfName("1.6");
1387: /** possible PDF version (catalog) */
1388: public static final PdfName PDF_VERSION_1_7 = new PdfName("1.7");
1389:
1390: /** Stores the version information for the header and the catalog. */
1391: protected PdfVersionImp pdf_version = new PdfVersionImp();
1392:
1393: /** @see com.lowagie.text.pdf.interfaces.PdfVersion#setPdfVersion(char) */
1394: public void setPdfVersion(char version) {
1395: pdf_version.setPdfVersion(version);
1396: }
1397:
1398: /** @see com.lowagie.text.pdf.interfaces.PdfVersion#setAtLeastPdfVersion(char) */
1399: public void setAtLeastPdfVersion(char version) {
1400: pdf_version.setAtLeastPdfVersion(version);
1401: }
1402:
1403: /** @see com.lowagie.text.pdf.interfaces.PdfVersion#setPdfVersion(com.lowagie.text.pdf.PdfName) */
1404: public void setPdfVersion(PdfName version) {
1405: pdf_version.setPdfVersion(version);
1406: }
1407:
1408: /**
1409: * Returns the version information.
1410: */
1411: PdfVersionImp getPdfVersion() {
1412: return pdf_version;
1413: }
1414:
1415: // [C3] PdfViewerPreferences interface
1416:
1417: // page layout (section 13.1.1 of "iText in Action")
1418:
1419: /** A viewer preference */
1420: public static final int PageLayoutSinglePage = 1;
1421: /** A viewer preference */
1422: public static final int PageLayoutOneColumn = 2;
1423: /** A viewer preference */
1424: public static final int PageLayoutTwoColumnLeft = 4;
1425: /** A viewer preference */
1426: public static final int PageLayoutTwoColumnRight = 8;
1427: /** A viewer preference */
1428: public static final int PageLayoutTwoPageLeft = 16;
1429: /** A viewer preference */
1430: public static final int PageLayoutTwoPageRight = 32;
1431:
1432: // page mode (section 13.1.2 of "iText in Action")
1433:
1434: /** A viewer preference */
1435: public static final int PageModeUseNone = 64;
1436: /** A viewer preference */
1437: public static final int PageModeUseOutlines = 128;
1438: /** A viewer preference */
1439: public static final int PageModeUseThumbs = 256;
1440: /** A viewer preference */
1441: public static final int PageModeFullScreen = 512;
1442: /** A viewer preference */
1443: public static final int PageModeUseOC = 1024;
1444: /** A viewer preference */
1445: public static final int PageModeUseAttachments = 2048;
1446:
1447: // values for setting viewer preferences in iText versions older than 2.x
1448:
1449: /** A viewer preference */
1450: public static final int HideToolbar = 1 << 12;
1451: /** A viewer preference */
1452: public static final int HideMenubar = 1 << 13;
1453: /** A viewer preference */
1454: public static final int HideWindowUI = 1 << 14;
1455: /** A viewer preference */
1456: public static final int FitWindow = 1 << 15;
1457: /** A viewer preference */
1458: public static final int CenterWindow = 1 << 16;
1459: /** A viewer preference */
1460: public static final int DisplayDocTitle = 1 << 17;
1461:
1462: /** A viewer preference */
1463: public static final int NonFullScreenPageModeUseNone = 1 << 18;
1464: /** A viewer preference */
1465: public static final int NonFullScreenPageModeUseOutlines = 1 << 19;
1466: /** A viewer preference */
1467: public static final int NonFullScreenPageModeUseThumbs = 1 << 20;
1468: /** A viewer preference */
1469: public static final int NonFullScreenPageModeUseOC = 1 << 21;
1470:
1471: /** A viewer preference */
1472: public static final int DirectionL2R = 1 << 22;
1473: /** A viewer preference */
1474: public static final int DirectionR2L = 1 << 23;
1475:
1476: /** A viewer preference */
1477: public static final int PrintScalingNone = 1 << 24;
1478:
1479: /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#setViewerPreferences(int) */
1480: public void setViewerPreferences(int preferences) {
1481: pdf.setViewerPreferences(preferences);
1482: }
1483:
1484: /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#addViewerPreference(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfObject) */
1485: public void addViewerPreference(PdfName key, PdfObject value) {
1486: pdf.addViewerPreference(key, value);
1487: }
1488:
1489: // [C4] Page labels
1490:
1491: /**
1492: * Use this method to add page labels
1493: * @param pageLabels the page labels
1494: */
1495: public void setPageLabels(PdfPageLabels pageLabels) {
1496: pdf.setPageLabels(pageLabels);
1497: }
1498:
1499: // [C5] named objects: named destinations, javascript, embedded files
1500:
1501: /**
1502: * Use this method to add a JavaScript action at the document level.
1503: * When the document opens, all this JavaScript runs.
1504: * @param js The JavaScript action
1505: */
1506: public void addJavaScript(PdfAction js) {
1507: pdf.addJavaScript(js);
1508: }
1509:
1510: /**
1511: * Use this method to add a JavaScript action at the document level.
1512: * When the document opens, all this JavaScript runs.
1513: * @param code the JavaScript code
1514: * @param unicode select JavaScript unicode. Note that the internal
1515: * Acrobat JavaScript engine does not support unicode,
1516: * so this may or may not work for you
1517: */
1518: public void addJavaScript(String code, boolean unicode) {
1519: addJavaScript(PdfAction.javaScript(code, this , unicode));
1520: }
1521:
1522: /**
1523: * Use this method to adds a JavaScript action at the document level.
1524: * When the document opens, all this JavaScript runs.
1525: * @param code the JavaScript code
1526: */
1527: public void addJavaScript(String code) {
1528: addJavaScript(code, false);
1529: }
1530:
1531: /**
1532: * Use this method to add a JavaScript action at the document level.
1533: * When the document opens, all this JavaScript runs.
1534: * @param name The name of the JS Action in the name tree
1535: * @param js The JavaScript action
1536: */
1537: public void addJavaScript(String name, PdfAction js) {
1538: pdf.addJavaScript(name, js);
1539: }
1540:
1541: /**
1542: * Use this method to add a JavaScript action at the document level.
1543: * When the document opens, all this JavaScript runs.
1544: * @param name The name of the JS Action in the name tree
1545: * @param code the JavaScript code
1546: * @param unicode select JavaScript unicode. Note that the internal
1547: * Acrobat JavaScript engine does not support unicode,
1548: * so this may or may not work for you
1549: */
1550: public void addJavaScript(String name, String code, boolean unicode) {
1551: addJavaScript(name, PdfAction.javaScript(code, this , unicode));
1552: }
1553:
1554: /**
1555: * Use this method to adds a JavaScript action at the document level.
1556: * When the document opens, all this JavaScript runs.
1557: * @param name The name of the JS Action in the name tree
1558: * @param code the JavaScript code
1559: */
1560: public void addJavaScript(String name, String code) {
1561: addJavaScript(name, code, false);
1562: }
1563:
1564: /**
1565: * Use this method to add a file attachment at the document level.
1566: * @param description the file description
1567: * @param fileStore an array with the file. If it's <CODE>null</CODE>
1568: * the file will be read from the disk
1569: * @param file the path to the file. It will only be used if
1570: * <CODE>fileStore</CODE> is not <CODE>null</CODE>
1571: * @param fileDisplay the actual file name stored in the pdf
1572: * @throws IOException on error
1573: */
1574: public void addFileAttachment(String description, byte fileStore[],
1575: String file, String fileDisplay) throws IOException {
1576: addFileAttachment(description, PdfFileSpecification
1577: .fileEmbedded(this , file, fileDisplay, fileStore));
1578: }
1579:
1580: /**
1581: * Use this method to add a file attachment at the document level.
1582: * @param description the file description
1583: * @param fs the file specification
1584: */
1585: public void addFileAttachment(String description,
1586: PdfFileSpecification fs) throws IOException {
1587: pdf.addFileAttachment(description, fs);
1588: }
1589:
1590: /**
1591: * Use this method to add a file attachment at the document level.
1592: * @param fs the file specification
1593: */
1594: public void addFileAttachment(PdfFileSpecification fs)
1595: throws IOException {
1596: addFileAttachment(null, fs);
1597: }
1598:
1599: // [C6] Actions (open and additional)
1600:
1601: /** action value */
1602: public static final PdfName DOCUMENT_CLOSE = PdfName.WC;
1603: /** action value */
1604: public static final PdfName WILL_SAVE = PdfName.WS;
1605: /** action value */
1606: public static final PdfName DID_SAVE = PdfName.DS;
1607: /** action value */
1608: public static final PdfName WILL_PRINT = PdfName.WP;
1609: /** action value */
1610: public static final PdfName DID_PRINT = PdfName.DP;
1611:
1612: /** @see com.lowagie.text.pdf.interfaces.PdfDocumentActions#setOpenAction(java.lang.String) */
1613: public void setOpenAction(String name) {
1614: pdf.setOpenAction(name);
1615: }
1616:
1617: /** @see com.lowagie.text.pdf.interfaces.PdfDocumentActions#setOpenAction(com.lowagie.text.pdf.PdfAction) */
1618: public void setOpenAction(PdfAction action) {
1619: pdf.setOpenAction(action);
1620: }
1621:
1622: /** @see com.lowagie.text.pdf.interfaces.PdfDocumentActions#setAdditionalAction(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfAction) */
1623: public void setAdditionalAction(PdfName actionType, PdfAction action)
1624: throws DocumentException {
1625: if (!(actionType.equals(DOCUMENT_CLOSE)
1626: || actionType.equals(WILL_SAVE)
1627: || actionType.equals(DID_SAVE)
1628: || actionType.equals(WILL_PRINT) || actionType
1629: .equals(DID_PRINT))) {
1630: throw new DocumentException(
1631: "Invalid additional action type: "
1632: + actionType.toString());
1633: }
1634: pdf.addAdditionalAction(actionType, action);
1635: }
1636:
1637: // [C7] portable collections
1638:
1639: /**
1640: * Use this method to add the Collection dictionary.
1641: * @param collection a dictionary of type PdfCollection
1642: */
1643: public void setCollection(PdfCollection collection) {
1644: setAtLeastPdfVersion(VERSION_1_7);
1645: pdf.setCollection(collection);
1646: }
1647:
1648: // [C8] AcroForm
1649:
1650: /** signature value */
1651: public static final int SIGNATURE_EXISTS = 1;
1652: /** signature value */
1653: public static final int SIGNATURE_APPEND_ONLY = 2;
1654:
1655: /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#getAcroForm() */
1656: public PdfAcroForm getAcroForm() {
1657: return pdf.getAcroForm();
1658: }
1659:
1660: /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#addAnnotation(com.lowagie.text.pdf.PdfAnnotation) */
1661: public void addAnnotation(PdfAnnotation annot) {
1662: pdf.addAnnotation(annot);
1663: }
1664:
1665: void addAnnotation(PdfAnnotation annot, int page) {
1666: addAnnotation(annot);
1667: }
1668:
1669: /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#addCalculationOrder(com.lowagie.text.pdf.PdfFormField) */
1670: public void addCalculationOrder(PdfFormField annot) {
1671: pdf.addCalculationOrder(annot);
1672: }
1673:
1674: /** @see com.lowagie.text.pdf.interfaces.PdfAnnotations#setSigFlags(int) */
1675: public void setSigFlags(int f) {
1676: pdf.setSigFlags(f);
1677: }
1678:
1679: // [C9] Metadata
1680:
1681: /** XMP Metadata for the document. */
1682: protected byte[] xmpMetadata = null;
1683:
1684: /**
1685: * Use this method to set the XMP Metadata.
1686: * @param xmpMetadata The xmpMetadata to set.
1687: */
1688: public void setXmpMetadata(byte[] xmpMetadata) {
1689: this .xmpMetadata = xmpMetadata;
1690: }
1691:
1692: /**
1693: * Use this method to set the XMP Metadata for each page.
1694: * @param xmpMetadata The xmpMetadata to set.
1695: */
1696: public void setPageXmpMetadata(byte[] xmpMetadata) {
1697: pdf.setXmpMetadata(xmpMetadata);
1698: }
1699:
1700: /**
1701: * Use this method to creates XMP Metadata based
1702: * on the metadata in the PdfDocument.
1703: */
1704: public void createXmpMetadata() {
1705: setXmpMetadata(createXmpMetadataBytes());
1706: }
1707:
1708: /**
1709: * @return an XmpMetadata byte array
1710: */
1711: private byte[] createXmpMetadataBytes() {
1712: ByteArrayOutputStream baos = new ByteArrayOutputStream();
1713: try {
1714: XmpWriter xmp = new XmpWriter(baos, pdf.getInfo(),
1715: pdfxConformance.getPDFXConformance());
1716: xmp.close();
1717: } catch (IOException ioe) {
1718: ioe.printStackTrace();
1719: }
1720: return baos.toByteArray();
1721: }
1722:
1723: // [C10] PDFX Conformance
1724: /** A PDF/X level. */
1725: public static final int PDFXNONE = 0;
1726: /** A PDF/X level. */
1727: public static final int PDFX1A2001 = 1;
1728: /** A PDF/X level. */
1729: public static final int PDFX32002 = 2;
1730: /** PDFA-1A level. */
1731: public static final int PDFA1A = 3;
1732: /** PDFA-1B level. */
1733: public static final int PDFA1B = 4;
1734:
1735: /** Stores the PDF/X level. */
1736: private PdfXConformanceImp pdfxConformance = new PdfXConformanceImp();
1737:
1738: /** @see com.lowagie.text.pdf.interfaces.PdfXConformance#setPDFXConformance(int) */
1739: public void setPDFXConformance(int pdfx) {
1740: if (pdfxConformance.getPDFXConformance() == pdfx)
1741: return;
1742: if (pdf.isOpen())
1743: throw new PdfXConformanceException(
1744: "PDFX conformance can only be set before opening the document.");
1745: if (crypto != null)
1746: throw new PdfXConformanceException(
1747: "A PDFX conforming document cannot be encrypted.");
1748: if (pdfx != PDFXNONE)
1749: setPdfVersion(VERSION_1_3);
1750: pdfxConformance.setPDFXConformance(pdfx);
1751: }
1752:
1753: /** @see com.lowagie.text.pdf.interfaces.PdfXConformance#getPDFXConformance() */
1754: public int getPDFXConformance() {
1755: return pdfxConformance.getPDFXConformance();
1756: }
1757:
1758: /** @see com.lowagie.text.pdf.interfaces.PdfXConformance#isPdfX() */
1759: public boolean isPdfX() {
1760: return pdfxConformance.isPdfX();
1761: }
1762:
1763: // [C11] Output intents
1764:
1765: /**
1766: * Use this method to set the values of the output intent dictionary.
1767: * Null values are allowed to suppress any key.
1768: * @param outputConditionIdentifier a value
1769: * @param outputCondition a value
1770: * @param registryName a value
1771: * @param info a value
1772: * @param destOutputProfile a value
1773: * @throws IOException on error
1774: */
1775: public void setOutputIntents(String outputConditionIdentifier,
1776: String outputCondition, String registryName, String info,
1777: byte destOutputProfile[]) throws IOException {
1778: getExtraCatalog();
1779: PdfDictionary out = new PdfDictionary(PdfName.OUTPUTINTENT);
1780: if (outputCondition != null)
1781: out.put(PdfName.OUTPUTCONDITION, new PdfString(
1782: outputCondition, PdfObject.TEXT_UNICODE));
1783: if (outputConditionIdentifier != null)
1784: out.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString(
1785: outputConditionIdentifier, PdfObject.TEXT_UNICODE));
1786: if (registryName != null)
1787: out.put(PdfName.REGISTRYNAME, new PdfString(registryName,
1788: PdfObject.TEXT_UNICODE));
1789: if (info != null)
1790: out.put(PdfName.INFO, new PdfString(info,
1791: PdfObject.TEXT_UNICODE));
1792: if (destOutputProfile != null) {
1793: PdfStream stream = new PdfStream(destOutputProfile);
1794: stream.flateCompress();
1795: out.put(PdfName.DESTOUTPUTPROFILE, addToBody(stream)
1796: .getIndirectReference());
1797: }
1798: out.put(PdfName.S, PdfName.GTS_PDFX);
1799: extraCatalog.put(PdfName.OUTPUTINTENTS, new PdfArray(out));
1800: }
1801:
1802: /**
1803: * Use this method to copy the output intent dictionary
1804: * from another document to this one.
1805: * @param reader the other document
1806: * @param checkExistence <CODE>true</CODE> to just check for the existence of a valid output intent
1807: * dictionary, <CODE>false</CODE> to insert the dictionary if it exists
1808: * @throws IOException on error
1809: * @return <CODE>true</CODE> if the output intent dictionary exists, <CODE>false</CODE>
1810: * otherwise
1811: */
1812: public boolean setOutputIntents(PdfReader reader,
1813: boolean checkExistence) throws IOException {
1814: PdfDictionary catalog = reader.getCatalog();
1815: PdfArray outs = (PdfArray) PdfReader.getPdfObject(catalog
1816: .get(PdfName.OUTPUTINTENTS));
1817: if (outs == null)
1818: return false;
1819: ArrayList arr = outs.getArrayList();
1820: if (arr.isEmpty())
1821: return false;
1822: PdfDictionary out = (PdfDictionary) PdfReader
1823: .getPdfObject((PdfObject) arr.get(0));
1824: PdfObject obj = PdfReader.getPdfObject(out.get(PdfName.S));
1825: if (obj == null || !PdfName.GTS_PDFX.equals(obj))
1826: return false;
1827: if (checkExistence)
1828: return true;
1829: PRStream stream = (PRStream) PdfReader.getPdfObject(out
1830: .get(PdfName.DESTOUTPUTPROFILE));
1831: byte destProfile[] = null;
1832: if (stream != null) {
1833: destProfile = PdfReader.getStreamBytes(stream);
1834: }
1835: setOutputIntents(getNameString(out,
1836: PdfName.OUTPUTCONDITIONIDENTIFIER), getNameString(out,
1837: PdfName.OUTPUTCONDITION), getNameString(out,
1838: PdfName.REGISTRYNAME),
1839: getNameString(out, PdfName.INFO), destProfile);
1840: return true;
1841: }
1842:
1843: private static String getNameString(PdfDictionary dic, PdfName key) {
1844: PdfObject obj = PdfReader.getPdfObject(dic.get(key));
1845: if (obj == null || !obj.isString())
1846: return null;
1847: return ((PdfString) obj).toUnicodeString();
1848: }
1849:
1850: // PDF Objects that have an impact on the PDF body
1851:
1852: // [F1] PdfEncryptionSettings interface
1853:
1854: // types of encryption
1855:
1856: /** Type of encryption */
1857: public static final int STANDARD_ENCRYPTION_40 = 0;
1858: /** Type of encryption */
1859: public static final int STANDARD_ENCRYPTION_128 = 1;
1860: /** Type of encryption */
1861: public static final int ENCRYPTION_AES_128 = 2;
1862: /** Mask to separate the encryption type from the encryption mode. */
1863: static final int ENCRYPTION_MASK = 7;
1864: /** Add this to the mode to keep the metadata in clear text */
1865: public static final int DO_NOT_ENCRYPT_METADATA = 8;
1866:
1867: // permissions
1868:
1869: /** The operation permitted when the document is opened with the user password */
1870: public static final int AllowPrinting = 4 + 2048;
1871: /** The operation permitted when the document is opened with the user password */
1872: public static final int AllowModifyContents = 8;
1873: /** The operation permitted when the document is opened with the user password */
1874: public static final int AllowCopy = 16;
1875: /** The operation permitted when the document is opened with the user password */
1876: public static final int AllowModifyAnnotations = 32;
1877: /** The operation permitted when the document is opened with the user password */
1878: public static final int AllowFillIn = 256;
1879: /** The operation permitted when the document is opened with the user password */
1880: public static final int AllowScreenReaders = 512;
1881: /** The operation permitted when the document is opened with the user password */
1882: public static final int AllowAssembly = 1024;
1883: /** The operation permitted when the document is opened with the user password */
1884: public static final int AllowDegradedPrinting = 4;
1885:
1886: // Strength of the encryption (kept for historical reasons)
1887: /** Type of standard encryption strength*/
1888: public static final boolean STRENGTH40BITS = false;
1889: /** Type of standard encryption strength */
1890: public static final boolean STRENGTH128BITS = true;
1891:
1892: /** Contains the business logic for cryptography. */
1893: protected PdfEncryption crypto;
1894:
1895: PdfEncryption getEncryption() {
1896: return crypto;
1897: }
1898:
1899: /** @see com.lowagie.text.pdf.interfaces.PdfEncryptionSettings#setEncryption(byte[], byte[], int, int) */
1900: public void setEncryption(byte userPassword[],
1901: byte ownerPassword[], int permissions, int encryptionType)
1902: throws DocumentException {
1903: if (pdf.isOpen())
1904: throw new DocumentException(
1905: "Encryption can only be added before opening the document.");
1906: crypto = new PdfEncryption();
1907: crypto.setCryptoMode(encryptionType, 0);
1908: crypto.setupAllKeys(userPassword, ownerPassword, permissions);
1909: }
1910:
1911: /** @see com.lowagie.text.pdf.interfaces.PdfEncryptionSettings#setEncryption(java.security.cert.Certificate[], int[], int) */
1912: public void setEncryption(Certificate[] certs, int[] permissions,
1913: int encryptionType) throws DocumentException {
1914: if (pdf.isOpen())
1915: throw new DocumentException(
1916: "Encryption can only be added before opening the document.");
1917: crypto = new PdfEncryption();
1918: if (certs != null) {
1919: for (int i = 0; i < certs.length; i++) {
1920: crypto.addRecipient(certs[i], permissions[i]);
1921: }
1922: }
1923: crypto.setCryptoMode(encryptionType, 0);
1924: crypto.getEncryptionDictionary();
1925: }
1926:
1927: /**
1928: * Sets the encryption options for this document. The userPassword and the
1929: * ownerPassword can be null or have zero length. In this case the ownerPassword
1930: * is replaced by a random string. The open permissions for the document can be
1931: * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
1932: * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
1933: * The permissions can be combined by ORing them.
1934: * @param userPassword the user password. Can be null or empty
1935: * @param ownerPassword the owner password. Can be null or empty
1936: * @param permissions the user permissions
1937: * @param strength128Bits <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
1938: * @throws DocumentException if the document is already open
1939: * @deprecated use the methods described in the PdfEncryptionSettings interface
1940: */
1941: public void setEncryption(byte userPassword[],
1942: byte ownerPassword[], int permissions,
1943: boolean strength128Bits) throws DocumentException {
1944: setEncryption(userPassword, ownerPassword, permissions,
1945: strength128Bits ? STANDARD_ENCRYPTION_128
1946: : STANDARD_ENCRYPTION_40);
1947: }
1948:
1949: /**
1950: * Sets the encryption options for this document. The userPassword and the
1951: * ownerPassword can be null or have zero length. In this case the ownerPassword
1952: * is replaced by a random string. The open permissions for the document can be
1953: * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
1954: * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
1955: * The permissions can be combined by ORing them.
1956: * @param strength <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
1957: * @param userPassword the user password. Can be null or empty
1958: * @param ownerPassword the owner password. Can be null or empty
1959: * @param permissions the user permissions
1960: * @throws DocumentException if the document is already open
1961: * @deprecated use the methods described in the PdfEncryptionSettings interface
1962: */
1963: public void setEncryption(boolean strength, String userPassword,
1964: String ownerPassword, int permissions)
1965: throws DocumentException {
1966: setEncryption(getISOBytes(userPassword),
1967: getISOBytes(ownerPassword), permissions,
1968: strength ? STANDARD_ENCRYPTION_128
1969: : STANDARD_ENCRYPTION_40);
1970: }
1971:
1972: /**
1973: * Sets the encryption options for this document. The userPassword and the
1974: * ownerPassword can be null or have zero length. In this case the ownerPassword
1975: * is replaced by a random string. The open permissions for the document can be
1976: * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
1977: * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
1978: * The permissions can be combined by ORing them.
1979: * @param encryptionType the type of encryption. It can be one of STANDARD_ENCRYPTION_40, STANDARD_ENCRYPTION_128 or ENCRYPTION_AES128.
1980: * Optionally DO_NOT_ENCRYPT_METADATA can be ored to output the metadata in cleartext
1981: * @param userPassword the user password. Can be null or empty
1982: * @param ownerPassword the owner password. Can be null or empty
1983: * @param permissions the user permissions
1984: * @throws DocumentException if the document is already open
1985: * @deprecated use the methods described in the PdfEncryptionSettings interface
1986: */
1987: public void setEncryption(int encryptionType, String userPassword,
1988: String ownerPassword, int permissions)
1989: throws DocumentException {
1990: setEncryption(getISOBytes(userPassword),
1991: getISOBytes(ownerPassword), permissions, encryptionType);
1992: }
1993:
1994: // [F2] compression
1995:
1996: /** Holds value of property fullCompression. */
1997: protected boolean fullCompression = false;
1998:
1999: /**
2000: * Use this method to find out if 1.5 compression is on.
2001: * @return the 1.5 compression status
2002: */
2003: public boolean isFullCompression() {
2004: return this .fullCompression;
2005: }
2006:
2007: /**
2008: * Use this method to set the document's compression to the
2009: * PDF 1.5 mode with object streams and xref streams.
2010: * It can be set at any time but once set it can't be unset.
2011: * <p>
2012: * If set before opening the document it will also set the pdf version to 1.5.
2013: */
2014: public void setFullCompression() {
2015: this .fullCompression = true;
2016: setAtLeastPdfVersion(VERSION_1_5);
2017: }
2018:
2019: // [F3] adding fonts
2020:
2021: /** The fonts of this document */
2022: protected HashMap documentFonts = new HashMap();
2023:
2024: /** The font number counter for the fonts in the document. */
2025: protected int fontNumber = 1;
2026:
2027: /**
2028: * Adds a <CODE>BaseFont</CODE> to the document but not to the page resources.
2029: * It is used for templates.
2030: * @param bf the <CODE>BaseFont</CODE> to add
2031: * @return an <CODE>Object[]</CODE> where position 0 is a <CODE>PdfName</CODE>
2032: * and position 1 is an <CODE>PdfIndirectReference</CODE>
2033: */
2034:
2035: FontDetails addSimple(BaseFont bf) {
2036: if (bf.getFontType() == BaseFont.FONT_TYPE_DOCUMENT) {
2037: return new FontDetails(new PdfName("F" + (fontNumber++)),
2038: ((DocumentFont) bf).getIndirectReference(), bf);
2039: }
2040: FontDetails ret = (FontDetails) documentFonts.get(bf);
2041: if (ret == null) {
2042: PdfXConformanceImp.checkPDFXConformance(this ,
2043: PdfXConformanceImp.PDFXKEY_FONT, bf);
2044: ret = new FontDetails(new PdfName("F" + (fontNumber++)),
2045: body.getPdfIndirectReference(), bf);
2046: documentFonts.put(bf, ret);
2047: }
2048: return ret;
2049: }
2050:
2051: void eliminateFontSubset(PdfDictionary fonts) {
2052: for (Iterator it = documentFonts.values().iterator(); it
2053: .hasNext();) {
2054: FontDetails ft = (FontDetails) it.next();
2055: if (fonts.get(ft.getFontName()) != null)
2056: ft.setSubset(false);
2057: }
2058: }
2059:
2060: // [F4] adding (and releasing) form XObjects
2061:
2062: /** The form XObjects in this document. The key is the xref and the value
2063: is Object[]{PdfName, template}.*/
2064: protected HashMap formXObjects = new HashMap();
2065:
2066: /** The name counter for the form XObjects name. */
2067: protected int formXObjectsCounter = 1;
2068:
2069: /**
2070: * Adds a template to the document but not to the page resources.
2071: * @param template the template to add
2072: * @param forcedName the template name, rather than a generated one. Can be null
2073: * @return the <CODE>PdfName</CODE> for this template
2074: */
2075:
2076: PdfName addDirectTemplateSimple(PdfTemplate template,
2077: PdfName forcedName) {
2078: PdfIndirectReference ref = template.getIndirectReference();
2079: Object obj[] = (Object[]) formXObjects.get(ref);
2080: PdfName name = null;
2081: try {
2082: if (obj == null) {
2083: if (forcedName == null) {
2084: name = new PdfName("Xf" + formXObjectsCounter);
2085: ++formXObjectsCounter;
2086: } else
2087: name = forcedName;
2088: if (template.getType() == PdfTemplate.TYPE_IMPORTED)
2089: template = null;
2090: formXObjects.put(ref, new Object[] { name, template });
2091: } else
2092: name = (PdfName) obj[0];
2093: } catch (Exception e) {
2094: throw new ExceptionConverter(e);
2095: }
2096: return name;
2097: }
2098:
2099: /**
2100: * Use this method to releases the memory used by a template.
2101: * This method writes the template to the output.
2102: * The template can still be added to any content
2103: * but changes to the template itself won't have any effect.
2104: * @param tp the template to release
2105: * @throws IOException on error
2106: */
2107: public void releaseTemplate(PdfTemplate tp) throws IOException {
2108: PdfIndirectReference ref = tp.getIndirectReference();
2109: Object[] objs = (Object[]) formXObjects.get(ref);
2110: if (objs == null || objs[1] == null)
2111: return;
2112: PdfTemplate template = (PdfTemplate) objs[1];
2113: if (template.getIndirectReference() instanceof PRIndirectReference)
2114: return;
2115: if (template.getType() == PdfTemplate.TYPE_TEMPLATE) {
2116: addToBody(template.getFormXObject(), template
2117: .getIndirectReference());
2118: objs[1] = null;
2119: }
2120: }
2121:
2122: // [F5] adding pages imported form other PDF documents
2123:
2124: protected HashMap importedPages = new HashMap();
2125:
2126: /**
2127: * Use this method to get a page from other PDF document.
2128: * The page can be used as any other PdfTemplate.
2129: * Note that calling this method more than once with the same parameters
2130: * will retrieve the same object.
2131: * @param reader the PDF document where the page is
2132: * @param pageNumber the page number. The first page is 1
2133: * @return the template representing the imported page
2134: */
2135: public PdfImportedPage getImportedPage(PdfReader reader,
2136: int pageNumber) {
2137: PdfReaderInstance inst = (PdfReaderInstance) importedPages
2138: .get(reader);
2139: if (inst == null) {
2140: inst = reader.getPdfReaderInstance(this );
2141: importedPages.put(reader, inst);
2142: }
2143: return inst.getImportedPage(pageNumber);
2144: }
2145:
2146: /**
2147: * Use this method to writes the reader to the document
2148: * and free the memory used by it.
2149: * The main use is when concatenating multiple documents
2150: * to keep the memory usage restricted to the current
2151: * appending document.
2152: * @param reader the <CODE>PdfReader</CODE> to free
2153: * @throws IOException on error
2154: */
2155: public void freeReader(PdfReader reader) throws IOException {
2156: currentPdfReaderInstance = (PdfReaderInstance) importedPages
2157: .get(reader);
2158: if (currentPdfReaderInstance == null)
2159: return;
2160: currentPdfReaderInstance.writeAllPages();
2161: currentPdfReaderInstance = null;
2162: importedPages.remove(reader);
2163: }
2164:
2165: /**
2166: * Use this method to gets the current document size.
2167: * This size only includes the data already writen
2168: * to the output stream, it does not include templates or fonts.
2169: * It is usefull if used with <CODE>freeReader()</CODE>
2170: * when concatenating many documents and an idea of
2171: * the current size is needed.
2172: * @return the approximate size without fonts or templates
2173: */
2174: public int getCurrentDocumentSize() {
2175: return body.offset() + body.size() * 20 + 0x48;
2176: }
2177:
2178: protected PdfReaderInstance currentPdfReaderInstance;
2179:
2180: protected int getNewObjectNumber(PdfReader reader, int number,
2181: int generation) {
2182: return currentPdfReaderInstance.getNewObjectNumber(number,
2183: generation);
2184: }
2185:
2186: RandomAccessFileOrArray getReaderFile(PdfReader reader) {
2187: return currentPdfReaderInstance.getReaderFile();
2188: }
2189:
2190: // [F6] spot colors
2191:
2192: /** The colors of this document */
2193: protected HashMap documentColors = new HashMap();
2194:
2195: /** The color number counter for the colors in the document. */
2196: protected int colorNumber = 1;
2197:
2198: PdfName getColorspaceName() {
2199: return new PdfName("CS" + (colorNumber++));
2200: }
2201:
2202: /**
2203: * Adds a <CODE>SpotColor</CODE> to the document but not to the page resources.
2204: * @param spc the <CODE>SpotColor</CODE> to add
2205: * @return an <CODE>Object[]</CODE> where position 0 is a <CODE>PdfName</CODE>
2206: * and position 1 is an <CODE>PdfIndirectReference</CODE>
2207: */
2208: ColorDetails addSimple(PdfSpotColor spc) {
2209: ColorDetails ret = (ColorDetails) documentColors.get(spc);
2210: if (ret == null) {
2211: ret = new ColorDetails(getColorspaceName(), body
2212: .getPdfIndirectReference(), spc);
2213: documentColors.put(spc, ret);
2214: }
2215: return ret;
2216: }
2217:
2218: // [F7] document patterns
2219:
2220: /** The patterns of this document */
2221: protected HashMap documentPatterns = new HashMap();
2222:
2223: /** The patten number counter for the colors in the document. */
2224: protected int patternNumber = 1;
2225:
2226: PdfName addSimplePattern(PdfPatternPainter painter) {
2227: PdfName name = (PdfName) documentPatterns.get(painter);
2228: try {
2229: if (name == null) {
2230: name = new PdfName("P" + patternNumber);
2231: ++patternNumber;
2232: documentPatterns.put(painter, name);
2233: }
2234: } catch (Exception e) {
2235: throw new ExceptionConverter(e);
2236: }
2237: return name;
2238: }
2239:
2240: // [F8] shading patterns
2241:
2242: protected HashMap documentShadingPatterns = new HashMap();
2243:
2244: void addSimpleShadingPattern(PdfShadingPattern shading) {
2245: if (!documentShadingPatterns.containsKey(shading)) {
2246: shading.setName(patternNumber);
2247: ++patternNumber;
2248: documentShadingPatterns.put(shading, null);
2249: addSimpleShading(shading.getShading());
2250: }
2251: }
2252:
2253: // [F9] document shadings
2254:
2255: protected HashMap documentShadings = new HashMap();
2256:
2257: void addSimpleShading(PdfShading shading) {
2258: if (!documentShadings.containsKey(shading)) {
2259: documentShadings.put(shading, null);
2260: shading.setName(documentShadings.size());
2261: }
2262: }
2263:
2264: // [F10] extended graphics state (for instance for transparency)
2265:
2266: protected HashMap documentExtGState = new HashMap();
2267:
2268: PdfObject[] addSimpleExtGState(PdfDictionary gstate) {
2269: if (!documentExtGState.containsKey(gstate)) {
2270: PdfXConformanceImp.checkPDFXConformance(this ,
2271: PdfXConformanceImp.PDFXKEY_GSTATE, gstate);
2272: documentExtGState.put(gstate, new PdfObject[] {
2273: new PdfName("GS" + (documentExtGState.size() + 1)),
2274: getPdfIndirectReference() });
2275: }
2276: return (PdfObject[]) documentExtGState.get(gstate);
2277: }
2278:
2279: // [F11] adding properties (OCG, marked content)
2280:
2281: protected HashMap documentProperties = new HashMap();
2282:
2283: PdfObject[] addSimpleProperty(Object prop, PdfIndirectReference refi) {
2284: if (!documentProperties.containsKey(prop)) {
2285: if (prop instanceof PdfOCG)
2286: PdfXConformanceImp.checkPDFXConformance(this ,
2287: PdfXConformanceImp.PDFXKEY_LAYER, null);
2288: documentProperties.put(prop,
2289: new PdfObject[] {
2290: new PdfName("Pr"
2291: + (documentProperties.size() + 1)),
2292: refi });
2293: }
2294: return (PdfObject[]) documentProperties.get(prop);
2295: }
2296:
2297: boolean propertyExists(Object prop) {
2298: return documentProperties.containsKey(prop);
2299: }
2300:
2301: // [F12] tagged PDF
2302:
2303: protected boolean tagged = false;
2304: protected PdfStructureTreeRoot structureTreeRoot;
2305:
2306: /**
2307: * Mark this document for tagging. It must be called before open.
2308: */
2309: public void setTagged() {
2310: if (open)
2311: throw new IllegalArgumentException(
2312: "Tagging must be set before opening the document.");
2313: tagged = true;
2314: }
2315:
2316: /**
2317: * Check if the document is marked for tagging.
2318: * @return <CODE>true</CODE> if the document is marked for tagging
2319: */
2320: public boolean isTagged() {
2321: return tagged;
2322: }
2323:
2324: /**
2325: * Gets the structure tree root. If the document is not marked for tagging it will return <CODE>null</CODE>.
2326: * @return the structure tree root
2327: */
2328: public PdfStructureTreeRoot getStructureTreeRoot() {
2329: if (tagged && structureTreeRoot == null)
2330: structureTreeRoot = new PdfStructureTreeRoot(this );
2331: return structureTreeRoot;
2332: }
2333:
2334: // [F13] Optional Content Groups
2335: protected HashSet documentOCG = new HashSet();
2336: protected ArrayList documentOCGorder = new ArrayList();
2337: protected PdfOCProperties OCProperties;
2338: protected PdfArray OCGRadioGroup = new PdfArray();
2339:
2340: /**
2341: * Use this method to get the <B>Optional Content Properties Dictionary</B>.
2342: * Each call fills the dictionary with the current layer state.
2343: * It's advisable to only call this method right before close
2344: * and do any modifications at that time.
2345: * @return the Optional Content Properties Dictionary
2346: */
2347: public PdfOCProperties getOCProperties() {
2348: fillOCProperties(true);
2349: return OCProperties;
2350: }
2351:
2352: /**
2353: * Use this method to set a collection of optional content groups
2354: * whose states are intended to follow a "radio button" paradigm.
2355: * That is, the state of at most one optional content group
2356: * in the array should be ON at a time: if one group is turned
2357: * ON, all others must be turned OFF.
2358: * @param group the radio group
2359: */
2360: public void addOCGRadioGroup(ArrayList group) {
2361: PdfArray ar = new PdfArray();
2362: for (int k = 0; k < group.size(); ++k) {
2363: PdfLayer layer = (PdfLayer) group.get(k);
2364: if (layer.getTitle() == null)
2365: ar.add(layer.getRef());
2366: }
2367: if (ar.size() == 0)
2368: return;
2369: OCGRadioGroup.add(ar);
2370: }
2371:
2372: private static void getOCGOrder(PdfArray order, PdfLayer layer) {
2373: if (!layer.isOnPanel())
2374: return;
2375: if (layer.getTitle() == null)
2376: order.add(layer.getRef());
2377: ArrayList children = layer.getChildren();
2378: if (children == null)
2379: return;
2380: PdfArray kids = new PdfArray();
2381: if (layer.getTitle() != null)
2382: kids.add(new PdfString(layer.getTitle(),
2383: PdfObject.TEXT_UNICODE));
2384: for (int k = 0; k < children.size(); ++k) {
2385: getOCGOrder(kids, (PdfLayer) children.get(k));
2386: }
2387: if (kids.size() > 0)
2388: order.add(kids);
2389: }
2390:
2391: private void addASEvent(PdfName event, PdfName category) {
2392: PdfArray arr = new PdfArray();
2393: for (Iterator it = documentOCG.iterator(); it.hasNext();) {
2394: PdfLayer layer = (PdfLayer) it.next();
2395: PdfDictionary usage = (PdfDictionary) layer
2396: .get(PdfName.USAGE);
2397: if (usage != null && usage.get(category) != null)
2398: arr.add(layer.getRef());
2399: }
2400: if (arr.size() == 0)
2401: return;
2402: PdfDictionary d = (PdfDictionary) OCProperties.get(PdfName.D);
2403: PdfArray arras = (PdfArray) d.get(PdfName.AS);
2404: if (arras == null) {
2405: arras = new PdfArray();
2406: d.put(PdfName.AS, arras);
2407: }
2408: PdfDictionary as = new PdfDictionary();
2409: as.put(PdfName.EVENT, event);
2410: as.put(PdfName.CATEGORY, new PdfArray(category));
2411: as.put(PdfName.OCGS, arr);
2412: arras.add(as);
2413: }
2414:
2415: private void fillOCProperties(boolean erase) {
2416: if (OCProperties == null)
2417: OCProperties = new PdfOCProperties();
2418: if (erase) {
2419: OCProperties.remove(PdfName.OCGS);
2420: OCProperties.remove(PdfName.D);
2421: }
2422: if (OCProperties.get(PdfName.OCGS) == null) {
2423: PdfArray gr = new PdfArray();
2424: for (Iterator it = documentOCG.iterator(); it.hasNext();) {
2425: PdfLayer layer = (PdfLayer) it.next();
2426: gr.add(layer.getRef());
2427: }
2428: OCProperties.put(PdfName.OCGS, gr);
2429: }
2430: if (OCProperties.get(PdfName.D) != null)
2431: return;
2432: ArrayList docOrder = new ArrayList(documentOCGorder);
2433: for (Iterator it = docOrder.iterator(); it.hasNext();) {
2434: PdfLayer layer = (PdfLayer) it.next();
2435: if (layer.getParent() != null)
2436: it.remove();
2437: }
2438: PdfArray order = new PdfArray();
2439: for (Iterator it = docOrder.iterator(); it.hasNext();) {
2440: PdfLayer layer = (PdfLayer) it.next();
2441: getOCGOrder(order, layer);
2442: }
2443: PdfDictionary d = new PdfDictionary();
2444: OCProperties.put(PdfName.D, d);
2445: d.put(PdfName.ORDER, order);
2446: PdfArray gr = new PdfArray();
2447: for (Iterator it = documentOCG.iterator(); it.hasNext();) {
2448: PdfLayer layer = (PdfLayer) it.next();
2449: if (!layer.isOn())
2450: gr.add(layer.getRef());
2451: }
2452: if (gr.size() > 0)
2453: d.put(PdfName.OFF, gr);
2454: if (OCGRadioGroup.size() > 0)
2455: d.put(PdfName.RBGROUPS, OCGRadioGroup);
2456: addASEvent(PdfName.VIEW, PdfName.ZOOM);
2457: addASEvent(PdfName.VIEW, PdfName.VIEW);
2458: addASEvent(PdfName.PRINT, PdfName.PRINT);
2459: addASEvent(PdfName.EXPORT, PdfName.EXPORT);
2460: d.put(PdfName.LISTMODE, PdfName.VISIBLEPAGES);
2461: }
2462:
2463: void registerLayer(PdfOCG layer) {
2464: PdfXConformanceImp.checkPDFXConformance(this ,
2465: PdfXConformanceImp.PDFXKEY_LAYER, null);
2466: if (layer instanceof PdfLayer) {
2467: PdfLayer la = (PdfLayer) layer;
2468: if (la.getTitle() == null) {
2469: if (!documentOCG.contains(layer)) {
2470: documentOCG.add(layer);
2471: documentOCGorder.add(layer);
2472: }
2473: } else {
2474: documentOCGorder.add(layer);
2475: }
2476: } else
2477: throw new IllegalArgumentException(
2478: "Only PdfLayer is accepted.");
2479: }
2480:
2481: // User methods to change aspects of the page
2482:
2483: // [U1] page size
2484:
2485: /**
2486: * Use this method to get the size of the media box.
2487: * @return a Rectangle
2488: */
2489: public Rectangle getPageSize() {
2490: return pdf.getPageSize();
2491: }
2492:
2493: /**
2494: * Use this method to set the crop box.
2495: * The crop box should not be rotated even if the page is rotated.
2496: * This change only takes effect in the next page.
2497: * @param crop the crop box
2498: */
2499: public void setCropBoxSize(Rectangle crop) {
2500: pdf.setCropBoxSize(crop);
2501: }
2502:
2503: /**
2504: * Use this method to set the page box sizes.
2505: * Allowed names are: "crop", "trim", "art" and "bleed".
2506: * @param boxName the box size
2507: * @param size the size
2508: */
2509: public void setBoxSize(String boxName, Rectangle size) {
2510: pdf.setBoxSize(boxName, size);
2511: }
2512:
2513: /**
2514: * Use this method to get the size of a trim, art, crop or bleed box,
2515: * or null if not defined.
2516: * @param boxName crop, trim, art or bleed
2517: */
2518: public Rectangle getBoxSize(String boxName) {
2519: return pdf.getBoxSize(boxName);
2520: }
2521:
2522: // [U2] take care of empty pages
2523:
2524: /**
2525: * Use this method to make sure a page is added,
2526: * even if it's empty. If you use setPageEmpty(false),
2527: * invoking newPage() after a blank page will add a newPage.
2528: * @param pageEmpty the state
2529: */
2530: public void setPageEmpty(boolean pageEmpty) {
2531: pdf.setPageEmpty(pageEmpty);
2532: }
2533:
2534: // [U3] page actions (open and close)
2535:
2536: /** action value */
2537: public static final PdfName PAGE_OPEN = PdfName.O;
2538: /** action value */
2539: public static final PdfName PAGE_CLOSE = PdfName.C;
2540:
2541: /** @see com.lowagie.text.pdf.interfaces.PdfPageActions#setPageAction(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfAction) */
2542: public void setPageAction(PdfName actionType, PdfAction action)
2543: throws DocumentException {
2544: if (!actionType.equals(PAGE_OPEN)
2545: && !actionType.equals(PAGE_CLOSE))
2546: throw new DocumentException(
2547: "Invalid page additional action type: "
2548: + actionType.toString());
2549: pdf.setPageAction(actionType, action);
2550: }
2551:
2552: /** @see com.lowagie.text.pdf.interfaces.PdfPageActions#setDuration(int) */
2553: public void setDuration(int seconds) {
2554: pdf.setDuration(seconds);
2555: }
2556:
2557: /** @see com.lowagie.text.pdf.interfaces.PdfPageActions#setTransition(com.lowagie.text.pdf.PdfTransition) */
2558: public void setTransition(PdfTransition transition) {
2559: pdf.setTransition(transition);
2560: }
2561:
2562: // [U4] Thumbnail image
2563:
2564: /**
2565: * Use this method to set the thumbnail image for the current page.
2566: * @param image the image
2567: * @throws PdfException on error
2568: * @throws DocumentException or error
2569: */
2570: public void setThumbnail(Image image) throws PdfException,
2571: DocumentException {
2572: pdf.setThumbnail(image);
2573: }
2574:
2575: // [U5] Transparency groups
2576:
2577: /**
2578: * A group attributes dictionary specifying the attributes
2579: * of the page's page group for use in the transparent
2580: * imaging model
2581: */
2582: protected PdfDictionary group;
2583:
2584: /**
2585: * Use this method to get the group dictionary.
2586: * @return Value of property group.
2587: */
2588: public PdfDictionary getGroup() {
2589: return this .group;
2590: }
2591:
2592: /**
2593: * Use this method to set the group dictionary.
2594: * @param group New value of property group.
2595: */
2596: public void setGroup(PdfDictionary group) {
2597: this .group = group;
2598: }
2599:
2600: // [U6] space char ratio
2601:
2602: /** The default space-char ratio. */
2603: public static final float SPACE_CHAR_RATIO_DEFAULT = 2.5f;
2604: /** Disable the inter-character spacing. */
2605: public static final float NO_SPACE_CHAR_RATIO = 10000000f;
2606:
2607: /**
2608: * The ratio between the extra word spacing and the extra character spacing.
2609: * Extra word spacing will grow <CODE>ratio</CODE> times more than extra character spacing.
2610: */
2611: private float spaceCharRatio = SPACE_CHAR_RATIO_DEFAULT;
2612:
2613: /**
2614: * Use this method to gets the space/character extra spacing ratio
2615: * for fully justified text.
2616: * @return the space/character extra spacing ratio
2617: */
2618: public float getSpaceCharRatio() {
2619: return spaceCharRatio;
2620: }
2621:
2622: /**
2623: * Use this method to set the ratio between the extra word spacing and
2624: * the extra character spacing when the text is fully justified.
2625: * Extra word spacing will grow <CODE>spaceCharRatio</CODE> times more
2626: * than extra character spacing. If the ratio is <CODE>PdfWriter.NO_SPACE_CHAR_RATIO</CODE>
2627: * then the extra character spacing will be zero.
2628: * @param spaceCharRatio the ratio between the extra word spacing and the extra character spacing
2629: */
2630: public void setSpaceCharRatio(float spaceCharRatio) {
2631: if (spaceCharRatio < 0.001f)
2632: this .spaceCharRatio = 0.001f;
2633: else
2634: this .spaceCharRatio = spaceCharRatio;
2635: }
2636:
2637: // [U7] run direction (doesn't actually do anything)
2638:
2639: /** Use the default run direction. */
2640: public static final int RUN_DIRECTION_DEFAULT = 0;
2641: /** Do not use bidirectional reordering. */
2642: public static final int RUN_DIRECTION_NO_BIDI = 1;
2643: /** Use bidirectional reordering with left-to-right
2644: * preferential run direction.
2645: */
2646: public static final int RUN_DIRECTION_LTR = 2;
2647: /** Use bidirectional reordering with right-to-left
2648: * preferential run direction.
2649: */
2650: public static final int RUN_DIRECTION_RTL = 3;
2651:
2652: protected int runDirection = RUN_DIRECTION_NO_BIDI;
2653:
2654: /**
2655: * Use this method to set the run direction.
2656: * This is only used as a placeholder as it does not affect anything.
2657: * @param runDirection the run direction
2658: */
2659: public void setRunDirection(int runDirection) {
2660: if (runDirection < RUN_DIRECTION_NO_BIDI
2661: || runDirection > RUN_DIRECTION_RTL)
2662: throw new RuntimeException("Invalid run direction: "
2663: + runDirection);
2664: this .runDirection = runDirection;
2665: }
2666:
2667: /**
2668: * Use this method to set the run direction.
2669: * @return the run direction
2670: */
2671: public int getRunDirection() {
2672: return runDirection;
2673: }
2674:
2675: // [U8] user units
2676:
2677: protected float userunit = 0f;
2678:
2679: /**
2680: * Use this method to get the user unit.
2681: * A user unit is a value that defines the default user space unit.
2682: * The minimum UserUnit is 1 (1 unit = 1/72 inch).
2683: * The maximum UserUnit is 75,000.
2684: * Note that this userunit only works starting with PDF1.6!
2685: * @return Returns the userunit.
2686: */
2687: public float getUserunit() {
2688: return userunit;
2689: }
2690:
2691: /**
2692: * Use this method to set the user unit.
2693: * A UserUnit is a value that defines the default user space unit.
2694: * The minimum UserUnit is 1 (1 unit = 1/72 inch).
2695: * The maximum UserUnit is 75,000.
2696: * Note that this userunit only works starting with PDF1.6!
2697: * @param userunit The userunit to set.
2698: * @throws DocumentException on error
2699: */
2700: public void setUserunit(float userunit) throws DocumentException {
2701: if (userunit < 1f || userunit > 75000f)
2702: throw new DocumentException(
2703: "UserUnit should be a value between 1 and 75000.");
2704: this .userunit = userunit;
2705: setAtLeastPdfVersion(VERSION_1_6);
2706: }
2707:
2708: // Miscellaneous topics
2709:
2710: // [M1] Color settings
2711:
2712: protected PdfDictionary defaultColorspace = new PdfDictionary();
2713:
2714: /**
2715: * Use this method to get the default colorspaces.
2716: * @return the default colorspaces
2717: */
2718: public PdfDictionary getDefaultColorspace() {
2719: return defaultColorspace;
2720: }
2721:
2722: /**
2723: * Use this method to sets the default colorspace that will be applied
2724: * to all the document. The colorspace is only applied if another colorspace
2725: * with the same name is not present in the content.
2726: * <p>
2727: * The colorspace is applied immediately when creating templates and
2728: * at the page end for the main document content.
2729: * @param key the name of the colorspace. It can be <CODE>PdfName.DEFAULTGRAY</CODE>, <CODE>PdfName.DEFAULTRGB</CODE>
2730: * or <CODE>PdfName.DEFAULTCMYK</CODE>
2731: * @param cs the colorspace. A <CODE>null</CODE> or <CODE>PdfNull</CODE> removes any colorspace with the same name
2732: */
2733: public void setDefaultColorspace(PdfName key, PdfObject cs) {
2734: if (cs == null || cs.isNull())
2735: defaultColorspace.remove(key);
2736: defaultColorspace.put(key, cs);
2737: }
2738:
2739: // [M2] spot patterns
2740:
2741: protected HashMap documentSpotPatterns = new HashMap();
2742: protected ColorDetails patternColorspaceRGB;
2743: protected ColorDetails patternColorspaceGRAY;
2744: protected ColorDetails patternColorspaceCMYK;
2745:
2746: ColorDetails addSimplePatternColorspace(Color color) {
2747: int type = ExtendedColor.getType(color);
2748: if (type == ExtendedColor.TYPE_PATTERN
2749: || type == ExtendedColor.TYPE_SHADING)
2750: throw new RuntimeException(
2751: "An uncolored tile pattern can not have another pattern or shading as color.");
2752: try {
2753: switch (type) {
2754: case ExtendedColor.TYPE_RGB:
2755: if (patternColorspaceRGB == null) {
2756: patternColorspaceRGB = new ColorDetails(
2757: getColorspaceName(), body
2758: .getPdfIndirectReference(), null);
2759: PdfArray array = new PdfArray(PdfName.PATTERN);
2760: array.add(PdfName.DEVICERGB);
2761: addToBody(array, patternColorspaceRGB
2762: .getIndirectReference());
2763: }
2764: return patternColorspaceRGB;
2765: case ExtendedColor.TYPE_CMYK:
2766: if (patternColorspaceCMYK == null) {
2767: patternColorspaceCMYK = new ColorDetails(
2768: getColorspaceName(), body
2769: .getPdfIndirectReference(), null);
2770: PdfArray array = new PdfArray(PdfName.PATTERN);
2771: array.add(PdfName.DEVICECMYK);
2772: addToBody(array, patternColorspaceCMYK
2773: .getIndirectReference());
2774: }
2775: return patternColorspaceCMYK;
2776: case ExtendedColor.TYPE_GRAY:
2777: if (patternColorspaceGRAY == null) {
2778: patternColorspaceGRAY = new ColorDetails(
2779: getColorspaceName(), body
2780: .getPdfIndirectReference(), null);
2781: PdfArray array = new PdfArray(PdfName.PATTERN);
2782: array.add(PdfName.DEVICEGRAY);
2783: addToBody(array, patternColorspaceGRAY
2784: .getIndirectReference());
2785: }
2786: return patternColorspaceGRAY;
2787: case ExtendedColor.TYPE_SEPARATION: {
2788: ColorDetails details = addSimple(((SpotColor) color)
2789: .getPdfSpotColor());
2790: ColorDetails patternDetails = (ColorDetails) documentSpotPatterns
2791: .get(details);
2792: if (patternDetails == null) {
2793: patternDetails = new ColorDetails(
2794: getColorspaceName(), body
2795: .getPdfIndirectReference(), null);
2796: PdfArray array = new PdfArray(PdfName.PATTERN);
2797: array.add(details.getIndirectReference());
2798: addToBody(array, patternDetails
2799: .getIndirectReference());
2800: documentSpotPatterns.put(details, patternDetails);
2801: }
2802: return patternDetails;
2803: }
2804: default:
2805: throw new RuntimeException(
2806: "Invalid color type in PdfWriter.addSimplePatternColorspace().");
2807: }
2808: } catch (Exception e) {
2809: throw new RuntimeException(e.getMessage());
2810: }
2811: }
2812:
2813: // [M3] Images
2814:
2815: /**
2816: * Use this method to get the strictImageSequence status.
2817: * @return value of property strictImageSequence
2818: */
2819: public boolean isStrictImageSequence() {
2820: return pdf.isStrictImageSequence();
2821: }
2822:
2823: /**
2824: * Use this method to set the image sequence, so that it follows
2825: * the text in strict order (or not).
2826: * @param strictImageSequence new value of property strictImageSequence
2827: *
2828: */
2829: public void setStrictImageSequence(boolean strictImageSequence) {
2830: pdf.setStrictImageSequence(strictImageSequence);
2831: }
2832:
2833: /**
2834: * Use this method to clear text wrapping around images (if applicable).
2835: * @throws DocumentException
2836: */
2837: public void clearTextWrap() throws DocumentException {
2838: pdf.clearTextWrap();
2839: }
2840:
2841: /** Dictionary, containing all the images of the PDF document */
2842: protected PdfDictionary imageDictionary = new PdfDictionary();
2843:
2844: /** This is the list with all the images in the document. */
2845: private HashMap images = new HashMap();
2846:
2847: /**
2848: * Use this method to adds an image to the document
2849: * but not to the page resources. It is used with
2850: * templates and <CODE>Document.add(Image)</CODE>.
2851: * Use this method only if you know what you're doing!
2852: * @param image the <CODE>Image</CODE> to add
2853: * @return the name of the image added
2854: * @throws PdfException on error
2855: * @throws DocumentException on error
2856: */
2857: public PdfName addDirectImageSimple(Image image)
2858: throws PdfException, DocumentException {
2859: return addDirectImageSimple(image, null);
2860: }
2861:
2862: /**
2863: * Adds an image to the document but not to the page resources.
2864: * It is used with templates and <CODE>Document.add(Image)</CODE>.
2865: * Use this method only if you know what you're doing!
2866: * @param image the <CODE>Image</CODE> to add
2867: * @param fixedRef the reference to used. It may be <CODE>null</CODE>,
2868: * a <CODE>PdfIndirectReference</CODE> or a <CODE>PRIndirectReference</CODE>.
2869: * @return the name of the image added
2870: * @throws PdfException on error
2871: * @throws DocumentException on error
2872: */
2873: public PdfName addDirectImageSimple(Image image,
2874: PdfIndirectReference fixedRef) throws PdfException,
2875: DocumentException {
2876: PdfName name;
2877: // if the images is already added, just retrieve the name
2878: if (images.containsKey(image.getMySerialId())) {
2879: name = (PdfName) images.get(image.getMySerialId());
2880: }
2881: // if it's a new image, add it to the document
2882: else {
2883: if (image.isImgTemplate()) {
2884: name = new PdfName("img" + images.size());
2885: if (image instanceof ImgWMF) {
2886: try {
2887: ImgWMF wmf = (ImgWMF) image;
2888: wmf.readWMF(PdfTemplate.createTemplate(this , 0,
2889: 0));
2890: } catch (Exception e) {
2891: throw new DocumentException(e);
2892: }
2893: }
2894: } else {
2895: PdfIndirectReference dref = image.getDirectReference();
2896: if (dref != null) {
2897: PdfName rname = new PdfName("img" + images.size());
2898: images.put(image.getMySerialId(), rname);
2899: imageDictionary.put(rname, dref);
2900: return rname;
2901: }
2902: Image maskImage = image.getImageMask();
2903: PdfIndirectReference maskRef = null;
2904: if (maskImage != null) {
2905: PdfName mname = (PdfName) images.get(maskImage
2906: .getMySerialId());
2907: maskRef = getImageReference(mname);
2908: }
2909: PdfImage i = new PdfImage(image, "img" + images.size(),
2910: maskRef);
2911: if (image.hasICCProfile()) {
2912: PdfICCBased icc = new PdfICCBased(image
2913: .getICCProfile());
2914: PdfIndirectReference iccRef = add(icc);
2915: PdfArray iccArray = new PdfArray();
2916: iccArray.add(PdfName.ICCBASED);
2917: iccArray.add(iccRef);
2918: PdfObject colorspace = i.get(PdfName.COLORSPACE);
2919: if (colorspace != null && colorspace.isArray()) {
2920: ArrayList ar = ((PdfArray) colorspace)
2921: .getArrayList();
2922: if (ar.size() > 1
2923: && PdfName.INDEXED.equals(ar.get(0)))
2924: ar.set(1, iccArray);
2925: else
2926: i.put(PdfName.COLORSPACE, iccArray);
2927: } else
2928: i.put(PdfName.COLORSPACE, iccArray);
2929: }
2930: add(i, fixedRef);
2931: name = i.name();
2932: }
2933: images.put(image.getMySerialId(), name);
2934: }
2935: return name;
2936: }
2937:
2938: /**
2939: * Writes a <CODE>PdfImage</CODE> to the outputstream.
2940: *
2941: * @param pdfImage the image to be added
2942: * @return a <CODE>PdfIndirectReference</CODE> to the encapsulated image
2943: * @throws PdfException when a document isn't open yet, or has been closed
2944: */
2945:
2946: PdfIndirectReference add(PdfImage pdfImage,
2947: PdfIndirectReference fixedRef) throws PdfException {
2948: if (!imageDictionary.contains(pdfImage.name())) {
2949: PdfXConformanceImp.checkPDFXConformance(this ,
2950: PdfXConformanceImp.PDFXKEY_IMAGE, pdfImage);
2951: if (fixedRef instanceof PRIndirectReference) {
2952: PRIndirectReference r2 = (PRIndirectReference) fixedRef;
2953: fixedRef = new PdfIndirectReference(0,
2954: getNewObjectNumber(r2.getReader(), r2
2955: .getNumber(), r2.getGeneration()));
2956: }
2957: try {
2958: if (fixedRef == null)
2959: fixedRef = addToBody(pdfImage)
2960: .getIndirectReference();
2961: else
2962: addToBody(pdfImage, fixedRef);
2963: } catch (IOException ioe) {
2964: throw new ExceptionConverter(ioe);
2965: }
2966: imageDictionary.put(pdfImage.name(), fixedRef);
2967: return fixedRef;
2968: }
2969: return (PdfIndirectReference) imageDictionary.get(pdfImage
2970: .name());
2971: }
2972:
2973: /**
2974: * return the <CODE>PdfIndirectReference</CODE> to the image with a given name.
2975: *
2976: * @param name the name of the image
2977: * @return a <CODE>PdfIndirectReference</CODE>
2978: */
2979:
2980: PdfIndirectReference getImageReference(PdfName name) {
2981: return (PdfIndirectReference) imageDictionary.get(name);
2982: }
2983:
2984: protected PdfIndirectReference add(PdfICCBased icc) {
2985: PdfIndirectObject object;
2986: try {
2987: object = addToBody(icc);
2988: } catch (IOException ioe) {
2989: throw new ExceptionConverter(ioe);
2990: }
2991: return object.getIndirectReference();
2992: }
2993:
2994: // [M4] Old table functionality; do we still need it?
2995:
2996: /**
2997: * Sometimes it is necessary to know where the just added <CODE>Table</CODE> ends.
2998: *
2999: * For instance to avoid to add another table in a page that is ending up, because
3000: * the new table will be probably splitted just after the header (it is an
3001: * unpleasant effect, isn't it?).
3002: *
3003: * Added on September 8th, 2001
3004: * by Francesco De Milato
3005: * francesco.demilato@tiscalinet.it
3006: * @param table the <CODE>Table</CODE>
3007: * @return the bottom height of the just added table
3008: * @deprecated this method will probably disappear in one of the next releases
3009: */
3010:
3011: public float getTableBottom(Table table) {
3012: return pdf.bottom(table) - pdf.indentBottom();
3013: }
3014:
3015: /**
3016: * Gets a pre-rendered table.
3017: * (Contributed by dperezcar@fcc.es)
3018: * @param table Contains the table definition. Its contents are deleted, after being pre-rendered.
3019: * @return a PdfTable
3020: * @deprecated this method will probably disappear in one of the next releases
3021: */
3022:
3023: public PdfTable getPdfTable(Table table) {
3024: return pdf.getPdfTable(table, true);
3025: }
3026:
3027: /**
3028: * Row additions to the original {@link Table} used to build the {@link PdfTable} are processed and pre-rendered,
3029: * and then the contents are deleted.
3030: * If the pre-rendered table doesn't fit, then it is fully rendered and its data discarded.
3031: * There shouldn't be any column change in the underlying {@link Table} object.
3032: * (Contributed by dperezcar@fcc.es)
3033: *
3034: * @param table The pre-rendered table obtained from {@link #getPdfTable(Table)}
3035: * @return true if the table is rendered and emptied.
3036: * @throws DocumentException
3037: * @see #getPdfTable(Table)
3038: * @deprecated this method will probably disappear in one of the next releases
3039: */
3040:
3041: public boolean breakTableIfDoesntFit(PdfTable table)
3042: throws DocumentException {
3043: return pdf.breakTableIfDoesntFit(table);
3044: }
3045:
3046: /**
3047: * Checks if a <CODE>Table</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
3048: *
3049: * @param table the table that has to be checked
3050: * @param margin a certain margin
3051: * @return <CODE>true</CODE> if the <CODE>Table</CODE> fits the page, <CODE>false</CODE> otherwise.
3052: * @deprecated this method will probably disappear in one of the next releases
3053: */
3054:
3055: public boolean fitsPage(Table table, float margin) {
3056: return pdf.bottom(table) > pdf.indentBottom() + margin;
3057: }
3058:
3059: /**
3060: * Checks if a <CODE>Table</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
3061: *
3062: * @param table the table that has to be checked
3063: * @return <CODE>true</CODE> if the <CODE>Table</CODE> fits the page, <CODE>false</CODE> otherwise.
3064: * @deprecated this method will probably disappear in one of the next releases
3065: */
3066:
3067: public boolean fitsPage(Table table) {
3068: return fitsPage(table, 0);
3069: }
3070:
3071: /**
3072: * Checks if a <CODE>PdfPTable</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
3073: *
3074: * @param table the table that has to be checked
3075: * @param margin a certain margin
3076: * @return <CODE>true</CODE> if the <CODE>PdfPTable</CODE> fits the page, <CODE>false</CODE> otherwise.
3077: * @deprecated this method will probably disappear in one of the next releases
3078: */
3079: public boolean fitsPage(PdfPTable table, float margin) {
3080: return pdf.fitsPage(table, margin);
3081: }
3082:
3083: /**
3084: * Checks if a <CODE>PdfPTable</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
3085: *
3086: * @param table the table that has to be checked
3087: * @return <CODE>true</CODE> if the <CODE>PdfPTable</CODE> fits the page, <CODE>false</CODE> otherwise.
3088: * @deprecated this method will probably disappear in one of the next releases
3089: */
3090: public boolean fitsPage(PdfPTable table) {
3091: return pdf.fitsPage(table, 0);
3092: }
3093:
3094: /**
3095: * A flag indicating the presence of structure elements that contain user properties attributes.
3096: */
3097: private boolean userProperties;
3098:
3099: /**
3100: * Gets the flag indicating the presence of structure elements that contain user properties attributes.
3101: * @return the user properties flag
3102: */
3103: public boolean isUserProperties() {
3104: return this .userProperties;
3105: }
3106:
3107: /**
3108: * Sets the flag indicating the presence of structure elements that contain user properties attributes.
3109: * @param userProperties the user properties flag
3110: */
3111: public void setUserProperties(boolean userProperties) {
3112: this.userProperties = userProperties;
3113: }
3114: }
|