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         return this.certChain;
724     }
725     
726     /**
727      * Gets the certificate revocation list.
728      * @return the certificate revocation list
729      */

730     public java.security.cert.CRL JavaDoc[] getCrlList() {
731         return this.crlList;
732     }
733     
734     /**
735      * Gets the filter used to sign the document.
736      * @return the filter used to sign the document
737      */

738     public com.lowagie.text.pdf.PdfName getFilter() {
739         return filter;
740     }
741     
742     /**
743      * Checks if a new field was created.
744      * @return <CODE>true</CODE> if a new field was created, <CODE>false</CODE> if signing
745      * an existing field or if the signature is invisible
746      */

747     public boolean isNewField() {
748         return this.newField;
749     }
750     
751     /**
752      * Gets the page number of the field.
753      * @return the page number of the field
754      */

755     public int getPage() {
756         return page;
757     }
758     
759     /**
760      * Gets the field name.
761      * @return the field name
762      */

763     public java.lang.String JavaDoc getFieldName() {
764         return fieldName;
765     }
766     
767     /**
768      * Gets the rectangle that represent the position and dimension of the signature in the page.
769      * @return the rectangle that represent the position and dimension of the signature in the page
770      */

771     public com.lowagie.text.Rectangle getPageRect() {
772         return pageRect;
773     }
774     
775     /**
776      * Gets the signature date.
777      * @return the signature date
778      */

779     public java.util.Calendar JavaDoc getSignDate() {
780         return signDate;
781     }
782     
783     /**
784      * Sets the signature date.
785      * @param signDate the signature date
786      */

787     public void setSignDate(java.util.Calendar JavaDoc signDate) {
788         this.signDate = signDate;
789     }
790     
791     com.lowagie.text.pdf.ByteBuffer getSigout() {
792         return sigout;
793     }
794     
795     void setSigout(com.lowagie.text.pdf.ByteBuffer sigout) {
796         this.sigout = sigout;
797     }
798     
799     java.io.OutputStream JavaDoc getOriginalout() {
800         return originalout;
801     }
802     
803     void setOriginalout(java.io.OutputStream JavaDoc originalout) {
804         this.originalout = originalout;
805     }
806     
807     /**
808      * Gets the temporary file.
809      * @return the temporary file or <CODE>null</CODE> is the document is created in memory
810      */

811     public java.io.File JavaDoc getTempFile() {
812         return tempFile;
813     }
814     
815     void setTempFile(java.io.File JavaDoc tempFile) {
816         this.tempFile = tempFile;
817     }
818
819     /**
820      * Gets a new signature fied name that doesn't clash with any existing name.
821      * @return a new signature fied name
822      */

823     public String JavaDoc getNewSigName() {
824         AcroFields af = writer.getAcroFields();
825         String JavaDoc name = "Signature";
826         int step = 0;
827         boolean found = false;
828         while (!found) {
829             ++step;
830             String JavaDoc n1 = name + step;
831             if (af.getFieldItem(n1) != null)
832                 continue;
833             n1 += ".";
834             found = true;
835             for (Iterator JavaDoc it = af.getFields().keySet().iterator(); it.hasNext();) {
836                 String JavaDoc fn = (String JavaDoc)it.next();
837                 if (fn.startsWith(n1)) {
838                     found = false;
839                     break;
840                 }
841             }
842         }
843         name += step;
844         return name;
845     }
846     
847     /**
848      * This is the first method to be called when using external signatures. The general sequence is:
849      * preClose(), getDocumentBytes() and close().
850      * <p>
851      * If calling preClose() <B>dont't</B> call PdfStamper.close().
852      * <p>
853      * No external signatures are allowed if this methos is called.
854      * @throws IOException on error
855      * @throws DocumentException on error
856      */

