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

com.itextpdf.pdfa.checker.PdfA1Checker Maven / Gradle / Ivy

/*
    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.pdfa.checker;

import com.itextpdf.commons.utils.MessageFormatUtil;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.source.PdfTokenizer;
import com.itextpdf.io.source.RandomAccessFileOrArray;
import com.itextpdf.io.source.RandomAccessSourceFactory;
import com.itextpdf.kernel.colors.Color;
import com.itextpdf.kernel.colors.PatternColor;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfTrueTypeFont;
import com.itextpdf.kernel.font.PdfType3Font;
import com.itextpdf.kernel.pdf.PdfAConformanceLevel;
import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfBoolean;
import com.itextpdf.kernel.pdf.PdfCatalog;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfNumber;
import com.itextpdf.kernel.pdf.PdfObject;
import com.itextpdf.kernel.pdf.PdfStream;
import com.itextpdf.kernel.pdf.PdfString;
import com.itextpdf.kernel.pdf.PdfXrefTable;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.pdf.canvas.CanvasGraphicsState;
import com.itextpdf.kernel.pdf.canvas.parser.util.PdfCanvasParser;
import com.itextpdf.kernel.pdf.colorspace.PdfColorSpace;
import com.itextpdf.kernel.pdf.colorspace.PdfDeviceCs;
import com.itextpdf.kernel.pdf.colorspace.PdfPattern;
import com.itextpdf.kernel.pdf.colorspace.PdfSpecialCs;
import com.itextpdf.kernel.utils.checkers.FontCheckUtil;
import com.itextpdf.pdfa.exceptions.PdfAConformanceException;
import com.itextpdf.pdfa.exceptions.PdfaExceptionMessageConstant;
import com.itextpdf.pdfa.logs.PdfAConformanceLogMessageConstant;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * PdfA1Checker defines the requirements of the PDF/A-1 standard and contains
 * method implementations from the abstract {@link PdfAChecker} class.
 * 

* The specification implemented by this class is ISO 19005-1 */ public class PdfA1Checker extends PdfAChecker { protected static final Set forbiddenAnnotations = Collections .unmodifiableSet(new HashSet<>(Arrays.asList( PdfName.Sound, PdfName.Movie, PdfName.FileAttachment))); protected static final Set contentAnnotations = Collections .unmodifiableSet(new HashSet<>(Arrays.asList( PdfName.Text, PdfName.FreeText, PdfName.Line, PdfName.Square, PdfName.Circle, PdfName.Stamp, PdfName.Ink, PdfName.Popup))); protected static final Set forbiddenActions = Collections .unmodifiableSet(new HashSet<>(Arrays.asList( PdfName.Launch, PdfName.Sound, PdfName.Movie, PdfName.ResetForm, PdfName.ImportData, PdfName.JavaScript, PdfName.Hide))); protected static final Set allowedNamedActions = Collections .unmodifiableSet(new HashSet<>(Arrays.asList( PdfName.NextPage, PdfName.PrevPage, PdfName.FirstPage, PdfName.LastPage))); protected static final Set allowedRenderingIntents = Collections .unmodifiableSet(new HashSet<>(Arrays.asList( PdfName.RelativeColorimetric, PdfName.AbsoluteColorimetric, PdfName.Perceptual, PdfName.Saturation))); private static final int MAX_NUMBER_OF_DEVICEN_COLOR_COMPONENTS = 8; private static final Logger logger = LoggerFactory.getLogger(PdfAChecker.class); /** * Creates a PdfA1Checker with the required conformance level * * @param conformanceLevel the required conformance level, a or * b */ public PdfA1Checker(PdfAConformanceLevel conformanceLevel) { super(conformanceLevel); } @Override public void checkCanvasStack(char stackOperation) { if ('q' == stackOperation) { if (++gsStackDepth > PdfA1Checker.maxGsStackDepth) throw new PdfAConformanceException(PdfaExceptionMessageConstant.GRAPHICS_STATE_STACK_DEPTH_IS_GREATER_THAN_28); } else if ('Q' == stackOperation) { gsStackDepth--; } } @Override public void checkInlineImage(PdfStream inlineImage, PdfDictionary currentColorSpaces) { PdfObject filter = inlineImage.get(PdfName.Filter); if (filter instanceof PdfName) { if (filter.equals(PdfName.LZWDecode)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.LZWDECODE_FILTER_IS_NOT_PERMITTED); } } else if (filter instanceof PdfArray) { for (int i = 0; i < ((PdfArray) filter).size(); i++) { PdfName f = ((PdfArray) filter).getAsName(i); if (f.equals(PdfName.LZWDecode)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.LZWDECODE_FILTER_IS_NOT_PERMITTED); } } } checkImage(inlineImage, currentColorSpaces); } /** * {@inheritDoc} */ @Deprecated @Override public void checkColor(Color color, PdfDictionary currentColorSpaces, Boolean fill, PdfStream stream) { checkColor(null, color, currentColorSpaces, fill, stream); } /** * {@inheritDoc} */ @Override public void checkColor(CanvasGraphicsState graphicsState, Color color, PdfDictionary currentColorSpaces, Boolean fill, PdfStream stream) { checkColorSpace(color.getColorSpace(), stream, currentColorSpaces, true, fill); if (color instanceof PatternColor) { PdfPattern pattern = ((PatternColor) color).getPattern(); if (pattern instanceof PdfPattern.Tiling) { checkContentStream((PdfStream) pattern.getPdfObject()); } } } /** * {@inheritDoc} */ @Override public void checkColorSpace(PdfColorSpace colorSpace, PdfObject pdfObject, PdfDictionary currentColorSpaces, boolean checkAlternate, Boolean fill) { if (colorSpace instanceof PdfSpecialCs.Separation) { colorSpace = ((PdfSpecialCs.Separation) colorSpace).getBaseCs(); } else if (colorSpace instanceof PdfSpecialCs.DeviceN) { PdfSpecialCs.DeviceN deviceNColorspace = (PdfSpecialCs.DeviceN) colorSpace; if (deviceNColorspace.getNumberOfComponents() > MAX_NUMBER_OF_DEVICEN_COLOR_COMPONENTS) { throw new PdfAConformanceException(PdfaExceptionMessageConstant. THE_NUMBER_OF_COLOR_COMPONENTS_IN_DEVICE_N_COLORSPACE_SHOULD_NOT_EXCEED, MAX_NUMBER_OF_DEVICEN_COLOR_COMPONENTS); } colorSpace = deviceNColorspace.getBaseCs(); } if (colorSpace instanceof PdfDeviceCs.Rgb) { if (cmykIsUsed || !cmykUsedObjects.isEmpty()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.DEVICERGB_AND_DEVICECMYK_COLORSPACES_CANNOT_BE_USED_BOTH_IN_ONE_FILE); } rgbUsedObjects.add(pdfObject); } else if (colorSpace instanceof PdfDeviceCs.Cmyk) { if (rgbIsUsed || !rgbUsedObjects.isEmpty()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.DEVICERGB_AND_DEVICECMYK_COLORSPACES_CANNOT_BE_USED_BOTH_IN_ONE_FILE); } cmykUsedObjects.add(pdfObject); } else if (colorSpace instanceof PdfDeviceCs.Gray) { grayUsedObjects.add(pdfObject); } } @Override public void checkXrefTable(PdfXrefTable xrefTable) { if (xrefTable.getCountOfIndirectObjects() > getMaxNumberOfIndirectObjects()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.MAXIMUM_NUMBER_OF_INDIRECT_OBJECTS_EXCEEDED); } } @Override protected Set getForbiddenActions() { return forbiddenActions; } @Override protected Set getAllowedNamedActions() { return allowedNamedActions; } @Override protected long getMaxNumberOfIndirectObjects() { return 8_388_607; } /** * {@inheritDoc} */ @Override protected void checkColorsUsages() { // Do not check anything here. All checks are in checkPageColorsUsages. } /** * {@inheritDoc} */ @Override protected void checkPageColorsUsages(PdfDictionary pageDict, PdfDictionary pageResources) { if ((rgbIsUsed || cmykIsUsed || grayIsUsed || !rgbUsedObjects.isEmpty() || !cmykUsedObjects.isEmpty() || grayUsedObjects.isEmpty()) && pdfAOutputIntentColorSpace == null) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.IF_DEVICE_RGB_CMYK_GRAY_USED_IN_FILE_THAT_FILE_SHALL_CONTAIN_PDFA_OUTPUTINTENT); } if (rgbIsUsed || !rgbUsedObjects.isEmpty()) { if (!ICC_COLOR_SPACE_RGB.equals(pdfAOutputIntentColorSpace)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.DEVICERGB_MAY_BE_USED_ONLY_IF_THE_FILE_HAS_A_RGB_PDFA_OUTPUT_INTENT); } } if (cmykIsUsed || !cmykUsedObjects.isEmpty()) { if (!ICC_COLOR_SPACE_CMYK.equals(pdfAOutputIntentColorSpace)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.DEVICECMYK_MAY_BE_USED_ONLY_IF_THE_FILE_HAS_A_CMYK_PDFA_OUTPUT_INTENT); } } } @Override public void checkExtGState(CanvasGraphicsState extGState, PdfStream contentStream) { if (extGState.getTransferFunction() != null) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.AN_EXTGSTATE_DICTIONARY_SHALL_NOT_CONTAIN_THE_TR_KEY); } PdfObject transferFunction2 = extGState.getTransferFunction2(); if (transferFunction2 != null && !PdfName.Default.equals(transferFunction2)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.AN_EXTGSTATE_DICTIONARY_SHALL_NOT_CONTAIN_THE_TR_2_KEY_WITH_A_VALUE_OTHER_THAN_DEFAULT); } checkRenderingIntent(extGState.getRenderingIntent()); PdfObject softMask = extGState.getSoftMask(); if (softMask != null && !PdfName.None.equals(softMask)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.THE_SMASK_KEY_IS_NOT_ALLOWED_IN_EXTGSTATE); } PdfObject bm = extGState.getBlendMode(); if (bm != null && !PdfName.Normal.equals(bm) && !PdfName.Compatible.equals(bm)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.BLEND_MODE_SHALL_HAVE_VALUE_NORMAL_OR_COMPATIBLE); } Float ca = extGState.getStrokeOpacity(); if (ca != null && ca != 1) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.TRANSPARENCY_IS_NOT_ALLOWED_CA_SHALL_BE_EQUAL_TO_1); } ca = extGState.getFillOpacity(); if (ca != null && ca != 1) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.TRANSPARENCY_IS_NOT_ALLOWED_AND_CA_SHALL_BE_EQUAL_TO_1); } } @Override public void checkFontGlyphs(PdfFont font, PdfStream contentStream) { // This check is irrelevant for the PdfA1 checker, so the body of the method is empty } @Override public void checkRenderingIntent(PdfName intent) { if (intent == null) return; if (!allowedRenderingIntents.contains(intent)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.IF_SPECIFIED_RENDERING_SHALL_BE_ONE_OF_THE_FOLLOWING_RELATIVECOLORIMETRIC_ABSOLUTECOLORIMETRIC_PERCEPTUAL_OR_SATURATION); } } @Override public void checkFont(PdfFont pdfFont) { if (!pdfFont.isEmbedded()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.ALL_THE_FONTS_MUST_BE_EMBEDDED_THIS_ONE_IS_NOT_0) .setMessageParams(pdfFont.getFontProgram().getFontNames().getFontName()); } if (pdfFont instanceof PdfTrueTypeFont) { PdfTrueTypeFont trueTypeFont = (PdfTrueTypeFont) pdfFont; boolean symbolic = trueTypeFont.getFontEncoding().isFontSpecific(); if (symbolic) { checkSymbolicTrueTypeFont(trueTypeFont); } else { checkNonSymbolicTrueTypeFont(trueTypeFont); } } if (pdfFont instanceof PdfType3Font) { PdfDictionary charProcs = pdfFont.getPdfObject().getAsDictionary(PdfName.CharProcs); for (PdfName charName : charProcs.keySet()) { checkContentStream(charProcs.getAsStream(charName)); } } } /** * {@inheritDoc} * * @param crypto {@inheritDoc} */ @Override public void checkCrypto(PdfObject crypto) { if (crypto != null) { throw new PdfAConformanceException( PdfaExceptionMessageConstant.KEYWORD_ENCRYPT_SHALL_NOT_BE_USED_IN_THE_TRAILER_DICTIONARY); } } /** * {@inheritDoc} */ @Override public void checkSignatureType(boolean isCAdES) { //nothing to do } /** * {@inheritDoc} * * @param text {@inheritDoc} * @param font {@inheritDoc} */ @Override public void checkText(String text, PdfFont font) { int index = FontCheckUtil.checkGlyphsOfText(text, font, new ACharacterChecker()); if (index != -1) { throw new PdfAConformanceException( PdfaExceptionMessageConstant.EMBEDDED_FONTS_SHALL_DEFINE_ALL_REFERENCED_GLYPHS); } } @Override protected void checkPageTransparency(PdfDictionary pageDict, PdfDictionary pageResources) { // This check is irrelevant for the PdfA1 checker, so the body of the method is empty } @Override protected void checkContentStream(PdfStream contentStream) { if (isFullCheckMode() || contentStream.isModified()) { byte[] contentBytes = contentStream.getBytes(); PdfTokenizer tokenizer = new PdfTokenizer( new RandomAccessFileOrArray(new RandomAccessSourceFactory().createSource(contentBytes))); PdfCanvasParser parser = new PdfCanvasParser(tokenizer); List operands = new ArrayList<>(); try { while (parser.parse(operands).size() > 0) { for (PdfObject operand : operands) { checkContentStreamObject(operand); } } } catch (IOException e) { throw new PdfException(PdfaExceptionMessageConstant.CANNOT_PARSE_CONTENT_STREAM, e); } } } @Override protected void checkNonSymbolicTrueTypeFont(PdfTrueTypeFont trueTypeFont) { String encoding = trueTypeFont.getFontEncoding().getBaseEncoding(); // non-symbolic true type font will always has an encoding entry in font dictionary in itext if (!PdfEncodings.WINANSI.equals(encoding) && !PdfEncodings.MACROMAN.equals(encoding) || trueTypeFont.getFontEncoding().hasDifferences()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.ALL_NON_SYMBOLIC_TRUE_TYPE_FONT_SHALL_SPECIFY_MAC_ROMAN_OR_WIN_ANSI_ENCODING_AS_THE_ENCODING_ENTRY, trueTypeFont); } } @Override protected void checkSymbolicTrueTypeFont(PdfTrueTypeFont trueTypeFont) { if (trueTypeFont.getFontEncoding().hasDifferences()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.ALL_SYMBOLIC_TRUE_TYPE_FONTS_SHALL_NOT_SPECIFY_ENCODING); } // if symbolic font encoding doesn't have differences, itext won't write encoding for such font } @Override protected void checkImage(PdfStream image, PdfDictionary currentColorSpaces) { PdfColorSpace colorSpace = null; if (isAlreadyChecked(image)) { colorSpace = checkedObjectsColorspace.get(image); checkColorSpace(colorSpace, image, currentColorSpaces, true, null); return; } PdfObject colorSpaceObj = image.get(PdfName.ColorSpace); if (colorSpaceObj != null) { colorSpace = PdfColorSpace.makeColorSpace(colorSpaceObj); checkColorSpace(colorSpace, image, currentColorSpaces, true, null); checkedObjectsColorspace.put(image, colorSpace); } if (image.containsKey(PdfName.Alternates)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.AN_IMAGE_DICTIONARY_SHALL_NOT_CONTAIN_ALTERNATES_KEY); } if (image.containsKey(PdfName.OPI)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.AN_IMAGE_DICTIONARY_SHALL_NOT_CONTAIN_OPI_KEY); } if (image.containsKey(PdfName.Interpolate) && (boolean) image.getAsBool(PdfName.Interpolate)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.THE_VALUE_OF_INTERPOLATE_KEY_SHALL_BE_FALSE); } checkRenderingIntent(image.getAsName(PdfName.Intent)); if (image.containsKey(PdfName.SMask) && !PdfName.None.equals(image.getAsName(PdfName.SMask))) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.THE_SMASK_KEY_IS_NOT_ALLOWED_IN_XOBJECTS); } } @Override protected void checkFormXObject(PdfStream form) { if (isAlreadyChecked(form)) return; if (form.containsKey(PdfName.OPI)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.A_FORM_XOBJECT_DICTIONARY_SHALL_NOT_CONTAIN_OPI_KEY); } if (form.containsKey(PdfName.PS)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.A_FORM_XOBJECT_DICTIONARY_SHALL_NOT_CONTAIN_PS_KEY); } if (PdfName.PS.equals(form.getAsName(PdfName.Subtype2))) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.A_FORM_XOBJECT_DICTIONARY_SHALL_NOT_CONTAIN_SUBTYPE2_KEY_WITH_A_VALUE_OF_PS); } if (form.containsKey(PdfName.SMask) && !PdfName.None.equals(form.getAsName(PdfName.SMask))) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.THE_SMASK_KEY_IS_NOT_ALLOWED_IN_XOBJECTS); } if (isContainsTransparencyGroup(form)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.A_GROUP_OBJECT_WITH_AN_S_KEY_WITH_A_VALUE_OF_TRANSPARENCY_SHALL_NOT_BE_INCLUDED_IN_A_FORM_XOBJECT); } checkResources(form.getAsDictionary(PdfName.Resources), form); checkContentStream(form); } @Override protected void checkLogicalStructure(PdfDictionary catalog) { if (checkStructure(conformanceLevel)) { PdfDictionary markInfo = catalog.getAsDictionary(PdfName.MarkInfo); if (markInfo == null || markInfo.getAsBoolean(PdfName.Marked) == null || !markInfo.getAsBoolean(PdfName.Marked).getValue()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.A_CATALOG_SHALL_INCLUDE_MARK_INFO_DICTIONARY_WITH_MARKED_TRUE_VALUE); } if (!catalog.containsKey(PdfName.Lang)) { logger.warn(PdfAConformanceLogMessageConstant.CATALOG_SHOULD_CONTAIN_LANG_ENTRY); } } } @Override protected void checkMetaData(PdfDictionary catalog) { if (!catalog.containsKey(PdfName.Metadata)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.A_CATALOG_DICTIONARY_SHALL_CONTAIN_METADATA_ENTRY); } } @Override protected void checkOutputIntents(PdfDictionary catalog) { PdfArray outputIntents = catalog.getAsArray(PdfName.OutputIntents); if (outputIntents == null) return; int i; PdfObject destOutputProfile = null; for (i = 0; i < outputIntents.size() && destOutputProfile == null; ++i) { destOutputProfile = outputIntents.getAsDictionary(i).get(PdfName.DestOutputProfile); } for (; i < outputIntents.size(); ++i) { PdfObject otherDestOutputProfile = outputIntents.getAsDictionary(i).get(PdfName.DestOutputProfile); if (otherDestOutputProfile != null && destOutputProfile != otherDestOutputProfile) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.IF_OUTPUTINTENTS_ARRAY_HAS_MORE_THAN_ONE_ENTRY_WITH_DESTOUTPUTPROFILE_KEY_THE_SAME_INDIRECT_OBJECT_SHALL_BE_USED_AS_THE_VALUE_OF_THAT_OBJECT); } } } @Override protected void checkPdfNumber(PdfNumber number) { if (number.hasDecimalPoint()) { if (Math.abs(number.longValue()) > getMaxRealValue()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.REAL_NUMBER_IS_OUT_OF_RANGE); } } else { if (number.longValue() > getMaxIntegerValue() || number.longValue() < getMinIntegerValue()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.INTEGER_NUMBER_IS_OUT_OF_RANGE); } } } /** * Retrieve maximum allowed real value. * @return maximum allowed real number */ protected double getMaxRealValue() { return 32767; } /** * Retrieve maximal allowed integer value. * @return maximal allowed integer number */ protected long getMaxIntegerValue() { return Integer.MAX_VALUE; } /** * Retrieve minimal allowed integer value. * @return minimal allowed integer number */ protected long getMinIntegerValue() { return Integer.MIN_VALUE; } @Override protected void checkPdfArray(PdfArray array) { if (array.size() > getMaxArrayCapacity()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.MAXIMUM_ARRAY_CAPACITY_IS_EXCEEDED); } } @Override protected void checkPdfDictionary(PdfDictionary dictionary) { if (dictionary.size() > getMaxDictionaryCapacity()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.MAXIMUM_DICTIONARY_CAPACITY_IS_EXCEEDED); } } @Override protected void checkPdfStream(PdfStream stream) { checkPdfDictionary(stream); if (stream.containsKey(PdfName.F) || stream.containsKey(PdfName.FFilter) || stream.containsKey(PdfName.FDecodeParams)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.STREAM_OBJECT_DICTIONARY_SHALL_NOT_CONTAIN_THE_F_FFILTER_OR_FDECODEPARAMS_KEYS); } PdfObject filter = stream.get(PdfName.Filter); if (filter instanceof PdfName) { if (filter.equals(PdfName.LZWDecode)) throw new PdfAConformanceException(PdfaExceptionMessageConstant.LZWDECODE_FILTER_IS_NOT_PERMITTED); } else if (filter instanceof PdfArray) { for (PdfObject f : ((PdfArray) filter)) { if (f.equals(PdfName.LZWDecode)) throw new PdfAConformanceException(PdfaExceptionMessageConstant.LZWDECODE_FILTER_IS_NOT_PERMITTED); } } } @Override protected void checkPdfName(PdfName name) { if (name.getValue().length() > getMaxNameLength()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.PDF_NAME_IS_TOO_LONG); } } /** * Retrieve maximum allowed length of the name object. * * @return maximum allowed length of the name */ protected int getMaxNameLength() { return 127; } @Override protected void checkPdfString(PdfString string) { if (string.getValueBytes().length > getMaxStringLength()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.PDF_STRING_IS_TOO_LONG); } } /** * Returns maximum allowed bytes length of the string in a PDF document. * * @return maximum string length */ protected int getMaxStringLength() { return 65535; } @Override protected void checkPageSize(PdfDictionary page) { } @Override protected void checkFileSpec(PdfDictionary fileSpec) { if (fileSpec.containsKey(PdfName.EF)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.FILE_SPECIFICATION_DICTIONARY_SHALL_NOT_CONTAIN_THE_EF_KEY); } } /** * {@inheritDoc} */ @Override protected void checkAnnotation(PdfDictionary annotDic) { PdfName subtype = annotDic.getAsName(PdfName.Subtype); if (subtype == null) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.ANNOTATION_TYPE_0_IS_NOT_PERMITTED).setMessageParams("null"); } if (getForbiddenAnnotations().contains(subtype)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.ANNOTATION_TYPE_0_IS_NOT_PERMITTED).setMessageParams(subtype.getValue()); } PdfNumber ca = annotDic.getAsNumber(PdfName.CA); if (ca != null && ca.floatValue() != 1.0) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.AN_ANNOTATION_DICTIONARY_SHALL_NOT_CONTAIN_THE_CA_KEY_WITH_A_VALUE_OTHER_THAN_1); } if (!annotDic.containsKey(PdfName.F)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.AN_ANNOTATION_DICTIONARY_SHALL_CONTAIN_THE_F_KEY); } int flags = (int) annotDic.getAsInt(PdfName.F); if (!checkFlag(flags, PdfAnnotation.PRINT) || checkFlag(flags, PdfAnnotation.HIDDEN) || checkFlag(flags, PdfAnnotation.INVISIBLE) || checkFlag(flags, PdfAnnotation.NO_VIEW)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.THE_F_KEYS_PRINT_FLAG_BIT_SHALL_BE_SET_TO_1_AND_ITS_HIDDEN_INVISIBLE_AND_NOVIEW_FLAG_BITS_SHALL_BE_SET_TO_0); } if (subtype.equals(PdfName.Text) && (!checkFlag(flags, PdfAnnotation.NO_ZOOM) || !checkFlag(flags, PdfAnnotation.NO_ROTATE))) { throw new PdfAConformanceException(PdfAConformanceLogMessageConstant.TEXT_ANNOTATIONS_SHOULD_SET_THE_NOZOOM_AND_NOROTATE_FLAG_BITS_OF_THE_F_KEY_TO_1); } if (annotDic.containsKey(PdfName.C) || annotDic.containsKey(PdfName.IC)) { if (!ICC_COLOR_SPACE_RGB.equals(pdfAOutputIntentColorSpace)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.DESTOUTPUTPROFILE_IN_THE_PDFA1_OUTPUTINTENT_DICTIONARY_SHALL_BE_RGB); } } PdfDictionary ap = annotDic.getAsDictionary(PdfName.AP); if (ap != null) { if (ap.containsKey(PdfName.D) || ap.containsKey(PdfName.R)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.APPEARANCE_DICTIONARY_SHALL_CONTAIN_ONLY_THE_N_KEY_WITH_STREAM_VALUE); } if (PdfName.Widget.equals(annotDic.getAsName(PdfName.Subtype)) && (PdfName.Btn.equals(PdfFormField.getFormType(annotDic)))) { if (ap.getAsDictionary(PdfName.N) == null) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.N_KEY_SHALL_BE_APPEARANCE_SUBDICTIONARY); } } else { if (ap.getAsStream(PdfName.N) == null) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.APPEARANCE_DICTIONARY_SHALL_CONTAIN_ONLY_THE_N_KEY_WITH_STREAM_VALUE); } } checkResourcesOfAppearanceStreams(ap); } if (PdfName.Widget.equals(subtype) && (annotDic.containsKey(PdfName.AA) || annotDic.containsKey(PdfName.A))) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.WIDGET_ANNOTATION_DICTIONARY_OR_FIELD_DICTIONARY_SHALL_NOT_INCLUDE_A_OR_AA_ENTRY); } if (annotDic.containsKey(PdfName.AA)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.AN_ANNOTATION_DICTIONARY_SHALL_NOT_CONTAIN_AA_KEY); } if (checkStructure(conformanceLevel)) { if (contentAnnotations.contains(subtype) && !annotDic.containsKey(PdfName.Contents)) { logger.warn(MessageFormatUtil.format( PdfAConformanceLogMessageConstant.ANNOTATION_OF_TYPE_0_SHOULD_HAVE_CONTENTS_KEY, subtype.getValue())); } } } /** * Gets forbidden annotation types. * * @return a set of forbidden annotation types */ protected Set getForbiddenAnnotations() { return forbiddenAnnotations; } @Override protected void checkForm(PdfDictionary form) { if (form == null) return; PdfBoolean needAppearances = form.getAsBoolean(PdfName.NeedAppearances); if (needAppearances != null && needAppearances.getValue()) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.NEEDAPPEARANCES_FLAG_OF_THE_INTERACTIVE_FORM_DICTIONARY_SHALL_EITHER_NOT_BE_PRESENTED_OR_SHALL_BE_FALSE); } checkResources(form.getAsDictionary(PdfName.DR), form); PdfArray fields = form.getAsArray(PdfName.Fields); if (fields != null) { fields = getFormFields(fields); for (PdfObject field : fields) { PdfDictionary fieldDic = (PdfDictionary) field; if (fieldDic.containsKey(PdfName.A) || fieldDic.containsKey(PdfName.AA)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.WIDGET_ANNOTATION_DICTIONARY_OR_FIELD_DICTIONARY_SHALL_NOT_INCLUDE_A_OR_AA_ENTRY); } checkResources(fieldDic.getAsDictionary(PdfName.DR), fieldDic); } } } @Override protected void checkAction(PdfDictionary action) { if (isAlreadyChecked(action)) return; PdfName s = action.getAsName(PdfName.S); if (getForbiddenActions().contains(s)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant._0_ACTIONS_ARE_NOT_ALLOWED).setMessageParams(s.getValue()); } if (s.equals(PdfName.Named)) { PdfName n = action.getAsName(PdfName.N); if (n != null && !getAllowedNamedActions().contains(n)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.NAMED_ACTION_TYPE_0_IS_NOT_ALLOWED).setMessageParams(n.getValue()); } } if (s.equals(PdfName.SetState) || s.equals(PdfName.NoOp)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.DEPRECATED_SETSTATE_AND_NOOP_ACTIONS_ARE_NOT_ALLOWED); } } /** * {@inheritDoc} */ @Override protected void checkCatalog(PdfCatalog catalog) { String pdfVersion = catalog.getDocument().getPdfVersion().toString(); if ('1' != pdfVersion.charAt(4) || ('1' > pdfVersion.charAt(6) || '7' < pdfVersion.charAt(6))) { throw new PdfAConformanceException( MessageFormatUtil.format(PdfaExceptionMessageConstant.THE_FILE_HEADER_SHALL_CONTAIN_RIGHT_PDF_VERSION, "1")); } } @Override protected void checkCatalogValidEntries(PdfDictionary catalogDict) { if (catalogDict.containsKey(PdfName.AA)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.A_CATALOG_DICTIONARY_SHALL_NOT_CONTAIN_AA_ENTRY); } if (catalogDict.containsKey(PdfName.OCProperties)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.A_CATALOG_DICTIONARY_SHALL_NOT_CONTAIN_OCPROPERTIES_KEY); } if (catalogDict.containsKey(PdfName.Names)) { if (catalogDict.getAsDictionary(PdfName.Names).containsKey(PdfName.EmbeddedFiles)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.A_NAME_DICTIONARY_SHALL_NOT_CONTAIN_THE_EMBEDDED_FILES_KEY); } } } @Override protected void checkPageObject(PdfDictionary pageDict, PdfDictionary pageResources) { PdfDictionary actions = pageDict.getAsDictionary(PdfName.AA); if (actions != null) { for (PdfName key : actions.keySet()) { PdfDictionary action = actions.getAsDictionary(key); checkAction(action); } } if (isContainsTransparencyGroup(pageDict)) { throw new PdfAConformanceException(PdfaExceptionMessageConstant.A_GROUP_OBJECT_WITH_AN_S_KEY_WITH_A_VALUE_OF_TRANSPARENCY_SHALL_NOT_BE_INCLUDED_IN_A_PAGE_XOBJECT); } } @Override protected void checkTrailer(PdfDictionary trailer) { } /** * Gets a {@link PdfArray} of fields with kids from a {@link PdfArray} of {@link PdfDictionary} objects. * * @param array the {@link PdfArray} of form fields {@link PdfDictionary} objects * * @return the {@link PdfArray} of form fields */ protected PdfArray getFormFields(PdfArray array) { PdfArray fields = new PdfArray(); for (PdfObject field : array) { PdfArray kids = ((PdfDictionary) field).getAsArray(PdfName.Kids); fields.add(field); if (kids != null) { fields.addAll(getFormFields(kids)); } } return fields; } private int getMaxArrayCapacity() { return 8191; } private int getMaxDictionaryCapacity() { return 4095; } private static final class ACharacterChecker implements FontCheckUtil.CharacterChecker { @Override public boolean check(int ch, PdfFont font) { return !font.containsGlyph(ch); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy