001/*
002 * $Id: PdfStamper.java 4886 2011-05-29 14:04:23Z blowagie $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf;
045
046import java.io.File;
047import java.io.FileOutputStream;
048import java.io.IOException;
049import java.io.InputStream;
050import java.io.OutputStream;
051import java.security.SignatureException;
052import java.security.cert.Certificate;
053import java.util.HashMap;
054import java.util.List;
055import java.util.Map;
056
057import com.itextpdf.text.DocWriter;
058import com.itextpdf.text.DocumentException;
059import com.itextpdf.text.ExceptionConverter;
060import com.itextpdf.text.Image;
061import com.itextpdf.text.Rectangle;
062import com.itextpdf.text.error_messages.MessageLocalization;
063import com.itextpdf.text.pdf.collection.PdfCollection;
064import com.itextpdf.text.pdf.interfaces.PdfEncryptionSettings;
065import com.itextpdf.text.pdf.interfaces.PdfViewerPreferences;
066
067/** Applies extra content to the pages of a PDF document.
068 * This extra content can be all the objects allowed in PdfContentByte
069 * including pages from other Pdfs. The original PDF will keep
070 * all the interactive elements including bookmarks, links and form fields.
071 * <p>
072 * It is also possible to change the field values and to
073 * flatten them. New fields can be added but not flattened.
074 * @author Paulo Soares
075 */
076public class PdfStamper
077        implements PdfViewerPreferences, PdfEncryptionSettings {
078    /**
079     * The writer
080     */
081    protected PdfStamperImp stamper;
082    private Map<String, String> moreInfo;
083    private boolean hasSignature;
084    private PdfSignatureAppearance sigApp;
085
086    /** Starts the process of adding extra content to an existing PDF
087     * document.
088     * @param reader the original document. It cannot be reused
089     * @param os the output stream
090     * @throws DocumentException on error
091     * @throws IOException on error
092     */
093    public PdfStamper(final PdfReader reader, final OutputStream os) throws DocumentException, IOException {
094        stamper = new PdfStamperImp(reader, os, '\0', false);
095    }
096
097    /**
098     * Starts the process of adding extra content to an existing PDF
099     * document.
100     * @param reader the original document. It cannot be reused
101     * @param os the output stream
102     * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
103     * document
104     * @throws DocumentException on error
105     * @throws IOException on error
106     */
107    public PdfStamper(final PdfReader reader, final OutputStream os, final char pdfVersion) throws DocumentException, IOException {
108        stamper = new PdfStamperImp(reader, os, pdfVersion, false);
109    }
110
111    /**
112     * Starts the process of adding extra content to an existing PDF
113     * document, possibly as a new revision.
114     * @param reader the original document. It cannot be reused
115     * @param os the output stream
116     * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
117     * document
118     * @param append if <CODE>true</CODE> appends the document changes as a new revision. This is
119     * only useful for multiple signatures as nothing is gained in speed or memory
120     * @throws DocumentException on error
121     * @throws IOException on error
122     */
123    public PdfStamper(final PdfReader reader, final OutputStream os, final char pdfVersion, final boolean append) throws DocumentException, IOException {
124        stamper = new PdfStamperImp(reader, os, pdfVersion, append);
125    }
126
127    /** Gets the optional <CODE>String</CODE> map to add or change values in
128     * the info dictionary.
129     * @return the map or <CODE>null</CODE>
130     *
131     */
132    public Map<String, String> getMoreInfo() {
133        return this.moreInfo;
134    }
135
136    /** An optional <CODE>String</CODE> map to add or change values in
137     * the info dictionary. Entries with <CODE>null</CODE>
138     * values delete the key in the original info dictionary
139     * @param moreInfo additional entries to the info dictionary
140     *
141     */
142    public void setMoreInfo(final Map<String, String> moreInfo) {
143        this.moreInfo = moreInfo;
144    }
145
146    /**
147     * Replaces a page from this document with a page from other document. Only the content
148     * is replaced not the fields and annotations. This method must be called before
149     * getOverContent() or getUndercontent() are called for the same page.
150     * @param r the <CODE>PdfReader</CODE> from where the new page will be imported
151     * @param pageImported the page number of the imported page
152     * @param pageReplaced the page to replace in this document
153     * @since iText 2.1.1
154     */
155    public void replacePage(final PdfReader r, final int pageImported, final int pageReplaced) {
156        stamper.replacePage(r, pageImported, pageReplaced);
157    }
158
159    /**
160     * Inserts a blank page. All the pages above and including <CODE>pageNumber</CODE> will
161     * be shifted up. If <CODE>pageNumber</CODE> is bigger than the total number of pages
162     * the new page will be the last one.
163     * @param pageNumber the page number position where the new page will be inserted
164     * @param mediabox the size of the new page
165     */
166    public void insertPage(final int pageNumber, final Rectangle mediabox) {
167        stamper.insertPage(pageNumber, mediabox);
168    }
169
170    /**
171     * Gets the signing instance. The appearances and other parameters can the be set.
172     * @return the signing instance
173     */
174    public PdfSignatureAppearance getSignatureAppearance() {
175        return sigApp;
176    }
177
178    /**
179     * Closes the document. No more content can be written after the
180     * document is closed.
181     * <p>
182     * If closing a signed document with an external signature the closing must be done
183     * in the <CODE>PdfSignatureAppearance</CODE> instance.
184     * @throws DocumentException on error
185     * @throws IOException on error
186     */
187    public void close() throws DocumentException, IOException {
188        if (!hasSignature) {
189            stamper.close(moreInfo);
190            return;
191        }
192        sigApp.preClose();
193        PdfSigGenericPKCS sig = sigApp.getSigStandard();
194        PdfLiteral lit = (PdfLiteral)sig.get(PdfName.CONTENTS);
195        int totalBuf = (lit.getPosLength() - 2) / 2;
196        byte buf[] = new byte[8192];
197        int n;
198        InputStream inp = sigApp.getRangeStream();
199        try {
200            while ((n = inp.read(buf)) > 0) {
201                sig.getSigner().update(buf, 0, n);
202            }
203        }
204        catch (SignatureException se) {
205            throw new ExceptionConverter(se);
206        }
207        buf = new byte[totalBuf];
208        byte[] bsig = sig.getSignerContents();
209        System.arraycopy(bsig, 0, buf, 0, bsig.length);
210        PdfString str = new PdfString(buf);
211        str.setHexWriting(true);
212        PdfDictionary dic = new PdfDictionary();
213        dic.put(PdfName.CONTENTS, str);
214        sigApp.close(dic);
215        stamper.reader.close();
216    }
217
218    /** Gets a <CODE>PdfContentByte</CODE> to write under the page of
219     * the original document.
220     * @param pageNum the page number where the extra content is written
221     * @return a <CODE>PdfContentByte</CODE> to write under the page of
222     * the original document
223     */
224    public PdfContentByte getUnderContent(final int pageNum) {
225        return stamper.getUnderContent(pageNum);
226    }
227
228    /** Gets a <CODE>PdfContentByte</CODE> to write over the page of
229     * the original document.
230     * @param pageNum the page number where the extra content is written
231     * @return a <CODE>PdfContentByte</CODE> to write over the page of
232     * the original document
233     */
234    public PdfContentByte getOverContent(final int pageNum) {
235        return stamper.getOverContent(pageNum);
236    }
237
238    /** Checks if the content is automatically adjusted to compensate
239     * the original page rotation.
240     * @return the auto-rotation status
241     */
242    public boolean isRotateContents() {
243        return stamper.isRotateContents();
244    }
245
246    /** Flags the content to be automatically adjusted to compensate
247     * the original page rotation. The default is <CODE>true</CODE>.
248     * @param rotateContents <CODE>true</CODE> to set auto-rotation, <CODE>false</CODE>
249     * otherwise
250     */
251    public void setRotateContents(final boolean rotateContents) {
252        stamper.setRotateContents(rotateContents);
253    }
254
255    /** Sets the encryption options for this document. The userPassword and the
256     *  ownerPassword can be null or have zero length. In this case the ownerPassword
257     *  is replaced by a random string. The open permissions for the document can be
258     *  AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
259     *  AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
260     *  The permissions can be combined by ORing them.
261     * @param userPassword the user password. Can be null or empty
262     * @param ownerPassword the owner password. Can be null or empty
263     * @param permissions the user permissions
264     * @param strength128Bits <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
265     * @throws DocumentException if anything was already written to the output
266     */
267    public void setEncryption(final byte userPassword[], final byte ownerPassword[], final int permissions, final boolean strength128Bits) throws DocumentException {
268        if (stamper.isAppend())
269            throw new DocumentException(MessageLocalization.getComposedMessage("append.mode.does.not.support.changing.the.encryption.status"));
270        if (stamper.isContentWritten())
271            throw new DocumentException(MessageLocalization.getComposedMessage("content.was.already.written.to.the.output"));
272        stamper.setEncryption(userPassword, ownerPassword, permissions, strength128Bits ? PdfWriter.STANDARD_ENCRYPTION_128 : PdfWriter.STANDARD_ENCRYPTION_40);
273    }
274
275    /** Sets the encryption options for this document. The userPassword and the
276     *  ownerPassword can be null or have zero length. In this case the ownerPassword
277     *  is replaced by a random string. The open permissions for the document can be
278     *  AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
279     *  AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
280     *  The permissions can be combined by ORing them.
281     * @param userPassword the user password. Can be null or empty
282     * @param ownerPassword the owner password. Can be null or empty
283     * @param permissions the user permissions
284     * @param encryptionType the type of encryption. It can be one of STANDARD_ENCRYPTION_40, STANDARD_ENCRYPTION_128 or ENCRYPTION_AES128.
285     * Optionally DO_NOT_ENCRYPT_METADATA can be ored to output the metadata in cleartext
286     * @throws DocumentException if the document is already open
287     */
288    public void setEncryption(final byte userPassword[], final byte ownerPassword[], final int permissions, final int encryptionType) throws DocumentException {
289        if (stamper.isAppend())
290            throw new DocumentException(MessageLocalization.getComposedMessage("append.mode.does.not.support.changing.the.encryption.status"));
291        if (stamper.isContentWritten())
292            throw new DocumentException(MessageLocalization.getComposedMessage("content.was.already.written.to.the.output"));
293        stamper.setEncryption(userPassword, ownerPassword, permissions, encryptionType);
294    }
295
296    /**
297     * Sets the encryption options for this document. The userPassword and the
298     *  ownerPassword can be null or have zero length. In this case the ownerPassword
299     *  is replaced by a random string. The open permissions for the document can be
300     *  AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
301     *  AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
302     *  The permissions can be combined by ORing them.
303     * @param strength <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
304     * @param userPassword the user password. Can be null or empty
305     * @param ownerPassword the owner password. Can be null or empty
306     * @param permissions the user permissions
307     * @throws DocumentException if anything was already written to the output
308     */
309    public void setEncryption(final boolean strength, final String userPassword, final String ownerPassword, final int permissions) throws DocumentException {
310        setEncryption(DocWriter.getISOBytes(userPassword), DocWriter.getISOBytes(ownerPassword), permissions, strength);
311    }
312
313    /**
314     * Sets the encryption options for this document. The userPassword and the
315     *  ownerPassword can be null or have zero length. In this case the ownerPassword
316     *  is replaced by a random string. The open permissions for the document can be
317     *  AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
318     *  AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
319     *  The permissions can be combined by ORing them.
320     * @param encryptionType the type of encryption. It can be one of STANDARD_ENCRYPTION_40, STANDARD_ENCRYPTION_128 or ENCRYPTION_AES128.
321     * Optionally DO_NOT_ENCRYPT_METADATA can be ored to output the metadata in cleartext
322     * @param userPassword the user password. Can be null or empty
323     * @param ownerPassword the owner password. Can be null or empty
324     * @param permissions the user permissions
325     * @throws DocumentException if anything was already written to the output
326     */
327    public void setEncryption(final int encryptionType, final String userPassword, final String ownerPassword, final int permissions) throws DocumentException {
328        setEncryption(DocWriter.getISOBytes(userPassword), DocWriter.getISOBytes(ownerPassword), permissions, encryptionType);
329    }
330
331    /**
332     * Sets the certificate encryption options for this document. An array of one or more public certificates
333     * must be provided together with an array of the same size for the permissions for each certificate.
334     *  The open permissions for the document can be
335     *  AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
336     *  AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
337     *  The permissions can be combined by ORing them.
338     * Optionally DO_NOT_ENCRYPT_METADATA can be ored to output the metadata in cleartext
339     * @param certs the public certificates to be used for the encryption
340     * @param permissions the user permissions for each of the certificates
341     * @param encryptionType the type of encryption. It can be one of STANDARD_ENCRYPTION_40, STANDARD_ENCRYPTION_128 or ENCRYPTION_AES128.
342     * @throws DocumentException if the encryption was set too late
343     */
344     public void setEncryption(final Certificate[] certs, final int[] permissions, final int encryptionType) throws DocumentException {
345        if (stamper.isAppend())
346            throw new DocumentException(MessageLocalization.getComposedMessage("append.mode.does.not.support.changing.the.encryption.status"));
347        if (stamper.isContentWritten())
348            throw new DocumentException(MessageLocalization.getComposedMessage("content.was.already.written.to.the.output"));
349        stamper.setEncryption(certs, permissions, encryptionType);
350     }
351
352    /** Gets a page from other PDF document. Note that calling this method more than
353     * once with the same parameters will retrieve the same object.
354     * @param reader the PDF document where the page is
355     * @param pageNumber the page number. The first page is 1
356     * @return the template representing the imported page
357     */
358    public PdfImportedPage getImportedPage(final PdfReader reader, final int pageNumber) {
359        return stamper.getImportedPage(reader, pageNumber);
360    }
361
362    /** Gets the underlying PdfWriter.
363     * @return the underlying PdfWriter
364     */
365    public PdfWriter getWriter() {
366        return stamper;
367    }
368
369    /** Gets the underlying PdfReader.
370     * @return the underlying PdfReader
371     */
372    public PdfReader getReader() {
373        return stamper.reader;
374    }
375
376    /** Gets the <CODE>AcroFields</CODE> object that allows to get and set field values
377     * and to merge FDF forms.
378     * @return the <CODE>AcroFields</CODE> object
379     */
380    public AcroFields getAcroFields() {
381        return stamper.getAcroFields();
382    }
383
384    /** Determines if the fields are flattened on close. The fields added with
385     * {@link #addAnnotation(PdfAnnotation,int)} will never be flattened.
386     * @param flat <CODE>true</CODE> to flatten the fields, <CODE>false</CODE>
387     * to keep the fields
388     */
389    public void setFormFlattening(final boolean flat) {
390        stamper.setFormFlattening(flat);
391    }
392
393    /** Determines if the FreeText annotations are flattened on close.
394     * @param flat <CODE>true</CODE> to flatten the FreeText annotations, <CODE>false</CODE>
395     * (the default) to keep the FreeText annotations as active content.
396     */
397    public void setFreeTextFlattening(final boolean flat) {
398        stamper.setFreeTextFlattening(flat);
399        }
400
401    /**
402     * Adds an annotation of form field in a specific page. This page number
403     * can be overridden with {@link PdfAnnotation#setPlaceInPage(int)}.
404     * @param annot the annotation
405     * @param page the page
406     */
407    public void addAnnotation(final PdfAnnotation annot, final int page) {
408        stamper.addAnnotation(annot, page);
409    }
410
411    /**
412     * Adds an empty signature.
413     * @param name      the name of the signature
414     * @param page      the page number
415     * @param llx       lower left x coordinate of the signature's position
416     * @param lly       lower left y coordinate of the signature's position
417     * @param urx       upper right x coordinate of the signature's position
418     * @param ury       upper right y coordinate of the signature's position
419     * @return  a signature form field
420     * @since   2.1.4
421     */
422    public PdfFormField addSignature(final String name, final int page, final float llx, final float lly, final float urx, final float ury) {
423        PdfAcroForm acroForm = stamper.getAcroForm();
424        PdfFormField signature = PdfFormField.createSignature(stamper);
425        acroForm.setSignatureParams(signature, name, llx, lly, urx, ury);
426        acroForm.drawSignatureAppearences(signature, llx, lly, urx, ury);
427        addAnnotation(signature, page);
428        return signature;
429    }
430
431    /**
432     * Adds the comments present in an FDF file.
433     * @param fdf the FDF file
434     * @throws IOException on error
435     */
436    public void addComments(final FdfReader fdf) throws IOException {
437        stamper.addComments(fdf);
438    }
439
440    /**
441     * Sets the bookmarks. The list structure is defined in
442     * {@link SimpleBookmark}.
443     * @param outlines the bookmarks or <CODE>null</CODE> to remove any
444     */
445    public void setOutlines(final List<HashMap<String, Object>> outlines) {
446        stamper.setOutlines(outlines);
447    }
448
449    /**
450     * Sets the thumbnail image for a page.
451     * @param image the image
452     * @param page the page
453     * @throws PdfException on error
454     * @throws DocumentException on error
455     */
456    public void setThumbnail(final Image image, final int page) throws PdfException, DocumentException {
457        stamper.setThumbnail(image, page);
458    }
459
460    /**
461     * Adds <CODE>name</CODE> to the list of fields that will be flattened on close,
462     * all the other fields will remain. If this method is never called or is called
463     * with invalid field names, all the fields will be flattened.
464     * <p>
465     * Calling <CODE>setFormFlattening(true)</CODE> is needed to have any kind of
466     * flattening.
467     * @param name the field name
468     * @return <CODE>true</CODE> if the field exists, <CODE>false</CODE> otherwise
469     */
470    public boolean partialFormFlattening(final String name) {
471        return stamper.partialFormFlattening(name);
472    }
473
474    /** Adds a JavaScript action at the document level. When the document
475     * opens all this JavaScript runs. The existing JavaScript will be replaced.
476     * @param js the JavaScript code
477     */
478    public void addJavaScript(final String js) {
479        stamper.addJavaScript(js, !PdfEncodings.isPdfDocEncoding(js));
480    }
481
482    /** Adds a file attachment at the document level. Existing attachments will be kept.
483     * @param description the file description
484     * @param fileStore an array with the file. If it's <CODE>null</CODE>
485     * the file will be read from the disk
486     * @param file the path to the file. It will only be used if
487     * <CODE>fileStore</CODE> is not <CODE>null</CODE>
488     * @param fileDisplay the actual file name stored in the pdf
489     * @throws IOException on error
490     */
491    public void addFileAttachment(final String description, final byte fileStore[], final String file, final String fileDisplay) throws IOException {
492        addFileAttachment(description, PdfFileSpecification.fileEmbedded(stamper, file, fileDisplay, fileStore));
493    }
494
495    /** Adds a file attachment at the document level. Existing attachments will be kept.
496     * @param description the file description
497     * @param fs the file specification
498     * @throws IOException
499     */
500    public void addFileAttachment(final String description, final PdfFileSpecification fs) throws IOException {
501        stamper.addFileAttachment(description, fs);
502    }
503
504    /**
505     * This is the most simple way to change a PDF into a
506     * portable collection. Choose one of the following names:
507     * <ul>
508     * <li>PdfName.D (detailed view)
509     * <li>PdfName.T (tiled view)
510     * <li>PdfName.H (hidden)
511     * </ul>
512     * Pass this name as a parameter and your PDF will be
513     * a portable collection with all the embedded and
514     * attached files as entries.
515     * @param initialView can be PdfName.D, PdfName.T or PdfName.H
516     */
517    public void makePackage( final PdfName initialView ) {
518        PdfCollection collection = new PdfCollection(0);
519        collection.put(PdfName.VIEW, initialView);
520        stamper.makePackage( collection );
521    }
522
523    /**
524     * Adds or replaces the Collection Dictionary in the Catalog.
525     * @param   collection      the new collection dictionary.
526     */
527    public void makePackage(final PdfCollection collection) {
528        stamper.makePackage(collection);
529    }
530
531    /**
532     * Sets the viewer preferences.
533     * @param preferences the viewer preferences
534     * @see PdfViewerPreferences#setViewerPreferences(int)
535     */
536    public void setViewerPreferences(final int preferences) {
537        stamper.setViewerPreferences(preferences);
538    }
539
540    /** Adds a viewer preference
541     * @param key a key for a viewer preference
542     * @param value the value for the viewer preference
543     * @see PdfViewerPreferences#addViewerPreference
544     */
545
546    public void addViewerPreference(final PdfName key, final PdfObject value) {
547        stamper.addViewerPreference(key, value);
548    }
549
550    /**
551     * Sets the XMP metadata.
552     * @param xmp
553     * @see PdfWriter#setXmpMetadata(byte[])
554     */
555    public void setXmpMetadata(final byte[] xmp) {
556        stamper.setXmpMetadata(xmp);
557    }
558
559    /**
560     * Gets the 1.5 compression status.
561     * @return <code>true</code> if the 1.5 compression is on
562     */
563    public boolean isFullCompression() {
564        return stamper.isFullCompression();
565    }
566
567    /**
568     * Sets the document's compression to the new 1.5 mode with object streams and xref
569     * streams. It can be set at any time but once set it can't be unset.
570     */
571    public void setFullCompression() {
572        if (stamper.isAppend())
573            return;
574        stamper.setFullCompression();
575    }
576
577    /**
578     * Sets the open and close page additional action.
579     * @param actionType the action type. It can be <CODE>PdfWriter.PAGE_OPEN</CODE>
580     * or <CODE>PdfWriter.PAGE_CLOSE</CODE>
581     * @param action the action to perform
582     * @param page the page where the action will be applied. The first page is 1
583     * @throws PdfException if the action type is invalid
584     */
585    public void setPageAction(final PdfName actionType, final PdfAction action, final int page) throws PdfException {
586        stamper.setPageAction(actionType, action, page);
587    }
588
589    /**
590     * Sets the display duration for the page (for presentations)
591     * @param seconds   the number of seconds to display the page. A negative value removes the entry
592     * @param page the page where the duration will be applied. The first page is 1
593     */
594    public void setDuration(final int seconds, final int page) {
595        stamper.setDuration(seconds, page);
596    }
597
598    /**
599     * Sets the transition for the page
600     * @param transition   the transition object. A <code>null</code> removes the transition
601     * @param page the page where the transition will be applied. The first page is 1
602     */
603    public void setTransition(final PdfTransition transition, final int page) {
604        stamper.setTransition(transition, page);
605    }
606
607    /**
608     * Applies a digital signature to a document, possibly as a new revision, making
609     * possible multiple signatures. The returned PdfStamper
610     * can be used normally as the signature is only applied when closing.
611     * <p>
612     * A possible use for adding a signature without invalidating an existing one is:
613     * <p>
614     * <pre>
615     * KeyStore ks = KeyStore.getInstance("pkcs12");
616     * ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
617     * String alias = (String)ks.aliases().nextElement();
618     * PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
619     * Certificate[] chain = ks.getCertificateChain(alias);
620     * PdfReader reader = new PdfReader("original.pdf");
621     * FileOutputStream fout = new FileOutputStream("signed.pdf");
622     * PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0', new
623     * File("/temp"), true);
624     * PdfSignatureAppearance sap = stp.getSignatureAppearance();
625     * sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
626     * sap.setReason("I'm the author");
627     * sap.setLocation("Lisbon");
628     * // comment next line to have an invisible signature
629     * sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
630     * stp.close();
631     * </pre>
632     * @param reader the original document
633     * @param os the output stream or <CODE>null</CODE> to keep the document in the temporary file
634     * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
635     * document
636     * @param tempFile location of the temporary file. If it's a directory a temporary file will be created there.
637     *     If it's a file it will be used directly. The file will be deleted on exit unless <CODE>os</CODE> is null.
638     *     In that case the document can be retrieved directly from the temporary file. If it's <CODE>null</CODE>
639     *     no temporary file will be created and memory will be used
640     * @param append if <CODE>true</CODE> the signature and all the other content will be added as a
641     * new revision thus not invalidating existing signatures
642     * @return a <CODE>PdfStamper</CODE>
643     * @throws DocumentException on error
644     * @throws IOException on error
645     */
646    public static PdfStamper createSignature(final PdfReader reader, final OutputStream os, final char pdfVersion, File tempFile, final boolean append) throws DocumentException, IOException {
647        PdfStamper stp;
648        if (tempFile == null) {
649            ByteBuffer bout = new ByteBuffer();
650            stp = new PdfStamper(reader, bout, pdfVersion, append);
651            stp.sigApp = new PdfSignatureAppearance(stp.stamper);
652            stp.sigApp.setSigout(bout);
653        }
654        else {
655            if (tempFile.isDirectory())
656                tempFile = File.createTempFile("pdf", null, tempFile);
657            FileOutputStream fout = new FileOutputStream(tempFile);
658            stp = new PdfStamper(reader, fout, pdfVersion, append);
659            stp.sigApp = new PdfSignatureAppearance(stp.stamper);
660            stp.sigApp.setTempFile(tempFile);
661        }
662        stp.sigApp.setOriginalout(os);
663        stp.sigApp.setStamper(stp);
664        stp.hasSignature = true;
665        PdfDictionary catalog = reader.getCatalog();
666        PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.ACROFORM), catalog);
667        if (acroForm != null) {
668            acroForm.remove(PdfName.NEEDAPPEARANCES);
669            stp.stamper.markUsed(acroForm);
670        }
671        return stp;
672    }
673
674    /**
675     * Applies a digital signature to a document. The returned PdfStamper
676     * can be used normally as the signature is only applied when closing.
677     * <p>
678     * Note that the pdf is created in memory.
679     * <p>
680     * A possible use is:
681     * <p>
682     * <pre>
683     * KeyStore ks = KeyStore.getInstance("pkcs12");
684     * ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
685     * String alias = (String)ks.aliases().nextElement();
686     * PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
687     * Certificate[] chain = ks.getCertificateChain(alias);
688     * PdfReader reader = new PdfReader("original.pdf");
689     * FileOutputStream fout = new FileOutputStream("signed.pdf");
690     * PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
691     * PdfSignatureAppearance sap = stp.getSignatureAppearance();
692     * sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
693     * sap.setReason("I'm the author");
694     * sap.setLocation("Lisbon");
695     * // comment next line to have an invisible signature
696     * sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
697     * stp.close();
698     * </pre>
699     * @param reader the original document
700     * @param os the output stream
701     * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
702     * document
703     * @throws DocumentException on error
704     * @throws IOException on error
705     * @return a <CODE>PdfStamper</CODE>
706     */
707    public static PdfStamper createSignature(final PdfReader reader, final OutputStream os, final char pdfVersion) throws DocumentException, IOException {
708        return createSignature(reader, os, pdfVersion, null, false);
709    }
710
711    /**
712     * Applies a digital signature to a document. The returned PdfStamper
713     * can be used normally as the signature is only applied when closing.
714     * <p>
715     * A possible use is:
716     * <p>
717     * <pre>
718     * KeyStore ks = KeyStore.getInstance("pkcs12");
719     * ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
720     * String alias = (String)ks.aliases().nextElement();
721     * PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
722     * Certificate[] chain = ks.getCertificateChain(alias);
723     * PdfReader reader = new PdfReader("original.pdf");
724     * FileOutputStream fout = new FileOutputStream("signed.pdf");
725     * PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0', new File("/temp"));
726     * PdfSignatureAppearance sap = stp.getSignatureAppearance();
727     * sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
728     * sap.setReason("I'm the author");
729     * sap.setLocation("Lisbon");
730     * // comment next line to have an invisible signature
731     * sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
732     * stp.close();
733     * </pre>
734     * @param reader the original document
735     * @param os the output stream or <CODE>null</CODE> to keep the document in the temporary file
736     * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
737     * document
738     * @param tempFile location of the temporary file. If it's a directory a temporary file will be created there.
739     *     If it's a file it will be used directly. The file will be deleted on exit unless <CODE>os</CODE> is null.
740     *     In that case the document can be retrieved directly from the temporary file. If it's <CODE>null</CODE>
741     *     no temporary file will be created and memory will be used
742     * @return a <CODE>PdfStamper</CODE>
743     * @throws DocumentException on error
744     * @throws IOException on error
745     */
746    public static PdfStamper createSignature(final PdfReader reader, final OutputStream os, final char pdfVersion, final File tempFile) throws DocumentException, IOException
747    {
748        return createSignature(reader, os, pdfVersion, tempFile, false);
749    }
750
751    /**
752     * Gets the PdfLayer objects in an existing document as a Map
753     * with the names/titles of the layers as keys.
754     * @return  a Map with all the PdfLayers in the document (and the name/title of the layer as key)
755     * @since   2.1.2
756     */
757    public Map<String, PdfLayer> getPdfLayers() {
758        return stamper.getPdfLayers();
759    }
760}