857     public void preClose() throws IOException JavaDoc, DocumentException {
858         preClose(null);
859     }
860     /**
861      * This is the first method to be called when using external signatures. The general sequence is:
862      * preClose(), getDocumentBytes() and close().
863      * <p>
864      * If calling preClose() <B>dont't</B> call PdfStamper.close().
865      * <p>
866      * If using an external signature <CODE>exclusionSizes</CODE> must contain at least
867      * the <CODE>PdfName.CONTENTS</CODE> key with the size that it will take in the
868      * document. Note that due to the hex string coding this size should be
869      * byte_size*2+2.
870      * @param exclusionSizes a <CODE>HashMap</CODE> with names and sizes to be excluded in the signature
871      * calculation. The key is a <CODE>PdfName</CODE> and the value an
872      * <CODE>Integer</CODE>. At least the <CODE>PdfName.CONTENTS</CODE> must be present
873      * @throws IOException on error
874      * @throws DocumentException on error
875      */

876     public void preClose(HashMap JavaDoc exclusionSizes) throws IOException JavaDoc, DocumentException {
877         if (preClosed)
878             throw new DocumentException("Document already pre closed.");
879         preClosed = true;
880         AcroFields af = writer.getAcroFields();
881         String JavaDoc name = getFieldName();
882         boolean fieldExists = !(isInvisible() || isNewField());
883         int flags = PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_LOCKED;
884         PdfIndirectReference refSig = writer.getPdfIndirectReference();
885         writer.setSigFlags(3);
886         if (fieldExists) {
887             ArrayList JavaDoc widgets = af.getFieldItem(name).widgets;
888             PdfDictionary widget = (PdfDictionary)widgets.get(0);
889             writer.markUsed(widget);
890             widget.put(PdfName.P, writer.getPageReference(getPage()));
891             widget.put(PdfName.V, refSig);
892             PdfObject obj = PdfReader.getPdfObjectRelease(widget.get(PdfName.F));
893             if (obj != null && obj.isNumber())
894                 flags = ((PdfNumber)obj).intValue() | PdfAnnotation.FLAGS_LOCKED;
895             widget.put(PdfName.F, new PdfNumber(flags));
896             PdfDictionary ap = new PdfDictionary();
897             ap.put(PdfName.N, getAppearance().getIndirectReference());
898             widget.put(PdfName.AP, ap);
899         }
900         else {
901             PdfFormField sigField = PdfFormField.createSignature(writer);
902             sigField.setFieldName(name);
903             sigField.put(PdfName.V, refSig);
904             sigField.setFlags(flags);
905
906             int pagen = getPage();
907             if (!isInvisible())
908                 sigField.setWidget(getPageRect(), null);
909             else
910                 sigField.setWidget(new Rectangle(0, 0), null);
911             sigField.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, getAppearance());
912             sigField.setPage(pagen);
913             writer.addAnnotation(sigField, pagen);
914         }
915
916         exclusionLocations = new HashMap JavaDoc();
917         if (cryptoDictionary == null) {
918             if (PdfName.ADOBE_PPKLITE.equals(getFilter()))
919                 sigStandard = new PdfSigGenericPKCS.PPKLite(getProvider());
920             else if (PdfName.ADOBE_PPKMS.equals(getFilter()))
921                 sigStandard = new PdfSigGenericPKCS.PPKMS(getProvider());
922             else if (PdfName.VERISIGN_PPKVS.equals(getFilter()))
923                 sigStandard = new PdfSigGenericPKCS.VeriSign(getProvider());
924             else
925                 throw new IllegalArgumentException JavaDoc("Unknown filter: " + getFilter());
926             sigStandard.setExternalDigest(externalDigest, externalRSAdata, digestEncryptionAlgorithm);
927             if (getReason() != null)
928                 sigStandard.setReason(getReason());
929             if (getLocation() != null)
930                 sigStandard.setLocation(getLocation());
931             if (getContact() != null)
932                 sigStandard.setContact(getContact());
933             sigStandard.put(PdfName.M, new PdfDate(getSignDate()));
934             sigStandard.setSignInfo(getPrivKey(), getCertChain(), getCrlList());
935             PdfString contents = (PdfString)sigStandard.get(PdfName.CONTENTS);
936             PdfLiteral lit = new PdfLiteral((contents.toString().length() + (PdfName.ADOBE_PPKLITE.equals(getFilter())?0:64)) * 2 + 2);
937             exclusionLocations.put(PdfName.CONTENTS, lit);
938             sigStandard.put(PdfName.CONTENTS, lit);
939             lit = new PdfLiteral(80);
940             exclusionLocations.put(PdfName.BYTERANGE, lit);
941             sigStandard.put(PdfName.BYTERANGE, lit);
942             if (certificationLevel > 0) {
943                 addDocMDP(sigStandard);
944             }
945             if (signatureEvent != null)
946                 signatureEvent.getSignatureDictionary(sigStandard);
947             writer.addToBody(sigStandard, refSig, false);
948         }
949         else {
950             PdfLiteral lit = new PdfLiteral(80);
951             exclusionLocations.put(PdfName.BYTERANGE, lit);
952             cryptoDictionary.put(PdfName.BYTERANGE, lit);
953             for (Iterator JavaDoc it = exclusionSizes.entrySet().iterator(); it.hasNext();) {
954                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc)it.next();
955                 PdfName key = (PdfName)entry.getKey();
956                 Integer JavaDoc v = (Integer JavaDoc)entry.getValue();
957                 lit = new PdfLiteral(v.intValue());
958                 exclusionLocations.put(key, lit);
959                 cryptoDictionary.put(key, lit);
960             }
961             if (certificationLevel > 0)
962                 addDocMDP(cryptoDictionary);
963             if (signatureEvent != null)
964                 signatureEvent.getSignatureDictionary(cryptoDictionary);
965             writer.addToBody(cryptoDictionary, refSig, false);
966         }
967         if (certificationLevel > 0) {
968           // add DocMDP entry to root
969
PdfDictionary docmdp = new PdfDictionary();
970              docmdp.put(new PdfName("DocMDP"), refSig);
971              writer.reader.getCatalog().put(new PdfName("Perms"), docmdp);
972         }
973         writer.close(stamper.getMoreInfo());
974         
975         range = new int[exclusionLocations.size() * 2];
976         int byteRangePosition = ((PdfLiteral)exclusionLocations.get(PdfName.BYTERANGE)).getPosition();
977         exclusionLocations.remove(PdfName.BYTERANGE);
978         int idx = 1;
979         for (Iterator JavaDoc it = exclusionLocations.values().iterator(); it.hasNext();) {
980             PdfLiteral lit = (PdfLiteral)it.next();
981             int n = lit.getPosition();
982             range[idx++] = n;
983             range[idx++] = lit.getPosLength() + n;
984         }
985         Arrays.sort(range, 1, range.length - 1);
986         for (int k = 3; k < range.length - 2; k += 2)
987             range[k] -= range[k - 1];
988         
989         if (tempFile == null) {
990             bout = sigout.getBuffer();
991             boutLen = sigout.size();
992             range[range.length - 1] = boutLen - range[range.length - 2];
993             ByteBuffer bf = new ByteBuffer();
994             bf.append('[');
995             for (int k = 0; k < range.length; ++k)
996                 bf.append(range[k]).append(' ');
997             bf.append(']');
998             System.arraycopy(bf.getBuffer(), 0, bout, byteRangePosition, bf.size());
999         }
1000        else {
1001            try {
1002                raf = new RandomAccessFile JavaDoc(tempFile, "rw");
1003                int boutLen = (int)raf.length();
1004                range[range.length - 1] = boutLen - range[range.length - 2];
1005                ByteBuffer bf = new ByteBuffer();
1006                bf.append('[');
1007                for (int k = 0; k < range.length; ++k)
1008                    bf.append(range[k]).append(' ');
1009                bf.append(']');
1010                raf.seek(byteRangePosition);
1011                raf.write(bf.getBuffer(), 0, bf.size());
1012            }
1013            catch (IOException JavaDoc e) {
1014                try{raf.close();}catch(Exception JavaDoc ee){}
1015                try{tempFile.delete();}catch(Exception JavaDoc ee){}
1016                throw e;
1017            }
1018        }
1019    }
1020    
1021    /**
1022     * This is the last method to be called when using external signatures. The general sequence is:
1023     * preClose(), getDocumentBytes() and close().
1024     * <p>
1025     * <CODE>update</CODE> is a <CODE>PdfDictionary</CODE> that must have exactly the
1026     * same keys as the ones provided in {@link #preClose(HashMap)}.
1027     * @param update a <CODE>PdfDictionary</CODE> with the key/value that will fill the holes defined
1028     * in {@link #preClose(HashMap)}
1029     * @throws DocumentException on error
1030     * @throws IOException on error
1031     */

