KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > PdfSignatureAppearance


1 /*
2  * $Id: PdfSignatureAppearance.java 2752 2007-05-15 14:58:33Z blowagie $
3  *
4  * Copyright 2004-2006 by Paulo Soares.
5  *
6  * The contents of this file are subject to the Mozilla Public License Version 1.1
7  * (the "License"); you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the License.
13  *
14  * The Original Code is 'iText, a free JAVA-PDF library'.
15  *
16  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18  * All Rights Reserved.
19  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21  *
22  * Contributor(s): all the names of the contributors are added in the source code
23  * where applicable.
24  *
25  * Alternatively, the contents of this file may be used under the terms of the
26  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27  * provisions of LGPL are applicable instead of those above. If you wish to
28  * allow use of your version of this file only under the terms of the LGPL
29  * License and not to allow others to use your version of this file under
30  * the MPL, indicate your decision by deleting the provisions above and
31  * replace them with the notice and other provisions required by the LGPL.
32  * If you do not delete the provisions above, a recipient may use your version
33  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34  *
35  * This library is free software; you can redistribute it and/or modify it
36  * under the terms of the MPL as stated above or under the terms of the GNU
37  * Library General Public License as published by the Free Software Foundation;
38  * either version 2 of the License, or any later version.
39  *
40  * This library is distributed in the hope that it will be useful, but WITHOUT
41  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43  * details.
44  *
45  * If you didn't download this code from the following link, you should check if
46  * you aren't using an obsolete version:
47  * http://www.lowagie.com/iText/
48  */

49 package com.lowagie.text.pdf;
50
51 import java.io.EOFException JavaDoc;
52 import java.io.File JavaDoc;
53 import java.io.IOException JavaDoc;
54 import java.io.InputStream JavaDoc;
55 import java.io.OutputStream JavaDoc;
56 import java.io.RandomAccessFile JavaDoc;
57 import java.security.PrivateKey JavaDoc;
58 import java.security.cert.CRL JavaDoc;
59 import java.security.cert.Certificate JavaDoc;
60 import java.security.cert.X509Certificate JavaDoc;
61 import java.text.SimpleDateFormat JavaDoc;
62 import java.util.ArrayList JavaDoc;
63 import java.util.Arrays JavaDoc;
64 import java.util.Calendar JavaDoc;
65 import java.util.GregorianCalendar JavaDoc;
66 import java.util.HashMap JavaDoc;
67 import java.util.Iterator JavaDoc;
68 import java.util.Map JavaDoc;
69
70 import com.lowagie.text.Chunk;
71 import com.lowagie.text.DocumentException;
72 import com.lowagie.text.Element;
73 import com.lowagie.text.ExceptionConverter;
74 import com.lowagie.text.Font;
75 import com.lowagie.text.Image;
76 import com.lowagie.text.Paragraph;
77 import com.lowagie.text.Phrase;
78 import com.lowagie.text.Rectangle;
79
80 /**
81  * This class takes care of the cryptographic options and appearances that form a signature.
82  */

