Package org.apache.pdfbox.pdmodel.interactive.form

Source Code of org.apache.pdfbox.pdmodel.interactive.form.PDFieldTreeNode

/*
* 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.ArrayList;
import java.util.List;

import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.common.COSArrayList;
import org.apache.pdfbox.pdmodel.common.COSObjectable;
import org.apache.pdfbox.pdmodel.common.PDTextStream;
import org.apache.pdfbox.pdmodel.fdf.FDFField;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;

/**
* A field in an interactive form.
*
* @author Andreas Lehmk�hler
*
*/
public abstract class PDFieldTreeNode implements COSObjectable
{
    /**
     * Ff flags.
     */
    private static final int FLAG_READ_ONLY = 1;
    private static final int FLAG_REQUIRED = 1 << 1;
    private static final int FLAG_NO_EXPORT = 1 << 2;

    /**
     * Field type Text.
     */
    private static final String FIELD_TYPE_TEXT = "Tx";
    /**
     * Field type Button.
     */
    private static final String FIELD_TYPE_BUTTON = "Btn";
    /**
     * Field type Button.
     */
    private static final String FIELD_TYPE_CHOICE = "Ch";
    /**
     * Field type Button.
     */
    private static final String FIELD_TYPE_SIGNATURE = "Sig";

    private PDAcroForm acroForm;

    private COSDictionary dictionary;

    private PDFieldTreeNode parent = null;

    /**
     * Constructor.
     *
     * @param theAcroForm The form that this field is part of.
     */
    protected PDFieldTreeNode(PDAcroForm theAcroForm)
    {
        this(theAcroForm, new COSDictionary(), null);
    }

    /**
     * Constructor.
     *
     * @param theAcroForm The form that this field is part of.
     * @param field the PDF object to represent as a field.
     * @param parentNode the parent node of the node to be created
     */
    protected PDFieldTreeNode(PDAcroForm theAcroForm, COSDictionary field, PDFieldTreeNode parentNode)
    {
        acroForm = theAcroForm;
        dictionary = field;
        parent = parentNode;
    }

    /**
     * Get the FT entry of the field. This is a read only field and is set depending on the actual type. The field type
     * is an inheritable attribute.
     *
     * @return The Field type.
     *
     */
    public abstract String getFieldType();

    /**
     * getValue gets the value of the "V" entry.
     *
     * @return The value of this entry.
     *
     */
    public abstract Object getValue();

    /**
     * setValue sets the entry "V" to the given value.
     *
     * @param value the value
     *
     */
    public abstract void setValue(Object value);

    /**
     * getDefaultValue gets the value of the "DV" entry.
     *
     * @return The value of this field.
     *
     */
    public abstract Object getDefaultValue();

    /**
     * setDefaultValue sets the entry "DV" to the given value.
     *
     * @param value the value
     *
     */
    public abstract void setDefaultValue(Object value);

    /**
     * sets the field to be read-only.
     *
     * @param readonly The new flag for readonly.
     */
    public void setReadonly(boolean readonly)
    {
        getDictionary().setFlag(COSName.FF, FLAG_READ_ONLY, readonly);
    }

    /**
     *
     * @return true if the field is readonly
     */
    public boolean isReadonly()
    {
        return getDictionary().getFlag(COSName.FF, FLAG_READ_ONLY);
    }

    /**
     * sets the field to be required.
     *
     * @param required The new flag for required.
     */
    public void setRequired(boolean required)
    {
        getDictionary().setFlag(COSName.FF, FLAG_REQUIRED, required);
    }

    /**
     *
     * @return true if the field is required
     */
    public boolean isRequired()
    {
        return getDictionary().getFlag(COSName.FF, FLAG_REQUIRED);
    }

    /**
     * sets the field to be not exported..
     *
     * @param noExport The new flag for noExport.
     */
    public void setNoExport(boolean noExport)
    {
        getDictionary().setFlag(COSName.FF, FLAG_NO_EXPORT, noExport);
    }

    /**
     *
     * @return true if the field is not to be exported.
     */
    public boolean isNoExport()
    {
        return getDictionary().getFlag(COSName.FF, FLAG_NO_EXPORT);
    }

    /**
     * This will get the flags for this field.
     *
     * @return flags The set of flags.
     */
    public abstract int getFieldFlags();