1032    public void close(PdfDictionary update) throws IOException JavaDoc, DocumentException {
1033        try {
1034            if (!preClosed)
1035                throw new DocumentException("preClose() must be called first.");
1036            ByteBuffer bf = new ByteBuffer();
1037            for (Iterator JavaDoc it = update.getKeys().iterator(); it.hasNext();) {
1038                PdfName key = (PdfName)it.next();
1039                PdfObject obj = update.get(key);
1040                PdfLiteral lit = (PdfLiteral)exclusionLocations.get(key);
1041                if (lit == null)
1042                    throw new IllegalArgumentException JavaDoc("The key " + key.toString() + " didn't reserve space in preClose().");
1043                bf.reset();
1044                obj.toPdf(null, bf);
1045                if (bf.size() > lit.getPosLength())
1046                    throw new IllegalArgumentException JavaDoc("The key " + key.toString() + " is too big. Is " + bf.size() + ", reserved " + lit.getPosLength());
1047                if (tempFile == null)
1048                    System.arraycopy(bf.getBuffer(), 0, bout, lit.getPosition(), bf.size());
1049                else {
1050                    raf.seek(lit.getPosition());
1051                    raf.write(bf.getBuffer(), 0, bf.size());
1052                }
1053            }
1054            if (update.size() != exclusionLocations.size())
1055                throw new IllegalArgumentException JavaDoc("The update dictionary has less keys than required.");
1056            if (tempFile == null) {
1057                originalout.write(bout, 0, boutLen);
1058            }
1059            else {
1060                if (originalout != null) {
1061                    raf.seek(0);
1062                    int length = (int)raf.length();
1063                    byte buf[] = new byte[8192];
1064                    while (length > 0) {
1065                        int r = raf.read(buf, 0, Math.min(buf.length, length));
1066                        if (r < 0)
1067                            throw new EOFException JavaDoc("Unexpected EOF");
1068                        originalout.write(buf, 0, r);
1069                        length -= r;
1070                    }
1071                }
1072            }
1073        }
1074        finally {
1075            if (tempFile != null) {
1076                try{raf.close();}catch(Exception JavaDoc ee){}
1077                if (originalout != null)
1078                    try{tempFile.delete();}catch(Exception JavaDoc ee){}
1079            }
1080            if (originalout != null)
1081                try{originalout.close();}catch(Exception JavaDoc e){}
1082        }
1083    }
1084    
1085    private void addDocMDP(PdfDictionary crypto) {
1086        PdfDictionary reference = new PdfDictionary();
1087        PdfDictionary transformParams = new PdfDictionary();
1088        transformParams.put(PdfName.P, new PdfNumber(certificationLevel));
1089        transformParams.put(PdfName.V, new PdfName("1.2"));
1090        transformParams.put(PdfName.TYPE, PdfName.TRANSFORMPARAMS);
1091        reference.put(PdfName.TRANSFORMMETHOD, PdfName.DOCMDP);
1092        reference.put(PdfName.TYPE, PdfName.SIGREF);
1093        reference.put(PdfName.TRANSFORMPARAMS, transformParams);
1094        reference.put(new PdfName("DigestValue"), new PdfString("aa"));
1095        PdfArray loc = new PdfArray();
1096        loc.add(new PdfNumber(0));
1097        loc.add(new PdfNumber(0));
1098        reference.put(new PdfName("DigestLocation"), loc);
1099        reference.put(new PdfName("DigestMethod"), new PdfName("MD5"));
1100        reference.put(PdfName.DATA, writer.reader.getTrailer().get(PdfName.ROOT));
1101        PdfArray types = new PdfArray();
1102        types.add(reference);
1103        crypto.put(PdfName.REFERENCE, types);
1104    }
1105    
1106    /**
1107     * Gets the document bytes that are hashable when using external signatures. The general sequence is:
1108     * preClose(), getRangeStream() and close().
1109     * <p>
1110     * @return the document bytes that are hashable
1111     */

