001/* 002 * $Id: PdfSignatureAppearance.java 4784 2011-03-15 08:33:00Z 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.EOFException; 047import java.io.File; 048import java.io.IOException; 049import java.io.InputStream; 050import java.io.OutputStream; 051import java.io.RandomAccessFile; 052import java.security.PrivateKey; 053import java.security.cert.CRL; 054import java.security.cert.Certificate; 055import java.security.cert.X509Certificate; 056import java.text.SimpleDateFormat; 057import java.util.Arrays; 058import java.util.Calendar; 059import java.util.GregorianCalendar; 060import java.util.HashMap; 061import java.util.Map; 062 063import com.itextpdf.text.Chunk; 064import com.itextpdf.text.DocumentException; 065import com.itextpdf.text.Element; 066import com.itextpdf.text.ExceptionConverter; 067import com.itextpdf.text.Font; 068import com.itextpdf.text.Image; 069import com.itextpdf.text.Paragraph; 070import com.itextpdf.text.Phrase; 071import com.itextpdf.text.Rectangle; 072import com.itextpdf.text.error_messages.MessageLocalization; 073 074/** 075 * This class takes care of the cryptographic options and appearances that form a signature. 076 */ 077public class PdfSignatureAppearance { 078 079 /** 080 * Signature rendering modes 081 * @since 5.0.1 082 */ 083 public enum RenderingMode { 084 /** 085 * The rendering mode is just the description. 086 */ 087 DESCRIPTION, 088 /** 089 * The rendering mode is the name of the signer and the description. 090 */ 091 NAME_AND_DESCRIPTION, 092 /** 093 * The rendering mode is an image and the description. 094 */ 095 GRAPHIC_AND_DESCRIPTION, 096 /** 097 * The rendering mode is just an image. 098 */ 099 GRAPHIC 100 } 101 102 /** 103 * The self signed filter. 104 */ 105 public static final PdfName SELF_SIGNED = PdfName.ADOBE_PPKLITE; 106 /** 107 * The VeriSign filter. 108 */ 109 public static final PdfName VERISIGN_SIGNED = PdfName.VERISIGN_PPKVS; 110 /** 111 * The Windows Certificate Security. 112 */ 113 public static final PdfName WINCER_SIGNED = PdfName.ADOBE_PPKMS; 114 115 public static final int NOT_CERTIFIED = 0; 116 public static final int CERTIFIED_NO_CHANGES_ALLOWED = 1; 117 public static final int CERTIFIED_FORM_FILLING = 2; 118 public static final int CERTIFIED_FORM_FILLING_AND_ANNOTATIONS = 3; 119 120 private static final float TOP_SECTION = 0.3f; 121 private static final float MARGIN = 2; 122 private Rectangle rect; 123 private Rectangle pageRect; 124 private PdfTemplate app[] = new PdfTemplate[5]; 125 private PdfTemplate frm; 126 private PdfStamperImp writer; 127 private String layer2Text; 128 private String reason; 129 private String location; 130 private Calendar signDate; 131 private String provider; 132 private int page = 1; 133 private String fieldName; 134 private PrivateKey privKey; 135 private Certificate[] certChain; 136 private CRL[] crlList; 137 private PdfName filter; 138 private boolean newField; 139 private ByteBuffer sigout; 140 private OutputStream originalout; 141 private File tempFile; 142 private PdfDictionary cryptoDictionary; 143 private PdfStamper stamper; 144 private boolean preClosed = false; 145 private PdfSigGenericPKCS sigStandard; 146 private int range[]; 147 private RandomAccessFile raf; 148 private byte bout[]; 149 private int boutLen; 150 private byte externalDigest[]; 151 private byte externalRSAdata[]; 152 private String digestEncryptionAlgorithm; 153 private HashMap<PdfName, PdfLiteral> exclusionLocations; 154 155 PdfSignatureAppearance(PdfStamperImp writer) { 156 this.writer = writer; 157 signDate = new GregorianCalendar(); 158 fieldName = getNewSigName(); 159 } 160 161 private RenderingMode renderingMode = RenderingMode.DESCRIPTION; 162 163 /** 164 * Gets the rendering mode for this signature. 165 * @return the rendering mode for this signature 166 * @since 5.0.1 167 */ 168 public RenderingMode getRenderingMode() { 169 return renderingMode; 170 } 171 172 /** 173 * Sets the rendering mode for this signature. 174 * @param renderingMode the rendering mode 175 * @since 5.0.1 176 */ 177 public void setRenderingMode(RenderingMode renderingMode) { 178 this.renderingMode = renderingMode; 179 } 180 181 private Image signatureGraphic = null; 182 183 /** 184 * Gets the Image object to render. 185 * @return the image 186 */ 187 public Image getSignatureGraphic() { 188 return signatureGraphic; 189 } 190 191 /** 192 * Sets the Image object to render when Render is set to <CODE>RenderingMode.GRAPHIC</CODE> 193 * or <CODE>RenderingMode.GRAPHIC_AND_DESCRIPTION</CODE>. 194 * @param signatureGraphic image rendered. If <CODE>null</CODE> the mode is defaulted 195 * to <CODE>RenderingMode.DESCRIPTION</CODE> 196 */ 197 public void setSignatureGraphic(Image signatureGraphic) { 198 this.signatureGraphic = signatureGraphic; 199 } 200 201 /** 202 * Sets the signature text identifying the signer. 203 * @param text the signature text identifying the signer. If <CODE>null</CODE> or not set 204 * a standard description will be used 205 */ 206 public void setLayer2Text(String text) { 207 layer2Text = text; 208 } 209 210 /** 211 * Gets the signature text identifying the signer if set by setLayer2Text(). 212 * @return the signature text identifying the signer 213 */ 214 public String getLayer2Text() { 215 return layer2Text; 216 } 217 218 /** 219 * Sets the text identifying the signature status. 220 * @param text the text identifying the signature status. If <CODE>null</CODE> or not set 221 * the description "Signature Not Verified" will be used 222 */ 223 public void setLayer4Text(String text) { 224 layer4Text = text; 225 } 226 227 /** 228 * Gets the text identifying the signature status if set by setLayer4Text(). 229 * @return the text identifying the signature status 230 */ 231 public String getLayer4Text() { 232 return layer4Text; 233 } 234 235 /** 236 * Gets the rectangle representing the signature dimensions. 237 * @return the rectangle representing the signature dimensions. It may be <CODE>null</CODE> 238 * or have zero width or height for invisible signatures 239 */ 240 public Rectangle getRect() { 241 return rect; 242 } 243 244 /** 245 * Gets the visibility status of the signature. 246 * @return the visibility status of the signature 247 */ 248 public boolean isInvisible() { 249 return rect == null || rect.getWidth() == 0 || rect.getHeight() == 0; 250 } 251 252 /** 253 * Sets the cryptographic parameters. 254 * @param privKey the private key 255 * @param certChain the certificate chain 256 * @param crlList the certificate revocation list. It may be <CODE>null</CODE> 257 * @param filter the cryptographic filter type. It can be SELF_SIGNED, VERISIGN_SIGNED or WINCER_SIGNED 258 */ 259 public void setCrypto(PrivateKey privKey, Certificate[] certChain, CRL[] crlList, PdfName filter) { 260 this.privKey = privKey; 261 this.certChain = certChain; 262 this.crlList = crlList; 263 this.filter = filter; 264 } 265 266 /** 267 * Sets the signature to be visible. It creates a new visible signature field. 268 * @param pageRect the position and dimension of the field in the page 269 * @param page the page to place the field. The fist page is 1 270 * @param fieldName the field name or <CODE>null</CODE> to generate automatically a new field name 271 */ 272 public void setVisibleSignature(Rectangle pageRect, int page, String fieldName) { 273 if (fieldName != null) { 274 if (fieldName.indexOf('.') >= 0) 275 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("field.names.cannot.contain.a.dot")); 276 AcroFields af = writer.getAcroFields(); 277 AcroFields.Item item = af.getFieldItem(fieldName); 278 if (item != null) 279 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.field.1.already.exists", fieldName)); 280 this.fieldName = fieldName; 281 } 282 if (page < 1 || page > writer.reader.getNumberOfPages()) 283 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("invalid.page.number.1", page)); 284 this.pageRect = new Rectangle(pageRect); 285 this.pageRect.normalize(); 286 rect = new Rectangle(this.pageRect.getWidth(), this.pageRect.getHeight()); 287 this.page = page; 288 newField = true; 289 } 290 291 /** 292 * Sets the signature to be visible. An empty signature field with the same name must already exist. 293 * @param fieldName the existing empty signature field name 294 */ 295 public void setVisibleSignature(String fieldName) { 296 AcroFields af = writer.getAcroFields(); 297 AcroFields.Item item = af.getFieldItem(fieldName); 298 if (item == null) 299 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.field.1.does.not.exist", fieldName)); 300 PdfDictionary merged = item.getMerged(0); 301 if (!PdfName.SIG.equals(PdfReader.getPdfObject(merged.get(PdfName.FT)))) 302 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.field.1.is.not.a.signature.field", fieldName)); 303 this.fieldName = fieldName; 304 PdfArray r = merged.getAsArray(PdfName.RECT); 305 float llx = r.getAsNumber(0).floatValue(); 306 float lly = r.getAsNumber(1).floatValue(); 307 float urx = r.getAsNumber(2).floatValue(); 308 float ury = r.getAsNumber(3).floatValue(); 309 pageRect = new Rectangle(llx, lly, urx, ury); 310 pageRect.normalize(); 311 page = item.getPage(0).intValue(); 312 int rotation = writer.reader.getPageRotation(page); 313 Rectangle pageSize = writer.reader.getPageSizeWithRotation(page); 314 switch (rotation) { 315 case 90: 316 pageRect = new Rectangle( 317 pageRect.getBottom(), 318 pageSize.getTop() - pageRect.getLeft(), 319 pageRect.getTop(), 320 pageSize.getTop() - pageRect.getRight()); 321 break; 322 case 180: 323 pageRect = new Rectangle( 324 pageSize.getRight() - pageRect.getLeft(), 325 pageSize.getTop() - pageRect.getBottom(), 326 pageSize.getRight() - pageRect.getRight(), 327 pageSize.getTop() - pageRect.getTop()); 328 break; 329 case 270: 330 pageRect = new Rectangle( 331 pageSize.getRight() - pageRect.getBottom(), 332 pageRect.getLeft(), 333 pageSize.getRight() - pageRect.getTop(), 334 pageRect.getRight()); 335 break; 336 } 337 if (rotation != 0) 338 pageRect.normalize(); 339 rect = new Rectangle(this.pageRect.getWidth(), this.pageRect.getHeight()); 340 } 341 342 /** 343 * Gets a template layer to create a signature appearance. The layers can go from 0 to 4. 344 * <p> 345 * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A> 346 * for further details. 347 * @param layer the layer 348 * @return a template 349 */ 350 public PdfTemplate getLayer(int layer) { 351 if (layer < 0 || layer >= app.length) 352 return null; 353 PdfTemplate t = app[layer]; 354 if (t == null) { 355 t = app[layer] = new PdfTemplate(writer); 356 t.setBoundingBox(rect); 357 writer.addDirectTemplateSimple(t, new PdfName("n" + layer)); 358 } 359 return t; 360 } 361 362 /** 363 * Gets the template that aggregates all appearance layers. This corresponds to the /FRM resource. 364 * <p> 365 * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A> 366 * for further details. 367 * @return the template that aggregates all appearance layers 368 */ 369 public PdfTemplate getTopLayer() { 370 if (frm == null) { 371 frm = new PdfTemplate(writer); 372 frm.setBoundingBox(rect); 373 writer.addDirectTemplateSimple(frm, new PdfName("FRM")); 374 } 375 return frm; 376 } 377 378 /** 379 * Gets the main appearance layer. 380 * <p> 381 * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A> 382 * for further details. 383 * @return the main appearance layer 384 * @throws DocumentException on error 385 */ 386 public PdfTemplate getAppearance() throws DocumentException { 387 if (isInvisible()) { 388 PdfTemplate t = new PdfTemplate(writer); 389 t.setBoundingBox(new Rectangle(0, 0)); 390 writer.addDirectTemplateSimple(t, null); 391 return t; 392 } 393 if (app[0] == null) { 394 PdfTemplate t = app[0] = new PdfTemplate(writer); 395 t.setBoundingBox(new Rectangle(100, 100)); 396 writer.addDirectTemplateSimple(t, new PdfName("n0")); 397 t.setLiteral("% DSBlank\n"); 398 } 399 if (app[1] == null && !acro6Layers) { 400 PdfTemplate t = app[1] = new PdfTemplate(writer); 401 t.setBoundingBox(new Rectangle(100, 100)); 402 writer.addDirectTemplateSimple(t, new PdfName("n1")); 403 t.setLiteral(questionMark); 404 } 405 if (app[2] == null) { 406 String text; 407 if (layer2Text == null) { 408 StringBuffer buf = new StringBuffer(); 409 buf.append("Digitally signed by ").append(PdfPKCS7.getSubjectFields((X509Certificate)certChain[0]).getField("CN")).append('\n'); 410 SimpleDateFormat sd = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z"); 411 buf.append("Date: ").append(sd.format(signDate.getTime())); 412 if (reason != null) 413 buf.append('\n').append("Reason: ").append(reason); 414 if (location != null) 415 buf.append('\n').append("Location: ").append(location); 416 text = buf.toString(); 417 } 418 else 419 text = layer2Text; 420 PdfTemplate t = app[2] = new PdfTemplate(writer); 421 t.setBoundingBox(rect); 422 writer.addDirectTemplateSimple(t, new PdfName("n2")); 423 if (image != null) { 424 if (imageScale == 0) { 425 t.addImage(image, rect.getWidth(), 0, 0, rect.getHeight(), 0, 0); 426 } 427 else { 428 float usableScale = imageScale; 429 if (imageScale < 0) 430 usableScale = Math.min(rect.getWidth() / image.getWidth(), rect.getHeight() / image.getHeight()); 431 float w = image.getWidth() * usableScale; 432 float h = image.getHeight() * usableScale; 433 float x = (rect.getWidth() - w) / 2; 434 float y = (rect.getHeight() - h) / 2; 435 t.addImage(image, w, 0, 0, h, x, y); 436 } 437 } 438 Font font; 439 if (layer2Font == null) 440 font = new Font(); 441 else 442 font = new Font(layer2Font); 443 float size = font.getSize(); 444 445 Rectangle dataRect = null; 446 Rectangle signatureRect = null; 447 448 if (renderingMode == RenderingMode.NAME_AND_DESCRIPTION || 449 renderingMode == RenderingMode.GRAPHIC_AND_DESCRIPTION && this.signatureGraphic != null) { 450 // origin is the bottom-left 451 signatureRect = new Rectangle( 452 MARGIN, 453 MARGIN, 454 rect.getWidth() / 2 - MARGIN, 455 rect.getHeight() - MARGIN); 456 dataRect = new Rectangle( 457 rect.getWidth() / 2 + MARGIN / 2, 458 MARGIN, 459 rect.getWidth() - MARGIN / 2, 460 rect.getHeight() - MARGIN); 461 462 if (rect.getHeight() > rect.getWidth()) { 463 signatureRect = new Rectangle( 464 MARGIN, 465 rect.getHeight() / 2, 466 rect.getWidth() - MARGIN, 467 rect.getHeight()); 468 dataRect = new Rectangle( 469 MARGIN, 470 MARGIN, 471 rect.getWidth() - MARGIN, 472 rect.getHeight() / 2 - MARGIN); 473 } 474 } 475 else if (renderingMode == RenderingMode.GRAPHIC) { 476 if (signatureGraphic == null) { 477 throw new IllegalStateException(MessageLocalization.getComposedMessage("a.signature.image.should.be.present.when.rendering.mode.is.graphic.only")); 478 } 479 signatureRect = new Rectangle( 480 MARGIN, 481 MARGIN, 482 rect.getWidth() - MARGIN, // take all space available 483 rect.getHeight() - MARGIN); 484 } 485 else { 486 dataRect = new Rectangle( 487 MARGIN, 488 MARGIN, 489 rect.getWidth() - MARGIN, 490 rect.getHeight() * (1 - TOP_SECTION) - MARGIN); 491 } 492 493 switch (renderingMode) { 494 case NAME_AND_DESCRIPTION: 495 String signedBy = PdfPKCS7.getSubjectFields((X509Certificate)certChain[0]).getField("CN"); 496 Rectangle sr2 = new Rectangle(signatureRect.getWidth() - MARGIN, signatureRect.getHeight() - MARGIN ); 497 float signedSize = fitText(font, signedBy, sr2, -1, runDirection); 498 499 ColumnText ct2 = new ColumnText(t); 500 ct2.setRunDirection(runDirection); 501 ct2.setSimpleColumn(new Phrase(signedBy, font), signatureRect.getLeft(), signatureRect.getBottom(), signatureRect.getRight(), signatureRect.getTop(), signedSize, Element.ALIGN_LEFT); 502 503 ct2.go(); 504 break; 505 case GRAPHIC_AND_DESCRIPTION: 506 ct2 = new ColumnText(t); 507 ct2.setRunDirection(runDirection); 508 ct2.setSimpleColumn(signatureRect.getLeft(), signatureRect.getBottom(), signatureRect.getRight(), signatureRect.getTop(), 0, Element.ALIGN_RIGHT); 509 510 Image im = Image.getInstance(signatureGraphic); 511 im.scaleToFit(signatureRect.getWidth(), signatureRect.getHeight()); 512 513 Paragraph p = new Paragraph(); 514 // must calculate the point to draw from to make image appear in middle of column 515 float x = 0; 516 // experimentation found this magic number to counteract Adobe's signature graphic, which 517 // offsets the y co-ordinate by 15 units 518 float y = -im.getScaledHeight() + 15; 519 520 x = x + (signatureRect.getWidth() - im.getScaledWidth()) / 2; 521 y = y - (signatureRect.getHeight() - im.getScaledHeight()) / 2; 522 p.add(new Chunk(im, x + (signatureRect.getWidth() - im.getScaledWidth()) / 2, y, false)); 523 ct2.addElement(p); 524 ct2.go(); 525 break; 526 case GRAPHIC: 527 ct2 = new ColumnText(t); 528 ct2.setRunDirection(runDirection); 529 ct2.setSimpleColumn(signatureRect.getLeft(), signatureRect.getBottom(), signatureRect.getRight(), signatureRect.getTop(), 0, Element.ALIGN_RIGHT); 530 531 im = Image.getInstance(signatureGraphic); 532 im.scaleToFit(signatureRect.getWidth(), signatureRect.getHeight()); 533 534 p = new Paragraph(); 535 // must calculate the point to draw from to make image appear in middle of column 536 x = 0; 537 // experimentation found this magic number to counteract Adobe's signature graphic, which 538 // offsets the y co-ordinate by 15 units 539 y = -im.getScaledHeight() + 15; 540 541 x = x + (signatureRect.getWidth() - im.getScaledWidth()) / 2; 542 y = y - (signatureRect.getHeight() - im.getScaledHeight()) / 2; 543 p.add(new Chunk(im, x, y, false)); 544 ct2.addElement(p); 545 ct2.go(); 546 break; 547 default: 548 } 549 550 if(renderingMode != RenderingMode.GRAPHIC) { 551 if (size <= 0) { 552 Rectangle sr = new Rectangle(dataRect.getWidth(), dataRect.getHeight()); 553 size = fitText(font, text, sr, 12, runDirection); 554 } 555 ColumnText ct = new ColumnText(t); 556 ct.setRunDirection(runDirection); 557 ct.setSimpleColumn(new Phrase(text, font), dataRect.getLeft(), dataRect.getBottom(), dataRect.getRight(), dataRect.getTop(), size, Element.ALIGN_LEFT); 558 ct.go(); 559 } 560 } 561 if (app[3] == null && !acro6Layers) { 562 PdfTemplate t = app[3] = new PdfTemplate(writer); 563 t.setBoundingBox(new Rectangle(100, 100)); 564 writer.addDirectTemplateSimple(t, new PdfName("n3")); 565 t.setLiteral("% DSBlank\n"); 566 } 567 if (app[4] == null && !acro6Layers) { 568 PdfTemplate t = app[4] = new PdfTemplate(writer); 569 t.setBoundingBox(new Rectangle(0, rect.getHeight() * (1 - TOP_SECTION), rect.getRight(), rect.getTop())); 570 writer.addDirectTemplateSimple(t, new PdfName("n4")); 571 Font font; 572 if (layer2Font == null) 573 font = new Font(); 574 else 575 font = new Font(layer2Font); 576 float size = font.getSize(); 577 String text = "Signature Not Verified"; 578 if (layer4Text != null) 579 text = layer4Text; 580 Rectangle sr = new Rectangle(rect.getWidth() - 2 * MARGIN, rect.getHeight() * TOP_SECTION - 2 * MARGIN); 581 size = fitText(font, text, sr, 15, runDirection); 582 ColumnText ct = new ColumnText(t); 583 ct.setRunDirection(runDirection); 584 ct.setSimpleColumn(new Phrase(text, font), MARGIN, 0, rect.getWidth() - MARGIN, rect.getHeight() - MARGIN, size, Element.ALIGN_LEFT); 585 ct.go(); 586 } 587 int rotation = writer.reader.getPageRotation(page); 588 Rectangle rotated = new Rectangle(rect); 589 int n = rotation; 590 while (n > 0) { 591 rotated = rotated.rotate(); 592 n -= 90; 593 } 594 if (frm == null) { 595 frm = new PdfTemplate(writer); 596 frm.setBoundingBox(rotated); 597 writer.addDirectTemplateSimple(frm, new PdfName("FRM")); 598 float scale = Math.min(rect.getWidth(), rect.getHeight()) * 0.9f; 599 float x = (rect.getWidth() - scale) / 2; 600 float y = (rect.getHeight() - scale) / 2; 601 scale /= 100; 602 if (rotation == 90) 603 frm.concatCTM(0, 1, -1, 0, rect.getHeight(), 0); 604 else if (rotation == 180) 605 frm.concatCTM(-1, 0, 0, -1, rect.getWidth(), rect.getHeight()); 606 else if (rotation == 270) 607 frm.concatCTM(0, -1, 1, 0, 0, rect.getWidth()); 608 frm.addTemplate(app[0], 0, 0); 609 if (!acro6Layers) 610 frm.addTemplate(app[1], scale, 0, 0, scale, x, y); 611 frm.addTemplate(app[2], 0, 0); 612 if (!acro6Layers) { 613 frm.addTemplate(app[3], scale, 0, 0, scale, x, y); 614 frm.addTemplate(app[4], 0, 0); 615 } 616 } 617 PdfTemplate napp = new PdfTemplate(writer); 618 napp.setBoundingBox(rotated); 619 writer.addDirectTemplateSimple(napp, null); 620 napp.addTemplate(frm, 0, 0); 621 return napp; 622 } 623 624 /** 625 * Fits the text to some rectangle adjusting the font size as needed. 626 * @param font the font to use 627 * @param text the text 628 * @param rect the rectangle where the text must fit 629 * @param maxFontSize the maximum font size 630 * @param runDirection the run direction 631 * @return the calculated font size that makes the text fit 632 */ 633 public static float fitText(Font font, String text, Rectangle rect, float maxFontSize, int runDirection) { 634 try { 635 ColumnText ct = null; 636 int status = 0; 637 if (maxFontSize <= 0) { 638 int cr = 0; 639 int lf = 0; 640 char t[] = text.toCharArray(); 641 for (int k = 0; k < t.length; ++k) { 642 if (t[k] == '\n') 643 ++lf; 644 else if (t[k] == '\r') 645 ++cr; 646 } 647 int minLines = Math.max(cr, lf) + 1; 648 maxFontSize = Math.abs(rect.getHeight()) / minLines - 0.001f; 649 } 650 font.setSize(maxFontSize); 651 Phrase ph = new Phrase(text, font); 652 ct = new ColumnText(null); 653 ct.setSimpleColumn(ph, rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop(), maxFontSize, Element.ALIGN_LEFT); 654 ct.setRunDirection(runDirection); 655 status = ct.go(true); 656 if ((status & ColumnText.NO_MORE_TEXT) != 0) 657 return maxFontSize; 658 float precision = 0.1f; 659 float min = 0; 660 float max = maxFontSize; 661 float size = maxFontSize; 662 for (int k = 0; k < 50; ++k) { //just in case it doesn't converge 663 size = (min + max) / 2; 664 ct = new ColumnText(null); 665 font.setSize(size); 666 ct.setSimpleColumn(new Phrase(text, font), rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop(), size, Element.ALIGN_LEFT); 667 ct.setRunDirection(runDirection); 668 status = ct.go(true); 669 if ((status & ColumnText.NO_MORE_TEXT) != 0) { 670 if (max - min < size * precision) 671 return size; 672 min = size; 673 } 674 else 675 max = size; 676 } 677 return size; 678 } 679 catch (Exception e) { 680 throw new ExceptionConverter(e); 681 } 682 } 683 684 /** 685 * Sets the digest/signature to an external calculated value. 686 * @param digest the digest. This is the actual signature 687 * @param RSAdata the extra data that goes into the data tag in PKCS#7 688 * @param digestEncryptionAlgorithm the encryption algorithm. It may must be <CODE>null</CODE> if the <CODE>digest</CODE> 689 * is also <CODE>null</CODE>. If the <CODE>digest</CODE> is not <CODE>null</CODE> 690 * then it may be "RSA" or "DSA" 691 */ 692 public void setExternalDigest(byte digest[], byte RSAdata[], String digestEncryptionAlgorithm) { 693 externalDigest = digest; 694 externalRSAdata = RSAdata; 695 this.digestEncryptionAlgorithm = digestEncryptionAlgorithm; 696 } 697 698 /** 699 * Gets the signing reason. 700 * @return the signing reason 701 */ 702 public String getReason() { 703 return this.reason; 704 } 705 706 /** 707 * Sets the signing reason. 708 * @param reason the signing reason 709 */ 710 public void setReason(String reason) { 711 this.reason = reason; 712 } 713 714 /** 715 * Gets the signing location. 716 * @return the signing location 717 */ 718 public String getLocation() { 719 return this.location; 720 } 721 722 /** 723 * Sets the signing location. 724 * @param location the signing location 725 */ 726 public void setLocation(String location) { 727 this.location = location; 728 } 729 730 /** 731 * Returns the Cryptographic Service Provider that will sign the document. 732 * @return provider the name of the provider, for example "SUN", 733 * or <code>null</code> to use the default provider. 734 */ 735 public String getProvider() { 736 return this.provider; 737 } 738 739 /** 740 * Sets the Cryptographic Service Provider that will sign the document. 741 * 742 * @param provider the name of the provider, for example "SUN", or 743 * <code>null</code> to use the default provider. 744 */ 745 public void setProvider(String provider) { 746 this.provider = provider; 747 } 748 749 /** 750 * Gets the private key. 751 * @return the private key 752 */ 753 public java.security.PrivateKey getPrivKey() { 754 return privKey; 755 } 756 757 /** 758 * Gets the certificate chain. 759 * @return the certificate chain 760 */ 761 public java.security.cert.Certificate[] getCertChain() { 762 return this.certChain; 763 } 764 765 /** 766 * Gets the certificate revocation list. 767 * @return the certificate revocation list 768 */ 769 public java.security.cert.CRL[] getCrlList() { 770 return this.crlList; 771 } 772 773 /** 774 * Gets the filter used to sign the document. 775 * @return the filter used to sign the document 776 */ 777 public com.itextpdf.text.pdf.PdfName getFilter() { 778 return filter; 779 } 780 781 /** 782 * Checks if a new field was created. 783 * @return <CODE>true</CODE> if a new field was created, <CODE>false</CODE> if signing 784 * an existing field or if the signature is invisible 785 */ 786 public boolean isNewField() { 787 return this.newField; 788 } 789 790 /** 791 * Gets the page number of the field. 792 * @return the page number of the field 793 */ 794 public int getPage() { 795 return page; 796 } 797 798 /** 799 * Gets the field name. 800 * @return the field name 801 */ 802 public java.lang.String getFieldName() { 803 return fieldName; 804 } 805 806 /** 807 * Gets the rectangle that represent the position and dimension of the signature in the page. 808 * @return the rectangle that represent the position and dimension of the signature in the page 809 */ 810 public com.itextpdf.text.Rectangle getPageRect() { 811 return pageRect; 812 } 813 814 /** 815 * Gets the signature date. 816 * @return the signature date 817 */ 818 public java.util.Calendar getSignDate() { 819 return signDate; 820 } 821 822 /** 823 * Sets the signature date. 824 * @param signDate the signature date 825 */ 826 public void setSignDate(java.util.Calendar signDate) { 827 this.signDate = signDate; 828 } 829 830 com.itextpdf.text.pdf.ByteBuffer getSigout() { 831 return sigout; 832 } 833 834 void setSigout(com.itextpdf.text.pdf.ByteBuffer sigout) { 835 this.sigout = sigout; 836 } 837 838 java.io.OutputStream getOriginalout() { 839 return originalout; 840 } 841 842 void setOriginalout(java.io.OutputStream originalout) { 843 this.originalout = originalout; 844 } 845 846 /** 847 * Gets the temporary file. 848 * @return the temporary file or <CODE>null</CODE> is the document is created in memory 849 */ 850 public java.io.File getTempFile() { 851 return tempFile; 852 } 853 854 void setTempFile(java.io.File tempFile) { 855 this.tempFile = tempFile; 856 } 857 858 /** 859 * Gets a new signature fied name that doesn't clash with any existing name. 860 * @return a new signature fied name 861 */ 862 public String getNewSigName() { 863 AcroFields af = writer.getAcroFields(); 864 String name = "Signature"; 865 int step = 0; 866 boolean found = false; 867 while (!found) { 868 ++step; 869 String n1 = name + step; 870 if (af.getFieldItem(n1) != null) 871 continue; 872 n1 += "."; 873 found = true; 874 for (Object element : af.getFields().keySet()) { 875 String fn = (String)element; 876 if (fn.startsWith(n1)) { 877 found = false; 878 break; 879 } 880 } 881 } 882 name += step; 883 return name; 884 } 885 886 /** 887 * This is the first method to be called when using external signatures. The general sequence is: 888 * preClose(), getDocumentBytes() and close(). 889 * <p> 890 * If calling preClose() <B>dont't</B> call PdfStamper.close(). 891 * <p> 892 * No external signatures are allowed if this method is called. 893 * @throws IOException on error 894 * @throws DocumentException on error 895 */ 896 public void preClose() throws IOException, DocumentException { 897 preClose(null); 898 } 899 /** 900 * This is the first method to be called when using external signatures. The general sequence is: 901 * preClose(), getDocumentBytes() and close(). 902 * <p> 903 * If calling preClose() <B>dont't</B> call PdfStamper.close(). 904 * <p> 905 * If using an external signature <CODE>exclusionSizes</CODE> must contain at least 906 * the <CODE>PdfName.CONTENTS</CODE> key with the size that it will take in the 907 * document. Note that due to the hex string coding this size should be 908 * byte_size*2+2. 909 * @param exclusionSizes a <CODE>HashMap</CODE> with names and sizes to be excluded in the signature 910 * calculation. The key is a <CODE>PdfName</CODE> and the value an 911 * <CODE>Integer</CODE>. At least the <CODE>PdfName.CONTENTS</CODE> must be present 912 * @throws IOException on error 913 * @throws DocumentException on error 914 */ 915 public void preClose(HashMap<PdfName, Integer> exclusionSizes) throws IOException, DocumentException { 916 if (preClosed) 917 throw new DocumentException(MessageLocalization.getComposedMessage("document.already.pre.closed")); 918 preClosed = true; 919 AcroFields af = writer.getAcroFields(); 920 String name = getFieldName(); 921 boolean fieldExists = !(isInvisible() || isNewField()); 922 PdfIndirectReference refSig = writer.getPdfIndirectReference(); 923 writer.setSigFlags(3); 924 if (fieldExists) { 925 PdfDictionary widget = af.getFieldItem(name).getWidget(0); 926 writer.markUsed(widget); 927 widget.put(PdfName.P, writer.getPageReference(getPage())); 928 widget.put(PdfName.V, refSig); 929 PdfObject obj = PdfReader.getPdfObjectRelease(widget.get(PdfName.F)); 930 int flags = 0; 931 if (obj != null && obj.isNumber()) 932 flags = ((PdfNumber)obj).intValue(); 933 flags |= PdfAnnotation.FLAGS_LOCKED; 934 widget.put(PdfName.F, new PdfNumber(flags)); 935 PdfDictionary ap = new PdfDictionary(); 936 ap.put(PdfName.N, getAppearance().getIndirectReference()); 937 widget.put(PdfName.AP, ap); 938 } 939 else { 940 PdfFormField sigField = PdfFormField.createSignature(writer); 941 sigField.setFieldName(name); 942 sigField.put(PdfName.V, refSig); 943 sigField.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_LOCKED); 944 945 int pagen = getPage(); 946 if (!isInvisible()) 947 sigField.setWidget(getPageRect(), null); 948 else 949 sigField.setWidget(new Rectangle(0, 0), null); 950 sigField.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, getAppearance()); 951 sigField.setPage(pagen); 952 writer.addAnnotation(sigField, pagen); 953 } 954 955 exclusionLocations = new HashMap<PdfName, PdfLiteral>(); 956 if (cryptoDictionary == null) { 957 if (PdfName.ADOBE_PPKLITE.equals(getFilter())) 958 sigStandard = new PdfSigGenericPKCS.PPKLite(getProvider()); 959 else if (PdfName.ADOBE_PPKMS.equals(getFilter())) 960 sigStandard = new PdfSigGenericPKCS.PPKMS(getProvider()); 961 else if (PdfName.VERISIGN_PPKVS.equals(getFilter())) 962 sigStandard = new PdfSigGenericPKCS.VeriSign(getProvider()); 963 else 964 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("unknown.filter.1", getFilter())); 965 sigStandard.setExternalDigest(externalDigest, externalRSAdata, digestEncryptionAlgorithm); 966 if (getReason() != null) 967 sigStandard.setReason(getReason()); 968 if (getLocation() != null) 969 sigStandard.setLocation(getLocation()); 970 if (getContact() != null) 971 sigStandard.setContact(getContact()); 972 sigStandard.put(PdfName.M, new PdfDate(getSignDate())); 973 sigStandard.setSignInfo(getPrivKey(), getCertChain(), getCrlList()); 974 PdfString contents = (PdfString)sigStandard.get(PdfName.CONTENTS); 975 PdfLiteral lit = new PdfLiteral((contents.toString().length() + (PdfName.ADOBE_PPKLITE.equals(getFilter())?0:64)) * 2 + 2); 976 exclusionLocations.put(PdfName.CONTENTS, lit); 977 sigStandard.put(PdfName.CONTENTS, lit); 978 lit = new PdfLiteral(80); 979 exclusionLocations.put(PdfName.BYTERANGE, lit); 980 sigStandard.put(PdfName.BYTERANGE, lit); 981 if (certificationLevel > 0) { 982 addDocMDP(sigStandard); 983 } 984 if (signatureEvent != null) 985 signatureEvent.getSignatureDictionary(sigStandard); 986 writer.addToBody(sigStandard, refSig, false); 987 } 988 else { 989 PdfLiteral lit = new PdfLiteral(80); 990 exclusionLocations.put(PdfName.BYTERANGE, lit); 991 cryptoDictionary.put(PdfName.BYTERANGE, lit); 992 for (Map.Entry<PdfName, Integer> entry: exclusionSizes.entrySet()) { 993 PdfName key = entry.getKey(); 994 Integer v = entry.getValue(); 995 lit = new PdfLiteral(v.intValue()); 996 exclusionLocations.put(key, lit); 997 cryptoDictionary.put(key, lit); 998 } 999 if (certificationLevel > 0) 1000 addDocMDP(cryptoDictionary); 1001 if (signatureEvent != null) 1002 signatureEvent.getSignatureDictionary(cryptoDictionary); 1003 writer.addToBody(cryptoDictionary, refSig, false); 1004 } 1005 if (certificationLevel > 0) { 1006 // add DocMDP entry to root 1007 PdfDictionary docmdp = new PdfDictionary(); 1008 docmdp.put(new PdfName("DocMDP"), refSig); 1009 writer.reader.getCatalog().put(new PdfName("Perms"), docmdp); 1010 } 1011 writer.close(stamper.getMoreInfo()); 1012 1013 range = new int[exclusionLocations.size() * 2]; 1014 int byteRangePosition = exclusionLocations.get(PdfName.BYTERANGE).getPosition(); 1015 exclusionLocations.remove(PdfName.BYTERANGE); 1016 int idx = 1; 1017 for (PdfLiteral lit: exclusionLocations.values()) { 1018 int n = lit.getPosition(); 1019 range[idx++] = n; 1020 range[idx++] = lit.getPosLength() + n; 1021 } 1022 Arrays.sort(range, 1, range.length - 1); 1023 for (int k = 3; k < range.length - 2; k += 2) 1024 range[k] -= range[k - 1]; 1025 1026 if (tempFile == null) { 1027 bout = sigout.getBuffer(); 1028 boutLen = sigout.size(); 1029 range[range.length - 1] = boutLen - range[range.length - 2]; 1030 ByteBuffer bf = new ByteBuffer(); 1031 bf.append('['); 1032 for (int k = 0; k < range.length; ++k) 1033 bf.append(range[k]).append(' '); 1034 bf.append(']'); 1035 System.arraycopy(bf.getBuffer(), 0, bout, byteRangePosition, bf.size()); 1036 } 1037 else { 1038 try { 1039 raf = new RandomAccessFile(tempFile, "rw"); 1040 int boutLen = (int)raf.length(); 1041 range[range.length - 1] = boutLen - range[range.length - 2]; 1042 ByteBuffer bf = new ByteBuffer(); 1043 bf.append('['); 1044 for (int k = 0; k < range.length; ++k) 1045 bf.append(range[k]).append(' '); 1046 bf.append(']'); 1047 raf.seek(byteRangePosition); 1048 raf.write(bf.getBuffer(), 0, bf.size()); 1049 } 1050 catch (IOException e) { 1051 try{raf.close();}catch(Exception ee){} 1052 try{tempFile.delete();}catch(Exception ee){} 1053 throw e; 1054 } 1055 } 1056 } 1057 1058 /** 1059 * This is the last method to be called when using external signatures. The general sequence is: 1060 * preClose(), getDocumentBytes() and close(). 1061 * <p> 1062 * <CODE>update</CODE> is a <CODE>PdfDictionary</CODE> that must have exactly the 1063 * same keys as the ones provided in {@link #preClose(HashMap)}. 1064 * @param update a <CODE>PdfDictionary</CODE> with the key/value that will fill the holes defined 1065 * in {@link #preClose(HashMap)} 1066 * @throws DocumentException on error 1067 * @throws IOException on error 1068 */ 1069 public void close(PdfDictionary update) throws IOException, DocumentException { 1070 try { 1071 if (!preClosed) 1072 throw new DocumentException(MessageLocalization.getComposedMessage("preclose.must.be.called.first")); 1073 ByteBuffer bf = new ByteBuffer(); 1074 for (PdfName key: update.getKeys()) { 1075 PdfObject obj = update.get(key); 1076 PdfLiteral lit = exclusionLocations.get(key); 1077 if (lit == null) 1078 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.key.1.didn.t.reserve.space.in.preclose", key.toString())); 1079 bf.reset(); 1080 obj.toPdf(null, bf); 1081 if (bf.size() > lit.getPosLength()) 1082 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.key.1.is.too.big.is.2.reserved.3", key.toString(), String.valueOf(bf.size()), String.valueOf(lit.getPosLength()))); 1083 if (tempFile == null) 1084 System.arraycopy(bf.getBuffer(), 0, bout, lit.getPosition(), bf.size()); 1085 else { 1086 raf.seek(lit.getPosition()); 1087 raf.write(bf.getBuffer(), 0, bf.size()); 1088 } 1089 } 1090 if (update.size() != exclusionLocations.size()) 1091 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.update.dictionary.has.less.keys.than.required")); 1092 if (tempFile == null) { 1093 originalout.write(bout, 0, boutLen); 1094 } 1095 else { 1096 if (originalout != null) { 1097 raf.seek(0); 1098 int length = (int)raf.length(); 1099 byte buf[] = new byte[8192]; 1100 while (length > 0) { 1101 int r = raf.read(buf, 0, Math.min(buf.length, length)); 1102 if (r < 0) 1103 throw new EOFException(MessageLocalization.getComposedMessage("unexpected.eof")); 1104 originalout.write(buf, 0, r); 1105 length -= r; 1106 } 1107 } 1108 } 1109 } 1110 finally { 1111 if (tempFile != null) { 1112 try{raf.close();}catch(Exception ee){} 1113 if (originalout != null) 1114 try{tempFile.delete();}catch(Exception ee){} 1115 } 1116 if (originalout != null) 1117 try{originalout.close();}catch(Exception e){} 1118 } 1119 } 1120 1121 private void addDocMDP(PdfDictionary crypto) { 1122 PdfDictionary reference = new PdfDictionary(); 1123 PdfDictionary transformParams = new PdfDictionary(); 1124 transformParams.put(PdfName.P, new PdfNumber(certificationLevel)); 1125 transformParams.put(PdfName.V, new PdfName("1.2")); 1126 transformParams.put(PdfName.TYPE, PdfName.TRANSFORMPARAMS); 1127 reference.put(PdfName.TRANSFORMMETHOD, PdfName.DOCMDP); 1128 reference.put(PdfName.TYPE, PdfName.SIGREF); 1129 reference.put(PdfName.TRANSFORMPARAMS, transformParams); 1130 reference.put(new PdfName("DigestValue"), new PdfString("aa")); 1131 PdfArray loc = new PdfArray(); 1132 loc.add(new PdfNumber(0)); 1133 loc.add(new PdfNumber(0)); 1134 reference.put(new PdfName("DigestLocation"), loc); 1135 reference.put(new PdfName("DigestMethod"), new PdfName("MD5")); 1136 reference.put(PdfName.DATA, writer.reader.getTrailer().get(PdfName.ROOT)); 1137 PdfArray types = new PdfArray(); 1138 types.add(reference); 1139 crypto.put(PdfName.REFERENCE, types); 1140 } 1141 1142 /** 1143 * Gets the document bytes that are hashable when using external signatures. The general sequence is: 1144 * preClose(), getRangeStream() and close(). 1145 * <p> 1146 * @return the document bytes that are hashable 1147 */ 1148 public InputStream getRangeStream() { 1149 return new PdfSignatureAppearance.RangeStream(raf, bout, range); 1150 } 1151 1152 /** 1153 * Gets the user made signature dictionary. This is the dictionary at the /V key. 1154 * @return the user made signature dictionary 1155 */ 1156 public com.itextpdf.text.pdf.PdfDictionary getCryptoDictionary() { 1157 return cryptoDictionary; 1158 } 1159 1160 /** 1161 * Sets a user made signature dictionary. This is the dictionary at the /V key. 1162 * @param cryptoDictionary a user made signature dictionary 1163 */ 1164 public void setCryptoDictionary(com.itextpdf.text.pdf.PdfDictionary cryptoDictionary) { 1165 this.cryptoDictionary = cryptoDictionary; 1166 } 1167 1168 /** 1169 * Gets the <CODE>PdfStamper</CODE> associated with this instance. 1170 * @return the <CODE>PdfStamper</CODE> associated with this instance 1171 */ 1172 public com.itextpdf.text.pdf.PdfStamper getStamper() { 1173 return stamper; 1174 } 1175 1176 void setStamper(com.itextpdf.text.pdf.PdfStamper stamper) { 1177 this.stamper = stamper; 1178 } 1179 1180 /** 1181 * Checks if the document is in the process of closing. 1182 * @return <CODE>true</CODE> if the document is in the process of closing, 1183 * <CODE>false</CODE> otherwise 1184 */ 1185 public boolean isPreClosed() { 1186 return preClosed; 1187 } 1188 1189 /** 1190 * Gets the instance of the standard signature dictionary. This instance 1191 * is only available after pre close. 1192 * <p> 1193 * The main use is to insert external signatures. 1194 * @return the instance of the standard signature dictionary 1195 */ 1196 public com.itextpdf.text.pdf.PdfSigGenericPKCS getSigStandard() { 1197 return sigStandard; 1198 } 1199 1200 /** 1201 * Gets the signing contact. 1202 * @return the signing contact 1203 */ 1204 public String getContact() { 1205 return this.contact; 1206 } 1207 1208 /** 1209 * Sets the signing contact. 1210 * @param contact the signing contact 1211 */ 1212 public void setContact(String contact) { 1213 this.contact = contact; 1214 } 1215 1216 /** 1217 * Gets the n2 and n4 layer font. 1218 * @return the n2 and n4 layer font 1219 */ 1220 public Font getLayer2Font() { 1221 return this.layer2Font; 1222 } 1223 1224 /** 1225 * Sets the n2 and n4 layer font. If the font size is zero, auto-fit will be used. 1226 * @param layer2Font the n2 and n4 font 1227 */ 1228 public void setLayer2Font(Font layer2Font) { 1229 this.layer2Font = layer2Font; 1230 } 1231 1232 /** 1233 * Gets the Acrobat 6.0 layer mode. 1234 * @return the Acrobat 6.0 layer mode 1235 */ 1236 public boolean isAcro6Layers() { 1237 return this.acro6Layers; 1238 } 1239 1240 /** 1241 * Acrobat 6.0 and higher recommends that only layer n0 and n2 be present. This method sets that mode. 1242 * @param acro6Layers if <code>true</code> only the layers n0 and n2 will be present 1243 */ 1244 public void setAcro6Layers(boolean acro6Layers) { 1245 this.acro6Layers = acro6Layers; 1246 } 1247 1248 /** Sets the run direction in the n2 and n4 layer. 1249 * @param runDirection the run direction 1250 */ 1251 public void setRunDirection(int runDirection) { 1252 if (runDirection < PdfWriter.RUN_DIRECTION_DEFAULT || runDirection > PdfWriter.RUN_DIRECTION_RTL) 1253 throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.run.direction.1", runDirection)); 1254 this.runDirection = runDirection; 1255 } 1256 1257 /** Gets the run direction. 1258 * @return the run direction 1259 */ 1260 public int getRunDirection() { 1261 return runDirection; 1262 } 1263 1264 /** 1265 * Getter for property signatureEvent. 1266 * @return Value of property signatureEvent. 1267 */ 1268 public SignatureEvent getSignatureEvent() { 1269 return this.signatureEvent; 1270 } 1271 1272 /** 1273 * Sets the signature event to allow modification of the signature dictionary. 1274 * @param signatureEvent the signature event 1275 */ 1276 public void setSignatureEvent(SignatureEvent signatureEvent) { 1277 this.signatureEvent = signatureEvent; 1278 } 1279 1280 /** 1281 * Gets the background image for the layer 2. 1282 * @return the background image for the layer 2 1283 */ 1284 public Image getImage() { 1285 return this.image; 1286 } 1287 1288 /** 1289 * Sets the background image for the layer 2. 1290 * @param image the background image for the layer 2 1291 */ 1292 public void setImage(Image image) { 1293 this.image = image; 1294 } 1295 1296 /** 1297 * Gets the scaling to be applied to the background image. 1298 * @return the scaling to be applied to the background image 1299 */ 1300 public float getImageScale() { 1301 return this.imageScale; 1302 } 1303 1304 /** 1305 * Sets the scaling to be applied to the background image. If it's zero the image 1306 * will fully fill the rectangle. If it's less than zero the image will fill the rectangle but 1307 * will keep the proportions. If it's greater than zero that scaling will be applied. 1308 * In any of the cases the image will always be centered. It's zero by default. 1309 * @param imageScale the scaling to be applied to the background image 1310 */ 1311 public void setImageScale(float imageScale) { 1312 this.imageScale = imageScale; 1313 } 1314 1315 /** 1316 * Commands to draw a yellow question mark in a stream content 1317 */ 1318 public static final String questionMark = 1319 "% DSUnknown\n" + 1320 "q\n" + 1321 "1 G\n" + 1322 "1 g\n" + 1323 "0.1 0 0 0.1 9 0 cm\n" + 1324 "0 J 0 j 4 M []0 d\n" + 1325 "1 i \n" + 1326 "0 g\n" + 1327 "313 292 m\n" + 1328 "313 404 325 453 432 529 c\n" + 1329 "478 561 504 597 504 645 c\n" + 1330 "504 736 440 760 391 760 c\n" + 1331 "286 760 271 681 265 626 c\n" + 1332 "265 625 l\n" + 1333 "100 625 l\n" + 1334 "100 828 253 898 381 898 c\n" + 1335 "451 898 679 878 679 650 c\n" + 1336 "679 555 628 499 538 435 c\n" + 1337 "488 399 467 376 467 292 c\n" + 1338 "313 292 l\n" + 1339 "h\n" + 1340 "308 214 170 -164 re\n" + 1341 "f\n" + 1342 "0.44 G\n" + 1343 "1.2 w\n" + 1344 "1 1 0.4 rg\n" + 1345 "287 318 m\n" + 1346 "287 430 299 479 406 555 c\n" + 1347 "451 587 478 623 478 671 c\n" + 1348 "478 762 414 786 365 786 c\n" + 1349 "260 786 245 707 239 652 c\n" + 1350 "239 651 l\n" + 1351 "74 651 l\n" + 1352 "74 854 227 924 355 924 c\n" + 1353 "425 924 653 904 653 676 c\n" + 1354 "653 581 602 525 512 461 c\n" + 1355 "462 425 441 402 441 318 c\n" + 1356 "287 318 l\n" + 1357 "h\n" + 1358 "282 240 170 -164 re\n" + 1359 "B\n" + 1360 "Q\n"; 1361 1362 /** 1363 * Holds value of property contact. 1364 */ 1365 private String contact; 1366 1367 /** 1368 * Holds value of property layer2Font. 1369 */ 1370 private Font layer2Font; 1371 1372 /** 1373 * Holds value of property layer4Text. 1374 */ 1375 private String layer4Text; 1376 1377 /** 1378 * Holds value of property acro6Layers. 1379 */ 1380 private boolean acro6Layers; 1381 1382 /** 1383 * Holds value of property runDirection. 1384 */ 1385 private int runDirection = PdfWriter.RUN_DIRECTION_NO_BIDI; 1386 1387 /** 1388 * Holds value of property signatureEvent. 1389 */ 1390 private SignatureEvent signatureEvent; 1391 1392 /** 1393 * Holds value of property image. 1394 */ 1395 private Image image; 1396 1397 /** 1398 * Holds value of property imageScale. 1399 */ 1400 private float imageScale; 1401 1402 /** 1403 * 1404 */ 1405 private static class RangeStream extends InputStream { 1406 private byte b[] = new byte[1]; 1407 private RandomAccessFile raf; 1408 private byte bout[]; 1409 private int range[]; 1410 private int rangePosition = 0; 1411 1412 private RangeStream(RandomAccessFile raf, byte bout[], int range[]) { 1413 this.raf = raf; 1414 this.bout = bout; 1415 this.range = range; 1416 } 1417 1418 /** 1419 * @see java.io.InputStream#read() 1420 */ 1421 @Override 1422 public int read() throws IOException { 1423 int n = read(b); 1424 if (n != 1) 1425 return -1; 1426 return b[0] & 0xff; 1427 } 1428 1429 /** 1430 * @see java.io.InputStream#read(byte[], int, int) 1431 */ 1432 @Override 1433 public int read(byte[] b, int off, int len) throws IOException { 1434 if (b == null) { 1435 throw new NullPointerException(); 1436 } else if (off < 0 || off > b.length || len < 0 || 1437 off + len > b.length || off + len < 0) { 1438 throw new IndexOutOfBoundsException(); 1439 } else if (len == 0) { 1440 return 0; 1441 } 1442 if (rangePosition >= range[range.length - 2] + range[range.length - 1]) { 1443 return -1; 1444 } 1445 for (int k = 0; k < range.length; k += 2) { 1446 int start = range[k]; 1447 int end = start + range[k + 1]; 1448 if (rangePosition < start) 1449 rangePosition = start; 1450 if (rangePosition >= start && rangePosition < end) { 1451 int lenf = Math.min(len, end - rangePosition); 1452 if (raf == null) 1453 System.arraycopy(bout, rangePosition, b, off, lenf); 1454 else { 1455 raf.seek(rangePosition); 1456 raf.readFully(b, off, lenf); 1457 } 1458 rangePosition += lenf; 1459 return lenf; 1460 } 1461 } 1462 return -1; 1463 } 1464 } 1465 1466 /** 1467 * An interface to retrieve the signature dictionary for modification. 1468 */ 1469 public interface SignatureEvent { 1470 /** 1471 * Allows modification of the signature dictionary. 1472 * @param sig the signature dictionary 1473 */ 1474 public void getSignatureDictionary(PdfDictionary sig); 1475 } 1476 1477 private int certificationLevel = NOT_CERTIFIED; 1478 1479 /** 1480 * Gets the certified status of this document. 1481 * @return the certified status 1482 */ 1483 public int getCertificationLevel() { 1484 return this.certificationLevel; 1485 } 1486 1487 /** 1488 * Sets the document type to certified instead of simply signed. 1489 * @param certificationLevel the values can be: <code>NOT_CERTIFIED</code>, <code>CERTIFIED_NO_CHANGES_ALLOWED</code>, 1490 * <code>CERTIFIED_FORM_FILLING</code> and <code>CERTIFIED_FORM_FILLING_AND_ANNOTATIONS</code> 1491 */ 1492 public void setCertificationLevel(int certificationLevel) { 1493 this.certificationLevel = certificationLevel; 1494 } 1495}