    /**
     * This will set the flags for this field.
     *
     * @param flags The new flags.
     */
    public void setFieldFlags(int flags)
    {
        getDictionary().setInt(COSName.FF, flags);
    }

    /**
     * This will import a fdf field from a fdf document.
     *
     * @param fdfField The fdf field to import.
     *
     * @throws IOException If there is an error importing the data for this field.
     */
    public void importFDF(FDFField fdfField) throws IOException
    {
        Object fieldValue = fdfField.getValue();
        int fieldFlags = getFieldFlags();

        if (fieldValue != null)
        {
            if (fieldValue instanceof String)
            {
                setValue((String) fieldValue);
            }
            else if (fieldValue instanceof PDTextStream)
            {
                setValue(((PDTextStream) fieldValue).getAsString());
            }
            else
            {
                throw new IOException("Unknown field type:" + fieldValue.getClass().getName());
            }
        }
        Integer ff = fdfField.getFieldFlags();
        if (ff != null)
        {
            setFieldFlags(ff);
        }
        else
        {
            // these are suppose to be ignored if the Ff is set.
            Integer setFf = fdfField.getSetFieldFlags();

            if (setFf != null)
            {
                int setFfInt = setFf;
                fieldFlags = fieldFlags | setFfInt;
                setFieldFlags(fieldFlags);
            }

            Integer clrFf = fdfField.getClearFieldFlags();
            if (clrFf != null)
            {
                // we have to clear the bits of the document fields for every bit that is
                // set in this field.
                //
                // Example:
                // docFf = 1011
                // clrFf = 1101
                // clrFfValue = 0010;
                // newValue = 1011 & 0010 which is 0010
                int clrFfValue = clrFf;
                clrFfValue ^= 0xFFFFFFFF;
                fieldFlags = fieldFlags & clrFfValue;
                setFieldFlags(fieldFlags);
            }
        }

        PDAnnotationWidget widget = getWidget();
        if (widget != null)
        {
            int annotFlags = widget.getAnnotationFlags();
            Integer f = fdfField.getWidgetFieldFlags();
            if (f != null && widget != null)
            {
                widget.setAnnotationFlags(f);
            }
            else
            {
                // these are suppose to be ignored if the F is set.
                Integer setF = fdfField.getSetWidgetFieldFlags();
                if (setF != null)
                {
                    annotFlags = annotFlags | setF;
                    widget.setAnnotationFlags(annotFlags);
                }

                Integer clrF = fdfField.getClearWidgetFieldFlags();
                if (clrF != null)
                {
                    // we have to clear the bits of the document fields for every bit that is
                    // set in this field.
                    //
                    // Example:
                    // docF = 1011
                    // clrF = 1101
                    // clrFValue = 0010;
                    // newValue = 1011 & 0010 which is 0010
                    int clrFValue = clrF;
                    clrFValue ^= 0xFFFFFFFFL;
                    annotFlags = annotFlags & clrFValue;
                    widget.setAnnotationFlags(annotFlags);
                }
            }
        }
        List<FDFField> fdfKids = fdfField.getKids();
        List<COSObjectable> pdKids = getKids();
        for (int i = 0; fdfKids != null && i < fdfKids.size(); i++)
        {
            FDFField fdfChild = fdfKids.get(i);
            String fdfName = fdfChild.getPartialFieldName();
            for (COSObjectable pdKid : pdKids)
            {
                if (pdKid instanceof PDFieldTreeNode)
                {
                    PDFieldTreeNode pdChild = (PDFieldTreeNode) pdKid;
                    if (fdfName != null && fdfName.equals(pdChild.getPartialName()))
                    {
                        pdChild.importFDF(fdfChild);
                    }
                }
            }
        }
    }

    /**
     * This will get the single associated widget that is part of this field. This occurs when the Widget is embedded in
     * the fields dictionary. Sometimes there are multiple sub widgets associated with this field, in which case you
     * want to use getKids(). If the kids entry is specified, then the first entry in that list will be returned.
     *
     * @return The widget that is associated with this field.
     */
    public PDAnnotationWidget getWidget()
    {
        PDAnnotationWidget retval = null;
        List<COSObjectable> kids = getKids();
        if (kids == null)
        {
            retval = new PDAnnotationWidget(getDictionary());
        }
        else if (kids.size() > 0)
        {
            Object firstKid = kids.get(0);
            if (firstKid instanceof PDAnnotationWidget)
            {
                retval = (PDAnnotationWidget) firstKid;
            }
            else
            {
                retval = ((PDFieldTreeNode) firstKid).getWidget();
            }
        }
        else
        {
            retval = null;
        }
        return retval;
    }

    /**
     * Get the parent field to this field, or null if none exists.
     *
     * @return The parent field.
     *
     */
    public PDFieldTreeNode getParent()
    {
        return parent;
    }

    /**
     * Set the parent of this field.
     *
     * @param parentNode The parent to this field.
     */
    public void setParent(PDFieldTreeNode parentNode)
    {
        parent = parentNode;
        if (parentNode != null)
        {
            getDictionary().setItem(COSName.PARENT, parent.getDictionary());
        }
        else
        {
            getDictionary().removeItem(COSName.PARENT);
        }
    }

    /**
     * This will find one of the child elements. The name array are the components of the name to search down the tree
     * of names. The nameIndex is where to start in that array. This method is called recursively until it finds the end
     * point based on the name array.
     *
     * @param name An array that picks the path to the field.
     * @param nameIndex The index into the array.
     * @return The field at the endpoint or null if none is found.
     */
    public PDFieldTreeNode findKid(String[] name, int nameIndex)
    {
        PDFieldTreeNode retval = null;
        COSArray kids = (COSArray) getDictionary().getDictionaryObject(COSName.KIDS);
        if (kids != null)
        {
            for (int i = 0; retval == null && i < kids.size(); i++)
            {
                COSDictionary kidDictionary = (COSDictionary) kids.getObject(i);
                if (name[nameIndex].equals(kidDictionary.getString(COSName.T)))
                {
                    retval = (PDFieldTreeNode) PDFieldTreeNode.createField(acroForm, kidDictionary, this);
                    if (name.length > nameIndex + 1)
                    {
                        retval = retval.findKid(name, nameIndex + 1);
                    }
                }
            }
        }
        return retval;
    }

    /**
     * This will get all the kids of this field. The values in the list will either be PDWidget or PDField. Normally
     * they will be PDWidget objects unless this is a non-terminal field and they will be child PDField objects.
     *
     * @return A list of either PDWidget or PDField objects.
     */
    public List<COSObjectable> getKids()
    {
        List<COSObjectable> retval = null;
        COSArray kids = (COSArray) dictionary.getDictionaryObject(COSName.KIDS);
        if (kids != null)
        {
            List<COSObjectable> kidsList = new ArrayList<COSObjectable>();
            for (int i = 0; i < kids.size(); i++)
            {
                COSDictionary kidDictionary = (COSDictionary) kids.getObject(i);
                if (kidDictionary == null)
                {
                    continue;
                }
                COSDictionary parent = (COSDictionary) kidDictionary.getDictionaryObject(
                        COSName.PARENT, COSName.P);
                if (kidDictionary.getDictionaryObject(COSName.FT) != null
                        || (parent != null && parent.getDictionaryObject(COSName.FT) != null))
                {
                    PDFieldTreeNode field = PDFieldTreeNode.createField(acroForm, kidDictionary, this);
                    if (field != null)
                    {
                        kidsList.add(field);
                    }
                }
                else if ("Widget".equals(kidDictionary.getNameAsString(COSName.SUBTYPE)))
                {
                    kidsList.add(new PDAnnotationWidget(kidDictionary));
                }
                else
                {
                    PDFieldTreeNode field = PDFieldTreeNode.createField(acroForm, kidDictionary, this);
                    if (field != null)
                    {
                        kidsList.add(field);
                    }
                }
            }
            retval = new COSArrayList<COSObjectable>(kidsList, kids);
        }
        return retval;
    }

    /**
     * This will set the list of kids.
     *
     * @param kids The list of child widgets.
     */
    public void setKids(List<COSObjectable> kids)
    {
        COSArray kidsArray = COSArrayList.converterToCOSArray(kids);
        getDictionary().setItem(COSName.KIDS, kidsArray);
    }

    /**
     * This will return a string representation of this field.
     *
     * @return A string representation of this field.
     */
    @Override
    public String toString()
    {
        return "" + getDictionary().getDictionaryObject(COSName.V);
    }

    /**
     * This will get the acroform that this field is part of.
     *
     * @return The form this field is on.
     */
    public PDAcroForm getAcroForm()
    {
        return acroForm;
    }

    /**
     * This will set the form this field is on.
     *
     * @param value The new form to use.
     */
    public void setAcroForm(PDAcroForm value)
    {
        acroForm = value;
    }

    /**
     * This will get the dictionary associated with this field.
     *
     * @return The dictionary that this class wraps.
     */
    public COSDictionary getDictionary()
    {
        return dictionary;
    }

    /**
     * Convert this standard java object to a COS object.
     *
     * @return The cos object that matches this Java object.
     */
    public COSBase getCOSObject()
    {
        return dictionary;
    }

    /**
     * Returns the partial name of the field.
     *
     * @return the name of the field
     */
    public String getPartialName()
    {
        return getDictionary().getString(COSName.T);
    }

    /**
     * This will set the partial name of the field.
     *
     * @param name The new name for the field.
     */
    public void setPartialName(String name)
    {
        getDictionary().setString(COSName.T, name);
    }

    /**
     * Returns the fully qualified name of the field, which is a concatenation of the names of all the parents fields.
     *
     * @return the name of the field
     *
     * @throws IOException If there is an error generating the fully qualified name.
     */
    public String getFullyQualifiedName() throws IOException
    {
        String finalName = getPartialName();
        String parentName = getParent() != null ? getParent().getFullyQualifiedName() : null;
        if (parentName != null)
        {
            if (finalName != null)
            {
                finalName = parentName + "." + finalName;
            }
            else
            {
                finalName = parentName;
            }
        }
        return finalName;
    }

    /**
     * Gets the alternate name of the field.
     *
     * @return the alternate name of the field
     */
    public String getAlternateFieldName()
    {
        return this.getDictionary().getString(COSName.TU);
    }

    /**
     * This will set the alternate name of the field.
     *
     * @param alternateFieldName the alternate name of the field
     */
    public void setAlternateFieldName(String alternateFieldName)
    {
        this.getDictionary().setString(COSName.TU, alternateFieldName);
    }

    /**
     * Creates a COSField subclass from the given field.
     * @param form the form that the field is part of
     * @param field the dictionary representing a field element
     * @param parentNode the parent node of the node to be created
     * @return the corresponding PDField instance
     */
    public static PDFieldTreeNode createField(PDAcroForm form, COSDictionary field, PDFieldTreeNode parentNode)
    {
        String fieldType = findFieldType(field);
        if (FIELD_TYPE_CHOICE.equals(fieldType))
        {
            int flags = field.getInt(COSName.FF, 0);
            if ((flags & PDChoice.FLAG_COMBO) != 0)
            {
                return new PDComboBox(form, field, parentNode);
            }
            else
            {
                return new PDListBox(form, field, parentNode);
            }
        }
        else if (FIELD_TYPE_TEXT.equals(fieldType))
        {
            return new PDTextField(form, field, parentNode);
        }
        else if (FIELD_TYPE_SIGNATURE.equals(fieldType))
        {
            return new PDSignatureField(form, field, parentNode);
        }
        else if (FIELD_TYPE_BUTTON.equals(fieldType))
        {
            int flags = field.getInt(COSName.FF, 0);
            // BJL: I have found that the radio flag bit is not always set
            // and that sometimes there is just a kids dictionary.
            // so, if there is a kids dictionary then it must be a radio button group.
            if ((flags & PDButton.FLAG_RADIO) != 0 || field.getDictionaryObject(COSName.KIDS) != null)
            {
                return new PDRadioButton(form, field, parentNode);
            }
            else if ((flags & PDButton.FLAG_PUSHBUTTON) != 0)
            {
                return new PDPushButton(form, field, parentNode);
            }
            else
            {
                return new PDCheckbox(form, field, parentNode);
            }
        }
        else
        {
            return new PDNonTerminalField(form, field, parentNode);
        }
    }

    private static String findFieldType(COSDictionary dic)
    {
        String retval = dic.getNameAsString(COSName.FT);
        if (retval == null)
        {
            COSDictionary parent = (COSDictionary) dic.getDictionaryObject(COSName.PARENT,
                    COSName.P);
            if (parent != null)
            {
                retval = findFieldType(parent);
            }
        }
        return retval;
    }

}
TOP

Related Classes of org.apache.pdfbox.pdmodel.interactive.form.PDFieldTreeNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.