1112    public InputStream JavaDoc getRangeStream() {
1113        return new PdfSignatureAppearance.RangeStream(raf, bout, range);
1114    }
1115    
1116    /**
1117     * Gets the user made signature dictionary. This is the dictionary at the /V key.
1118     * @return the user made signature dictionary
1119     */

1120    public com.lowagie.text.pdf.PdfDictionary getCryptoDictionary() {
1121        return cryptoDictionary;
1122    }
1123    
1124    /**
1125     * Sets a user made signature dictionary. This is the dictionary at the /V key.
1126     * @param cryptoDictionary a user made signature dictionary
1127     */

1128    public void setCryptoDictionary(com.lowagie.text.pdf.PdfDictionary cryptoDictionary) {
1129        this.cryptoDictionary = cryptoDictionary;
1130    }
1131    
1132    /**
1133     * Gets the <CODE>PdfStamper</CODE> associated with this instance.
1134     * @return the <CODE>PdfStamper</CODE> associated with this instance
1135     */

1136    public com.lowagie.text.pdf.PdfStamper getStamper() {
1137        return stamper;
1138    }
1139    
1140    void setStamper(com.lowagie.text.pdf.PdfStamper stamper) {
1141        this.stamper = stamper;
1142    }
1143    
1144    /**
1145     * Checks if the document is in the process of closing.
1146     * @return <CODE>true</CODE> if the document is in the process of closing,
1147     * <CODE>false</CODE> otherwise
1148     */

