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