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