1149    public boolean isPreClosed() {
1150        return preClosed;
1151    }
1152    
1153    /**
1154     * Gets the instance of the standard signature dictionary. This instance
1155     * is only available after pre close.
1156     * <p>
1157     * The main use is to insert external signatures.
1158     * @return the instance of the standard signature dictionary
1159     */

1160    public com.lowagie.text.pdf.PdfSigGenericPKCS getSigStandard() {
1161        return sigStandard;
1162    }
1163    
1164    /**
1165     * Gets the signing contact.
1166     * @return the signing contact
1167     */

1168    public String JavaDoc getContact() {
1169        return this.contact;
1170    }
1171    
1172    /**
1173     * Sets the signing contact.
1174     * @param contact the signing contact
1175     */

1176    public void setContact(String JavaDoc contact) {
1177        this.contact = contact;
1178    }
1179    
1180    /**
1181     * Gets the n2 and n4 layer font.
1182     * @return the n2 and n4 layer font
1183     */

1184    public Font getLayer2Font() {
1185        return this.layer2Font;
1186    }
1187    
1188    /**
1189     * Sets the n2 and n4 layer font. If the font size is zero, auto-fit will be used.
1190     * @param layer2Font the n2 and n4 font
1191     */

1192    public void setLayer2Font(Font layer2Font) {
1193        this.layer2Font = layer2Font;
1194    }
1195    
1196    /**
1197     * Gets the Acrobat 6.0 layer mode.
1198     * @return the Acrobat 6.0 layer mode
1199     */

1200    public boolean isAcro6Layers() {
1201        return this.acro6Layers;
1202    }
1203    
1204    /**
1205     * Acrobat 6.0 and higher recomends that only layer n2 and n4 be present. This method sets that mode.
1206     * @param acro6Layers if <code>true</code> only the layers n2 and n4 will be present
1207     */

1208    public void setAcro6Layers(boolean acro6Layers) {
1209        this.acro6Layers = acro6Layers;
1210    }
1211    
1212    /** Sets the run direction in the n2 and n4 layer.
1213     * @param runDirection the run direction
1214     */

