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

com.itextpdf.pdfua.checkers.utils.AnnotationCheckUtil Maven / Gradle / Ivy

The 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.pdfua.checkers.utils;

import com.itextpdf.commons.utils.MessageFormatUtil;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfObject;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.pdf.tagging.IStructureNode;
import com.itextpdf.kernel.pdf.tagging.PdfObjRef;
import com.itextpdf.kernel.pdf.tagging.PdfStructElem;
import com.itextpdf.pdfua.exceptions.PdfUAConformanceException;
import com.itextpdf.pdfua.exceptions.PdfUAExceptionMessageConstants;

/**
 * Class that provides methods for checking PDF/UA compliance of annotations.
 */
public final class AnnotationCheckUtil {
    private AnnotationCheckUtil() {
        // Empty constructor.
    }

    /**
     * Is annotation visible: {@code true} if hidden flag isn't
     * set and annotation intersects CropBox (default value is MediaBox).
     *
     * @param annotDict annotation to check
     *
     * @return {@code true} if annotation should be checked, otherwise {@code false}
     */
    public static boolean isAnnotationVisible(PdfDictionary annotDict) {
        if (annotDict.getAsNumber(PdfName.F) != null) {
            int flags = annotDict.getAsNumber(PdfName.F).intValue();
            if ((flags & PdfAnnotation.HIDDEN) != 0) {
                return false;
            }
        }
        if (annotDict.getAsDictionary(PdfName.P) != null) {
            PdfDictionary page = annotDict.getAsDictionary(PdfName.P);
            PdfArray pageBox = page.getAsArray(PdfName.CropBox) == null ? page.getAsArray(PdfName.MediaBox) : page.getAsArray(PdfName.CropBox);
            if (pageBox != null && annotDict.getAsArray(PdfName.Rect) != null) {
                PdfArray annotBox = annotDict.getAsArray(PdfName.Rect);
                try {
                    if (pageBox.toRectangle().getIntersection(annotBox.toRectangle()) == null) {
                        return false;
                    }
                } catch (PdfException ignore) {
                    // ignore
                }
            }
        }
        return true;
    }

    /**
     * Helper class that checks the conformance of annotations while iterating the tag tree structure.
     */
    public static class AnnotationHandler extends ContextAwareTagTreeIteratorHandler {

        /**
         * Creates a new instance of the {@link AnnotationCheckUtil.AnnotationHandler}.
         *
         * @param context The validation context.
         */
        public AnnotationHandler(PdfUAValidationContext context) {
            super(context);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void nextElement(IStructureNode elem) {
            if (!(elem instanceof PdfObjRef)) {
                return;
            }
            PdfObjRef objRef = (PdfObjRef) elem;
            PdfDictionary annotObj = objRef.getReferencedObject();
            if (annotObj == null) {
                return;
            }

            if (annotObj.getAsDictionary(PdfName.P) != null) {
                PdfDictionary pageDict = annotObj.getAsDictionary(PdfName.P);
                if (!PdfName.S.equals(pageDict.getAsName(PdfName.Tabs))) {
                    throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.PAGE_WITH_ANNOT_DOES_NOT_HAVE_TABS_WITH_S);
                }
            }

            PdfName subtype = annotObj.getAsName(PdfName.Subtype);

            if (!isAnnotationVisible(annotObj) || PdfName.Popup.equals(subtype)) {
                return;
            }

            if (PdfName.PrinterMark.equals(subtype)) {
                throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.PRINTER_MARK_IS_NOT_PERMITTED);
            }

            if (PdfName.TrapNet.equals(subtype)) {
                throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.ANNOT_TRAP_NET_IS_NOT_PERMITTED);
            }

            if (!PdfName.Widget.equals(subtype) && !(annotObj.containsKey(PdfName.Contents)
                    || annotObj.containsKey(PdfName.Alt))) {
                throw new PdfUAConformanceException(MessageFormatUtil.format(
                        PdfUAExceptionMessageConstants.ANNOTATION_OF_TYPE_0_SHOULD_HAVE_CONTENTS_OR_ALT_KEY, subtype.getValue()));
            }

            if (PdfName.Link.equals(subtype)) {
                PdfStructElem parentLink = context.getElementIfRoleMatches(PdfName.Link, objRef.getParent());
                if (parentLink == null) {
                    throw new PdfUAConformanceException(
                            PdfUAExceptionMessageConstants.LINK_ANNOT_IS_NOT_NESTED_WITHIN_LINK);
                }
                if (!annotObj.containsKey(PdfName.Contents)) {
                    throw new PdfUAConformanceException(PdfUAExceptionMessageConstants.LINK_ANNOTATION_SHOULD_HAVE_CONTENTS_KEY);
                }
            }

            if (PdfName.Screen.equals(subtype)) {
                PdfDictionary action = annotObj.getAsDictionary(PdfName.A);
                PdfDictionary additionalActions = annotObj.getAsDictionary(PdfName.AA);
                ActionCheckUtil.checkAction(action);
                checkAAEntry(additionalActions);
            }
        }

        private static void checkAAEntry(PdfDictionary additionalActions) {
            if (additionalActions != null) {
                for (PdfObject val : additionalActions.values()) {
                    if (val instanceof PdfDictionary) {
                        ActionCheckUtil.checkAction((PdfDictionary) val);
                    }
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy