# PDSignatureField.java

    /*
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package org.apache.pdfbox.pdmodel.interactive.form;

    import java.io.IOException;
    import java.util.HashSet;
    import java.util.Set;

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.pdfbox.cos.COSDictionary;
    import org.apache.pdfbox.cos.COSName;
    import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
    import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSeedValue;
    import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;

    /**
     * A signature field is a form field that contains a digital signature.
     *
     * @author Ben Litchfield
     * @author Thomas Chojecki
     */
    public class PDSignatureField extends PDTerminalField
    {
    	private static final Log LOG = LogFactory.getLog(PDSignatureField.class);

        /**
         * @see PDTerminalField#PDTerminalField(PDAcroForm)
         *
         * @param acroForm The acroForm for this field.
         */
        public PDSignatureField(PDAcroForm acroForm)
        {
            super(acroForm);
            getCOSObject().setItem(COSName.FT, COSName.SIG);
            PDAnnotationWidget firstWidget = getWidgets().get(0);
            firstWidget.setLocked(true);
            firstWidget.setPrinted(true);
            setPartialName(generatePartialName());
        }

        /**
         * Constructor.
         *
         * @param acroForm The form that this field is part of.
         * @param field the PDF object to represent as a field.
         * @param parent the parent node of the node to be created
         */
        PDSignatureField(PDAcroForm acroForm, COSDictionary field, PDNonTerminalField parent)
        {
            super(acroForm, field, parent);
        }

        /**
         * Generate a unique name for the signature.
         *
         * @return the signature's unique name
         */
        private String generatePartialName()
        {
            String fieldName = "Signature";
            Set<String> nameSet = new HashSet<>();
            getAcroForm().getFieldTree().forEach(field -> nameSet.add(field.getPartialName()));
            int i = 1;
            while (nameSet.contains(fieldName + i))
            {
                ++i;
            }
            return fieldName+i;
        }

        /**
         * Get the signature dictionary.
         *
         * @return the signature dictionary
         *
         */
        public PDSignature getSignature()
        {
            return getValue();
        }

        /**
         * Sets the value of this field to be the given signature.
         *
         * @param value is the PDSignatureField
         */
        public void setValue(PDSignature value) throws IOException
        {
            getCOSObject().setItem(COSName.V, value);
            applyChange();
        }

        /**
         * <b>This will throw an UnsupportedOperationException if used as the signature fields
         * value can't be set using a String</b>
         *
         * @param value the plain text value.
         *
         * @throws UnsupportedOperationException in all cases!
         */
        @Override
        public void setValue(String value)
        {
            throw new UnsupportedOperationException("Signature fields don't support setting the value as String "
                    + "- use setValue(PDSignature value) instead");
        }

        /**
         * Sets the default value of this field to be the given signature.
         *
         * @param value is the PDSignatureField
         */
        public void setDefaultValue(PDSignature value)
        {
            getCOSObject().setItem(COSName.DV, value);
        }

        /**
         * Returns the signature contained in this field.
         *
         * @return A signature dictionary.
         */
        public PDSignature getValue()
        {
            COSDictionary value = getCOSObject().getCOSDictionary(COSName.V);
            return value != null ? new PDSignature(value) : null;
        }

        /**
         * Returns the default value, if any.
         *
         * @return A signature dictionary.
         */
        public PDSignature getDefaultValue()
        {
            COSDictionary value = getCOSObject().getCOSDictionary(COSName.DV);
            return value != null ? new PDSignature(value) : null;
        }

        @Override
        public String getValueAsString()
        {
            PDSignature signature = getValue();
            return signature != null ? signature.toString() : "";
        }

        /**
         * <p>(Optional; PDF 1.5) A seed value dictionary containing information
         * that constrains the properties of a signature that is applied to the
         * field.</p>
         *
         * @return the seed value dictionary as PDSeedValue
         */
        public PDSeedValue getSeedValue()
        {
            COSDictionary dict = getCOSObject().getCOSDictionary(COSName.SV);
            return dict != null ? new PDSeedValue(dict) : null;
        }

        /**
         * <p>(Optional; PDF 1.) A seed value dictionary containing information
         * that constrains the properties of a signature that is applied to the
         * field.</p>
         *
         * @param sv is the seed value dictionary as PDSeedValue
         */
        public void setSeedValue(PDSeedValue sv)
        {
            if (sv != null)
            {
                getCOSObject().setItem(COSName.SV, sv);
            }
        }

        @Override
        void constructAppearances() throws IOException
        {
            PDAnnotationWidget widget = this.getWidgets().get(0);
            if (widget != null)
            {
                // check if the signature is visible
                if (widget.getRectangle() == null ||
                    Float.compare(widget.getRectangle().getHeight(), 0) == 0 && Float.compare(widget.getRectangle().getWidth(), 0) == 0 ||
                    widget.isNoView() ||  widget.isHidden())
                {
                    return;
                }

                // TODO: implement appearance generation for signatures (PDFBOX-3524)
                LOG.warn("Appearance generation for signature fields not implemented here. "
                        + "You need to generate/update that manually, see the "
                        + "CreateVisibleSignature*.java files in the examples subproject "
                        + "of the source code download");
            }
        }
    }