1215    public void setRunDirection(int runDirection) {
1216        if (runDirection < PdfWriter.RUN_DIRECTION_DEFAULT || runDirection > PdfWriter.RUN_DIRECTION_RTL)
1217            throw new RuntimeException JavaDoc("Invalid run direction: " + runDirection);
1218        this.runDirection = runDirection;
1219    }
1220    
1221    /** Gets the run direction.
1222     * @return the run direction
1223     */

1224    public int getRunDirection() {
1225        return runDirection;
1226    }
1227    
1228    /**
1229     * Getter for property signatureEvent.
1230     * @return Value of property signatureEvent.
1231     */

1232    public SignatureEvent getSignatureEvent() {
1233        return this.signatureEvent;
1234    }
1235    
1236    /**
1237     * Sets the signature event to allow modification of the signature dictionary.
1238     * @param signatureEvent the signature event
1239     */

1240    public void setSignatureEvent(SignatureEvent signatureEvent) {
1241        this.signatureEvent = signatureEvent;
1242    }
1243    
1244    /**
1245     * Gets the background image for the layer 2.
1246     * @return the background image for the layer 2
1247     */

1248    public Image getImage() {
1249        return this.image;
1250    }
1251    
1252    /**
1253     * Sets the background image for the layer 2.
1254     * @param image the background image for the layer 2
1255     */

1256    public void setImage(Image image) {
1257        this.image = image;
1258    }
1259    
1260    /**
1261     * Gets the scaling to be applied to the background image.
1262     * @return the scaling to be applied to the background image
1263     */

1264    public float getImageScale() {
1265        return this.imageScale;
1266    }
1267    
1268    /**
1269     * Sets the scaling to be applied to the background image. If it's zero the image
1270     * will fully fill the rectangle. If it's less than zero the image will fill the rectangle but
1271     * will keep the proportions. If it's greater than zero that scaling will be applied.
1272     * In any of the cases the image will always be centered. It's zero by default.
1273     * @param imageScale the scaling to be applied to the background image
1274     */

1275    public void setImageScale(float imageScale) {
1276        this.imageScale = imageScale;
1277    }
1278    
1279    /**
1280     * Commands to draw a yellow question mark in a stream content
1281     */

1282    public static final String JavaDoc questionMark =
1283        "% DSUnknown\n" +
1284        "q\n" +
1285        "1 G\n" +
1286        "1 g\n" +
1287        "0.1 0 0 0.1 9 0 cm\n" +
1288        "0 J 0 j 4 M []0 d\n" +
1289        "1 i \n" +
1290        "0 g\n" +
1291        "313 292 m\n" +
1292        "313 404 325 453 432 529 c\n" +
1293        "478 561 504 597 504 645 c\n" +
1294        "504 736 440 760 391 760 c\n" +
1295        "286 760 271 681 265 626 c\n" +
1296        "265 625 l\n" +
1297        "100 625 l\n" +
1298        "100 828 253 898 381 898 c\n" +
1299        "451 898 679 878 679 650 c\n" +
1300        "679 555 628 499 538 435 c\n" +
1301        "488 399 467 376 467 292 c\n" +
1302        "313 292 l\n" +
1303        "h\n" +
1304        "308 214 170 -164 re\n" +
1305        "f\n" +
1306        "0.44 G\n" +
1307        "1.2 w\n" +
1308        "1 1 0.4 rg\n" +
1309        "287 318 m\n" +
1310        "287 430 299 479 406 555 c\n" +
1311        "451 587 478 623 478 671 c\n" +
1312        "478 762 414 786 365 786 c\n" +
1313        "260 786 245 707 239 652 c\n" +
1314        "239 651 l\n" +
1315        "74 651 l\n" +
1316        "74 854 227 924 355 924 c\n" +
1317        "425 924 653 904 653 676 c\n" +
1318        "653 581 602 525 512 461 c\n" +
1319        "462 425 441 402 441 318 c\n" +
1320        "287 318 l\n" +
1321        "h\n" +
1322        "282 240 170 -164 re\n" +
1323        "B\n" +
1324        "Q\n";
1325    
1326    /**
1327     * Holds value of property contact.
1328     */

1329    private String JavaDoc contact;
1330    
1331    /**
1332     * Holds value of property layer2Font.
1333     */

1334    private Font layer2Font;
1335    
1336    /**
1337     * Holds value of property layer4Text.
1338     */

1339    private String JavaDoc layer4Text;
1340    
1341    /**
1342     * Holds value of property acro6Layers.
1343     */

1344    private boolean acro6Layers;
1345    
1346    /**
1347     * Holds value of property runDirection.
1348     */

1349    private int runDirection = PdfWriter.RUN_DIRECTION_NO_BIDI;
1350    
1351    /**
1352     * Holds value of property signatureEvent.
1353     */

1354    private SignatureEvent signatureEvent;
1355    
1356    /**
1357     * Holds value of property image.
1358     */

1359    private Image image;
1360    
1361    /**
1362     * Holds value of property imageScale.
1363     */

1364    private float imageScale;
1365    
1366    /**
1367     *
1368     */

1369    private static class RangeStream extends InputStream JavaDoc {
1370        private byte b[] = new byte[1];
1371        private RandomAccessFile JavaDoc raf;
1372        private byte bout[];
1373        private int range[];
1374        private int rangePosition = 0;
1375        
1376        private RangeStream(RandomAccessFile JavaDoc raf, byte bout[], int range[]) {
1377            this.raf = raf;
1378            this.bout = bout;
1379            this.range = range;
1380        }
1381        
1382        /**
1383         * @see java.io.InputStream#read()
1384         */

1385        public int read() throws IOException JavaDoc {
1386            int n = read(b);
1387            if (n != 1)
1388                return -1;
1389            return b[0] & 0xff;
1390        }
1391        
1392        /**
1393         * @see java.io.InputStream#read(byte[], int, int)
1394         */

1395        public int read(byte[] b, int off, int len) throws IOException JavaDoc {
1396            if (b == null) {
1397                throw new NullPointerException JavaDoc();
1398            } else if ((off < 0) || (off > b.length) || (len < 0) ||
1399            ((off + len) > b.length) || ((off + len) < 0)) {
1400                throw new IndexOutOfBoundsException JavaDoc();
1401            } else if (len == 0) {
1402                return 0;
1403            }
1404            if (rangePosition >= range[range.length - 2] + range[range.length - 1]) {
1405                return -1;
1406            }
1407            for (int k = 0; k < range.length; k += 2) {
1408                int start = range[k];
1409                int end = start + range[k + 1];
1410                if (rangePosition < start)
1411                    rangePosition = start;
1412                if (rangePosition >= start && rangePosition < end) {
1413                    int lenf = Math.min(len, end - rangePosition);
1414                    if (raf == null)
1415                        System.arraycopy(bout, rangePosition, b, off, lenf);
1416                    else {
1417                        raf.seek(rangePosition);
1418                        raf.readFully(b, off, lenf);
1419                    }
1420                    rangePosition += lenf;
1421                    return lenf;
1422                }
1423            }
1424            return -1;
1425        }
1426    }
1427    
1428    /**
1429     * An interface to retrieve the signature dictionary for modification.
1430     */

1431    public interface SignatureEvent {
1432        /**
1433         * Allows modification of the signature dictionary.
1434         * @param sig the signature dictionary
1435         */

1436        public void getSignatureDictionary(PdfDictionary sig);
1437    }
1438
1439    private int certificationLevel = NOT_CERTIFIED;
1440
1441    /**
1442     * Gets the certified status of this document.
1443     * @return the certified status
1444     */

1445    public int getCertificationLevel() {
1446        return this.certificationLevel;
1447    }
1448
1449    /**
1450     * Sets the document type to certified instead of simply signed.
1451     * @param certificationLevel the values can be: <code>NOT_CERTIFIED</code>, <code>CERTIFIED_NO_CHANGES_ALLOWED</code>,
1452     * <code>CERTIFIED_FORM_FILLING</code> and <code>CERTIFIED_FORM_FILLING_AND_ANNOTATIONS</code>
1453     */

1454    public void setCertificationLevel(int certificationLevel) {
1455        this.certificationLevel = certificationLevel;
1456    }
1457}
Popular Tags