83 public class PdfSignatureAppearance {
84     
85     /**
86      * The rendering mode is just the description
87      */

88     public static final int SignatureRenderDescription = 0;
89     /**
90      * The rendering mode is the name of the signer and the description
91      */

92     public static final int SignatureRenderNameAndDescription = 1;
93     /**
94      * The rendering mode is an image and the description
95      */

96     public static final int SignatureRenderGraphicAndDescription = 2;
97
98     /**
99      * The self signed filter.
100      */

101     public static final PdfName SELF_SIGNED = PdfName.ADOBE_PPKLITE;
102     /**
103      * The VeriSign filter.
104      */

105     public static final PdfName VERISIGN_SIGNED = PdfName.VERISIGN_PPKVS;
106     /**
107      * The Windows Certificate Security.
108      */

109     public static final PdfName WINCER_SIGNED = PdfName.ADOBE_PPKMS;
110
111     public static final int NOT_CERTIFIED = 0;
112     public static final int CERTIFIED_NO_CHANGES_ALLOWED = 1;
113     public static final int CERTIFIED_FORM_FILLING = 2;
114     public static final int CERTIFIED_FORM_FILLING_AND_ANNOTATIONS = 3;
115     
116     private static final float TOP_SECTION = 0.3f;
117     private static final float MARGIN = 2;
118     private Rectangle rect;
119     private Rectangle pageRect;
120     private PdfTemplate app[] = new PdfTemplate[5];
121     private PdfTemplate frm;
122     private PdfStamperImp writer;
123     private String JavaDoc layer2Text;
124     private String JavaDoc reason;
125     private String JavaDoc location;
126     private Calendar JavaDoc signDate;
127     private String JavaDoc provider;
128     private int page = 1;
129     private String JavaDoc fieldName;
130     private PrivateKey JavaDoc privKey;
131     private Certificate JavaDoc[] certChain;
132     private CRL JavaDoc[] crlList;
133     private PdfName filter;
134     private boolean newField;
135     private ByteBuffer sigout;
136     private OutputStream JavaDoc originalout;
137     private File JavaDoc tempFile;
138     private PdfDictionary cryptoDictionary;
139     private PdfStamper stamper;
140     private boolean preClosed = false;
141     private PdfSigGenericPKCS sigStandard;
142     private int range[];
143     private RandomAccessFile JavaDoc raf;
144     private byte bout[];
145     private int boutLen;
146     private byte externalDigest[];
147     private byte externalRSAdata[];
148     private String JavaDoc digestEncryptionAlgorithm;
149     private HashMap JavaDoc exclusionLocations;
150         
151     PdfSignatureAppearance(PdfStamperImp writer) {
152         this.writer = writer;
153         signDate = new GregorianCalendar JavaDoc();
154         fieldName = getNewSigName();
155     }
156     
157     private int render = SignatureRenderDescription;
158
159     /**
160     * Gets the rendering mode for this signature.
161     * @return the rendering mode for this signature
162     */

163     public int getRender() {
164         return render;
165     }
166
167     /**
168      * Sets the rendering mode for this signature.
169      * The rendering modes can be the constants <CODE>SignatureRenderDescription</CODE>,
170      * <CODE>SignatureRenderNameAndDescription</CODE> or <CODE>SignatureRenderGraphicAndDescription</CODE>.
171      * The two last modes should be used with Acrobat 6 layer type.
172      * @param render the render mode
173      */

174     public void setRender(int render) {
175         this.render = render;
176     }
177
178     private Image signatureGraphic = null;
179
180     /**
181     * Gets the Image object to render.
182     * @return the image
183     */

184     public Image getSignatureGraphic() {
185         return signatureGraphic;
186     }
187
188     /**
189      * Sets the Image object to render when Render is set to <CODE>SignatureRenderGraphicAndDescription</CODE>
190      * @param signatureGraphic image rendered. If <CODE>null</CODE> the mode is defaulted
191      * to <CODE>SignatureRenderDescription</CODE>
192      */

193     public void setSignatureGraphic(Image signatureGraphic) {
194         this.signatureGraphic = signatureGraphic;
195     }
196
197     /**
198      * Sets the signature text identifying the signer.
199      * @param text the signature text identifying the signer. If <CODE>null</CODE> or not set
200      * a standard description will be used
201      */

202     public void setLayer2Text(String JavaDoc text) {
203         layer2Text = text;
204     }
205     
206     /**
207      * Gets the signature text identifying the signer if set by setLayer2Text().
208      * @return the signature text identifying the signer
209      */

210     public String JavaDoc getLayer2Text() {
211         return layer2Text;
212     }
213     
214     /**
215      * Sets the text identifying the signature status.
216      * @param text the text identifying the signature status. If <CODE>null</CODE> or not set
217      * the description "Signature Not Verified" will be used
218      */

219     public void setLayer4Text(String JavaDoc text) {
220         layer4Text = text;
221     }
222     
223     /**
224      * Gets the text identifying the signature status if set by setLayer4Text().
225      * @return the text identifying the signature status
226      */

227     public String JavaDoc getLayer4Text() {
228         return layer4Text;
229     }
230     
231     /**
232      * Gets the rectangle representing the signature dimensions.
233      * @return the rectangle representing the signature dimensions. It may be <CODE>null</CODE>
234      * or have zero width or height for invisible signatures
235      */

236     public Rectangle getRect() {
237         return rect;
238     }
239     
240     /**
241      * Gets the visibility status of the signature.
242      * @return the visibility status of the signature
243      */

244     public boolean isInvisible() {
245         return (rect == null || rect.getWidth() == 0 || rect.getHeight() == 0);
246     }
247     
248     /**
249      * Sets the cryptographic parameters.
250      * @param privKey the private key
251      * @param certChain the certificate chain
252      * @param crlList the certificate revocation list. It may be <CODE>null</CODE>
253      * @param filter the crytographic filter type. It can be SELF_SIGNED, VERISIGN_SIGNED or WINCER_SIGNED
254      */

255     public void setCrypto(PrivateKey JavaDoc privKey, Certificate JavaDoc[] certChain, CRL JavaDoc[] crlList, PdfName filter) {
256         this.privKey = privKey;
257         this.certChain = certChain;
258         this.crlList = crlList;
259         this.filter = filter;
260     }
261     
262     /**
263      * Sets the signature to be visible. It creates a new visible signature field.
264      * @param pageRect the position and dimension of the field in the page
265      * @param page the page to place the field. The fist page is 1
266      * @param fieldName the field name or <CODE>null</CODE> to generate automatically a new field name
267      */

268     public void setVisibleSignature(Rectangle pageRect, int page, String JavaDoc fieldName) {
269         if (fieldName != null) {
270             if (fieldName.indexOf('.') >= 0)
271                 throw new IllegalArgumentException JavaDoc("Field names cannot contain a dot.");
272             AcroFields af = writer.getAcroFields();
273             AcroFields.Item item = af.getFieldItem(fieldName);
274             if (item != null)
275                 throw new IllegalArgumentException JavaDoc("The field " + fieldName + " already exists.");
276             this.fieldName = fieldName;
277         }
278         if (page < 1 || page > writer.reader.getNumberOfPages())
279             throw new IllegalArgumentException JavaDoc("Invalid page number: " + page);
280         this.pageRect = new Rectangle(pageRect);
281         this.pageRect.normalize();
282         rect = new Rectangle(this.pageRect.getWidth(), this.pageRect.getHeight());
283         this.page = page;
284         newField = true;
285     }
286     
287     /**
288      * Sets the signature to be visible. An empty signature field with the same name must already exist.
289      * @param fieldName the existing empty signature field name
290      */

291     public void setVisibleSignature(String JavaDoc fieldName) {
292         AcroFields af = writer.getAcroFields();
293         AcroFields.Item item = af.getFieldItem(fieldName);
294         if (item == null)
295             throw new IllegalArgumentException JavaDoc("The field " + fieldName + " does not exist.");
296         PdfDictionary merged = (PdfDictionary)item.merged.get(0);
297         if (!PdfName.SIG.equals(PdfReader.getPdfObject(merged.get(PdfName.FT))))
298             throw new IllegalArgumentException JavaDoc("The field " + fieldName + " is not a signature field.");
299         this.fieldName = fieldName;
300         PdfArray r = (PdfArray)PdfReader.getPdfObject(merged.get(PdfName.RECT));
301         ArrayList JavaDoc ar = r.getArrayList();
302         float llx = ((PdfNumber)PdfReader.getPdfObject((PdfObject)ar.get(0))).floatValue();
303         float lly = ((PdfNumber)PdfReader.getPdfObject((PdfObject)ar.get(1))).floatValue();
304         float urx = ((PdfNumber)PdfReader.getPdfObject((PdfObject)ar.get(2))).floatValue();
305         float ury = ((PdfNumber)PdfReader.getPdfObject((PdfObject)ar.get(3))).floatValue();
306         pageRect = new Rectangle(llx, lly, urx, ury);
307         pageRect.normalize();
308         page = ((Integer JavaDoc)item.page.get(0)).intValue();
309         int rotation = writer.reader.getPageRotation(page);
310         Rectangle pageSize = writer.reader.getPageSizeWithRotation(page);
311         switch (rotation) {
312             case 90:
313                 pageRect = new Rectangle(
314                 pageRect.getBottom(),
315                 pageSize.getTop() - pageRect.getLeft(),
316                 pageRect.getTop(),
317                 pageSize.getTop() - pageRect.getRight());
318                 break;
319             case 180:
320                 pageRect = new Rectangle(
321                 pageSize.getRight() - pageRect.getLeft(),
322                 pageSize.getTop() - pageRect.getBottom(),
323                 pageSize.getRight() - pageRect.getRight(),
324                 pageSize.getTop() - pageRect.getTop());
325                 break;
326             case 270:
327                 pageRect = new Rectangle(
328                 pageSize.getRight() - pageRect.getBottom(),
329                 pageRect.getLeft(),
330                 pageSize.getRight() - pageRect.getTop(),
331                 pageRect.getRight());
332                 break;
333         }
334         if (rotation != 0)
335             pageRect.normalize();
336         rect = new Rectangle(this.pageRect.getWidth(), this.pageRect.getHeight());
337     }
338     
339     /**
340      * Gets a template layer to create a signature appearance. The layers can go from 0 to 4.
341      * <p>
342      * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A>
343      * for further details.
344      * @param layer the layer
345      * @return a template
346      */

347     public PdfTemplate getLayer(int layer) {
348         if (layer < 0 || layer >= app.length)
349             return null;
350         PdfTemplate t = app[layer];
351         if (t == null) {
352             t = app[layer] = new PdfTemplate(writer);
353             t.setBoundingBox(rect);
354             writer.addDirectTemplateSimple(t, new PdfName("n" + layer));
355         }
356         return t;
357     }
358     
359     /**
360      * Gets the template that aggregates all appearance layers. This corresponds to the /FRM resource.
361      * <p>
362      * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A>
363      * for further details.
364      * @return the template that aggregates all appearance layers
365      */

366     public PdfTemplate getTopLayer() {
367         if (frm == null) {
368             frm = new PdfTemplate(writer);
369             frm.setBoundingBox(rect);
370             writer.addDirectTemplateSimple(frm, new PdfName("FRM"));
371         }
372         return frm;
373     }
374     
375     /**
376      * Gets the main appearance layer.
377      * <p>
378      * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A>
379      * for further details.
380      * @return the main appearance layer
381      * @throws DocumentException on error
382      */

383     public PdfTemplate getAppearance() throws DocumentException {
384         if (isInvisible()) {
385             PdfTemplate t = new PdfTemplate(writer);
386             t.setBoundingBox(new Rectangle(0, 0));
387             writer.addDirectTemplateSimple(t, null);
388             return t;
389         }
390         if (app[0] == null) {
391             PdfTemplate t = app[0] = new PdfTemplate(writer);
392             t.setBoundingBox(new Rectangle(100, 100));
393             writer.addDirectTemplateSimple(t, new PdfName("n0"));
394             t.setLiteral("% DSBlank\n");
395         }
396         if (app[1] == null && !acro6Layers) {
397             PdfTemplate t = app[1] = new PdfTemplate(writer);
398             t.setBoundingBox(new Rectangle(100, 100));
399             writer.addDirectTemplateSimple(t, new PdfName("n1"));
400             t.setLiteral(questionMark);
401         }
402         if (app[2] == null) {
403             String JavaDoc text;
404             if (layer2Text == null) {
405                 StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
406                 buf.append("Digitally signed by ").append(PdfPKCS7.getSubjectFields((X509Certificate JavaDoc)certChain[0]).getField("CN")).append('\n');
407                 SimpleDateFormat JavaDoc sd = new SimpleDateFormat JavaDoc("yyyy.MM.dd HH:mm:ss z");
408                 buf.append("Date: ").append(sd.format(signDate.getTime()));
409                 if (reason != null)
410                     buf.append('\n').append("Reason: ").append(reason);
411                 if (location != null)
412                     buf.append('\n').append("Location: ").append(location);
413                 text = buf.toString();
414             }
415             else
416                 text = layer2Text;
417             PdfTemplate t = app[2] = new PdfTemplate(writer);
418             t.setBoundingBox(rect);
419             writer.addDirectTemplateSimple(t, new PdfName("n2"));
420             if (image != null) {
421                 if (imageScale == 0) {
422                     t.addImage(image, rect.getWidth(), 0, 0, rect.getHeight(), 0, 0);
423                 }
424                 else {
425                     float usableScale = imageScale;
426                     if (imageScale < 0)
427                         usableScale = Math.min(rect.getWidth() / image.getWidth(), rect.getHeight() / image.getHeight());
428                     float w = image.getWidth() * usableScale;
429                     float h = image.getHeight() * usableScale;
430                     float x = (rect.getWidth() - w) / 2;
431                     float y = (rect.getHeight() - h) / 2;
432                     t.addImage(image, w, 0, 0, h, x, y);
433                 }
434             }
435             Font font;
436             if (layer2Font == null)
437                 font = new Font();
438             else
439                 font = new Font(layer2Font);
440             float size = font.getSize();
441
442             Rectangle dataRect = null;
443             Rectangle signatureRect = null;
444
445             if (render == SignatureRenderNameAndDescription ||
446                 (render == SignatureRenderGraphicAndDescription && this.signatureGraphic != null)) {
447                 // origin is the bottom-left
448
signatureRect = new Rectangle(
449                     MARGIN,
450                     MARGIN,
451                     rect.getWidth() / 2 - MARGIN,
452                     rect.getHeight() - MARGIN);
453                 dataRect = new Rectangle(
454                     rect.getWidth() / 2 + MARGIN / 2,
455                     MARGIN,
456                     rect.getWidth() - MARGIN / 2,
457                     rect.getHeight() - MARGIN);
458
459                 if (rect.getHeight() > rect.getWidth()) {
460                     signatureRect = new Rectangle(
461                         MARGIN,
462                         rect.getHeight() / 2,
463                         rect.getWidth() - MARGIN,
464                         rect.getHeight());
465                     dataRect = new Rectangle(
466                         MARGIN,
467                         MARGIN,
468                         rect.getWidth() - MARGIN,
469                         rect.getHeight() / 2 - MARGIN);
470                 }
471             }
472             else {
473                 dataRect = new Rectangle(
474                     MARGIN,
475                     MARGIN,
476                     rect.getWidth() - MARGIN,
477                     rect.getHeight() * (1 - TOP_SECTION) - MARGIN);
478             }
479
480             if (render == SignatureRenderNameAndDescription) {
481                 String JavaDoc signedBy = PdfPKCS7.getSubjectFields((X509Certificate JavaDoc)certChain[0]).getField("CN");
482                 Rectangle sr2 = new Rectangle(signatureRect.getWidth() - MARGIN, signatureRect.getHeight() - MARGIN );
483                 float signedSize = fitText(font, signedBy, sr2, -1, runDirection);
484
485                 ColumnText ct2 = new ColumnText(t);
486                 ct2.setRunDirection(runDirection);
487                 ct2.setSimpleColumn(new Phrase(signedBy, font), signatureRect.getLeft(), signatureRect.getBottom(), signatureRect.getRight(), signatureRect.getTop(), signedSize, Element.ALIGN_LEFT);
488
489                 ct2.go();
490             }
491             else if (render == SignatureRenderGraphicAndDescription) {
492                 ColumnText ct2 = new ColumnText(t);
493                 ct2.setRunDirection(runDirection);
494                 ct2.setSimpleColumn(signatureRect.getLeft(), signatureRect.getBottom(), signatureRect.getRight(), signatureRect.getTop(), 0, Element.ALIGN_RIGHT);
495
496                 Image im = Image.getInstance(signatureGraphic);
497                 im.scaleToFit(signatureRect.getWidth(), signatureRect.getHeight());
498
499                 Paragraph p = new Paragraph();
500                 // must calculate the point to draw from to make image appear in middle of column
501
float x = 0;
502                 // experimentation found this magic number to counteract Adobe's signature graphic, which
503
// offsets the y co-ordinate by 15 units
504
float y = -im.getScaledHeight() + 15;
505
506                 x = x + (signatureRect.getWidth() - im.getScaledWidth()) / 2;
507                 y = y - (signatureRect.getHeight() - im.getScaledHeight()) / 2;
508                 p.add(new Chunk(im, x + (signatureRect.getWidth() - im.getScaledWidth()) / 2, y, false));
509                 ct2.addElement(p);
510                 ct2.go();
511             }
512             
513             if (size <= 0) {
514                 Rectangle sr = new Rectangle(dataRect.getWidth(), dataRect.getHeight());
515                 size = fitText(font, text, sr, 12, runDirection);
516             }
517             ColumnText ct = new ColumnText(t);
518             ct.setRunDirection(runDirection);
519             ct.setSimpleColumn(new Phrase(text, font), dataRect.getLeft(), dataRect.getBottom(), dataRect.getRight(), dataRect.getTop(), size, Element.ALIGN_LEFT);
520             ct.go();
521         }
522         if (app[3] == null && !acro6Layers) {
523             PdfTemplate t = app[3] = new PdfTemplate(writer);
524             t.setBoundingBox(new Rectangle(100, 100));
525             writer.addDirectTemplateSimple(t, new PdfName("n3"));
526             t.setLiteral("% DSBlank\n");
527         }
528         if (app[4] == null && !acro6Layers) {
529             PdfTemplate t = app[4] = new PdfTemplate(writer);
530             t.setBoundingBox(new Rectangle(0, rect.getHeight() * (1 - TOP_SECTION), rect.getRight(), rect.getTop()));
531             writer.addDirectTemplateSimple(t, new PdfName("n4"));
532             Font font;
533             if (layer2Font == null)
534                 font = new Font();
535             else
536                 font = new Font(layer2Font);
537             float size = font.getSize();
538             String JavaDoc text = "Signature Not Verified";
539             if (layer4Text != null)
540                 text = layer4Text;
541             Rectangle sr = new Rectangle(rect.getWidth() - 2 * MARGIN, rect.getHeight() * TOP_SECTION - 2 * MARGIN);
542             size = fitText(font, text, sr, 15, runDirection);
543             ColumnText ct = new ColumnText(t);
544             ct.setRunDirection(runDirection);
545             ct.setSimpleColumn(new Phrase(text, font), MARGIN, 0, rect.getWidth() - MARGIN, rect.getHeight() - MARGIN, size, Element.ALIGN_LEFT);
546             ct.go();
547         }
548         int rotation = writer.reader.getPageRotation(page);
549         Rectangle rotated = new Rectangle(rect);
550         int n = rotation;
551         while (n > 0) {
552             rotated = rotated.rotate();
553             n -= 90;
554         }
555         if (frm == null) {
556             frm = new PdfTemplate(writer);
557             frm.setBoundingBox(rotated);
558             writer.addDirectTemplateSimple(frm, new PdfName("FRM"));
559             float scale = Math.min(rect.getWidth(), rect.getHeight()) * 0.9f;
560             float x = (rect.getWidth() - scale) / 2;
561             float y = (rect.getHeight() - scale) / 2;
562             scale /= 100;
563             if (rotation == 90)
564                 frm.concatCTM(0, 1, -1, 0, rect.getHeight(), 0);
565             else if (rotation == 180)
566                 frm.concatCTM(-1, 0, 0, -1, rect.getWidth(), rect.getHeight());
567             else if (rotation == 270)
568                 frm.concatCTM(0, -1, 1, 0, 0, rect.getWidth());
569             frm.addTemplate(app[0], 0, 0);
570             if (!acro6Layers)
571                 frm.addTemplate(app[1], scale, 0, 0, scale, x, y);
572             frm.addTemplate(app[2], 0, 0);
573             if (!acro6Layers) {
574                 frm.addTemplate(app[3], scale, 0, 0, scale, x, y);
575                 frm.addTemplate(app[4], 0, 0);
576             }
577         }
578         PdfTemplate napp = new PdfTemplate(writer);
579         napp.setBoundingBox(rotated);
580         writer.addDirectTemplateSimple(napp, null);
581         napp.addTemplate(frm, 0, 0);
582         return napp;
583     }
584     
585     /**
586      * Fits the text to some rectangle adjusting the font size as needed.
587      * @param font the font to use
588      * @param text the text
589      * @param rect the rectangle where the text must fit
590      * @param maxFontSize the maximum font size
591      * @param runDirection the run direction
592      * @return the calculated font size that makes the text fit
593      */

594     public static float fitText(Font font, String JavaDoc text, Rectangle rect, float maxFontSize, int runDirection) {
595         try {
596             ColumnText ct = null;
597             int status = 0;
598             if (maxFontSize <= 0) {
599                 int cr = 0;
600                 int lf = 0;
601                 char t[] = text.toCharArray();
602                 for (int k = 0; k < t.length; ++k) {
603                     if (t[k] == '\n')
604                         ++lf;
605                     else if (t[k] == '\r')
606                         ++cr;
607                 }
608                 int minLines = Math.max(cr, lf) + 1;
609                 maxFontSize = Math.abs(rect.getHeight()) / minLines - 0.001f;
610             }
611             font.setSize(maxFontSize);
612             Phrase ph = new Phrase(text, font);
613             ct = new ColumnText(null);
614             ct.setSimpleColumn(ph, rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop(), maxFontSize, Element.ALIGN_LEFT);
615             ct.setRunDirection(runDirection);
616             status = ct.go(true);
617             if ((status & ColumnText.NO_MORE_TEXT) != 0)
618                 return maxFontSize;
619             float precision = 0.1f;
620             float min = 0;
621             float max = maxFontSize;
622             float size = maxFontSize;
623             for (int k = 0; k < 50; ++k) { //just in case it doesn't converge
624
size = (min + max) / 2;
625                 ct = new ColumnText(null);
626                 font.setSize(size);
627                 ct.setSimpleColumn(new Phrase(text, font), rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop(), size, Element.ALIGN_LEFT);
628                 ct.setRunDirection(runDirection);
629                 status = ct.go(true);
630                 if ((status & ColumnText.NO_MORE_TEXT) != 0) {
631                     if (max - min < size * precision)
632                         return size;
633                     min = size;
634                 }
635                 else
636                     max = size;
637             }
638             return size;
639         }
640         catch (Exception JavaDoc e) {
641             throw new ExceptionConverter(e);
642         }
643     }
644     
645     /**
646      * Sets the digest/signature to an external calculated value.
647      * @param digest the digest. This is the actual signature
648      * @param RSAdata the extra data that goes into the data tag in PKCS#7
649      * @param digestEncryptionAlgorithm the encryption algorithm. It may must be <CODE>null</CODE> if the <CODE>digest</CODE>
650      * is also <CODE>null</CODE>. If the <CODE>digest</CODE> is not <CODE>null</CODE>
651      * then it may be "RSA" or "DSA"
652      */

653     public void setExternalDigest(byte digest[], byte RSAdata[], String JavaDoc digestEncryptionAlgorithm) {
654         externalDigest = digest;
655         externalRSAdata = RSAdata;
656         this.digestEncryptionAlgorithm = digestEncryptionAlgorithm;
657     }
658
659     /**
660      * Gets the signing reason.
661      * @return the signing reason
662      */

663     public String JavaDoc getReason() {
664         return this.reason;
665     }
666     
667     /**
668      * Sets the signing reason.
669      * @param reason the signing reason
670      */

671     public void setReason(String JavaDoc reason) {
672         this.reason = reason;
673     }
674     
675     /**
676      * Gets the signing location.
677      * @return the signing location
678      */

679     public String JavaDoc getLocation() {
680         return this.location;
681     }
682     
683     /**
684      * Sets the signing location.
685      * @param location the signing location
686      */

687     public void setLocation(String JavaDoc location) {
688         this.location = location;
689     }
690         
691     /**
692      * Returns the Cryptographic Service Provider that will sign the document.
693      * @return provider the name of the provider, for example "SUN",
694      * or <code>null</code> to use the default provider.
695      */

696     public String JavaDoc getProvider() {
697         return this.provider;
698     }
699     
700     /**
701      * Sets the Cryptographic Service Provider that will sign the document.
702      *
703      * @param provider the name of the provider, for example "SUN", or
704      * <code>null</code> to use the default provider.
705      */

706     public void setProvider(String JavaDoc provider) {
707         this.provider = provider;
708     }
709     
710     /**
711      * Gets the private key.
712      * @return the private key
713      */

714     public java.security.PrivateKey JavaDoc getPrivKey() {
715         return privKey;
716     }
717     
718     /**
719      * Gets the certificate chain.
720      * @return the certificate chain
721      */

722     public java.security.cert.Certificate JavaDoc[] getCertChain() {
723