iText 5 itextpdf.jar Source Code

itextpdf.jar is a component in iText 5 Java library to provide core functionalities. iText Java library allows you to generate and manage PDF documents.

The Source Code files are provided at iText GitHub site.

You can compile it to generate your JAR file, using pom.xml as the build configuration file.

The source code of itextpdf-5.5.14.jar is provided below:

✍: FYIcenter.com

com/itextpdf/text/pdf/PdfSignatureAppearance.java

/*
 *
 * This file is part of the iText (R) project.
    Copyright (c) 1998-2020 iText Group NV
 * Authors: Bruno Lowagie, Paulo Soares, et al.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
 * ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
 * OF THIRD PARTY RIGHTS
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * http://itextpdf.com/terms-of-use/
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * a covered work must retain the producer line in every PDF that is created
 * or manipulated using iText.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the iText software without
 * disclosing the source code of your own applications.
 * These activities include: offering paid services to customers as an ASP,
 * serving PDFs on the fly in a web application, shipping iText with a closed
 * source product.
 *
 * For more information, please contact iText Software Corp. at this
 * address: sales@itextpdf.com
 */
package com.itextpdf.text.pdf;

import com.itextpdf.text.Chunk;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.Version;
import com.itextpdf.text.error_messages.MessageLocalization;
import com.itextpdf.text.io.RASInputStream;
import com.itextpdf.text.io.RandomAccessSource;
import com.itextpdf.text.io.RandomAccessSourceFactory;
import com.itextpdf.text.pdf.AcroFields.Item;
import com.itextpdf.text.pdf.interfaces.PdfVersion;
import com.itextpdf.text.pdf.security.CertificateInfo;
import com.itextpdf.text.pdf.security.CertificateInfo.X500Name;

import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;

/**
 * Class that takes care of the cryptographic options
 * and appearances that form a signature.
 */
public class PdfSignatureAppearance {

	/**
	 * Constructs a PdfSignatureAppearance object.
     * @param writer	the writer to which the signature will be written.
     */
    PdfSignatureAppearance(PdfStamperImp writer) {
        this.writer = writer;
        signDate = new GregorianCalendar();
        fieldName = getNewSigName();
        signatureCreator = Version.getInstance().getVersion();
    }

	/*
	 * SIGNATURE
	 */

    // signature types

    /** Approval signature */
    public static final int NOT_CERTIFIED = 0;

    /** Author signature, no changes allowed */
    public static final int CERTIFIED_NO_CHANGES_ALLOWED = 1;

    /** Author signature, form filling allowed */
    public static final int CERTIFIED_FORM_FILLING = 2;

    /** Author signature, form filling and annotations allowed */
    public static final int CERTIFIED_FORM_FILLING_AND_ANNOTATIONS = 3;

    /** The certification level */
    private int certificationLevel = NOT_CERTIFIED;

    /**
     * Sets the document type to certified instead of simply signed.
     * @param certificationLevel the values can be: <code>NOT_CERTIFIED</code>, <code>CERTIFIED_NO_CHANGES_ALLOWED</code>,
     * <code>CERTIFIED_FORM_FILLING</code> and <code>CERTIFIED_FORM_FILLING_AND_ANNOTATIONS</code>
     */
    public void setCertificationLevel(int certificationLevel) {
        this.certificationLevel = certificationLevel;
    }

    /**
     * Gets the certified status of this document.
     * @return the certified status
     */
    public int getCertificationLevel() {
        return this.certificationLevel;
    }

    // signature info

    /** The caption for the reason for signing. */
    private String reasonCaption = "Reason: ";

    /** The caption for the location of signing. */
    private String locationCaption = "Location: ";

    /** The reason for signing. */
    private String reason;

    /** Holds value of property location. */
    private String location;

    /** Holds value of property signDate. */
    private Calendar signDate;

    /**
     * Gets the signing reason.
     * @return the signing reason
     */
    public String getReason() {
        return this.reason;
    }

    /**
     * Sets the signing reason.
     * @param reason the signing reason
     */
    public void setReason(String reason) {
        this.reason = reason;
    }

    /**
     * Sets the caption for signing reason.
     * @param reasonCaption the signing reason caption
     */
    public void setReasonCaption(String reasonCaption) {
        this.reasonCaption = reasonCaption;
    }

    /**
     * Gets the signing location.
     * @return the signing location
     */
    public String getLocation() {
        return this.location;
    }

    /**
     * Sets the signing location.
     * @param location the signing location
     */
    public void setLocation(String location) {
        this.location = location;
    }

    /**
     * Sets the caption for the signing location.
     * @param locationCaption the signing location caption
     */
    public void setLocationCaption(String locationCaption) {
        this.locationCaption = locationCaption;
    }

    /** Holds value of the application that creates the signature */
    private String signatureCreator;

    /**
     * Gets the signature creator.
     * @return the signature creator
     */
    public String getSignatureCreator(){
    	return signatureCreator;
    }

    /**
     * Sets the name of the application used to create the signature.
     * @param signatureCreator the name of the signature creating application
     */
    public void setSignatureCreator(String signatureCreator){
    	this.signatureCreator = signatureCreator;
    }

    /** The contact name of the signer. */
    private String contact;

    /**
     * Gets the signing contact.
     * @return the signing contact
     */
    public String getContact() {
        return this.contact;
    }

    /**
     * Sets the signing contact.
     * @param contact the signing contact
     */
    public void setContact(String contact) {
        this.contact = contact;
    }

    /**
     * Gets the signature date.
     * @return the signature date
     */
    public java.util.Calendar getSignDate() {
        return signDate;
    }

    /**
     * Sets the signature date.
     * @param signDate the signature date
     */
    public void setSignDate(java.util.Calendar signDate) {
        this.signDate = signDate;
    }

    // the PDF file

    /** The file right before the signature is added (can be null). */
    private RandomAccessFile raf;
    /** The bytes of the file right before the signature is added (if raf is null) */
    private byte[] bout;
    /** Array containing the byte positions of the bytes that need to be hashed. */
    private long[] range;

    /**
     * Gets the document bytes that are hashable when using external signatures. The general sequence is:
     * preClose(), getRangeStream() and close().
     * <p>
     * @return the document bytes that are hashable
     */
    public InputStream getRangeStream() throws IOException {
    	RandomAccessSourceFactory fac = new RandomAccessSourceFactory();
        return new RASInputStream(fac.createRanged(getUnderlyingSource(), range));
    }

