All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.forms.fields.PdfFormAnnotationUtil Maven / Gradle / Ivy

There is a newer version: 8.0.5
Show newest version
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2024 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    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 .
 */
package com.itextpdf.forms.fields;

import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.pdf.tagging.StandardRoles;
import com.itextpdf.kernel.pdf.tagutils.TagTreePointer;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Utility class to work with widget annotations {@link com.itextpdf.forms.fields.PdfFormAnnotation}
 * and its dictionaries.
 */
public final class PdfFormAnnotationUtil {

    private PdfFormAnnotationUtil() {
        // Private constructor will prevent the instantiation of this class directly.
    }

    /**
     * Check that <PdfDictionary> object is widget annotation or merged field.
     *
     * @param fieldDict field dictionary to check.
     *
     * @return true if passed dictionary is a widget or merged field, false otherwise.
     */
    public static boolean isPureWidgetOrMergedField(PdfDictionary fieldDict) {
        if (fieldDict.isFlushed()) {
            return false;
        }
        PdfName subtype = fieldDict.getAsName(PdfName.Subtype);
        return PdfName.Widget.equals(subtype);
    }

    /**
     * Check that <PdfDictionary> object is pure widget annotation.
     *
     * @param fieldDict field dictionary to check.
     *
     * @return true if passed dictionary is a widget, false otherwise.
     */
    public static boolean isPureWidget(PdfDictionary fieldDict) {
        return isPureWidgetOrMergedField(fieldDict) && !PdfFormField.isFormField(fieldDict);
    }

    /**
     * Add widget annotation to the specified page.
     *
     * @param page       to which annotation should be added.
     * @param annotation widget annotation to add.
     */
    public static void addWidgetAnnotationToPage(PdfPage page, PdfAnnotation annotation) {
        addWidgetAnnotationToPage(page, annotation, -1);
    }

    /**
     * Add widget annotation to the specified page.
     *
     * @param page       to which annotation should be added.
     * @param annotation widget annotation to add.
     * @param index      the index at which specified annotation will be added. If {@code -1} then annotation
     *                   will be added to the end of an array.
     */
    public static void addWidgetAnnotationToPage(PdfPage page, PdfAnnotation annotation, int index) {
        if (page.containsAnnotation(annotation)) {
            return;
        }

        TagTreePointer tagPointer = null;
        PdfDocument document = page.getDocument();
        boolean tagged = document.isTagged();
        if (tagged) {
            tagPointer = document.getTagStructureContext().getAutoTaggingPointer();
            if (!StandardRoles.FORM.equals(tagPointer.getRole())) {
                tagPointer.addTag(StandardRoles.FORM);
            }
        }

        page.addAnnotation(index, annotation, true);

        if (tagged) {
            tagPointer.moveToParent();
        }
    }

    /**
     * Merge single widget annotation with its parent field.
     *
     * @param field parent field.
     */
    public static void mergeWidgetWithParentField(PdfFormField field) {
        PdfArray kids = field.getKids();
        if (kids != null && kids.size() == 1) {
            PdfDictionary kidDict = (PdfDictionary) kids.get(0);
            if (isPureWidget(kidDict)) {
                // kid is not merged field with widget
                kidDict.remove(PdfName.Parent);
                field.getPdfObject().mergeDifferent(kidDict);
                field.removeChildren();
                field.setChildField(PdfFormAnnotation.makeFormAnnotation(field.getPdfObject(), field.getDocument()));

                replaceAnnotationOnPage(kidDict, field.getPdfObject());
            }
        }
    }

    /**
     * Separate merged field to form field and pure widget annotation.
     * Do nothing if the incoming field is not merged field.
     *
     * @param field to separate.
     */
    public static void separateWidgetAndField(PdfFormField field) {
        PdfDictionary fieldDict = field.getPdfObject();
        // If field is merged with widget
        if (isPureWidgetOrMergedField(fieldDict)) {
            PdfDictionary widgetDict = new PdfDictionary(fieldDict);
            replaceAnnotationOnPage(fieldDict, widgetDict);
            Set dictKeys = new LinkedHashSet<>(fieldDict.keySet());
            // Split field dictionary onto two
            for (PdfName key : dictKeys) {
                if (PdfFormField.getFormFieldKeys().contains(key) || PdfName.Parent.equals(key)) {
                    widgetDict.remove(key);
                } else {
                    fieldDict.remove(key);
                }
            }

            List newKids = new ArrayList<>();
            newKids.add(PdfFormAnnotation.makeFormAnnotation(widgetDict, field.getDocument()));
            field.replaceKids(newKids);
        }
    }

    private static void replaceAnnotationOnPage(PdfDictionary oldAnnotDict, PdfDictionary newAnnotDict) {
        // Get page for the old annotation
        PdfAnnotation oldAnnot = PdfAnnotation.makeAnnotation(oldAnnotDict);
        PdfPage page = oldAnnot.getPage();

        // Remove old annotation and add new
        if (page != null) {
            int annotIndex = -1;
            PdfArray annots = page.getPdfObject().getAsArray(PdfName.Annots);
            if (annots != null) {
                annotIndex = annots.indexOf(oldAnnotDict);
            }
            page.removeAnnotation(oldAnnot, true);
            oldAnnotDict.remove(PdfName.P);
            if (annotIndex >= page.getAnnotsSize()) {
                annotIndex = -1;
            }
            if (newAnnotDict.get(PdfName.P) == null) {
                newAnnotDict.put(PdfName.P, page.getPdfObject());
            }
            addNewWidgetToPage(page, newAnnotDict, annotIndex);
        }
    }

    private static void addNewWidgetToPage(PdfPage currentPage, PdfDictionary field, int annotIndex) {
        PdfDictionary pageDic = field.getAsDictionary(PdfName.P);
        if (pageDic.isFlushed()) {
            return;
        }
        PdfDocument doc = pageDic.getIndirectReference().getDocument();
        PdfPage widgetPage = doc.getPage(pageDic);
        addWidgetAnnotationToPage(widgetPage == null ? currentPage : widgetPage,
                PdfAnnotation.makeAnnotation(field), annotIndex);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy