1 /*
2 * Copyright 2003, 2004 by Paulo Soares.
3 *
4 * The contents of this file are subject to the Mozilla Public License Version 1.1
5 * (the "License"); you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
7 *
8 * Software distributed under the License is distributed on an "AS IS" basis,
9 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
10 * for the specific language governing rights and limitations under the License.
11 *
12 * The Original Code is 'iText, a free JAVA-PDF library'.
13 *
14 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
15 * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
16 * All Rights Reserved.
17 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
18 * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
19 *
20 * Contributor(s): all the names of the contributors are added in the source code
21 * where applicable.
22 *
23 * Alternatively, the contents of this file may be used under the terms of the
24 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
25 * provisions of LGPL are applicable instead of those above. If you wish to
26 * allow use of your version of this file only under the terms of the LGPL
27 * License and not to allow others to use your version of this file under
28 * the MPL, indicate your decision by deleting the provisions above and
29 * replace them with the notice and other provisions required by the LGPL.
30 * If you do not delete the provisions above, a recipient may use your version
31 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
32 *
33 * This library is free software; you can redistribute it and/or modify it
34 * under the terms of the MPL as stated above or under the terms of the GNU
35 * Library General Public License as published by the Free Software Foundation;
36 * either version 2 of the License, or any later version.
37 *
38 * This library is distributed in the hope that it will be useful, but WITHOUT
39 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
40 * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
41 * details.
42 *
43 * If you didn't download this code from the following link, you should check if
44 * you aren't using an obsolete version:
45 * http://www.lowagie.com/iText/
46 */
47 package com.lowagie.text.pdf;
48
49 import java.io.File;
50 import java.io.FileOutputStream;
51 import java.io.IOException;
52 import java.io.InputStream;
53 import java.io.OutputStream;
54 import java.security.SignatureException;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58
59 import com.lowagie.text.DocWriter;
60 import com.lowagie.text.DocumentException;
61 import com.lowagie.text.ExceptionConverter;
62 import com.lowagie.text.Image;
63 import com.lowagie.text.Rectangle;
64 import com.lowagie.text.pdf.collection.PdfCollection;
65 import com.lowagie.text.pdf.interfaces.PdfEncryptionSettings;
66 import com.lowagie.text.pdf.interfaces.PdfViewerPreferences;
67 import java.security.cert.Certificate;
68
69 /** Applies extra content to the pages of a PDF document.
70 * This extra content can be all the objects allowed in PdfContentByte
71 * including pages from other Pdfs. The original PDF will keep
72 * all the interactive elements including bookmarks, links and form fields.
73 * <p>
74 * It is also possible to change the field values and to
75 * flatten them. New fields can be added but not flattened.
76 * @author Paulo Soares (psoares@consiste.pt)
77 */
78 public class PdfStamper
79 implements PdfViewerPreferences, PdfEncryptionSettings {
80 /**
81 * The writer
82 */
83 protected PdfStamperImp stamper;
84 private HashMap moreInfo;
85 private boolean hasSignature;
86 private PdfSignatureAppearance sigApp;
87
88 /** Starts the process of adding extra content to an existing PDF
89 * document.
90 * @param reader the original document. It cannot be reused
91 * @param os the output stream
92 * @throws DocumentException on error
93 * @throws IOException on error
94 */
95 public PdfStamper(PdfReader reader, OutputStream os) throws DocumentException, IOException {
96 stamper = new PdfStamperImp(reader, os, '\0', false);
97 }
98
99 /**
100 * Starts the process of adding extra content to an existing PDF
101 * document.
102 * @param reader the original document. It cannot be reused
103 * @param os the output stream
104 * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
105 * document
106 * @throws DocumentException on error
107 * @throws IOException on error
108 */
109 public PdfStamper(PdfReader reader, OutputStream os, char pdfVersion) throws DocumentException, IOException {
110 stamper = new PdfStamperImp(reader, os, pdfVersion, false);
111 }
112
113 /**
114 * Starts the process of adding extra content to an existing PDF
115 * document, possibly as a new revision.
116 * @param reader the original document. It cannot be reused
117 * @param os the output stream
118 * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
119 * document
120 * @param append if <CODE>true</CODE> appends the document changes as a new revision. This is
121 * only useful for multiple signatures as nothing is gained in speed or memory
122 * @throws DocumentException on error
123 * @throws IOException on error
124 */
125 public PdfStamper(PdfReader reader, OutputStream os, char pdfVersion, boolean append) throws DocumentException, IOException {
126 stamper = new PdfStamperImp(reader, os, pdfVersion, append);
127 }
128
129 /** Gets the optional <CODE>String</CODE> map to add or change values in
130 * the info dictionary.
131 * @return the map or <CODE>null</CODE>
132 *
133 */
134 public HashMap getMoreInfo() {
135 return this.moreInfo;
136 }
137
138 /** An optional <CODE>String</CODE> map to add or change values in
139 * the info dictionary. Entries with <CODE>null</CODE>
140 * values delete the key in the original info dictionary
141 * @param moreInfo additional entries to the info dictionary
142 *
143 */
144 public void setMoreInfo(HashMap moreInfo) {
145 this.moreInfo = moreInfo;
146 }
147
148 /**
149 * Replaces a page from this document with a page from other document. Only the content
150 * is replaced not the fields and annotations. This method must be called before
151 * getOverContent() or getUndercontent() are called for the same page.
152 * @param r the <CODE>PdfReader</CODE> from where the new page will be imported
153 * @param pageImported the page number of the imported page
154 * @param pageReplaced the page to replace in this document
155 * @since iText 2.1.1
156 */
157 public void replacePage(PdfReader r, int pageImported, int pageReplaced) {
158 stamper.replacePage(r, pageImported, pageReplaced);
159 }
160
161 /**
162 * Inserts a blank page. All the pages above and including <CODE>pageNumber</CODE> will
163 * be shifted up. If <CODE>pageNumber</CODE> is bigger than the total number of pages
164 * the new page will be the last one.
165 * @param pageNumber the page number position where the new page will be inserted
166 * @param mediabox the size of the new page
167 */
168 public void insertPage(int pageNumber, Rectangle mediabox) {
169 stamper.insertPage(pageNumber, mediabox);
170 }
171
172 /**
173 * Gets the signing instance. The appearances and other parameters can the be set.
174 * @return the signing instance
175 */
176 public PdfSignatureAppearance getSignatureAppearance() {
177 return sigApp;
178 }
179
180 /**
181 * Closes the document. No more content can be written after the
182 * document is closed.
183 * <p>
184 * If closing a signed document with an external signature the closing must be done
185 * in the <CODE>PdfSignatureAppearance</CODE> instance.
186 * @throws DocumentException on error
187 * @throws IOException on error
188 */
189 public void close() throws DocumentException, IOException {
190 if (!hasSignature) {
191 stamper.close(moreInfo);
192 return;
193 }
194 sigApp.preClose();
195 PdfSigGenericPKCS sig = sigApp.getSigStandard();
196 PdfLiteral lit = (PdfLiteral)sig.get(PdfName.CONTENTS);
197 int totalBuf = (lit.getPosLength() - 2) / 2;
198 byte buf[] = new byte[8192];
199 int n;
200 InputStream inp = sigApp.getRangeStream();
201 try {
202 while ((n = inp.read(buf)) > 0) {
203 sig.getSigner().update(buf, 0, n);
204 }
205 }
206 catch (SignatureException se) {
207 throw new ExceptionConverter(se);
208 }
209 buf = new byte[totalBuf];
210 byte[] bsig = sig.getSignerContents();
211 System.arraycopy(bsig, 0, buf, 0, bsig.length);
212 PdfString str = new PdfString(buf);
213 str.setHexWriting(true);
214 PdfDictionary dic = new PdfDictionary();
215 dic.put(PdfName.CONTENTS, str);
216 sigApp.close(dic);
217 stamper.reader.close();
218 }
219
220 /** Gets a <CODE>PdfContentByte</CODE> to write under the page of
221 * the original document.
222 * @param pageNum the page number where the extra content is written
223 * @return a <CODE>PdfContentByte</CODE> to write under the page of
224 * the original document
225 */
226 public PdfContentByte getUnderContent(int pageNum) {
227 return stamper.getUnderContent(pageNum);
228 }
229
230 /** Gets a <CODE>PdfContentByte</CODE> to write over the page of
231 * the original document.
232 * @param pageNum the page number where the extra content is written
233 * @return a <CODE>PdfContentByte</CODE> to write over the page of
234 * the original document
235 */
236 public PdfContentByte getOverContent(int pageNum) {
237 return stamper.getOverContent(pageNum);
238 }
239
240 /** Checks if the content is automatically adjusted to compensate
241 * the original page rotation.
242 * @return the auto-rotation status
243 */
244 public boolean isRotateContents() {
245 return stamper.isRotateContents();
246 }
247
248 /** Flags the content to be automatically adjusted to compensate
249 * the original page rotation. The default is <CODE>true</CODE>.
250 * @param rotateContents <CODE>true</CODE> to set auto-rotation, <CODE>false</CODE>
251 * otherwise
252 */
253 public void setRotateContents(boolean rotateContents) {
254 stamper.setRotateContents(rotateContents);
255 }
256
257 /** Sets the encryption options for this document. The userPassword and the
258 * ownerPassword can be null or have zero length. In this case the ownerPassword
259 * is replaced by a random string. The open permissions for the document can be
260 * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
261 * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
262 * The permissions can be combined by ORing them.
263 * @param userPassword the user password. Can be null or empty
264 * @param ownerPassword the owner password. Can be null or empty
265 * @param permissions the user permissions
266 * @param strength128Bits <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
267 * @throws DocumentException if anything was already written to the output
268 */
269 public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits) throws DocumentException {
270 if (stamper.isAppend())
271 throw new DocumentException("Append mode does not support changing the encryption status.");
272 if (stamper.isContentWritten())
273 throw new DocumentException("Content was already written to the output.");
274 stamper.setEncryption(userPassword, ownerPassword, permissions, strength128Bits ? PdfWriter.STANDARD_ENCRYPTION_128 : PdfWriter.STANDARD_ENCRYPTION_40);
275 }
276
277 /** Sets the encryption options for this document. The userPassword and the
278 * ownerPassword can be null or have zero length. In this case the ownerPassword
279 * is replaced by a random string. The open permissions for the document can be
280 * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
281 * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
282 * The permissions can be combined by ORing them.
283 * @param userPassword the user password. Can be null or empty
284 * @param ownerPassword the owner password. Can be null or empty
285 * @param permissions the user permissions
286 * @param encryptionType the type of encryption. It can be one of STANDARD_ENCRYPTION_40, STANDARD_ENCRYPTION_128 or ENCRYPTION_AES128.
287 * Optionally DO_NOT_ENCRYPT_METADATA can be ored to output the metadata in cleartext
288 * @throws DocumentException if the document is already open
289 */
290 public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, int encryptionType) throws DocumentException {
291 if (stamper.isAppend())
292 throw new DocumentException("Append mode does not support changing the encryption status.");
293 if (stamper.isContentWritten())
294 throw new DocumentException("Content was already written to the output.");
295 stamper.setEncryption(userPassword, ownerPassword, permissions, encryptionType);
296 }
297
298 /**
299 * Sets the encryption options for this document. The userPassword and the
300 * ownerPassword can be null or have zero length. In this case the ownerPassword
301 * is replaced by a random string. The open permissions for the document can be
302 * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
303 * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
304 * The permissions can be combined by ORing them.
305 * @param strength <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length
306 * @param userPassword the user password. Can be null or empty
307 * @param ownerPassword the owner password. Can be null or empty
308 * @param permissions the user permissions
309 * @throws DocumentException if anything was already written to the output
310 */
311 public void setEncryption(boolean strength, String userPassword, String ownerPassword, int permissions) throws DocumentException {
312 setEncryption(DocWriter.getISOBytes(userPassword), DocWriter.getISOBytes(ownerPassword), permissions, strength);
313 }
314
315 /**
316 * Sets the encryption options for this document. The userPassword and the
317 * ownerPassword can be null or have zero length. In this case the ownerPassword
318 * is replaced by a random string. The open permissions for the document can be
319 * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
320 * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
321 * The permissions can be combined by ORing them.
322 * @param encryptionType the type of encryption. It can be one of STANDARD_ENCRYPTION_40, STANDARD_ENCRYPTION_128 or ENCRYPTION_AES128.
323 * Optionally DO_NOT_ENCRYPT_METADATA can be ored to output the metadata in cleartext
324 * @param userPassword the user password. Can be null or empty
325 * @param ownerPassword the owner password. Can be null or empty
326 * @param permissions the user permissions
327 * @throws DocumentException if anything was already written to the output
328 */
329 public void setEncryption(int encryptionType, String userPassword, String ownerPassword, int permissions) throws DocumentException {
330 setEncryption(DocWriter.getISOBytes(userPassword), DocWriter.getISOBytes(ownerPassword), permissions, encryptionType);
331 }
332
333 /**
334 * Sets the certificate encryption options for this document. An array of one or more public certificates
335 * must be provided together with an array of the same size for the permissions for each certificate.
336 * The open permissions for the document can be
337 * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
338 * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
339 * The permissions can be combined by ORing them.
340 * Optionally DO_NOT_ENCRYPT_METADATA can be ored to output the metadata in cleartext
341 * @param certs the public certificates to be used for the encryption
342 * @param permissions the user permissions for each of the certificates
343 * @param encryptionType the type of encryption. It can be one of STANDARD_ENCRYPTION_40, STANDARD_ENCRYPTION_128 or ENCRYPTION_AES128.
344 * @throws DocumentException if the encryption was set too late
345 */
346 public void setEncryption(Certificate[] certs, int[] permissions, int encryptionType) throws DocumentException {
347 if (stamper.isAppend())
348 throw new DocumentException("Append mode does not support changing the encryption status.");
349 if (stamper.isContentWritten())
350 throw new DocumentException("Content was already written to the output.");
351 stamper.setEncryption(certs, permissions, encryptionType);
352 }
353
354 /** Gets a page from other PDF document. Note that calling this method more than
355 * once with the same parameters will retrieve the same object.
356 * @param reader the PDF document where the page is
357 * @param pageNumber the page number. The first page is 1
358 * @return the template representing the imported page
359 */
360 public PdfImportedPage getImportedPage(PdfReader reader, int pageNumber) {
361 return stamper.getImportedPage(reader, pageNumber);
362 }
363
364 /** Gets the underlying PdfWriter.
365 * @return the underlying PdfWriter
366 */
367 public PdfWriter getWriter() {
368 return stamper;
369 }
370
371 /** Gets the underlying PdfReader.
372 * @return the underlying PdfReader
373 */
374 public PdfReader getReader() {
375 return stamper.reader;
376 }
377
378 /** Gets the <CODE>AcroFields</CODE> object that allows to get and set field values
379 * and to merge FDF forms.
380 * @return the <CODE>AcroFields</CODE> object
381 */
382 public AcroFields getAcroFields() {
383 return stamper.getAcroFields();
384 }
385
386 /** Determines if the fields are flattened on close. The fields added with
387 * {@link #addAnnotation(PdfAnnotation,int)} will never be flattened.
388 * @param flat <CODE>true</CODE> to flatten the fields, <CODE>false</CODE>
389 * to keep the fields
390 */
391 public void setFormFlattening(boolean flat) {
392 stamper.setFormFlattening(flat);
393 }
394
395 /** Determines if the FreeText annotations are flattened on close.
396 * @param flat <CODE>true</CODE> to flatten the FreeText annotations, <CODE>false</CODE>
397 * (the default) to keep the FreeText annotations as active content.
398 */
399 public void setFreeTextFlattening(boolean flat) {
400 stamper.setFreeTextFlattening(flat);
401 }
402
403 /**
404 * Adds an annotation of form field in a specific page. This page number
405 * can be overridden with {@link PdfAnnotation#setPlaceInPage(int)}.
406 * @param annot the annotation
407 * @param page the page
408 */
409 public void addAnnotation(PdfAnnotation annot, int page) {
410 stamper.addAnnotation(annot, page);
411 }
412
413 /**
414 * Adds the comments present in an FDF file.
415 * @param fdf the FDF file
416 * @throws IOException on error
417 */
418 public void addComments(FdfReader fdf) throws IOException {
419 stamper.addComments(fdf);
420 }
421
422 /**
423 * Sets the bookmarks. The list structure is defined in
424 * {@link SimpleBookmark}.
425 * @param outlines the bookmarks or <CODE>null</CODE> to remove any
426 */
427 public void setOutlines(List outlines) {
428 stamper.setOutlines(outlines);
429 }
430
431 /**
432 * Sets the thumbnail image for a page.
433 * @param image the image
434 * @param page the page
435 * @throws PdfException on error
436 * @throws DocumentException on error
437 */
438 public void setThumbnail(Image image, int page) throws PdfException, DocumentException {
439 stamper.setThumbnail(image, page);
440 }
441
442 /**
443 * Adds <CODE>name</CODE> to the list of fields that will be flattened on close,
444 * all the other fields will remain. If this method is never called or is called
445 * with invalid field names, all the fields will be flattened.
446 * <p>
447 * Calling <CODE>setFormFlattening(true)</CODE> is needed to have any kind of
448 * flattening.
449 * @param name the field name
450 * @return <CODE>true</CODE> if the field exists, <CODE>false</CODE> otherwise
451 */
452 public boolean partialFormFlattening(String name) {
453 return stamper.partialFormFlattening(name);
454 }
455
456 /** Adds a JavaScript action at the document level. When the document
457 * opens all this JavaScript runs. The existing JavaScript will be replaced.
458 * @param js the JavaScript code
459 */
460 public void addJavaScript(String js) {
461 stamper.addJavaScript(js, !PdfEncodings.isPdfDocEncoding(js));
462 }
463
464 /** Adds a file attachment at the document level. Existing attachments will be kept.
465 * @param description the file description
466 * @param fileStore an array with the file. If it's <CODE>null</CODE>
467 * the file will be read from the disk
468 * @param file the path to the file. It will only be used if
469 * <CODE>fileStore</CODE> is not <CODE>null</CODE>
470 * @param fileDisplay the actual file name stored in the pdf
471 * @throws IOException on error
472 */
473 public void addFileAttachment(String description, byte fileStore[], String file, String fileDisplay) throws IOException {
474 addFileAttachment(description, PdfFileSpecification.fileEmbedded(stamper, file, fileDisplay, fileStore));
475 }
476
477 /** Adds a file attachment at the document level. Existing attachments will be kept.
478 * @param description the file description
479 * @param fs the file specification
480 */
481 public void addFileAttachment(String description, PdfFileSpecification fs) throws IOException {
482 stamper.addFileAttachment(description, fs);
483 }
484
485 /**
486 * This is the most simple way to change a PDF into a
487 * portable collection. Choose one of the following names:
488 * <ul>
489 * <li>PdfName.D (detailed view)
490 * <li>PdfName.T (tiled view)
491 * <li>PdfName.H (hidden)
492 * </ul>
493 * Pass this name as a parameter and your PDF will be
494 * a portable collection with all the embedded and
495 * attached files as entries.
496 * @param initialView can be PdfName.D, PdfName.T or PdfName.H
497 */
498 public void makePackage( PdfName initialView ) {
499 PdfCollection collection = new PdfCollection(0);
500 collection.put(PdfName.VIEW, initialView);
501 stamper.makePackage( collection );
502 }
503
504 /**
505 * Adds or replaces the Collection Dictionary in the Catalog.
506 * @param collection the new collection dictionary.
507 */
508 public void makePackage(PdfCollection collection) {
509 stamper.makePackage(collection);
510 }
511
512 /**
513 * Sets the viewer preferences.
514 * @param preferences the viewer preferences
515 * @see PdfViewerPreferences#setViewerPreferences(int)
516 */
517 public void setViewerPreferences(int preferences) {
518 stamper.setViewerPreferences(preferences);
519 }
520
521 /** Adds a viewer preference
522 * @param key a key for a viewer preference
523 * @param value the value for the viewer preference
524 * @see PdfViewerPreferences#addViewerPreference
525 */
526
527 public void addViewerPreference(PdfName key, PdfObject value) {
528 stamper.addViewerPreference(key, value);
529 }
530
531 /**
532 * Sets the XMP metadata.
533 * @param xmp
534 * @see PdfWriter#setXmpMetadata(byte[])
535 */
536 public void setXmpMetadata(byte[] xmp) {
537 stamper.setXmpMetadata(xmp);
538 }
539
540 /**
541 * Gets the 1.5 compression status.
542 * @return <code>true</code> if the 1.5 compression is on
543 */
544 public boolean isFullCompression() {
545 return stamper.isFullCompression();
546 }
547
548 /**
549 * Sets the document's compression to the new 1.5 mode with object streams and xref
550 * streams. It can be set at any time but once set it can't be unset.
551 */
552 public void setFullCompression() {
553 if (stamper.isAppend())
554 return;
555 stamper.setFullCompression();
556 }
557
558 /**
559 * Sets the open and close page additional action.
560 * @param actionType the action type. It can be <CODE>PdfWriter.PAGE_OPEN</CODE>
561 * or <CODE>PdfWriter.PAGE_CLOSE</CODE>
562 * @param action the action to perform
563 * @param page the page where the action will be applied. The first page is 1
564 * @throws PdfException if the action type is invalid
565 */
566 public void setPageAction(PdfName actionType, PdfAction action, int page) throws PdfException {
567 stamper.setPageAction(actionType, action, page);
568 }
569
570 /**
571 * Sets the display duration for the page (for presentations)
572 * @param seconds the number of seconds to display the page. A negative value removes the entry
573 * @param page the page where the duration will be applied. The first page is 1
574 */
575 public void setDuration(int seconds, int page) {
576 stamper.setDuration(seconds, page);
577 }
578
579 /**
580 * Sets the transition for the page
581 * @param transition the transition object. A <code>null</code> removes the transition
582 * @param page the page where the transition will be applied. The first page is 1
583 */
584 public void setTransition(PdfTransition transition, int page) {
585 stamper.setTransition(transition, page);
586 }
587
588 /**
589 * Applies a digital signature to a document, possibly as a new revision, making
590 * possible multiple signatures. The returned PdfStamper
591 * can be used normally as the signature is only applied when closing.
592 * <p>
593 * A possible use for adding a signature without invalidating an existing one is:
594 * <p>
595 * <pre>
596 * KeyStore ks = KeyStore.getInstance("pkcs12");
597 * ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
598 * String alias = (String)ks.aliases().nextElement();
599 * PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
600 * Certificate[] chain = ks.getCertificateChain(alias);
601 * PdfReader reader = new PdfReader("original.pdf");
602 * FileOutputStream fout = new FileOutputStream("signed.pdf");
603 * PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0', new
604 * File("/temp"), true);
605 * PdfSignatureAppearance sap = stp.getSignatureAppearance();
606 * sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
607 * sap.setReason("I'm the author");
608 * sap.setLocation("Lisbon");
609 * // comment next line to have an invisible signature
610 * sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
611 * stp.close();
612 * </pre>
613 * @param reader the original document
614 * @param os the output stream or <CODE>null</CODE> to keep the document in the temporary file
615 * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
616 * document
617 * @param tempFile location of the temporary file. If it's a directory a temporary file will be created there.
618 * If it's a file it will be used directly. The file will be deleted on exit unless <CODE>os</CODE> is null.
619 * In that case the document can be retrieved directly from the temporary file. If it's <CODE>null</CODE>
620 * no temporary file will be created and memory will be used
621 * @param append if <CODE>true</CODE> the signature and all the other content will be added as a
622 * new revision thus not invalidating existing signatures
623 * @return a <CODE>PdfStamper</CODE>
624 * @throws DocumentException on error
625 * @throws IOException on error
626 */
627 public static PdfStamper createSignature(PdfReader reader, OutputStream os, char pdfVersion, File tempFile, boolean append) throws DocumentException, IOException {
628 PdfStamper stp;
629 if (tempFile == null) {
630 ByteBuffer bout = new ByteBuffer();
631 stp = new PdfStamper(reader, bout, pdfVersion, append);
632 stp.sigApp = new PdfSignatureAppearance(stp.stamper);
633 stp.sigApp.setSigout(bout);
634 }
635 else {
636 if (tempFile.isDirectory())
637 tempFile = File.createTempFile("pdf", null, tempFile);
638 FileOutputStream fout = new FileOutputStream(tempFile);
639 stp = new PdfStamper(reader, fout, pdfVersion, append);
640 stp.sigApp = new PdfSignatureAppearance(stp.stamper);
641 stp.sigApp.setTempFile(tempFile);
642 }
643 stp.sigApp.setOriginalout(os);
644 stp.sigApp.setStamper(stp);
645 stp.hasSignature = true;
646 PdfDictionary catalog = reader.getCatalog();
647 PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.ACROFORM), catalog);
648 if (acroForm != null) {
649 acroForm.remove(PdfName.NEEDAPPEARANCES);
650 stp.stamper.markUsed(acroForm);
651 }
652 return stp;
653 }
654
655 /**
656 * Applies a digital signature to a document. The returned PdfStamper
657 * can be used normally as the signature is only applied when closing.
658 * <p>
659 * Note that the pdf is created in memory.
660 * <p>
661 * A possible use is:
662 * <p>
663 * <pre>
664 * KeyStore ks = KeyStore.getInstance("pkcs12");
665 * ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
666 * String alias = (String)ks.aliases().nextElement();
667 * PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
668 * Certificate[] chain = ks.getCertificateChain(alias);
669 * PdfReader reader = new PdfReader("original.pdf");
670 * FileOutputStream fout = new FileOutputStream("signed.pdf");
671 * PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
672 * PdfSignatureAppearance sap = stp.getSignatureAppearance();
673 * sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
674 * sap.setReason("I'm the author");
675 * sap.setLocation("Lisbon");
676 * // comment next line to have an invisible signature
677 * sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
678 * stp.close();
679 * </pre>
680 * @param reader the original document
681 * @param os the output stream
682 * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
683 * document
684 * @throws DocumentException on error
685 * @throws IOException on error
686 * @return a <CODE>PdfStamper</CODE>
687 */
688 public static PdfStamper createSignature(PdfReader reader, OutputStream os, char pdfVersion) throws DocumentException, IOException {
689 return createSignature(reader, os, pdfVersion, null, false);
690 }
691
692 /**
693 * Applies a digital signature to a document. The returned PdfStamper
694 * can be used normally as the signature is only applied when closing.
695 * <p>
696 * A possible use is:
697 * <p>
698 * <pre>
699 * KeyStore ks = KeyStore.getInstance("pkcs12");
700 * ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
701 * String alias = (String)ks.aliases().nextElement();
702 * PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
703 * Certificate[] chain = ks.getCertificateChain(alias);
704 * PdfReader reader = new PdfReader("original.pdf");
705 * FileOutputStream fout = new FileOutputStream("signed.pdf");
706 * PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0', new File("/temp"));
707 * PdfSignatureAppearance sap = stp.getSignatureAppearance();
708 * sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
709 * sap.setReason("I'm the author");
710 * sap.setLocation("Lisbon");
711 * // comment next line to have an invisible signature
712 * sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
713 * stp.close();
714 * </pre>
715 * @param reader the original document
716 * @param os the output stream or <CODE>null</CODE> to keep the document in the temporary file
717 * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
718 * document
719 * @param tempFile location of the temporary file. If it's a directory a temporary file will be created there.
720 * If it's a file it will be used directly. The file will be deleted on exit unless <CODE>os</CODE> is null.
721 * In that case the document can be retrieved directly from the temporary file. If it's <CODE>null</CODE>
722 * no temporary file will be created and memory will be used
723 * @return a <CODE>PdfStamper</CODE>
724 * @throws DocumentException on error
725 * @throws IOException on error
726 */
727 public static PdfStamper createSignature(PdfReader reader, OutputStream os, char pdfVersion, File tempFile) throws DocumentException, IOException
728 {
729 return createSignature(reader, os, pdfVersion, tempFile, false);
730 }
731
732 /**
733 * Gets the PdfLayer objects in an existing document as a Map
734 * with the names/titles of the layers as keys.
735 * @return a Map with all the PdfLayers in the document (and the name/title of the layer as key)
736 * @since 2.1.2
737 */
738 public Map getPdfLayers() {
739 return stamper.getPdfLayers();
740 }
741 }