    /**
     * @return the underlying source
     * @throws IOException
     */
    private RandomAccessSource getUnderlyingSource() throws IOException {
    	//TODO: get rid of separate byte[] and RandomAccessFile objects and just store a RandomAccessSource
    	RandomAccessSourceFactory fac = new RandomAccessSourceFactory();
    	return raf == null ? fac.createSource(bout) : fac.createSource(raf);
    }

    /** The signing certificate */
    private Certificate signCertificate;

    // Developer extenstion

    /**
     * Adds the appropriate developer extension.
     */
	public void addDeveloperExtension(final PdfDeveloperExtension de) {
		writer.addDeveloperExtension(de);
	}

    // Crypto dictionary

    /** The crypto dictionary */
    private PdfDictionary cryptoDictionary;
    /**
     * Gets the user made signature dictionary. This is the dictionary at the /V key.
     * @return the user made signature dictionary
     */
    public com.itextpdf.text.pdf.PdfDictionary getCryptoDictionary() {
        return cryptoDictionary;
    }

    /**
     * Sets a user made signature dictionary. This is the dictionary at the /V key.
     * @param cryptoDictionary a user made signature dictionary
     */
    public void setCryptoDictionary(com.itextpdf.text.pdf.PdfDictionary cryptoDictionary) {
        this.cryptoDictionary = cryptoDictionary;
    }

    /**
     * Sets the certificate used to provide the text in the appearance.
     * This certificate doesn't take part in the actual signing process.
     * @param signCertificate the certificate
     */
    public void setCertificate(Certificate signCertificate) {
        this.signCertificate = signCertificate;
    }

    public Certificate getCertificate() {
        return signCertificate;
    }

    // Signature event

    /**
     * An interface to retrieve the signature dictionary for modification.
     */
    public interface SignatureEvent {
        /**
         * Allows modification of the signature dictionary.
         * @param sig the signature dictionary
         */
        public void getSignatureDictionary(PdfDictionary sig);
    }

    /**
     * Holds value of property signatureEvent.
     */
    private SignatureEvent signatureEvent;

    /**
     * Getter for property signatureEvent.
     * @return Value of property signatureEvent.
     */
    public SignatureEvent getSignatureEvent() {
        return this.signatureEvent;
    }

    /**
     * Sets the signature event to allow modification of the signature dictionary.
     * @param signatureEvent the signature event
     */
    public void setSignatureEvent(SignatureEvent signatureEvent) {
        this.signatureEvent = signatureEvent;
    }

	/*
	 * SIGNATURE FIELD
	 */

    /** The name of the field */
    private String fieldName;

    /**
     * Gets the field name.
     * @return the field name
     */
    public java.lang.String getFieldName() {
        return fieldName;
    }

    /**
     * Gets a new signature field name that
     * doesn't clash with any existing name.
     * @return a new signature field name
     */
    public String getNewSigName() {
        AcroFields af = writer.getAcroFields();
        String name = "Signature";
        int step = 0;
        boolean found = false;
        while (!found) {
            ++step;
            String n1 = name + step;
            if (af.getFieldItem(n1) != null)
                continue;
            n1 += ".";
            found = true;
            for (Object element : af.getFields().keySet()) {
                String fn = (String)element;
                if (fn.startsWith(n1)) {
                    found = false;
                    break;
                }
            }
        }
        name += step;
        return name;
    }

    /**
     * The page where the signature will appear.
     */
    private int page = 1;

    /**
     * Gets the page number of the field.
     * @return the page number of the field
     */
    public int getPage() {
        return page;
    }

    /**
     * The coordinates of the rectangle for a visible signature,
     * or a zero-width, zero-height rectangle for an invisible signature.
     */
    private Rectangle rect;

    /**
     * Gets the rectangle representing the signature dimensions.
     * @return the rectangle representing the signature dimensions. It may be <CODE>null</CODE>
     * or have zero width or height for invisible signatures
     */
    public Rectangle getRect() {
        return rect;
    }

    /** rectangle that represent the position and dimension of the signature in the page. */
    private Rectangle pageRect;

    /**
     * Gets the rectangle that represent the position and dimension of the signature in the page.
     * @return the rectangle that represent the position and dimension of the signature in the page
     */
    public Rectangle getPageRect() {
        return pageRect;
    }

    /**
     * Gets the visibility status of the signature.
     * @return the visibility status of the signature
     */
    public boolean isInvisible() {
        return rect == null || rect.getWidth() == 0 || rect.getHeight() == 0;
    }

    /**
     * Sets the signature to be visible. It creates a new visible signature field.
     * @param pageRect the position and dimension of the field in the page
     * @param page the page to place the field. The fist page is 1
     * @param fieldName the field name or <CODE>null</CODE> to generate automatically a new field name
     */
    public void setVisibleSignature(Rectangle pageRect, int page, String fieldName) {
        if (fieldName != null) {
            if (fieldName.indexOf('.') >= 0)
                throw new IllegalArgumentException(MessageLocalization.getComposedMessage("field.names.cannot.contain.a.dot"));
            AcroFields af = writer.getAcroFields();
            AcroFields.Item item = af.getFieldItem(fieldName);
            if (item != null)
                throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.field.1.already.exists", fieldName));
            this.fieldName = fieldName;
        }
        if (page < 1 || page > writer.reader.getNumberOfPages())
            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("invalid.page.number.1", page));
        this.pageRect = new Rectangle(pageRect);
        this.pageRect.normalize();
        rect = new Rectangle(this.pageRect.getWidth(), this.pageRect.getHeight());
        this.page = page;
    }

    /**
     * Sets the signature to be visible. An empty signature field with the same name must already exist.
     * @param fieldName the existing empty signature field name
     */
    public void setVisibleSignature(String fieldName) {
        AcroFields af = writer.getAcroFields();
        AcroFields.Item item = af.getFieldItem(fieldName);
        if (item == null)
            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.field.1.does.not.exist", fieldName));
        PdfDictionary merged = item.getMerged(0);
        if (!PdfName.SIG.equals(PdfReader.getPdfObject(merged.get(PdfName.FT))))
            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.field.1.is.not.a.signature.field", fieldName));
        this.fieldName = fieldName;
        PdfArray r = merged.getAsArray(PdfName.RECT);
        float llx = r.getAsNumber(0).floatValue();
        float lly = r.getAsNumber(1).floatValue();
        float urx = r.getAsNumber(2).floatValue();
        float ury = r.getAsNumber(3).floatValue();
        pageRect = new Rectangle(llx, lly, urx, ury);
        pageRect.normalize();
        page = item.getPage(0).intValue();
        int rotation = writer.reader.getPageRotation(page);
        Rectangle pageSize = writer.reader.getPageSizeWithRotation(page);
        switch (rotation) {
            case 90:
                pageRect = new Rectangle(
                pageRect.getBottom(),
                pageSize.getTop() - pageRect.getLeft(),
                pageRect.getTop(),
                pageSize.getTop() - pageRect.getRight());
                break;
            case 180:
                pageRect = new Rectangle(
                pageSize.getRight() - pageRect.getLeft(),
                pageSize.getTop() - pageRect.getBottom(),
                pageSize.getRight() - pageRect.getRight(),
                pageSize.getTop() - pageRect.getTop());
                break;
            case 270:
                pageRect = new Rectangle(
                pageSize.getRight() - pageRect.getBottom(),
                pageRect.getLeft(),
                pageSize.getRight() - pageRect.getTop(),
                pageRect.getRight());
                break;
        }
        if (rotation != 0)
            pageRect.normalize();
        rect = new Rectangle(this.pageRect.getWidth(), this.pageRect.getHeight());
    }

	/*
	 * SIGNATURE APPEARANCE
	 */

    /**
     * Signature rendering modes
     * @since 5.0.1
     */
    public enum RenderingMode {
        /**
         * The rendering mode is just the description.
         */
        DESCRIPTION,
        /**
         * The rendering mode is the name of the signer and the description.
         */
        NAME_AND_DESCRIPTION,
        /**
         * The rendering mode is an image and the description.
         */
        GRAPHIC_AND_DESCRIPTION,
        /**
         * The rendering mode is just an image.
         */
        GRAPHIC
    }

    /** The rendering mode chosen for visible signatures */
    private RenderingMode renderingMode = RenderingMode.DESCRIPTION;

    /**
    * Gets the rendering mode for this signature.
    * @return the rendering mode for this signature
    * @since 5.0.1
    */
    public RenderingMode getRenderingMode() {
        return renderingMode;
    }

    /**
     * Sets the rendering mode for this signature.
     * @param renderingMode the rendering mode
     * @since 5.0.1
     */
    public void setRenderingMode(RenderingMode renderingMode) {
        this.renderingMode = renderingMode;
    }

    /** The image that needs to be used for a visible signature */
    private Image signatureGraphic = null;

    /**
    * Gets the Image object to render.
    * @return the image
    */
    public Image getSignatureGraphic() {
        return signatureGraphic;
    }

    /**
     * Sets the Image object to render when Render is set to <CODE>RenderingMode.GRAPHIC</CODE>
     * or <CODE>RenderingMode.GRAPHIC_AND_DESCRIPTION</CODE>.
     * @param signatureGraphic image rendered. If <CODE>null</CODE> the mode is defaulted
     * to <CODE>RenderingMode.DESCRIPTION</CODE>
     */
    public void setSignatureGraphic(Image signatureGraphic) {
        this.signatureGraphic = signatureGraphic;
    }

    /** Appearance compliant with the recommendations introduced in Acrobat 6? */
    private boolean acro6Layers = true;

    /**
     * Gets the Acrobat 6.0 layer mode.
     * @return the Acrobat 6.0 layer mode
     */
    public boolean isAcro6Layers() {
        return this.acro6Layers;
    }

    /**
     * Acrobat 6.0 and higher recommends that only layer n0 and n2 be present.
     * Use this method with value <code>false</code> if you want to ignore this recommendation.
     * @param acro6Layers if <code>true</code> only the layers n0 and n2 will be present
     * @deprecated Adobe no longer supports Adobe Acrobat / Reader versions older than 9
     */
    public void setAcro6Layers(boolean acro6Layers) {
        this.acro6Layers = acro6Layers;
    }

    /** Layers for a visible signature. */
    private PdfTemplate app[] = new PdfTemplate[5];

    /**
     * Gets a template layer to create a signature appearance. The layers can go from 0 to 4,
     * but only layer 0 and 2 will be used if acro6Layers is true.
     * <p>
     * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A>
     * for further details.
     * @param layer the layer
     * @return a template
     */
    public PdfTemplate getLayer(int layer) {
        if (layer < 0 || layer >= app.length)
            return null;
        PdfTemplate t = app[layer];
        if (t == null) {
            t = app[layer] = new PdfTemplate(writer);
            t.setBoundingBox(rect);
            writer.addDirectTemplateSimple(t, new PdfName("n" + layer));
        }
        return t;
    }

    /** Indicates if we need to reuse the existing appearance as layer 0. */
    private boolean reuseAppearance = false;

    /**
     * Indicates that the existing appearances needs to be reused as layer 0.
     */
    public void setReuseAppearance(boolean reuseAppearance) {
    	this.reuseAppearance = reuseAppearance;
    }

    // layer 1

    /** An appearance that can be used for layer 1 (if acro6Layers is false). */
    public static final String questionMark =
        "% DSUnknown\n" +
        "q\n" +
        "1 G\n" +
        "1 g\n" +
        "0.1 0 0 0.1 9 0 cm\n" +
        "0 J 0 j 4 M []0 d\n" +
        "1 i \n" +
        "0 g\n" +
        "313 292 m\n" +
        "313 404 325 453 432 529 c\n" +
        "478 561 504 597 504 645 c\n" +
        "504 736 440 760 391 760 c\n" +
        "286 760 271 681 265 626 c\n" +
        "265 625 l\n" +
        "100 625 l\n" +
        "100 828 253 898 381 898 c\n" +
        "451 898 679 878 679 650 c\n" +
        "679 555 628 499 538 435 c\n" +
        "488 399 467 376 467 292 c\n" +
        "313 292 l\n" +
        "h\n" +
        "308 214 170 -164 re\n" +
        "f\n" +
        "0.44 G\n" +
        "1.2 w\n" +
        "1 1 0.4 rg\n" +
        "287 318 m\n" +
        "287 430 299 479 406 555 c\n" +
        "451 587 478 623 478 671 c\n" +
        "478 762 414 786 365 786 c\n" +
        "260 786 245 707 239 652 c\n" +
        "239 651 l\n" +
        "74 651 l\n" +
        "74 854 227 924 355 924 c\n" +
        "425 924 653 904 653 676 c\n" +
        "653 581 602 525 512 461 c\n" +
        "462 425 441 402 441 318 c\n" +
        "287 318 l\n" +
        "h\n" +
        "282 240 170 -164 re\n" +
        "B\n" +
        "Q\n";

    // layer 2

    /** A background image for the text in layer 2. */
    private Image image;

    /**
     * Gets the background image for the layer 2.
     * @return the background image for the layer 2
     */
    public Image getImage() {
        return this.image;
    }

    /**
     * Sets the background image for the layer 2.
     * @param image the background image for the layer 2
     */
    public void setImage(Image image) {
        this.image = image;
    }

    /** the scaling to be applied to the background image.t  */
    private float imageScale;

    /**
     * Gets the scaling to be applied to the background image.
     * @return the scaling to be applied to the background image
     */
    public float getImageScale() {
        return this.imageScale;
    }

    /**
     * Sets the scaling to be applied to the background image. If it's zero the image
     * will fully fill the rectangle. If it's less than zero the image will fill the rectangle but
     * will keep the proportions. If it's greater than zero that scaling will be applied.
     * In any of the cases the image will always be centered. It's zero by default.
     * @param imageScale the scaling to be applied to the background image
     */
    public void setImageScale(float imageScale) {
        this.imageScale = imageScale;
    }

    /** The text that goes in Layer 2 of the signature appearance. */
    private String layer2Text;

    /**
     * Sets the signature text identifying the signer.
     * @param text the signature text identifying the signer. If <CODE>null</CODE> or not set
     * a standard description will be used
     */
    public void setLayer2Text(String text) {
        layer2Text = text;
    }

    /**
     * Gets the signature text identifying the signer if set by setLayer2Text().
     * @return the signature text identifying the signer
     */
    public String getLayer2Text() {
        return layer2Text;
    }

    /** Font for the text in Layer 2. */
    private Font layer2Font;

    /**
     * Gets the n2 and n4 layer font.
     * @return the n2 and n4 layer font
     */
    public Font getLayer2Font() {
        return this.layer2Font;
    }

    /**
     * Sets the n2 and n4 layer font. If the font size is zero, auto-fit will be used.
     * @param layer2Font the n2 and n4 font
     */
    public void setLayer2Font(Font layer2Font) {
        this.layer2Font = layer2Font;
    }

    /** Run direction for the text in layers 2 and 4. */
    private int runDirection = PdfWriter.RUN_DIRECTION_NO_BIDI;

    /** Sets the run direction in the n2 and n4 layer.
     * @param runDirection the run direction
     */
    public void setRunDirection(int runDirection) {
        if (runDirection < PdfWriter.RUN_DIRECTION_DEFAULT || runDirection > PdfWriter.RUN_DIRECTION_RTL)
            throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.run.direction.1", runDirection));
        this.runDirection = runDirection;
    }

    /** Gets the run direction.
     * @return the run direction
     */
    public int getRunDirection() {
        return runDirection;
    }

    // layer 4

    /** The text that goes in Layer 4 of the appearance. */
    private String layer4Text;

    /**
     * Sets the text identifying the signature status. Will be ignored if acro6Layers is true.
     * @param text the text identifying the signature status. If <CODE>null</CODE> or not set
     * the description "Signature Not Verified" will be used
     */
    public void setLayer4Text(String text) {
        layer4Text = text;
    }

    /**
     * Gets the text identifying the signature status if set by setLayer4Text().
     * @return the text identifying the signature status
     */
    public String getLayer4Text() {
        return layer4Text;
    }

    // all layers

    /** Template containing all layers drawn on top of each other. */
    private PdfTemplate frm;

    /**
     * Gets the template that aggregates all appearance layers. This corresponds to the /FRM resource.
     * <p>
     * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A>
     * for further details.
     * @return the template that aggregates all appearance layers
     */
    public PdfTemplate getTopLayer() {
        if (frm == null) {
            frm = new PdfTemplate(writer);
            frm.setBoundingBox(rect);
            writer.addDirectTemplateSimple(frm, new PdfName("FRM"));
        }
        return frm;
    }

    // creating the appearance

    /** extra space at the top. */
    private static final float TOP_SECTION = 0.3f;

    /** margin for the content inside the signature rectangle. */
    private static final float MARGIN = 2;

    /**
     * Gets the main appearance layer.
     * <p>
     * Consult <A HREF="http://partners.adobe.com/asn/developer/pdfs/tn/PPKAppearances.pdf">PPKAppearances.pdf</A>
     * for further details.
     * @return the main appearance layer
     * @throws DocumentException on error
     */
    public PdfTemplate getAppearance() throws DocumentException {
        if (isInvisible()) {
            PdfTemplate t = new PdfTemplate(writer);
            t.setBoundingBox(new Rectangle(0, 0));
            writer.addDirectTemplateSimple(t, null);
            return t;
        }

        if (app[0] == null && !reuseAppearance) {
            createBlankN0();
        }
        if (app[1] == null && !acro6Layers) {
            PdfTemplate t = app[1] = new PdfTemplate(writer);
            t.setBoundingBox(new Rectangle(100, 100));
            writer.addDirectTemplateSimple(t, new PdfName("n1"));
            t.setLiteral(questionMark);
        }
        if (app[2] == null) {
            String text;
            if (layer2Text == null) {
                StringBuilder buf = new StringBuilder();
                buf.append("Digitally signed by ");
                String name = null;
                X500Name x500name = CertificateInfo.getSubjectFields((X509Certificate)signCertificate);
                if (x500name != null) {
                	name = x500name.getField("CN");
                	if (name == null)
                		name = x500name.getField("E");
                }
                if (name == null)
                    name = "";
                buf.append(name).append('\n');
                SimpleDateFormat sd = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z");
                buf.append("Date: ").append(sd.format(signDate.getTime()));
                if (reason != null)
                    buf.append('\n').append(reasonCaption).append(reason);
                if (location != null)
                    buf.append('\n').append(locationCaption).append(location);
                text = buf.toString();
            }
            else
                text = layer2Text;
            PdfTemplate t = app[2] = new PdfTemplate(writer);
            t.setBoundingBox(rect);
            writer.addDirectTemplateSimple(t, new PdfName("n2"));
            if (image != null) {
                if (imageScale == 0) {
                    t.addImage(image, rect.getWidth(), 0, 0, rect.getHeight(), 0, 0);
                }
                else {
                    float usableScale = imageScale;
                    if (imageScale < 0)
                        usableScale = Math.min(rect.getWidth() / image.getWidth(), rect.getHeight() / image.getHeight());
                    float w = image.getWidth() * usableScale;
                    float h = image.getHeight() * usableScale;
                    float x = (rect.getWidth() - w) / 2;
                    float y = (rect.getHeight() - h) / 2;
                    t.addImage(image, w, 0, 0, h, x, y);
                }
            }
            Font font;
            if (layer2Font == null)
                font = new Font();
            else
                font = new Font(layer2Font);
            float size = font.getSize();

            Rectangle dataRect = null;
            Rectangle signatureRect = null;

            if (renderingMode == RenderingMode.NAME_AND_DESCRIPTION ||
                renderingMode == RenderingMode.GRAPHIC_AND_DESCRIPTION && this.signatureGraphic != null) {
                // origin is the bottom-left
                signatureRect = new Rectangle(
                    MARGIN,
                    MARGIN,
                    rect.getWidth() / 2 - MARGIN,
                    rect.getHeight() - MARGIN);
                dataRect = new Rectangle(
                    rect.getWidth() / 2 +  MARGIN / 2,
                    MARGIN,
                    rect.getWidth() - MARGIN / 2,
                    rect.getHeight() - MARGIN);

                if (rect.getHeight() > rect.getWidth()) {
                    signatureRect = new Rectangle(
                        MARGIN,
                        rect.getHeight() / 2,
                        rect.getWidth() - MARGIN,
                        rect.getHeight());
                    dataRect = new Rectangle(
                        MARGIN,
                        MARGIN,
                        rect.getWidth() - MARGIN,
                        rect.getHeight() / 2 - MARGIN);
                }
            }
            else if (renderingMode == RenderingMode.GRAPHIC) {
                if (signatureGraphic == null) {
                    throw new IllegalStateException(MessageLocalization.getComposedMessage("a.signature.image.should.be.present.when.rendering.mode.is.graphic.only"));
                }
                signatureRect = new Rectangle(
                        MARGIN,
                        MARGIN,
                        rect.getWidth() - MARGIN, // take all space available
                        rect.getHeight() - MARGIN);
            }
            else {
                dataRect = new Rectangle(
                        MARGIN,
                        MARGIN,
                        rect.getWidth() - MARGIN,
                        rect.getHeight() * (1 - TOP_SECTION) - MARGIN);
            }

            switch (renderingMode) {
            case NAME_AND_DESCRIPTION:
                String signedBy = CertificateInfo.getSubjectFields((X509Certificate)signCertificate).getField("CN");
                if (signedBy == null)
                    signedBy = CertificateInfo.getSubjectFields((X509Certificate)signCertificate).getField("E");
                if (signedBy == null)
                    signedBy = "";
                Rectangle sr2 = new Rectangle(signatureRect.getWidth() - MARGIN, signatureRect.getHeight() - MARGIN );
                float signedSize = ColumnText.fitText(font, signedBy, sr2, -1, runDirection);

                ColumnText ct2 = new ColumnText(t);
                ct2.setRunDirection(runDirection);
                ct2.setSimpleColumn(new Phrase(signedBy, font), signatureRect.getLeft(), signatureRect.getBottom(), signatureRect.getRight(), signatureRect.getTop(), signedSize, Element.ALIGN_LEFT);

                ct2.go();
                break;
            case GRAPHIC_AND_DESCRIPTION:
                if (signatureGraphic == null) {
                    throw new IllegalStateException(MessageLocalization.getComposedMessage("a.signature.image.should.be.present.when.rendering.mode.is.graphic.and.description"));
                }
                ct2 = new ColumnText(t);
                ct2.setRunDirection(runDirection);
                ct2.setSimpleColumn(signatureRect.getLeft(), signatureRect.getBottom(), signatureRect.getRight(), signatureRect.getTop(), 0, Element.ALIGN_RIGHT);

                Image im = Image.getInstance(signatureGraphic);
                im.scaleToFit(signatureRect.getWidth(), signatureRect.getHeight());

                Paragraph p = new Paragraph();
                // must calculate the point to draw from to make image appear in middle of column
                float x = 0;
                // experimentation found this magic number to counteract Adobe's signature graphic, which
                // offsets the y co-ordinate by 15 units
                float y = -im.getScaledHeight() + 15;

                x = x + (signatureRect.getWidth() - im.getScaledWidth()) / 2;
                y = y - (signatureRect.getHeight() - im.getScaledHeight()) / 2;
                p.add(new Chunk(im, x + (signatureRect.getWidth() - im.getScaledWidth()) / 2, y, false));
                ct2.addElement(p);
                ct2.go();
                break;
            case GRAPHIC:
                ct2 = new ColumnText(t);
                ct2.setRunDirection(runDirection);
                ct2.setSimpleColumn(signatureRect.getLeft(), signatureRect.getBottom(), signatureRect.getRight(), signatureRect.getTop(), 0, Element.ALIGN_RIGHT);

                im = Image.getInstance(signatureGraphic);
                im.scaleToFit(signatureRect.getWidth(), signatureRect.getHeight());

                p = new Paragraph(signatureRect.getHeight());
                // must calculate the point to draw from to make image appear in middle of column
                x = (signatureRect.getWidth() - im.getScaledWidth()) / 2;
                y = (signatureRect.getHeight() - im.getScaledHeight()) / 2;
                p.add(new Chunk(im, x, y, false));
                ct2.addElement(p);
                ct2.go();
                break;
            default:
            }

            if(renderingMode != RenderingMode.GRAPHIC) {
            	if (size <= 0) {
                    Rectangle sr = new Rectangle(dataRect.getWidth(), dataRect.getHeight());
                    size = ColumnText.fitText(font, text, sr, 12, runDirection);
                }
                ColumnText ct = new ColumnText(t);
                ct.setRunDirection(runDirection);
                ct.setSimpleColumn(new Phrase(text, font), dataRect.getLeft(), dataRect.getBottom(), dataRect.getRight(), dataRect.getTop(), size, Element.ALIGN_LEFT);
                ct.go();
            }
        }
        if (app[3] == null && !acro6Layers) {
            PdfTemplate t = app[3] = new PdfTemplate(writer);
            t.setBoundingBox(new Rectangle(100, 100));
            writer.addDirectTemplateSimple(t, new PdfName("n3"));
            t.setLiteral("% DSBlank\n");
        }
        if (app[4] == null && !acro6Layers) {
            PdfTemplate t = app[4] = new PdfTemplate(writer);
            t.setBoundingBox(new Rectangle(0, rect.getHeight() * (1 - TOP_SECTION), rect.getRight(), rect.getTop()));
            writer.addDirectTemplateSimple(t, new PdfName("n4"));
            Font font;
            if (layer2Font == null)
                font = new Font();
            else
                font = new Font(layer2Font);
            //float size = font.getSize();
            String text = "Signature Not Verified";
            if (layer4Text != null)
                text = layer4Text;
            Rectangle sr = new Rectangle(rect.getWidth() - 2 * MARGIN, rect.getHeight() * TOP_SECTION - 2 * MARGIN);
            float size = ColumnText.fitText(font, text, sr, 15, runDirection);
            ColumnText ct = new ColumnText(t);
            ct.setRunDirection(runDirection);
            ct.setSimpleColumn(new Phrase(text, font), MARGIN, 0, rect.getWidth() - MARGIN, rect.getHeight() - MARGIN, size, Element.ALIGN_LEFT);
            ct.go();
        }
        int rotation = writer.reader.getPageRotation(page);
        Rectangle rotated = new Rectangle(rect);
        int n = rotation;
        while (n > 0) {
            rotated = rotated.rotate();
            n -= 90;
        }
        if (frm == null) {
            frm = new PdfTemplate(writer);
            frm.setBoundingBox(rotated);
            writer.addDirectTemplateSimple(frm, new PdfName("FRM"));
            float scale = Math.min(rect.getWidth(), rect.getHeight()) * 0.9f;
            float x = (rect.getWidth() - scale) / 2;
            float y = (rect.getHeight() - scale) / 2;
            scale /= 100;
            if (rotation == 90)
                frm.concatCTM(0, 1, -1, 0, rect.getHeight(), 0);
            else if (rotation == 180)
                frm.concatCTM(-1, 0, 0, -1, rect.getWidth(), rect.getHeight());
            else if (rotation == 270)
                frm.concatCTM(0, -1, 1, 0, 0, rect.getWidth());
            if (reuseAppearance) {
                AcroFields af = writer.getAcroFields();
                PdfIndirectReference ref = af.getNormalAppearance(getFieldName());
                if (ref != null) {
                	frm.addTemplateReference(ref, new PdfName("n0"), 1, 0, 0, 1, 0, 0);
                }
                else {
                	reuseAppearance = false;
                    if (app[0] == null) {
                        createBlankN0();
                    }
                }
            }
            if (!reuseAppearance) {
            	frm.addTemplate(app[0], 0, 0);
            }
            if (!acro6Layers)
                frm.addTemplate(app[1], scale, 0, 0, scale, x, y);
            frm.addTemplate(app[2], 0, 0);
            if (!acro6Layers) {
                frm.addTemplate(app[3], scale, 0, 0, scale, x, y);
                frm.addTemplate(app[4], 0, 0);
            }
        }
        PdfTemplate napp = new PdfTemplate(writer);
        napp.setBoundingBox(rotated);
        writer.addDirectTemplateSimple(napp, null);
        napp.addTemplate(frm, 0, 0);
        return napp;
    }

    private void createBlankN0() {
        PdfTemplate t = app[0] = new PdfTemplate(writer);
        t.setBoundingBox(new Rectangle(100, 100));
        writer.addDirectTemplateSimple(t, new PdfName("n0"));
        t.setLiteral("% DSBlank\n");
    }

    /*
     * Creating the signed file.
     */

    /** The PdfStamper that creates the signed PDF. */
    private PdfStamper stamper;

    /**
     * Gets the <CODE>PdfStamper</CODE> associated with this instance.
     * @return the <CODE>PdfStamper</CODE> associated with this instance
     */
    public PdfStamper getStamper() {
        return stamper;
    }

    /**
     * Sets the PdfStamper
     * @param stamper PdfStamper
     */
    void setStamper(PdfStamper stamper) {
        this.stamper = stamper;
    }

    /** The PdfStamperImp object corresponding with the stamper. */
    private PdfStamperImp writer;

    /** A byte buffer containing the bytes of the Stamper. */
    private ByteBuffer sigout;

    /**
     * Getter for the byte buffer.
     */
    ByteBuffer getSigout() {
        return sigout;
    }

    /**
     * Setter for the byte buffer.
     */
    void setSigout(ByteBuffer sigout) {
        this.sigout = sigout;
    }

    /** OutputStream for the bytes of the stamper. */
    private OutputStream originalout;

    /**
     * Getter for the OutputStream.
     */
    OutputStream getOriginalout() {
        return originalout;
    }

    /**
     * Setter for the OutputStream.
     */
    void setOriginalout(OutputStream originalout) {
        this.originalout = originalout;
    }

    /** Temporary file in case you don't want to sign in memory. */
    private File tempFile;

    /**
     * Gets the temporary file.
     * @return the temporary file or <CODE>null</CODE> is the document is created in memory
     */
    public File getTempFile() {
        return tempFile;
    }

    /**
     * Setter for the temporary file.
     * @param tempFile
     */
    void setTempFile(File tempFile) {
        this.tempFile = tempFile;
    }

    /** Name and content of keys that can only be added in the close() method. */
    private HashMap<PdfName, PdfLiteral> exclusionLocations;

    /** Length of the output. */
    private int boutLen;

    /** Indicates if the stamper has already been pre-closed. */
    private boolean preClosed = false;

    /** Signature field lock dictionary */
    private PdfSigLockDictionary fieldLock;

    /**
     * Getter for the field lock dictionary.
     * @return Field lock dictionary.
     */
    public PdfSigLockDictionary getFieldLockDict() {
        return fieldLock;
    }

    /**
     * Setter for the field lock dictionary.
     * <p><strong>Be aware:</strong> if a signature is created on an existing signature field,
     * then its /Lock dictionary takes the precedence (if it exists).</p>
     *
     * @param fieldLock Field lock dictionary.
     */
    public void setFieldLockDict(PdfSigLockDictionary fieldLock) {
        this.fieldLock = fieldLock;
    }

    /**
     * Checks if the document is in the process of closing.
     * @return <CODE>true</CODE> if the document is in the process of closing,
     * <CODE>false</CODE> otherwise
     */
    public boolean isPreClosed() {
        return preClosed;
    }

    /**
     * This is the first method to be called when using external signatures. The general sequence is:
     * preClose(), getDocumentBytes() and close().
     * <p>
     * If calling preClose() <B>dont't</B> call PdfStamper.close().
     * <p>
     * <CODE>exclusionSizes</CODE> must contain at least
     * the <CODE>PdfName.CONTENTS</CODE> key with the size that it will take in the
     * document. Note that due to the hex string coding this size should be
     * byte_size*2+2.
     * @param exclusionSizes a <CODE>HashMap</CODE> with names and sizes to be excluded in the signature
     * calculation. The key is a <CODE>PdfName</CODE> and the value an
     * <CODE>Integer</CODE>. At least the <CODE>PdfName.CONTENTS</CODE> must be present
     * @throws IOException on error
     * @throws DocumentException on error
     */
	public void preClose(HashMap<PdfName, Integer> exclusionSizes) throws IOException, DocumentException {
        if (preClosed)
            throw new DocumentException(MessageLocalization.getComposedMessage("document.already.pre.closed"));
        stamper.mergeVerification();
        preClosed = true;
        AcroFields af = writer.getAcroFields();
        String name = getFieldName();
        boolean fieldExists = af.doesSignatureFieldExist(name);
        PdfIndirectReference refSig = writer.getPdfIndirectReference();
        writer.setSigFlags(3);
        PdfDictionary fieldLock = null;
        if (fieldExists) {
            PdfDictionary widget = af.getFieldItem(name).getWidget(0);
            writer.markUsed(widget);
            fieldLock = widget.getAsDict(PdfName.LOCK);

            if (fieldLock == null && this.fieldLock != null) {
                widget.put(PdfName.LOCK, writer.addToBody(this.fieldLock).getIndirectReference());
                fieldLock = this.fieldLock;
            }

            widget.put(PdfName.P, writer.getPageReference(getPage()));
            widget.put(PdfName.V, refSig);
            PdfObject obj = PdfReader.getPdfObjectRelease(widget.get(PdfName.F));
            int flags = 0;
            if (obj != null && obj.isNumber())
                flags = ((PdfNumber)obj).intValue();
            flags |= PdfAnnotation.FLAGS_LOCKED;
            widget.put(PdfName.F, new PdfNumber(flags));
            PdfDictionary ap = new PdfDictionary();
            ap.put(PdfName.N, getAppearance().getIndirectReference());
            widget.put(PdfName.AP, ap);
        }
        else {
            PdfFormField sigField = PdfFormField.createSignature(writer);
            sigField.setFieldName(name);
            sigField.put(PdfName.V, refSig);
            sigField.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_LOCKED);

            if (this.fieldLock != null) {
                sigField.put(PdfName.LOCK, writer.addToBody(this.fieldLock).getIndirectReference());
                fieldLock = this.fieldLock;
            }

            int pagen = getPage();
            if (!isInvisible())
                sigField.setWidget(getPageRect(), null);
            else
                sigField.setWidget(new Rectangle(0, 0), null);
            sigField.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, getAppearance());
            sigField.setPage(pagen);
            writer.addAnnotation(sigField, pagen);
        }

        exclusionLocations = new HashMap<PdfName, PdfLiteral>();
        if (cryptoDictionary == null) {
            throw new DocumentException("No crypto dictionary defined.");
        }
        else {
            PdfLiteral lit = new PdfLiteral(80);
            exclusionLocations.put(PdfName.BYTERANGE, lit);
            cryptoDictionary.put(PdfName.BYTERANGE, lit);
            for (Map.Entry<PdfName, Integer> entry: exclusionSizes.entrySet()) {
                PdfName key = entry.getKey();
                Integer v = entry.getValue();
                lit = new PdfLiteral(v.intValue());
                exclusionLocations.put(key, lit);
                cryptoDictionary.put(key, lit);
            }
            if (certificationLevel > 0)
                addDocMDP(cryptoDictionary);
            if (fieldLock != null)
            	addFieldMDP(cryptoDictionary, fieldLock);
            if (signatureEvent != null)
                signatureEvent.getSignatureDictionary(cryptoDictionary);
            writer.addToBody(cryptoDictionary, refSig, false);
        }
        if (certificationLevel > 0) {
          // add DocMDP entry to root
             PdfDictionary docmdp = new PdfDictionary();
             docmdp.put(new PdfName("DocMDP"), refSig);
             writer.reader.getCatalog().put(new PdfName("Perms"), docmdp);
        }
        writer.close(stamper.getMoreInfo());

        range = new long[exclusionLocations.size() * 2];
        long byteRangePosition = exclusionLocations.get(PdfName.BYTERANGE).getPosition();
        exclusionLocations.remove(PdfName.BYTERANGE);
        int idx = 1;
        for (PdfLiteral lit: exclusionLocations.values()) {
            long n = lit.getPosition();
            range[idx++] = n;
            range[idx++] = lit.getPosLength() + n;
        }
        Arrays.sort(range, 1, range.length - 1);
        for (int k = 3; k < range.length - 2; k += 2)
            range[k] -= range[k - 1];

        if (tempFile == null) {
            bout = sigout.getBuffer();
            boutLen = sigout.size();
            range[range.length - 1] = boutLen - range[range.length - 2];
            ByteBuffer bf = new ByteBuffer();
            bf.append('[');
            for (int k = 0; k < range.length; ++k)
                bf.append(range[k]).append(' ');
            bf.append(']');
            System.arraycopy(bf.getBuffer(), 0, bout, (int)byteRangePosition, bf.size());
        }
        else {
            try {
                raf = new RandomAccessFile(tempFile, "rw");
                long len = raf.length();
                range[range.length - 1] = len - range[range.length - 2];
                ByteBuffer bf = new ByteBuffer();
                bf.append('[');
                for (int k = 0; k < range.length; ++k)
                    bf.append(range[k]).append(' ');
                bf.append(']');
                raf.seek(byteRangePosition);
                raf.write(bf.getBuffer(), 0, bf.size());
            }
            catch (IOException e) {
                try{raf.close();}catch(Exception ee){}
                try{tempFile.delete();}catch(Exception ee){}
                throw e;
            }
        }
    }

    /**
     * Adds keys to the signature dictionary that define
     * the certification level and the permissions.
     * This method is only used for Certifying signatures.
     * @param crypto the signature dictionary
     */
    private void addDocMDP(PdfDictionary crypto) {
        PdfDictionary reference = new PdfDictionary();
        PdfDictionary transformParams = new PdfDictionary();
        transformParams.put(PdfName.P, new PdfNumber(certificationLevel));
        transformParams.put(PdfName.V, new PdfName("1.2"));
        transformParams.put(PdfName.TYPE, PdfName.TRANSFORMPARAMS);
        reference.put(PdfName.TRANSFORMMETHOD, PdfName.DOCMDP);
        reference.put(PdfName.TYPE, PdfName.SIGREF);
        reference.put(PdfName.TRANSFORMPARAMS, transformParams);
        if (writer.getPdfVersion().getVersion() < PdfWriter.VERSION_1_6) {
            reference.put(new PdfName("DigestValue"), new PdfString("aa"));
            PdfArray loc = new PdfArray();
            loc.add(new PdfNumber(0));
            loc.add(new PdfNumber(0));
            reference.put(new PdfName("DigestLocation"), loc);
            reference.put(new PdfName("DigestMethod"), new PdfName("MD5"));
        }
        reference.put(PdfName.DATA, writer.reader.getTrailer().get(PdfName.ROOT));
        PdfArray types = new PdfArray();
        types.add(reference);
        crypto.put(PdfName.REFERENCE, types);
    }

    /**
     * Adds keys to the signature dictionary that define
     * the field permissions.
     * This method is only used for signatures that lock fields.
     * @param crypto the signature dictionary
     */
    private void addFieldMDP(PdfDictionary crypto, PdfDictionary fieldLock) {
        PdfDictionary reference = new PdfDictionary();
        PdfDictionary transformParams = new PdfDictionary();
        transformParams.putAll(fieldLock);
        transformParams.put(PdfName.TYPE, PdfName.TRANSFORMPARAMS);
        transformParams.put(PdfName.V, new PdfName("1.2"));
        reference.put(PdfName.TRANSFORMMETHOD, PdfName.FIELDMDP);
        reference.put(PdfName.TYPE, PdfName.SIGREF);
        reference.put(PdfName.TRANSFORMPARAMS, transformParams);
        reference.put(new PdfName("DigestValue"), new PdfString("aa"));
        PdfArray loc = new PdfArray();
        loc.add(new PdfNumber(0));
        loc.add(new PdfNumber(0));
        reference.put(new PdfName("DigestLocation"), loc);
        reference.put(new PdfName("DigestMethod"), new PdfName("MD5"));
        reference.put(PdfName.DATA, writer.reader.getTrailer().get(PdfName.ROOT));
        PdfArray types = crypto.getAsArray(PdfName.REFERENCE);
        if (types == null)
        	types = new PdfArray();
        types.add(reference);
        crypto.put(PdfName.REFERENCE, types);
    }

    /**
     * This is the last method to be called when using external signatures. The general sequence is:
     * preClose(), getDocumentBytes() and close().
     * <p>
     * <CODE>update</CODE> is a <CODE>PdfDictionary</CODE> that must have exactly the
     * same keys as the ones provided in {@link #preClose(HashMap)}.
     * @param update a <CODE>PdfDictionary</CODE> with the key/value that will fill the holes defined
     * in {@link #preClose(HashMap)}
     * @throws DocumentException on error
     * @throws IOException on error
     */
    public void close(PdfDictionary update) throws IOException, DocumentException {
        try {
            if (!preClosed)
                throw new DocumentException(MessageLocalization.getComposedMessage("preclose.must.be.called.first"));
            ByteBuffer bf = new ByteBuffer();
            for (PdfName key: update.getKeys()) {
                PdfObject obj = update.get(key);
                PdfLiteral lit = exclusionLocations.get(key);
                if (lit == null)
                    throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.key.1.didn.t.reserve.space.in.preclose", key.toString()));
                bf.reset();
                obj.toPdf(null, bf);
                if (bf.size() > lit.getPosLength())
                    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())));
                if (tempFile == null)
                    System.arraycopy(bf.getBuffer(), 0, bout, (int)lit.getPosition(), bf.size());
                else {
                    raf.seek(lit.getPosition());
                    raf.write(bf.getBuffer(), 0, bf.size());
                }
            }
            if (update.size() != exclusionLocations.size())
                throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.update.dictionary.has.less.keys.than.required"));
            if (tempFile == null) {
                originalout.write(bout, 0, boutLen);
            }
            else {
                if (originalout != null) {
                    raf.seek(0);
                    long length = raf.length();
                    byte buf[] = new byte[8192];
                    while (length > 0) {
                        int r = raf.read(buf, 0, (int)Math.min((long)buf.length, length));
                        if (r < 0)
                            throw new EOFException(MessageLocalization.getComposedMessage("unexpected.eof"));
                        originalout.write(buf, 0, r);
                        length -= r;
                    }
                }
            }
        }
        finally {
        	writer.reader.close();
            if (tempFile != null) {
                try{raf.close();}catch(Exception ee){}
                if (originalout != null)
                    try{tempFile.delete();}catch(Exception ee){}
            }
            if (originalout != null)
                try{originalout.close();}catch(Exception e){}
        }
    }
}

com/itextpdf/text/pdf/PdfSignatureAppearance.java

 

Or download all of them as a single archive file:

File name: itextpdf-5.5.14-fyi.zip
File size: 2163839 bytes
Release date: 2009-10-09
Download 

 

iText-2.1.6.jar - iText, a JAVA-PDF library

iText layout.jar Source Code

Download and Install iText Java Library

⇑⇑ iText for PDF Generation

2021-07-03, 110493👍, 0💬