0001: /*
0002: * $Id: PdfSignatureAppearance.java 2752 2007-05-15 14:58:33Z blowagie $
0003: *
0004: * Copyright 2004-2006 by Paulo Soares.
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version 1.1
0007: * (the "License"); you may not use this file except in compliance with the License.
0008: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
0009: *
0010: * Software distributed under the License is distributed on an "AS IS" basis,
0011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0012: * for the specific language governing rights and limitations under the License.
0013: *
0014: * The Original Code is 'iText, a free JAVA-PDF library'.
0015: *
0016: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
0017: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
0018: * All Rights Reserved.
0019: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
0020: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
0021: *
0022: * Contributor(s): all the names of the contributors are added in the source code
0023: * where applicable.
0024: *
0025: * Alternatively, the contents of this file may be used under the terms of the
0026: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
0027: * provisions of LGPL are applicable instead of those above. If you wish to
0028: * allow use of your version of this file only under the terms of the LGPL
0029: * License and not to allow others to use your version of this file under
0030: * the MPL, indicate your decision by deleting the provisions above and
0031: * replace them with the notice and other provisions required by the LGPL.
0032: * If you do not delete the provisions above, a recipient may use your version
0033: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
0034: *
0035: * This library is free software; you can redistribute it and/or modify it
0036: * under the terms of the MPL as stated above or under the terms of the GNU
0037: * Library General Public License as published by the Free Software Foundation;
0038: * either version 2 of the License, or any later version.
0039: *
0040: * This library is distributed in the hope that it will be useful, but WITHOUT
0041: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
0042: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
0043: * details.
0044: *
0045: * If you didn't download this code from the following link, you should check if
0046: * you aren't using an obsolete version:
0047: * http://www.lowagie.com/iText/
0048: */
0049: package com.lowagie.text.pdf;
0050:
0051: import java.io.EOFException;
0052: import java.io.File;
0053: import java.io.IOException;
0054: import java.io.InputStream;
0055: import java.io.OutputStream;
0056: import java.io.RandomAccessFile;
0057: import java.security.PrivateKey;
0058: import java.security.cert.CRL;
0059: import java.security.cert.Certificate;
0060: import java.security.cert.X509Certificate;
0061: import java.text.SimpleDateFormat;
0062: import java.util.ArrayList;
0063: import java.util.Arrays;
0064: import java.util.Calendar;
0065: import java.util.GregorianCalendar;
0066: import java.util.HashMap;
0067: import java.util.Iterator;
0068: import java.util.Map;
0069:
0070: import com.lowagie.text.Chunk;
0071: import com.lowagie.text.DocumentException;
0072: import com.lowagie.text.Element;
0073: import com.lowagie.text.ExceptionConverter;
0074: import com.lowagie.text.Font;
0075: import com.lowagie.text.Image;
0076: import com.lowagie.text.Paragraph;
0077: import com.lowagie.text.Phrase;
0078: import com.lowagie.text.Rectangle;
0079:
0080: /**
0081: * This class takes care of the cryptographic options and appearances that form a signature.
0082: */
0083: public class PdfSignatureAppearance {
0084:
0085: /**
0086: * The rendering mode is just the description
0087: */
0088: public static final int SignatureRenderDescription = 0;
0089: /**
0090: * The rendering mode is the name of the signer and the description
0091: */
0092: public static final int SignatureRenderNameAndDescription = 1;
0093: /**
0094: * The rendering mode is an image and the description
0095: */
0096: public static final int SignatureRenderGraphicAndDescription = 2;
0097:
0098: /**
0099: * The self signed filter.
0100: */
0101: public static final PdfName SELF_SIGNED = PdfName.ADOBE_PPKLITE;
0102: /**
0103: * The VeriSign filter.
0104: */
0105: public static final PdfName VERISIGN_SIGNED = PdfName.VERISIGN_PPKVS;
0106: /**
0107: * The Windows Certificate Security.
0108: */
0109: public static final PdfName WINCER_SIGNED = PdfName.ADOBE_PPKMS;
0110:
0111: public static final int NOT_CERTIFIED = 0;
0112: public static final int CERTIFIED_NO_CHANGES_ALLOWED = 1;
0113: public static final int CERTIFIED_FORM_FILLING = 2;
0114: public static final int CERTIFIED_FORM_FILLING_AND_ANNOTATIONS = 3;
0115:
0116: private static final float TOP_SECTION = 0.3f;
0117: private static final float MARGIN = 2;
0118: private Rectangle rect;
0119: private Rectangle pageRect;
0120: private PdfTemplate app[] = new PdfTemplate[5];
0121: private PdfTemplate frm;
0122: private PdfStamperImp writer;
0123: private String layer2Text;
0124: private String reason;
0125: private String location;
0126: private Calendar signDate;
0127: private String provider;
0128: private int page = 1;
0129: private String fieldName;
0130: private PrivateKey privKey;
0131: private Certificate[] certChain;
0132: private CRL[] crlList;
0133: private PdfName filter;
0134: private boolean newField;
0135: private ByteBuffer sigout;
0136: private OutputStream originalout;
0137: private File tempFile;
0138: private PdfDictionary cryptoDictionary;
0139: private PdfStamper stamper;
0140: private boolean preClosed = false;
0141: private PdfSigGenericPKCS sigStandard;
0142: private int range[];
0143: private RandomAccessFile raf;
0144: private byte bout[];
0145: private int boutLen;
0146: private byte externalDigest[];
0147: private byte externalRSAdata[];
0148: private String digestEncryptionAlgorithm;
0149: private HashMap exclusionLocations;
0150:
0151: PdfSignatureAppearance(PdfStamperImp writer) {
0152: this .writer = writer;
0153: signDate = new GregorianCalendar();
0154: fieldName = getNewSigName();
0155: }
0156:
0157: private int render = SignatureRenderDescription;
0158:
0159: /**
0160: * Gets the rendering mode for this signature.
0161: * @return the rendering mode for this signature
0162: */
0163: public int getRender() {
0164: return render;
0165: }
0166:
0167: /**
0168: * Sets the rendering mode for this signature.
0169: * The rendering modes can be the constants <CODE>SignatureRenderDescription</CODE>,
0170: * <CODE>SignatureRenderNameAndDescription</CODE> or <CODE>SignatureRenderGraphicAndDescription</CODE>.
0171: * The two last modes should be used with Acrobat 6 layer type.
0172: * @param render the render mode
0173: */
0174: public void setRender(int render) {
0175: this .render = render;
0176: }
0177:
0178: private Image signatureGraphic = null;
0179:
0180: /**
0181: * Gets the Image object to render.
0182: * @return the image
0183: */
0184: public Image getSignatureGraphic() {
0185: return signatureGraphic;
0186: }
0187:
0188: /**
0189: * Sets the Image object to render when Render is set to <CODE>SignatureRenderGraphicAndDescription</CODE>
0190: * @param signatureGraphic image rendered. If <CODE>null</CODE> the mode is defaulted
0191: * to <CODE>SignatureRenderDescription</CODE>
0192: */
0193: public void setSignatureGraphic(Image signatureGraphic) {
0194: this .signatureGraphic = signatureGraphic;
0195: }
0196:
0197: /**
0198: * Sets the signature text identifying the signer.
0199: * @param text the signature text identifying the signer. If <CODE>null</CODE> or not set
0200: * a standard description will be used
0201: */
0202: public void setLayer2Text(String text) {
0203: layer2Text = text;
0204: }
0205:
0206: /**
0207: * Gets the signature text identifying the signer if set by setLayer2Text().
0208: * @return the signature text identifying the signer
0209: */
0210: public String getLayer2Text() {
0211: return layer2Text;
0212: }
0213:
0214: /**
0215: * Sets the text identifying the signature status.
0216: * @param text the text identifying the signature status. If <CODE>null</CODE> or not set
0217: * the description "Signature Not Verified" will be used
0218: */
0219: public void setLayer4Text(String text) {
0220: layer4Text = text;
0221: }
0222:
0223: /**
0224: * Gets the text identifying the signature status if set by setLayer4Text().
0225: * @return the text identifying the signature status
0226: */
0227: public String getLayer4Text() {
0228: return layer4Text;
0229: }
0230:
0231: /**
0232: * Gets the rectangle representing the signature dimensions.
0233: * @return the rectangle representing the signature dimensions. It may be <CODE>null</CODE>
0234: * or have zero width or height for invisible signatures
0235: */
0236: public Rectangle getRect() {
0237: return rect;
0238: }
0239:
0240: /**
0241: * Gets the visibility status of the signature.
0242: * @return the visibility status of the signature
0243: */
0244: public boolean isInvisible() {
0245: return (rect == null || rect.getWidth() == 0 || rect
0246: .getHeight() == 0);
0247: }
0248:
0249: /**
0250: * Sets the cryptographic parameters.
0251: * @param privKey the private key
0252: * @param certChain the certificate chain
0253: * @param crlList the certificate revocation list. It may be <CODE>null</CODE>
0254: * @param filter the crytographic filter type. It can be SELF_SIGNED, VERISIGN_SIGNED or WINCER_SIGNED
0255: */
0256: public void setCrypto(PrivateKey privKey, Certificate[] certChain,
0257: CRL[] crlList, PdfName filter) {
0258: this .privKey = privKey;
0259: this .certChain = certChain;
0260: this .crlList = crlList;
0261: this .filter = filter;
0262: }
0263:
0264: /**
0265: * Sets the signature to be visible. It creates a new visible signature field.
0266: * @param pageRect the position and dimension of the field in the page
0267: * @param page the page to place the field. The fist page is 1
0268: * @param fieldName the field name or <CODE>null</CODE> to generate automatically a new field name
0269: */
0270: public void setVisibleSignature(Rectangle pageRect, int page,
0271: String fieldName) {
0272: if (fieldName != null) {
0273: if (fieldName.indexOf('.') >= 0)
0274: throw new IllegalArgumentException(
0275: "Field names cannot contain a dot.");
0276: AcroFields af = writer.getAcroFields();
0277: AcroFields.Item item = af.getFieldItem(fieldName);
0278: if (item != null)
0279: throw new IllegalArgumentException("The field "
0280: + fieldName + " already exists.");
0281: this .fieldName = fieldName;
0282: }
0283: if (page < 1 || page > writer.reader.getNumberOfPages())
0284: throw new IllegalArgumentException("Invalid page number: "
0285: + page);
0286: this .pageRect = new Rectangle(pageRect);
0287: this .pageRect.normalize();
0288: rect = new Rectangle(this .pageRect.getWidth(), this .pageRect
0289: .getHeight());
0290: this .page = page;
0291: newField = true;
0292: }
0293:
0294: /**
0295: * Sets the signature to be visible. An empty signature field with the same name must already exist.
0296: * @param fieldName the existing empty signature field name
0297: */
0298: public void setVisibleSignature(String fieldName) {
0299: AcroFields af = writer.getAcroFields();
0300: AcroFields.Item item = af.getFieldItem(fieldName);
0301: if (item == null)
0302: throw new IllegalArgumentException("The field " + fieldName
0303: + " does not exist.");
0304: PdfDictionary merged = (PdfDictionary) item.merged.get(0);
0305: if (!PdfName.SIG.equals(PdfReader.getPdfObject(merged
0306: .get(PdfName.FT))))
0307: throw new IllegalArgumentException("The field " + fieldName
0308: + " is not a signature field.");
0309: this .fieldName = fieldName;
0310: PdfArray r = (PdfArray) PdfReader.getPdfObject(merged
0311: .get(PdfName.RECT));
0312: ArrayList ar = r.getArrayList();
0313: float llx = ((PdfNumber) PdfReader.getPdfObject((PdfObject) ar
0314: .get(0))).floatValue();
0315: float lly = ((PdfNumber) PdfReader.getPdfObject((PdfObject) ar
0316: .get(1))).floatValue();
0317: float urx = ((PdfNumber) PdfReader.getPdfObject((PdfObject) ar
0318: .get(2))).floatValue();
0319: float ury = ((PdfNumber) PdfReader.getPdfObject((PdfObject) ar
0320: .get(3))).floatValue();
0321: pageRect = new Rectangle(llx, lly, urx, ury);
0322: pageRect.normalize();
0323: page = ((Integer) item.page.get(0)).intValue();
0324: int rotation = writer.reader.getPageRotation(page);
0325: Rectangle pageSize = writer.reader
0326: .getPageSizeWithRotation(page);
0327: switch (rotation) {
0328: case 90:
0329: pageRect = new Rectangle(pageRect.getBottom(), pageSize
0330: .getTop()
0331: - pageRect.getLeft(), pageRect.getTop(), pageSize
0332: .getTop()
0333: - pageRect.getRight());
0334: break;
0335: case 180:
0336: pageRect = new Rectangle(pageSize.getRight()
0337: - pageRect.getLeft(), pageSize.getTop()
0338: - pageRect.getBottom(), pageSize.getRight()
0339: - pageRect.getRight(), pageSize.getTop()
0340: - pageRect.getTop());
0341: break;
0342: case 270:
0343: pageRect = new Rectangle(pageSize.getRight()
0344: - pageRect.getBottom(), pageRect.getLeft(),
0345: pageSize.getRight() - pageRect.getTop(), pageRect
0346: .getRight());
0347: break;
0348: }
0349: if (rotation != 0)
0350: pageRect.normalize();
0351: rect = new Rectangle(this .pageRect.getWidth(), this .pageRect
0352: .getHeight());
0353: }
0354:
0355: /**
0356: * Gets a template layer to create a signature appearance. The layers can go from 0 to 4.
0357: * <p>
0358: * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A>
0359: * for further details.
0360: * @param layer the layer
0361: * @return a template
0362: */
0363: public PdfTemplate getLayer(int layer) {
0364: if (layer < 0 || layer >= app.length)
0365: return null;
0366: PdfTemplate t = app[layer];
0367: if (t == null) {
0368: t = app[layer] = new PdfTemplate(writer);
0369: t.setBoundingBox(rect);
0370: writer.addDirectTemplateSimple(t, new PdfName("n" + layer));
0371: }
0372: return t;
0373: }
0374:
0375: /**
0376: * Gets the template that aggregates all appearance layers. This corresponds to the /FRM resource.
0377: * <p>
0378: * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A>
0379: * for further details.
0380: * @return the template that aggregates all appearance layers
0381: */
0382: public PdfTemplate getTopLayer() {
0383: if (frm == null) {
0384: frm = new PdfTemplate(writer);
0385: frm.setBoundingBox(rect);
0386: writer.addDirectTemplateSimple(frm, new PdfName("FRM"));
0387: }
0388: return frm;
0389: }
0390:
0391: /**
0392: * Gets the main appearance layer.
0393: * <p>
0394: * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A>
0395: * for further details.
0396: * @return the main appearance layer
0397: * @throws DocumentException on error
0398: */
0399: public PdfTemplate getAppearance() throws DocumentException {
0400: if (isInvisible()) {
0401: PdfTemplate t = new PdfTemplate(writer);
0402: t.setBoundingBox(new Rectangle(0, 0));
0403: writer.addDirectTemplateSimple(t, null);
0404: return t;
0405: }
0406: if (app[0] == null) {
0407: PdfTemplate t = app[0] = new PdfTemplate(writer);
0408: t.setBoundingBox(new Rectangle(100, 100));
0409: writer.addDirectTemplateSimple(t, new PdfName("n0"));
0410: t.setLiteral("% DSBlank\n");
0411: }
0412: if (app[1] == null && !acro6Layers) {
0413: PdfTemplate t = app[1] = new PdfTemplate(writer);
0414: t.setBoundingBox(new Rectangle(100, 100));
0415: writer.addDirectTemplateSimple(t, new PdfName("n1"));
0416: t.setLiteral(questionMark);
0417: }
0418: if (app[2] == null) {
0419: String text;
0420: if (layer2Text == null) {
0421: StringBuffer buf = new StringBuffer();
0422: buf.append("Digitally signed by ").append(
0423: PdfPKCS7.getSubjectFields(
0424: (X509Certificate) certChain[0])
0425: .getField("CN")).append('\n');
0426: SimpleDateFormat sd = new SimpleDateFormat(
0427: "yyyy.MM.dd HH:mm:ss z");
0428: buf.append("Date: ").append(
0429: sd.format(signDate.getTime()));
0430: if (reason != null)
0431: buf.append('\n').append("Reason: ").append(reason);
0432: if (location != null)
0433: buf.append('\n').append("Location: ").append(
0434: location);
0435: text = buf.toString();
0436: } else
0437: text = layer2Text;
0438: PdfTemplate t = app[2] = new PdfTemplate(writer);
0439: t.setBoundingBox(rect);
0440: writer.addDirectTemplateSimple(t, new PdfName("n2"));
0441: if (image != null) {
0442: if (imageScale == 0) {
0443: t.addImage(image, rect.getWidth(), 0, 0, rect
0444: .getHeight(), 0, 0);
0445: } else {
0446: float usableScale = imageScale;
0447: if (imageScale < 0)
0448: usableScale = Math.min(rect.getWidth()
0449: / image.getWidth(), rect.getHeight()
0450: / image.getHeight());
0451: float w = image.getWidth() * usableScale;
0452: float h = image.getHeight() * usableScale;
0453: float x = (rect.getWidth() - w) / 2;
0454: float y = (rect.getHeight() - h) / 2;
0455: t.addImage(image, w, 0, 0, h, x, y);
0456: }
0457: }
0458: Font font;
0459: if (layer2Font == null)
0460: font = new Font();
0461: else
0462: font = new Font(layer2Font);
0463: float size = font.getSize();
0464:
0465: Rectangle dataRect = null;
0466: Rectangle signatureRect = null;
0467:
0468: if (render == SignatureRenderNameAndDescription
0469: || (render == SignatureRenderGraphicAndDescription && this .signatureGraphic != null)) {
0470: // origin is the bottom-left
0471: signatureRect = new Rectangle(MARGIN, MARGIN, rect
0472: .getWidth()
0473: / 2 - MARGIN, rect.getHeight() - MARGIN);
0474: dataRect = new Rectangle(rect.getWidth() / 2 + MARGIN
0475: / 2, MARGIN, rect.getWidth() - MARGIN / 2, rect
0476: .getHeight()
0477: - MARGIN);
0478:
0479: if (rect.getHeight() > rect.getWidth()) {
0480: signatureRect = new Rectangle(MARGIN, rect
0481: .getHeight() / 2, rect.getWidth() - MARGIN,
0482: rect.getHeight());
0483: dataRect = new Rectangle(MARGIN, MARGIN, rect
0484: .getWidth()
0485: - MARGIN, rect.getHeight() / 2 - MARGIN);
0486: }
0487: } else {
0488: dataRect = new Rectangle(MARGIN, MARGIN, rect
0489: .getWidth()
0490: - MARGIN, rect.getHeight() * (1 - TOP_SECTION)
0491: - MARGIN);
0492: }
0493:
0494: if (render == SignatureRenderNameAndDescription) {
0495: String signedBy = PdfPKCS7.getSubjectFields(
0496: (X509Certificate) certChain[0]).getField("CN");
0497: Rectangle sr2 = new Rectangle(signatureRect.getWidth()
0498: - MARGIN, signatureRect.getHeight() - MARGIN);
0499: float signedSize = fitText(font, signedBy, sr2, -1,
0500: runDirection);
0501:
0502: ColumnText ct2 = new ColumnText(t);
0503: ct2.setRunDirection(runDirection);
0504: ct2.setSimpleColumn(new Phrase(signedBy, font),
0505: signatureRect.getLeft(), signatureRect
0506: .getBottom(), signatureRect.getRight(),
0507: signatureRect.getTop(), signedSize,
0508: Element.ALIGN_LEFT);
0509:
0510: ct2.go();
0511: } else if (render == SignatureRenderGraphicAndDescription) {
0512: ColumnText ct2 = new ColumnText(t);
0513: ct2.setRunDirection(runDirection);
0514: ct2.setSimpleColumn(signatureRect.getLeft(),
0515: signatureRect.getBottom(), signatureRect
0516: .getRight(), signatureRect.getTop(), 0,
0517: Element.ALIGN_RIGHT);
0518:
0519: Image im = Image.getInstance(signatureGraphic);
0520: im.scaleToFit(signatureRect.getWidth(), signatureRect
0521: .getHeight());
0522:
0523: Paragraph p = new Paragraph();
0524: // must calculate the point to draw from to make image appear in middle of column
0525: float x = 0;
0526: // experimentation found this magic number to counteract Adobe's signature graphic, which
0527: // offsets the y co-ordinate by 15 units
0528: float y = -im.getScaledHeight() + 15;
0529:
0530: x = x
0531: + (signatureRect.getWidth() - im
0532: .getScaledWidth()) / 2;
0533: y = y
0534: - (signatureRect.getHeight() - im
0535: .getScaledHeight()) / 2;
0536: p.add(new Chunk(im, x
0537: + (signatureRect.getWidth() - im
0538: .getScaledWidth()) / 2, y, false));
0539: ct2.addElement(p);
0540: ct2.go();
0541: }
0542:
0543: if (size <= 0) {
0544: Rectangle sr = new Rectangle(dataRect.getWidth(),
0545: dataRect.getHeight());
0546: size = fitText(font, text, sr, 12, runDirection);
0547: }
0548: ColumnText ct = new ColumnText(t);
0549: ct.setRunDirection(runDirection);
0550: ct.setSimpleColumn(new Phrase(text, font), dataRect
0551: .getLeft(), dataRect.getBottom(), dataRect
0552: .getRight(), dataRect.getTop(), size,
0553: Element.ALIGN_LEFT);
0554: ct.go();
0555: }
0556: if (app[3] == null && !acro6Layers) {
0557: PdfTemplate t = app[3] = new PdfTemplate(writer);
0558: t.setBoundingBox(new Rectangle(100, 100));
0559: writer.addDirectTemplateSimple(t, new PdfName("n3"));
0560: t.setLiteral("% DSBlank\n");
0561: }
0562: if (app[4] == null && !acro6Layers) {
0563: PdfTemplate t = app[4] = new PdfTemplate(writer);
0564: t
0565: .setBoundingBox(new Rectangle(0, rect.getHeight()
0566: * (1 - TOP_SECTION), rect.getRight(), rect
0567: .getTop()));
0568: writer.addDirectTemplateSimple(t, new PdfName("n4"));
0569: Font font;
0570: if (layer2Font == null)
0571: font = new Font();
0572: else
0573: font = new Font(layer2Font);
0574: float size = font.getSize();
0575: String text = "Signature Not Verified";
0576: if (layer4Text != null)
0577: text = layer4Text;
0578: Rectangle sr = new Rectangle(rect.getWidth() - 2 * MARGIN,
0579: rect.getHeight() * TOP_SECTION - 2 * MARGIN);
0580: size = fitText(font, text, sr, 15, runDirection);
0581: ColumnText ct = new ColumnText(t);
0582: ct.setRunDirection(runDirection);
0583: ct.setSimpleColumn(new Phrase(text, font), MARGIN, 0, rect
0584: .getWidth()
0585: - MARGIN, rect.getHeight() - MARGIN, size,
0586: Element.ALIGN_LEFT);
0587: ct.go();
0588: }
0589: int rotation = writer.reader.getPageRotation(page);
0590: Rectangle rotated = new Rectangle(rect);
0591: int n = rotation;
0592: while (n > 0) {
0593: rotated = rotated.rotate();
0594: n -= 90;
0595: }
0596: if (frm == null) {
0597: frm = new PdfTemplate(writer);
0598: frm.setBoundingBox(rotated);
0599: writer.addDirectTemplateSimple(frm, new PdfName("FRM"));
0600: float scale = Math.min(rect.getWidth(), rect.getHeight()) * 0.9f;
0601: float x = (rect.getWidth() - scale) / 2;
0602: float y = (rect.getHeight() - scale) / 2;
0603: scale /= 100;
0604: if (rotation == 90)
0605: frm.concatCTM(0, 1, -1, 0, rect.getHeight(), 0);
0606: else if (rotation == 180)
0607: frm.concatCTM(-1, 0, 0, -1, rect.getWidth(), rect
0608: .getHeight());
0609: else if (rotation == 270)
0610: frm.concatCTM(0, -1, 1, 0, 0, rect.getWidth());
0611: frm.addTemplate(app[0], 0, 0);
0612: if (!acro6Layers)
0613: frm.addTemplate(app[1], scale, 0, 0, scale, x, y);
0614: frm.addTemplate(app[2], 0, 0);
0615: if (!acro6Layers) {
0616: frm.addTemplate(app[3], scale, 0, 0, scale, x, y);
0617: frm.addTemplate(app[4], 0, 0);
0618: }
0619: }
0620: PdfTemplate napp = new PdfTemplate(writer);
0621: napp.setBoundingBox(rotated);
0622: writer.addDirectTemplateSimple(napp, null);
0623: napp.addTemplate(frm, 0, 0);
0624: return napp;
0625: }
0626:
0627: /**
0628: * Fits the text to some rectangle adjusting the font size as needed.
0629: * @param font the font to use
0630: * @param text the text
0631: * @param rect the rectangle where the text must fit
0632: * @param maxFontSize the maximum font size
0633: * @param runDirection the run direction
0634: * @return the calculated font size that makes the text fit
0635: */
0636: public static float fitText(Font font, String text, Rectangle rect,
0637: float maxFontSize, int runDirection) {
0638: try {
0639: ColumnText ct = null;
0640: int status = 0;
0641: if (maxFontSize <= 0) {
0642: int cr = 0;
0643: int lf = 0;
0644: char t[] = text.toCharArray();
0645: for (int k = 0; k < t.length; ++k) {
0646: if (t[k] == '\n')
0647: ++lf;
0648: else if (t[k] == '\r')
0649: ++cr;
0650: }
0651: int minLines = Math.max(cr, lf) + 1;
0652: maxFontSize = Math.abs(rect.getHeight()) / minLines
0653: - 0.001f;
0654: }
0655: font.setSize(maxFontSize);
0656: Phrase ph = new Phrase(text, font);
0657: ct = new ColumnText(null);
0658: ct.setSimpleColumn(ph, rect.getLeft(), rect.getBottom(),
0659: rect.getRight(), rect.getTop(), maxFontSize,
0660: Element.ALIGN_LEFT);
0661: ct.setRunDirection(runDirection);
0662: status = ct.go(true);
0663: if ((status & ColumnText.NO_MORE_TEXT) != 0)
0664: return maxFontSize;
0665: float precision = 0.1f;
0666: float min = 0;
0667: float max = maxFontSize;
0668: float size = maxFontSize;
0669: for (int k = 0; k < 50; ++k) { //just in case it doesn't converge
0670: size = (min + max) / 2;
0671: ct = new ColumnText(null);
0672: font.setSize(size);
0673: ct.setSimpleColumn(new Phrase(text, font), rect
0674: .getLeft(), rect.getBottom(), rect.getRight(),
0675: rect.getTop(), size, Element.ALIGN_LEFT);
0676: ct.setRunDirection(runDirection);
0677: status = ct.go(true);
0678: if ((status & ColumnText.NO_MORE_TEXT) != 0) {
0679: if (max - min < size * precision)
0680: return size;
0681: min = size;
0682: } else
0683: max = size;
0684: }
0685: return size;
0686: } catch (Exception e) {
0687: throw new ExceptionConverter(e);
0688: }
0689: }
0690:
0691: /**
0692: * Sets the digest/signature to an external calculated value.
0693: * @param digest the digest. This is the actual signature
0694: * @param RSAdata the extra data that goes into the data tag in PKCS#7
0695: * @param digestEncryptionAlgorithm the encryption algorithm. It may must be <CODE>null</CODE> if the <CODE>digest</CODE>
0696: * is also <CODE>null</CODE>. If the <CODE>digest</CODE> is not <CODE>null</CODE>
0697: * then it may be "RSA" or "DSA"
0698: */
0699: public void setExternalDigest(byte digest[], byte RSAdata[],
0700: String digestEncryptionAlgorithm) {
0701: externalDigest = digest;
0702: externalRSAdata = RSAdata;
0703: this .digestEncryptionAlgorithm = digestEncryptionAlgorithm;
0704: }
0705:
0706: /**
0707: * Gets the signing reason.
0708: * @return the signing reason
0709: */
0710: public String getReason() {
0711: return this .reason;
0712: }
0713:
0714: /**
0715: * Sets the signing reason.
0716: * @param reason the signing reason
0717: */
0718: public void setReason(String reason) {
0719: this .reason = reason;
0720: }
0721:
0722: /**
0723: * Gets the signing location.
0724: * @return the signing location
0725: */
0726: public String getLocation() {
0727: return this .location;
0728: }
0729:
0730: /**
0731: * Sets the signing location.
0732: * @param location the signing location
0733: */
0734: public void setLocation(String location) {
0735: this .location = location;
0736: }
0737:
0738: /**
0739: * Returns the Cryptographic Service Provider that will sign the document.
0740: * @return provider the name of the provider, for example "SUN",
0741: * or <code>null</code> to use the default provider.
0742: */
0743: public String getProvider() {
0744: return this .provider;
0745: }
0746:
0747: /**
0748: * Sets the Cryptographic Service Provider that will sign the document.
0749: *
0750: * @param provider the name of the provider, for example "SUN", or
0751: * <code>null</code> to use the default provider.
0752: */
0753: public void setProvider(String provider) {
0754: this .provider = provider;
0755: }
0756:
0757: /**
0758: * Gets the private key.
0759: * @return the private key
0760: */
0761: public java.security.PrivateKey getPrivKey() {
0762: return privKey;
0763: }
0764:
0765: /**
0766: * Gets the certificate chain.
0767: * @return the certificate chain
0768: */
0769: public java.security.cert.Certificate[] getCertChain() {
0770: return this .certChain;
0771: }
0772:
0773: /**
0774: * Gets the certificate revocation list.
0775: * @return the certificate revocation list
0776: */
0777: public java.security.cert.CRL[] getCrlList() {
0778: return this .crlList;
0779: }
0780:
0781: /**
0782: * Gets the filter used to sign the document.
0783: * @return the filter used to sign the document
0784: */
0785: public com.lowagie.text.pdf.PdfName getFilter() {
0786: return filter;
0787: }
0788:
0789: /**
0790: * Checks if a new field was created.
0791: * @return <CODE>true</CODE> if a new field was created, <CODE>false</CODE> if signing
0792: * an existing field or if the signature is invisible
0793: */
0794: public boolean isNewField() {
0795: return this .newField;
0796: }
0797:
0798: /**
0799: * Gets the page number of the field.
0800: * @return the page number of the field
0801: */
0802: public int getPage() {
0803: return page;
0804: }
0805:
0806: /**
0807: * Gets the field name.
0808: * @return the field name
0809: */
0810: public java.lang.String getFieldName() {
0811: return fieldName;
0812: }
0813:
0814: /**
0815: * Gets the rectangle that represent the position and dimension of the signature in the page.
0816: * @return the rectangle that represent the position and dimension of the signature in the page
0817: */
0818: public com.lowagie.text.Rectangle getPageRect() {
0819: return pageRect;
0820: }
0821:
0822: /**
0823: * Gets the signature date.
0824: * @return the signature date
0825: */
0826: public java.util.Calendar getSignDate() {
0827: return signDate;
0828: }
0829:
0830: /**
0831: * Sets the signature date.
0832: * @param signDate the signature date
0833: */
0834: public void setSignDate(java.util.Calendar signDate) {
0835: this .signDate = signDate;
0836: }
0837:
0838: com.lowagie.text.pdf.ByteBuffer getSigout() {
0839: return sigout;
0840: }
0841:
0842: void setSigout(com.lowagie.text.pdf.ByteBuffer sigout) {
0843: this .sigout = sigout;
0844: }
0845:
0846: java.io.OutputStream getOriginalout() {
0847: return originalout;
0848: }
0849:
0850: void setOriginalout(java.io.OutputStream originalout) {
0851: this .originalout = originalout;
0852: }
0853:
0854: /**
0855: * Gets the temporary file.
0856: * @return the temporary file or <CODE>null</CODE> is the document is created in memory
0857: */
0858: public java.io.File getTempFile() {
0859: return tempFile;
0860: }
0861:
0862: void setTempFile(java.io.File tempFile) {
0863: this .tempFile = tempFile;
0864: }
0865:
0866: /**
0867: * Gets a new signature fied name that doesn't clash with any existing name.
0868: * @return a new signature fied name
0869: */
0870: public String getNewSigName() {
0871: AcroFields af = writer.getAcroFields();
0872: String name = "Signature";
0873: int step = 0;
0874: boolean found = false;
0875: while (!found) {
0876: ++step;
0877: String n1 = name + step;
0878: if (af.getFieldItem(n1) != null)
0879: continue;
0880: n1 += ".";
0881: found = true;
0882: for (Iterator it = af.getFields().keySet().iterator(); it
0883: .hasNext();) {
0884: String fn = (String) it.next();
0885: if (fn.startsWith(n1)) {
0886: found = false;
0887: break;
0888: }
0889: }
0890: }
0891: name += step;
0892: return name;
0893: }
0894:
0895: /**
0896: * This is the first method to be called when using external signatures. The general sequence is:
0897: * preClose(), getDocumentBytes() and close().
0898: * <p>
0899: * If calling preClose() <B>dont't</B> call PdfStamper.close().
0900: * <p>
0901: * No external signatures are allowed if this methos is called.
0902: * @throws IOException on error
0903: * @throws DocumentException on error
0904: */
0905: public void preClose() throws IOException, DocumentException {
0906: preClose(null);
0907: }
0908:
0909: /**
0910: * This is the first method to be called when using external signatures. The general sequence is:
0911: * preClose(), getDocumentBytes() and close().
0912: * <p>
0913: * If calling preClose() <B>dont't</B> call PdfStamper.close().
0914: * <p>
0915: * If using an external signature <CODE>exclusionSizes</CODE> must contain at least
0916: * the <CODE>PdfName.CONTENTS</CODE> key with the size that it will take in the
0917: * document. Note that due to the hex string coding this size should be
0918: * byte_size*2+2.
0919: * @param exclusionSizes a <CODE>HashMap</CODE> with names and sizes to be excluded in the signature
0920: * calculation. The key is a <CODE>PdfName</CODE> and the value an
0921: * <CODE>Integer</CODE>. At least the <CODE>PdfName.CONTENTS</CODE> must be present
0922: * @throws IOException on error
0923: * @throws DocumentException on error
0924: */
0925: public void preClose(HashMap exclusionSizes) throws IOException,
0926: DocumentException {
0927: if (preClosed)
0928: throw new DocumentException("Document already pre closed.");
0929: preClosed = true;
0930: AcroFields af = writer.getAcroFields();
0931: String name = getFieldName();
0932: boolean fieldExists = !(isInvisible() || isNewField());
0933: int flags = PdfAnnotation.FLAGS_PRINT
0934: | PdfAnnotation.FLAGS_LOCKED;
0935: PdfIndirectReference refSig = writer.getPdfIndirectReference();
0936: writer.setSigFlags(3);
0937: if (fieldExists) {
0938: ArrayList widgets = af.getFieldItem(name).widgets;
0939: PdfDictionary widget = (PdfDictionary) widgets.get(0);
0940: writer.markUsed(widget);
0941: widget.put(PdfName.P, writer.getPageReference(getPage()));
0942: widget.put(PdfName.V, refSig);
0943: PdfObject obj = PdfReader.getPdfObjectRelease(widget
0944: .get(PdfName.F));
0945: if (obj != null && obj.isNumber())
0946: flags = ((PdfNumber) obj).intValue()
0947: | PdfAnnotation.FLAGS_LOCKED;
0948: widget.put(PdfName.F, new PdfNumber(flags));
0949: PdfDictionary ap = new PdfDictionary();
0950: ap.put(PdfName.N, getAppearance().getIndirectReference());
0951: widget.put(PdfName.AP, ap);
0952: } else {
0953: PdfFormField sigField = PdfFormField
0954: .createSignature(writer);
0955: sigField.setFieldName(name);
0956: sigField.put(PdfName.V, refSig);
0957: sigField.setFlags(flags);
0958:
0959: int pagen = getPage();
0960: if (!isInvisible())
0961: sigField.setWidget(getPageRect(), null);
0962: else
0963: sigField.setWidget(new Rectangle(0, 0), null);
0964: sigField.setAppearance(PdfAnnotation.APPEARANCE_NORMAL,
0965: getAppearance());
0966: sigField.setPage(pagen);
0967: writer.addAnnotation(sigField, pagen);
0968: }
0969:
0970: exclusionLocations = new HashMap();
0971: if (cryptoDictionary == null) {
0972: if (PdfName.ADOBE_PPKLITE.equals(getFilter()))
0973: sigStandard = new PdfSigGenericPKCS.PPKLite(
0974: getProvider());
0975: else if (PdfName.ADOBE_PPKMS.equals(getFilter()))
0976: sigStandard = new PdfSigGenericPKCS.PPKMS(getProvider());
0977: else if (PdfName.VERISIGN_PPKVS.equals(getFilter()))
0978: sigStandard = new PdfSigGenericPKCS.VeriSign(
0979: getProvider());
0980: else
0981: throw new IllegalArgumentException("Unknown filter: "
0982: + getFilter());
0983: sigStandard.setExternalDigest(externalDigest,
0984: externalRSAdata, digestEncryptionAlgorithm);
0985: if (getReason() != null)
0986: sigStandard.setReason(getReason());
0987: if (getLocation() != null)
0988: sigStandard.setLocation(getLocation());
0989: if (getContact() != null)
0990: sigStandard.setContact(getContact());
0991: sigStandard.put(PdfName.M, new PdfDate(getSignDate()));
0992: sigStandard.setSignInfo(getPrivKey(), getCertChain(),
0993: getCrlList());
0994: PdfString contents = (PdfString) sigStandard
0995: .get(PdfName.CONTENTS);
0996: PdfLiteral lit = new PdfLiteral((contents.toString()
0997: .length() + (PdfName.ADOBE_PPKLITE
0998: .equals(getFilter()) ? 0 : 64)) * 2 + 2);
0999: exclusionLocations.put(PdfName.CONTENTS, lit);
1000: sigStandard.put(PdfName.CONTENTS, lit);
1001: lit = new PdfLiteral(80);
1002: exclusionLocations.put(PdfName.BYTERANGE, lit);
1003: sigStandard.put(PdfName.BYTERANGE, lit);
1004: if (certificationLevel > 0) {
1005: addDocMDP(sigStandard);
1006: }
1007: if (signatureEvent != null)
1008: signatureEvent.getSignatureDictionary(sigStandard);
1009: writer.addToBody(sigStandard, refSig, false);
1010: } else {
1011: PdfLiteral lit = new PdfLiteral(80);
1012: exclusionLocations.put(PdfName.BYTERANGE, lit);
1013: cryptoDictionary.put(PdfName.BYTERANGE, lit);
1014: for (Iterator it = exclusionSizes.entrySet().iterator(); it
1015: .hasNext();) {
1016: Map.Entry entry = (Map.Entry) it.next();
1017: PdfName key = (PdfName) entry.getKey();
1018: Integer v = (Integer) entry.getValue();
1019: lit = new PdfLiteral(v.intValue());
1020: exclusionLocations.put(key, lit);
1021: cryptoDictionary.put(key, lit);
1022: }
1023: if (certificationLevel > 0)
1024: addDocMDP(cryptoDictionary);
1025: if (signatureEvent != null)
1026: signatureEvent.getSignatureDictionary(cryptoDictionary);
1027: writer.addToBody(cryptoDictionary, refSig, false);
1028: }
1029: if (certificationLevel > 0) {
1030: // add DocMDP entry to root
1031: PdfDictionary docmdp = new PdfDictionary();
1032: docmdp.put(new PdfName("DocMDP"), refSig);
1033: writer.reader.getCatalog()
1034: .put(new PdfName("Perms"), docmdp);
1035: }
1036: writer.close(stamper.getMoreInfo());
1037:
1038: range = new int[exclusionLocations.size() * 2];
1039: int byteRangePosition = ((PdfLiteral) exclusionLocations
1040: .get(PdfName.BYTERANGE)).getPosition();
1041: exclusionLocations.remove(PdfName.BYTERANGE);
1042: int idx = 1;
1043: for (Iterator it = exclusionLocations.values().iterator(); it
1044: .hasNext();) {
1045: PdfLiteral lit = (PdfLiteral) it.next();
1046: int n = lit.getPosition();
1047: range[idx++] = n;
1048: range[idx++] = lit.getPosLength() + n;
1049: }
1050: Arrays.sort(range, 1, range.length - 1);
1051: for (int k = 3; k < range.length - 2; k += 2)
1052: range[k] -= range[k - 1];
1053:
1054: if (tempFile == null) {
1055: bout = sigout.getBuffer();
1056: boutLen = sigout.size();
1057: range[range.length - 1] = boutLen - range[range.length - 2];
1058: ByteBuffer bf = new ByteBuffer();
1059: bf.append('[');
1060: for (int k = 0; k < range.length; ++k)
1061: bf.append(range[k]).append(' ');
1062: bf.append(']');
1063: System.arraycopy(bf.getBuffer(), 0, bout,
1064: byteRangePosition, bf.size());
1065: } else {
1066: try {
1067: raf = new RandomAccessFile(tempFile, "rw");
1068: int boutLen = (int) raf.length();
1069: range[range.length - 1] = boutLen
1070: - range[range.length - 2];
1071: ByteBuffer bf = new ByteBuffer();
1072: bf.append('[');
1073: for (int k = 0; k < range.length; ++k)
1074: bf.append(range[k]).append(' ');
1075: bf.append(']');
1076: raf.seek(byteRangePosition);
1077: raf.write(bf.getBuffer(), 0, bf.size());
1078: } catch (IOException e) {
1079: try {
1080: raf.close();
1081: } catch (Exception ee) {
1082: }
1083: try {
1084: tempFile.delete();
1085: } catch (Exception ee) {
1086: }
1087: throw e;
1088: }
1089: }
1090: }
1091:
1092: /**
1093: * This is the last method to be called when using external signatures. The general sequence is:
1094: * preClose(), getDocumentBytes() and close().
1095: * <p>
1096: * <CODE>update</CODE> is a <CODE>PdfDictionary</CODE> that must have exactly the
1097: * same keys as the ones provided in {@link #preClose(HashMap)}.
1098: * @param update a <CODE>PdfDictionary</CODE> with the key/value that will fill the holes defined
1099: * in {@link #preClose(HashMap)}
1100: * @throws DocumentException on error
1101: * @throws IOException on error
1102: */
1103: public void close(PdfDictionary update) throws IOException,
1104: DocumentException {
1105: try {
1106: if (!preClosed)
1107: throw new DocumentException(
1108: "preClose() must be called first.");
1109: ByteBuffer bf = new ByteBuffer();
1110: for (Iterator it = update.getKeys().iterator(); it
1111: .hasNext();) {
1112: PdfName key = (PdfName) it.next();
1113: PdfObject obj = update.get(key);
1114: PdfLiteral lit = (PdfLiteral) exclusionLocations
1115: .get(key);
1116: if (lit == null)
1117: throw new IllegalArgumentException("The key "
1118: + key.toString()
1119: + " didn't reserve space in preClose().");
1120: bf.reset();
1121: obj.toPdf(null, bf);
1122: if (bf.size() > lit.getPosLength())
1123: throw new IllegalArgumentException("The key "
1124: + key.toString() + " is too big. Is "
1125: + bf.size() + ", reserved "
1126: + lit.getPosLength());
1127: if (tempFile == null)
1128: System.arraycopy(bf.getBuffer(), 0, bout, lit
1129: .getPosition(), bf.size());
1130: else {
1131: raf.seek(lit.getPosition());
1132: raf.write(bf.getBuffer(), 0, bf.size());
1133: }
1134: }
1135: if (update.size() != exclusionLocations.size())
1136: throw new IllegalArgumentException(
1137: "The update dictionary has less keys than required.");
1138: if (tempFile == null) {
1139: originalout.write(bout, 0, boutLen);
1140: } else {
1141: if (originalout != null) {
1142: raf.seek(0);
1143: int length = (int) raf.length();
1144: byte buf[] = new byte[8192];
1145: while (length > 0) {
1146: int r = raf.read(buf, 0, Math.min(buf.length,
1147: length));
1148: if (r < 0)
1149: throw new EOFException("Unexpected EOF");
1150: originalout.write(buf, 0, r);
1151: length -= r;
1152: }
1153: }
1154: }
1155: } finally {
1156: if (tempFile != null) {
1157: try {
1158: raf.close();
1159: } catch (Exception ee) {
1160: }
1161: if (originalout != null)
1162: try {
1163: tempFile.delete();
1164: } catch (Exception ee) {
1165: }
1166: }
1167: if (originalout != null)
1168: try {
1169: originalout.close();
1170: } catch (Exception e) {
1171: }
1172: }
1173: }
1174:
1175: private void addDocMDP(PdfDictionary crypto) {
1176: PdfDictionary reference = new PdfDictionary();
1177: PdfDictionary transformParams = new PdfDictionary();
1178: transformParams.put(PdfName.P,
1179: new PdfNumber(certificationLevel));
1180: transformParams.put(PdfName.V, new PdfName("1.2"));
1181: transformParams.put(PdfName.TYPE, PdfName.TRANSFORMPARAMS);
1182: reference.put(PdfName.TRANSFORMMETHOD, PdfName.DOCMDP);
1183: reference.put(PdfName.TYPE, PdfName.SIGREF);
1184: reference.put(PdfName.TRANSFORMPARAMS, transformParams);
1185: reference.put(new PdfName("DigestValue"), new PdfString("aa"));
1186: PdfArray loc = new PdfArray();
1187: loc.add(new PdfNumber(0));
1188: loc.add(new PdfNumber(0));
1189: reference.put(new PdfName("DigestLocation"), loc);
1190: reference.put(new PdfName("DigestMethod"), new PdfName("MD5"));
1191: reference.put(PdfName.DATA, writer.reader.getTrailer().get(
1192: PdfName.ROOT));
1193: PdfArray types = new PdfArray();
1194: types.add(reference);
1195: crypto.put(PdfName.REFERENCE, types);
1196: }
1197:
1198: /**
1199: * Gets the document bytes that are hashable when using external signatures. The general sequence is:
1200: * preClose(), getRangeStream() and close().
1201: * <p>
1202: * @return the document bytes that are hashable
1203: */
1204: public InputStream getRangeStream() {
1205: return new PdfSignatureAppearance.RangeStream(raf, bout, range);
1206: }
1207:
1208: /**
1209: * Gets the user made signature dictionary. This is the dictionary at the /V key.
1210: * @return the user made signature dictionary
1211: */
1212: public com.lowagie.text.pdf.PdfDictionary getCryptoDictionary() {
1213: return cryptoDictionary;
1214: }
1215:
1216: /**
1217: * Sets a user made signature dictionary. This is the dictionary at the /V key.
1218: * @param cryptoDictionary a user made signature dictionary
1219: */
1220: public void setCryptoDictionary(
1221: com.lowagie.text.pdf.PdfDictionary cryptoDictionary) {
1222: this .cryptoDictionary = cryptoDictionary;
1223: }
1224:
1225: /**
1226: * Gets the <CODE>PdfStamper</CODE> associated with this instance.
1227: * @return the <CODE>PdfStamper</CODE> associated with this instance
1228: */
1229: public com.lowagie.text.pdf.PdfStamper getStamper() {
1230: return stamper;
1231: }
1232:
1233: void setStamper(com.lowagie.text.pdf.PdfStamper stamper) {
1234: this .stamper = stamper;
1235: }
1236:
1237: /**
1238: * Checks if the document is in the process of closing.
1239: * @return <CODE>true</CODE> if the document is in the process of closing,
1240: * <CODE>false</CODE> otherwise
1241: */
1242: public boolean isPreClosed() {
1243: return preClosed;
1244: }
1245:
1246: /**
1247: * Gets the instance of the standard signature dictionary. This instance
1248: * is only available after pre close.
1249: * <p>
1250: * The main use is to insert external signatures.
1251: * @return the instance of the standard signature dictionary
1252: */
1253: public com.lowagie.text.pdf.PdfSigGenericPKCS getSigStandard() {
1254: return sigStandard;
1255: }
1256:
1257: /**
1258: * Gets the signing contact.
1259: * @return the signing contact
1260: */
1261: public String getContact() {
1262: return this .contact;
1263: }
1264:
1265: /**
1266: * Sets the signing contact.
1267: * @param contact the signing contact
1268: */
1269: public void setContact(String contact) {
1270: this .contact = contact;
1271: }
1272:
1273: /**
1274: * Gets the n2 and n4 layer font.
1275: * @return the n2 and n4 layer font
1276: */
1277: public Font getLayer2Font() {
1278: return this .layer2Font;
1279: }
1280:
1281: /**
1282: * Sets the n2 and n4 layer font. If the font size is zero, auto-fit will be used.
1283: * @param layer2Font the n2 and n4 font
1284: */
1285: public void setLayer2Font(Font layer2Font) {
1286: this .layer2Font = layer2Font;
1287: }
1288:
1289: /**
1290: * Gets the Acrobat 6.0 layer mode.
1291: * @return the Acrobat 6.0 layer mode
1292: */
1293: public boolean isAcro6Layers() {
1294: return this .acro6Layers;
1295: }
1296:
1297: /**
1298: * Acrobat 6.0 and higher recomends that only layer n2 and n4 be present. This method sets that mode.
1299: * @param acro6Layers if <code>true</code> only the layers n2 and n4 will be present
1300: */
1301: public void setAcro6Layers(boolean acro6Layers) {
1302: this .acro6Layers = acro6Layers;
1303: }
1304:
1305: /** Sets the run direction in the n2 and n4 layer.
1306: * @param runDirection the run direction
1307: */
1308: public void setRunDirection(int runDirection) {
1309: if (runDirection < PdfWriter.RUN_DIRECTION_DEFAULT
1310: || runDirection > PdfWriter.RUN_DIRECTION_RTL)
1311: throw new RuntimeException("Invalid run direction: "
1312: + runDirection);
1313: this .runDirection = runDirection;
1314: }
1315:
1316: /** Gets the run direction.
1317: * @return the run direction
1318: */
1319: public int getRunDirection() {
1320: return runDirection;
1321: }
1322:
1323: /**
1324: * Getter for property signatureEvent.
1325: * @return Value of property signatureEvent.
1326: */
1327: public SignatureEvent getSignatureEvent() {
1328: return this .signatureEvent;
1329: }
1330:
1331: /**
1332: * Sets the signature event to allow modification of the signature dictionary.
1333: * @param signatureEvent the signature event
1334: */
1335: public void setSignatureEvent(SignatureEvent signatureEvent) {
1336: this .signatureEvent = signatureEvent;
1337: }
1338:
1339: /**
1340: * Gets the background image for the layer 2.
1341: * @return the background image for the layer 2
1342: */
1343: public Image getImage() {
1344: return this .image;
1345: }
1346:
1347: /**
1348: * Sets the background image for the layer 2.
1349: * @param image the background image for the layer 2
1350: */
1351: public void setImage(Image image) {
1352: this .image = image;
1353: }
1354:
1355: /**
1356: * Gets the scaling to be applied to the background image.
1357: * @return the scaling to be applied to the background image
1358: */
1359: public float getImageScale() {
1360: return this .imageScale;
1361: }
1362:
1363: /**
1364: * Sets the scaling to be applied to the background image. If it's zero the image
1365: * will fully fill the rectangle. If it's less than zero the image will fill the rectangle but
1366: * will keep the proportions. If it's greater than zero that scaling will be applied.
1367: * In any of the cases the image will always be centered. It's zero by default.
1368: * @param imageScale the scaling to be applied to the background image
1369: */
1370: public void setImageScale(float imageScale) {
1371: this .imageScale = imageScale;
1372: }
1373:
1374: /**
1375: * Commands to draw a yellow question mark in a stream content
1376: */
1377: public static final String questionMark = "% DSUnknown\n" + "q\n"
1378: + "1 G\n" + "1 g\n" + "0.1 0 0 0.1 9 0 cm\n"
1379: + "0 J 0 j 4 M []0 d\n" + "1 i \n" + "0 g\n"
1380: + "313 292 m\n" + "313 404 325 453 432 529 c\n"
1381: + "478 561 504 597 504 645 c\n"
1382: + "504 736 440 760 391 760 c\n"
1383: + "286 760 271 681 265 626 c\n" + "265 625 l\n"
1384: + "100 625 l\n" + "100 828 253 898 381 898 c\n"
1385: + "451 898 679 878 679 650 c\n"
1386: + "679 555 628 499 538 435 c\n"
1387: + "488 399 467 376 467 292 c\n" + "313 292 l\n" + "h\n"
1388: + "308 214 170 -164 re\n" + "f\n" + "0.44 G\n" + "1.2 w\n"
1389: + "1 1 0.4 rg\n" + "287 318 m\n"
1390: + "287 430 299 479 406 555 c\n"
1391: + "451 587 478 623 478 671 c\n"
1392: + "478 762 414 786 365 786 c\n"
1393: + "260 786 245 707 239 652 c\n" + "239 651 l\n"
1394: + "74 651 l\n" + "74 854 227 924 355 924 c\n"
1395: + "425 924 653 904 653 676 c\n"
1396: + "653 581 602 525 512 461 c\n"
1397: + "462 425 441 402 441 318 c\n" + "287 318 l\n" + "h\n"
1398: + "282 240 170 -164 re\n" + "B\n" + "Q\n";
1399:
1400: /**
1401: * Holds value of property contact.
1402: */
1403: private String contact;
1404:
1405: /**
1406: * Holds value of property layer2Font.
1407: */
1408: private Font layer2Font;
1409:
1410: /**
1411: * Holds value of property layer4Text.
1412: */
1413: private String layer4Text;
1414:
1415: /**
1416: * Holds value of property acro6Layers.
1417: */
1418: private boolean acro6Layers;
1419:
1420: /**
1421: * Holds value of property runDirection.
1422: */
1423: private int runDirection = PdfWriter.RUN_DIRECTION_NO_BIDI;
1424:
1425: /**
1426: * Holds value of property signatureEvent.
1427: */
1428: private SignatureEvent signatureEvent;
1429:
1430: /**
1431: * Holds value of property image.
1432: */
1433: private Image image;
1434:
1435: /**
1436: * Holds value of property imageScale.
1437: */
1438: private float imageScale;
1439:
1440: /**
1441: *
1442: */
1443: private static class RangeStream extends InputStream {
1444: private byte b[] = new byte[1];
1445: private RandomAccessFile raf;
1446: private byte bout[];
1447: private int range[];
1448: private int rangePosition = 0;
1449:
1450: private RangeStream(RandomAccessFile raf, byte bout[],
1451: int range[]) {
1452: this .raf = raf;
1453: this .bout = bout;
1454: this .range = range;
1455: }
1456:
1457: /**
1458: * @see java.io.InputStream#read()
1459: */
1460: public int read() throws IOException {
1461: int n = read(b);
1462: if (n != 1)
1463: return -1;
1464: return b[0] & 0xff;
1465: }
1466:
1467: /**
1468: * @see java.io.InputStream#read(byte[], int, int)
1469: */
1470: public int read(byte[] b, int off, int len) throws IOException {
1471: if (b == null) {
1472: throw new NullPointerException();
1473: } else if ((off < 0) || (off > b.length) || (len < 0)
1474: || ((off + len) > b.length) || ((off + len) < 0)) {
1475: throw new IndexOutOfBoundsException();
1476: } else if (len == 0) {
1477: return 0;
1478: }
1479: if (rangePosition >= range[range.length - 2]
1480: + range[range.length - 1]) {
1481: return -1;
1482: }
1483: for (int k = 0; k < range.length; k += 2) {
1484: int start = range[k];
1485: int end = start + range[k + 1];
1486: if (rangePosition < start)
1487: rangePosition = start;
1488: if (rangePosition >= start && rangePosition < end) {
1489: int lenf = Math.min(len, end - rangePosition);
1490: if (raf == null)
1491: System.arraycopy(bout, rangePosition, b, off,
1492: lenf);
1493: else {
1494: raf.seek(rangePosition);
1495: raf.readFully(b, off, lenf);
1496: }
1497: rangePosition += lenf;
1498: return lenf;
1499: }
1500: }
1501: return -1;
1502: }
1503: }
1504:
1505: /**
1506: * An interface to retrieve the signature dictionary for modification.
1507: */
1508: public interface SignatureEvent {
1509: /**
1510: * Allows modification of the signature dictionary.
1511: * @param sig the signature dictionary
1512: */
1513: public void getSignatureDictionary(PdfDictionary sig);
1514: }
1515:
1516: private int certificationLevel = NOT_CERTIFIED;
1517:
1518: /**
1519: * Gets the certified status of this document.
1520: * @return the certified status
1521: */
1522: public int getCertificationLevel() {
1523: return this .certificationLevel;
1524: }
1525:
1526: /**
1527: * Sets the document type to certified instead of simply signed.
1528: * @param certificationLevel the values can be: <code>NOT_CERTIFIED</code>, <code>CERTIFIED_NO_CHANGES_ALLOWED</code>,
1529: * <code>CERTIFIED_FORM_FILLING</code> and <code>CERTIFIED_FORM_FILLING_AND_ANNOTATIONS</code>
1530: */
1531: public void setCertificationLevel(int certificationLevel) {
1532: this.certificationLevel = certificationLevel;
1533: }
1534: }
|