/***************************************************************************** * * 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.padaf.preflight.helpers; import java.util.ArrayList; import java.util.List; import org.apache.padaf.preflight.DocumentHandler; import org.apache.padaf.preflight.ValidationException; import org.apache.padaf.preflight.ValidatorConfig; import org.apache.padaf.preflight.ValidationResult.ValidationError; import org.apache.padaf.preflight.actions.AbstractActionManager; import org.apache.padaf.preflight.utils.COSUtils; import org.apache.pdfbox.cos.COSBase; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSDocument; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; /** * This helper validates the book mark object (Outline Items) */ public class BookmarkValidationHelper extends AbstractValidationHelper { /** * * @param cfg * @throws ValidationException */ public BookmarkValidationHelper(ValidatorConfig cfg) throws ValidationException { super(cfg); } /* * (non-Javadoc) * * @see * net.awl.edoc.pdfa.validation.helpers.AbstractValidationHelper#innerValidate * (net.awl.edoc.pdfa.validation.DocumentHandler) */ @Override public List<ValidationError> innerValidate(DocumentHandler handler) throws ValidationException { List<ValidationError> result = new ArrayList<ValidationError>(0); PDDocumentCatalog catalog = handler.getDocument().getDocumentCatalog(); if (catalog != null) { PDDocumentOutline outlineHierarchy = catalog.getDocumentOutline(); if (outlineHierarchy != null) { // ---- Count entry is mandatory if there are childrens if (!isCountEntryPresent(outlineHierarchy.getCOSDictionary()) && (outlineHierarchy.getFirstChild() != null || outlineHierarchy.getLastChild() != null) ) { result.add(new ValidationError( ERROR_SYNTAX_TRAILER_OUTLINES_INVALID, "Outline Hierarchy doesn't have Count entry")); } else if (isCountEntryPositive(outlineHierarchy.getCOSDictionary(), handler.getDocument().getDocument()) && (outlineHierarchy.getFirstChild() == null || outlineHierarchy.getLastChild() == null)) { result.add(new ValidationError(ERROR_SYNTAX_TRAILER_OUTLINES_INVALID, "Outline Hierarchy doesn't have First and/or Last entry(ies)")); } else { exploreOutlineLevel(outlineHierarchy.getFirstChild(), handler, result); } } } else { throw new ValidationException( "There are no Catalog entry in the Document."); } return result; } /** * Return true if the Count entry is present in the given dictionary. * * @param outline * @return */ private boolean isCountEntryPresent(COSDictionary outline) { return outline.getItem(COSName.getPDFName("Count")) != null; } /** * return true if Count entry > 0 * @param outline * @param doc * @return */ private boolean isCountEntryPositive(COSDictionary outline, COSDocument doc) { COSBase countBase = outline.getItem(COSName.getPDFName("Count")); return COSUtils.isInteger(countBase, doc) && (COSUtils.getAsInteger(countBase, doc)>0); } /** * This method explores the Outline Item Level and call a validation method on * each Outline Item. If an invalid outline item is found, the result list is * updated. * * @param inputItem * The first outline item of the level * @param handler * The document handler which provides useful data for the level * exploration (ex : access to the PDDocument) * @param result * @return true if all items are valid in this level. * @throws ValidationException */ protected boolean exploreOutlineLevel(PDOutlineItem inputItem, DocumentHandler handler, List<ValidationError> result) throws ValidationException { PDOutlineItem currentItem = inputItem; while (currentItem != null) { if (!validateItem(currentItem, handler, result)) { return false; } currentItem = currentItem.getNextSibling(); } return true; } /** * This method checks the inputItem dictionary and call the * exploreOutlineLevel method on the first child if it is not null. * * @param inputItem * outline item to validate * @param handler * The document handler which provides useful data for the level * exploration (ex : access to the PDDocument) * @param result * @return * @throws ValidationException */ protected boolean validateItem(PDOutlineItem inputItem, DocumentHandler handler, List<ValidationError> result) throws ValidationException { boolean isValid = true; // ---- Dest entry isn't permitted if the A entry is present // A entry isn't permitted if the Dest entry is present // If the A enntry is present, the referenced actions is validated COSDictionary dictionary = inputItem.getCOSDictionary(); COSBase dest = dictionary.getItem(COSName .getPDFName(DICTIONARY_KEY_DESTINATION)); COSBase action = dictionary.getItem(COSName .getPDFName(DICTIONARY_KEY_ACTION)); if (action != null && dest != null) { result.add(new ValidationError(ERROR_SYNTAX_TRAILER_OUTLINES_INVALID, "Dest entry isn't permitted if the A entry is present")); return false; } else if (action != null) { List<AbstractActionManager> actions = this.actionFact.getActions(dictionary, handler .getDocument().getDocument()); for (AbstractActionManager act : actions) { isValid = isValid && act.valid(result); } } // else no specific validation // ---- check children PDOutlineItem fChild = inputItem.getFirstChild(); if (fChild != null) { if (!isCountEntryPresent(inputItem.getCOSDictionary())) { result .add(new ValidationError(ERROR_SYNTAX_TRAILER_OUTLINES_INVALID, "Outline item doesn't have Count entry but has at least one descendant.")); isValid = false; } else { // ---- there are some descendants, so dictionary must have a Count // entry isValid = isValid && exploreOutlineLevel(fChild, handler, result); } } return isValid; } }