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}