You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by gb...@apache.org on 2013/03/06 17:46:37 UTC

svn commit: r1453416 [12/16] - in /pdfbox/trunk/preflight: ./ src/main/java/org/apache/pdfbox/preflight/ src/main/java/org/apache/pdfbox/preflight/action/ src/main/java/org/apache/pdfbox/preflight/annotation/ src/main/java/org/apache/pdfbox/preflight/a...

Modified: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/CatalogValidationProcess.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/CatalogValidationProcess.java?rev=1453416&r1=1453415&r2=1453416&view=diff
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/CatalogValidationProcess.java (original)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/CatalogValidationProcess.java Wed Mar  6 16:46:35 2013
@@ -121,307 +121,345 @@ import org.apache.pdfbox.preflight.utils
 /**
  * This ValidationProcess check if the Catalog entries are confirming with the PDF/A-1b specification.
  */
-public class CatalogValidationProcess extends AbstractProcess {
+public class CatalogValidationProcess extends AbstractProcess
+{
 
-	protected PDDocumentCatalog catalog;
+    protected PDDocumentCatalog catalog;
 
-	protected List<String> listICC = new ArrayList<String>();
+    protected List<String> listICC = new ArrayList<String>();
 
-	public CatalogValidationProcess() {
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA43);
-		listICC.add(ICC_Characterization_Data_Registry_CGATS_TR_006);
-		listICC.add(ICC_Characterization_Data_Registry_CGATS_TR006);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA39);
-		listICC.add(ICC_Characterization_Data_Registry_JC200103);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA27);
-		listICC.add(ICC_Characterization_Data_Registry_EUROSB104);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA45);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA46);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA41);
-		listICC.add(ICC_Characterization_Data_Registry_CGATS_TR_001);
-		listICC.add(ICC_Characterization_Data_Registry_CGATS_TR_003);
-		listICC.add(ICC_Characterization_Data_Registry_CGATS_TR_005);
-		listICC.add(ICC_Characterization_Data_Registry_CGATS_TR001);
-		listICC.add(ICC_Characterization_Data_Registry_CGATS_TR003);
-		listICC.add(ICC_Characterization_Data_Registry_CGATS_TR005);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA28);
-		listICC.add(ICC_Characterization_Data_Registry_JCW2003);
-		listICC.add(ICC_Characterization_Data_Registry_EUROSB204);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA47);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA44);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA29);
-		listICC.add(ICC_Characterization_Data_Registry_JC200104);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA40);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA30);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA42);
-		listICC.add(ICC_Characterization_Data_Registry_IFRA26);
-		listICC.add(ICC_Characterization_Data_Registry_JCN2002);
-		listICC.add(ICC_Characterization_Data_Registry_CGATS_TR_002);
-		listICC.add(ICC_Characterization_Data_Registry_CGATS_TR002);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA33);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA37);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA31);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA35);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA32);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA34);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA36);
-		listICC.add(ICC_Characterization_Data_Registry_FOGRA38);
-		listICC.add(ICC_Characterization_Data_Registry_sRGB);
-		listICC.add(ICC_Characterization_Data_Registry_sRGB_IEC);
-		listICC.add(ICC_Characterization_Data_Registry_Adobe);
-		listICC.add(ICC_Characterization_Data_Registry_bg_sRGB);
-		listICC.add(ICC_Characterization_Data_Registry_sYCC);
-		listICC.add(ICC_Characterization_Data_Registry_scRGB);
-		listICC.add(ICC_Characterization_Data_Registry_scRGB_nl);
-		listICC.add(ICC_Characterization_Data_Registry_scYCC_nl);
-		listICC.add(ICC_Characterization_Data_Registry_ROMM);
-		listICC.add(ICC_Characterization_Data_Registry_RIMM);
-		listICC.add(ICC_Characterization_Data_Registry_ERIMM);
-		listICC.add(ICC_Characterization_Data_Registry_eciRGB);
-		listICC.add(ICC_Characterization_Data_Registry_opRGB);
-	}
-
-	protected boolean isStandardICCCharacterization(String name) {
-		for (String iccStandard : listICC) {
-			if (iccStandard.contains(name)) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	public void validate(PreflightContext ctx) throws ValidationException {
-		PDDocument pdfbox = ctx.getDocument();
-		this.catalog = pdfbox.getDocumentCatalog();
-
-		if (this.catalog == null) {
-			new ValidationError(ERROR_SYNTAX_NOCATALOG, "There are no Catalog entry in the Document.");
-			throw new ValidationException("There are no Catalog entry in the Document.");
-		}
-
-		validateActions(ctx);
-		validateLang(ctx);
-		validateNames(ctx);
-		validateOCProperties(ctx);
-		validateOutputIntent(ctx);
-	}
-
-	/**
-	 * This method validates if OpenAction entry contains forbidden action type.
-	 * It checks too if an Additional Action is present.
-	 * 
-	 * @param ctx
-	 * @throws ValidationException Propagate the ActionManager exception
-	 */
-	protected void validateActions(PreflightContext ctx) throws ValidationException {
-		ContextHelper.validateElement(ctx, catalog.getCOSDictionary(), ACTIONS_PROCESS);
-		// 	AA entry if forbidden in PDF/A-1
-		COSBase aa = catalog.getCOSDictionary().getItem(DICTIONARY_KEY_ADDITIONAL_ACTION);
-		if (aa != null) {
-			addValidationError(ctx, new ValidationError(ERROR_ACTION_FORBIDDEN_ADDITIONAL_ACTION, "The AA field is forbidden for the Catalog  when the PDF is a PDF/A"));
-		}
-	}
-
-	/**
-	 * The Lang element is optional but it is recommended. This method check the
-	 * Syntax of the Lang if this entry is present.
-	 * 
-	 * @param ctx
-	 * @throws ValidationException
-	 */
-	protected void validateLang(PreflightContext ctx) throws ValidationException {
-		String lang = catalog.getLanguage();
-		if (lang != null && !"".equals(lang) && !lang.matches("[A-Za-z]{1,8}(-[A-Za-z]{1,8})*")) {
-			addValidationError(ctx, new ValidationError(ERROR_SYNTAX_LANG_NOT_RFC1766));
-		}
-	}
-
-	/**
-	 * A Catalog shall not contain the EmbeddedFiles entry.
-	 * 
-	 * @param ctx
-	 * @throws ValidationException
-	 */
-	protected void validateNames(PreflightContext ctx) throws ValidationException {
-		PDDocumentNameDictionary names = catalog.getNames();
-		if (names != null) {
-			PDEmbeddedFilesNameTreeNode efs = names.getEmbeddedFiles();
-			if (efs != null) {
-				addValidationError(ctx, 
-						new ValidationError(ERROR_SYNTAX_TRAILER_CATALOG_EMBEDDEDFILES,
-								"EmbeddedFile entry is present in the Names dictionary"));
-			}
-		}
-	}
-
-	/**
-	 * A Catalog shall not contain the OCPProperties (Optional Content Properties) entry.
-	 * 
-	 * @param ctx
-	 * @throws ValidationException
-	 */
-	protected void validateOCProperties(PreflightContext ctx) throws ValidationException {
-		if (catalog.getOCProperties() != null) {
-			addValidationError(ctx, 
-					new ValidationError(ERROR_SYNTAX_TRAILER_CATALOG_OCPROPERTIES, 
-							"A Catalog shall not contain the OCPProperties entry."));
-		}
-	}
-
-	/**
-	 * This method checks the content of each OutputIntent. The S entry must
-	 * contain GTS_PDFA1. The DestOuputProfile must contain a valid ICC Profile
-	 * Stream.
-	 * 
-	 * If there are more than one OutputIntent, they have to use the same ICC
-	 * Profile.
-	 * 
-	 * This method returns a list of ValidationError. It is empty if no errors
-	 * have been found.
-	 * 
-	 * @param ctx
-	 * @throws ValidationException
-	 */
-	public void validateOutputIntent(PreflightContext ctx) throws ValidationException {
-		COSDocument cosDocument = ctx.getDocument().getDocument();
-		COSBase cBase = catalog.getCOSDictionary().getItem(COSName.getPDFName(DOCUMENT_DICTIONARY_KEY_OUTPUT_INTENTS));
-		COSArray outputIntents = COSUtils.getAsArray(cBase, cosDocument);
-
-		Map<COSObjectKey, Boolean> tmpDestOutputProfile = new HashMap<COSObjectKey, Boolean>();
-		for (int i = 0; outputIntents != null && i < outputIntents.size(); ++i) {
-			COSDictionary outputIntentDict = COSUtils.getAsDictionary(outputIntents.get(i), cosDocument);
-
-			if (outputIntentDict == null) {
-				addValidationError(ctx, 
-						new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_INVALID_ENTRY,
-								"OutputIntent object is null or isn't a dictionary"));
-			} else {
-				// S entry is mandatory and must be equals to GTS_PDFA1
-				String sValue = outputIntentDict.getNameAsString(OUTPUT_INTENT_DICTIONARY_KEY_S);
-				if (!OUTPUT_INTENT_DICTIONARY_VALUE_GTS_PDFA1.equals(sValue)) {
-					addValidationError(ctx, 
-							new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_S_VALUE_INVALID,
-									"The S entry of the OutputIntent isn't GTS_PDFA1"));
-					continue;
-				}
-
-				// OutputConditionIdentifier is a mandatory field
-				String outputConditionIdentifier = outputIntentDict.getString(OUTPUT_INTENT_DICTIONARY_KEY_OUTPUT_CONDITION_IDENTIFIER);
-				if (outputConditionIdentifier == null) {// empty string is authorized (it may be an application specific value)
-					addValidationError(ctx, 
-							new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_INVALID_ENTRY,
-									"The OutputIntentCondition is missing"));
-					continue;
-				}
-
-
-				/* If OutputConditionIdentifier is "Custom" or a non Standard ICC Characterization : 
-				 * DestOutputProfile and Info are mandatory  
-				 * DestOutputProfile must be a ICC Profile
-				 * 
-				 * Because of PDF/A conforming file needs to specify the color characteristics, the DestOutputProfile
-				 * is checked even if the OutputConditionIdentifier isn't "Custom"
-				 */
-				COSBase destOutputProfile = outputIntentDict.getItem(OUTPUT_INTENT_DICTIONARY_KEY_DEST_OUTPUT_PROFILE);
-				validateICCProfile(destOutputProfile, tmpDestOutputProfile, ctx);
-
-				PreflightConfiguration config = ctx.getConfig();
-				if (config.isLazyValidation() && !isStandardICCCharacterization(outputConditionIdentifier)) {
-					String info = outputIntentDict.getString(COSName.getPDFName(OUTPUT_INTENT_DICTIONARY_KEY_INFO));
-					if (info == null || "".equals(info)) {
-						ValidationError error = new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_INVALID_ENTRY, "The Info entry of a OutputIntent dictionary is missing");
-						error.setWarning(true);
-						addValidationError(ctx, error);
-						continue;
-					}
-				}
-			}
-		}
-	}
-
-
-	/**
-	 * This method checks the destOutputProfile which must be a valid ICCProfile.
-	 * 
-	 * If an other ICCProfile exists in the mapDestOutputProfile, a
-	 * ValdiationError (ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_MULTIPLE) is
-	 * returned because of only one profile is authorized. If the ICCProfile
-	 * already exist in the mapDestOutputProfile, the method returns null. If the
-	 * destOutputProfile contains an invalid ICCProfile, a ValidationError
-	 * (ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_INVALID) is returned If the
-	 * destOutputProfile is an empty stream, a
-	 * ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_INVALID_ENTRY) is returned.
-	 * 
-	 * If the destOutputFile is valid, mapDestOutputProfile is updated, the
-	 * ICCProfile is added to the document ctx and null is returned.
-	 * 
-	 * @param destOutputProfile
-	 * @param tmpDestOutputProfile
-	 * @param ctx
-	 * @return
-	 * @throws ValidationException
-	 */
-	protected void validateICCProfile(COSBase destOutputProfile, Map<COSObjectKey, Boolean> mapDestOutputProfile,
-			PreflightContext ctx) throws ValidationException {
-		try {
-			if (destOutputProfile == null) {
-				return ;
-			}
-
-			// destOutputProfile should be an instance of COSObject because of this is a object reference
-			if (destOutputProfile instanceof COSObject) {
-				if (mapDestOutputProfile.containsKey(new COSObjectKey((COSObject) destOutputProfile))) {
-					// the profile is already checked. continue
-					return ;
-				} else if (!mapDestOutputProfile.isEmpty()) {
-					// A DestOutputProfile exits but it isn't the same, error
-					addValidationError(ctx,	new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_MULTIPLE, "More than one ICCProfile is defined"));
-					return;
-				} 
-				// else  the profile will be kept in the tmpDestOutputProfile if it is valid
-			}
-
-			// keep reference to avoid multiple profile definition
-			mapDestOutputProfile.put(new COSObjectKey((COSObject) destOutputProfile), true);
-			COSDocument cosDocument = ctx.getDocument().getDocument();
-			PDStream stream = PDStream.createFromCOS(COSUtils.getAsStream(destOutputProfile, cosDocument));
-			if (stream == null) {
-				addValidationError(ctx,	new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_INVALID_ENTRY, "OutputIntent object uses a NULL Object"));
-				return;
-			}
-
-			ICC_Profile iccp = ICC_Profile.getInstance(stream.getByteArray());
-			PreflightConfiguration config = ctx.getConfig();
-			// check the ICC Profile version (6.2.2)
-			if (iccp.getMajorVersion() == 2) {
-				if (iccp.getMinorVersion() > 0x40) {
-					// in PDF 1.4, max version is 02h.40h (meaning V 3.5)
-					// see the ICCProfile specification (ICC.1:1998-09)page 13 - §6.1.3 : 
-					// The current profile version number is "2.4.0" (encoded as 02400000h")
-					ValidationError error = new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_TOO_RECENT, "Invalid version of the ICCProfile");
-					error.setWarning(config.isLazyValidation());
-					addValidationError(ctx, error);
-					return ;
-				} // else OK
-			} else if (iccp.getMajorVersion() > 2) {
-				// in PDF 1.4, max version is 02h.40h (meaning V 3.5)
-				// see the ICCProfile specification (ICC.1:1998-09)page 13 - §6.1.3 : 
-				// The current profile version number is "2.4.0" (encoded as 02400000h"
-				ValidationError error =  new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_TOO_RECENT, "Invalid version of the ICCProfile");
-				error.setWarning(config.isLazyValidation());
-				addValidationError(ctx, error);
-				return ;
-			} // else seems less than 2, so correct
-
-			if (ctx.getIccProfileWrapper() == null) {
-				ctx.setIccProfileWrapper(new ICCProfileWrapper(iccp));
-			}
-
-		} catch (IllegalArgumentException e) {
-			// this is not a ICC_Profile
-			addValidationError(ctx, new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_INVALID, "DestOutputProfile isn't a ICCProfile"));
-		} catch (IOException e) {
-			throw new ValidationException("Unable to parse the ICC Profile", e);
-		}
-	}
+    public CatalogValidationProcess()
+    {
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA43);
+        listICC.add(ICC_Characterization_Data_Registry_CGATS_TR_006);
+        listICC.add(ICC_Characterization_Data_Registry_CGATS_TR006);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA39);
+        listICC.add(ICC_Characterization_Data_Registry_JC200103);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA27);
+        listICC.add(ICC_Characterization_Data_Registry_EUROSB104);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA45);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA46);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA41);
+        listICC.add(ICC_Characterization_Data_Registry_CGATS_TR_001);
+        listICC.add(ICC_Characterization_Data_Registry_CGATS_TR_003);
+        listICC.add(ICC_Characterization_Data_Registry_CGATS_TR_005);
+        listICC.add(ICC_Characterization_Data_Registry_CGATS_TR001);
+        listICC.add(ICC_Characterization_Data_Registry_CGATS_TR003);
+        listICC.add(ICC_Characterization_Data_Registry_CGATS_TR005);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA28);
+        listICC.add(ICC_Characterization_Data_Registry_JCW2003);
+        listICC.add(ICC_Characterization_Data_Registry_EUROSB204);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA47);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA44);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA29);
+        listICC.add(ICC_Characterization_Data_Registry_JC200104);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA40);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA30);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA42);
+        listICC.add(ICC_Characterization_Data_Registry_IFRA26);
+        listICC.add(ICC_Characterization_Data_Registry_JCN2002);
+        listICC.add(ICC_Characterization_Data_Registry_CGATS_TR_002);
+        listICC.add(ICC_Characterization_Data_Registry_CGATS_TR002);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA33);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA37);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA31);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA35);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA32);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA34);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA36);
+        listICC.add(ICC_Characterization_Data_Registry_FOGRA38);
+        listICC.add(ICC_Characterization_Data_Registry_sRGB);
+        listICC.add(ICC_Characterization_Data_Registry_sRGB_IEC);
+        listICC.add(ICC_Characterization_Data_Registry_Adobe);
+        listICC.add(ICC_Characterization_Data_Registry_bg_sRGB);
+        listICC.add(ICC_Characterization_Data_Registry_sYCC);
+        listICC.add(ICC_Characterization_Data_Registry_scRGB);
+        listICC.add(ICC_Characterization_Data_Registry_scRGB_nl);
+        listICC.add(ICC_Characterization_Data_Registry_scYCC_nl);
+        listICC.add(ICC_Characterization_Data_Registry_ROMM);
+        listICC.add(ICC_Characterization_Data_Registry_RIMM);
+        listICC.add(ICC_Characterization_Data_Registry_ERIMM);
+        listICC.add(ICC_Characterization_Data_Registry_eciRGB);
+        listICC.add(ICC_Characterization_Data_Registry_opRGB);
+    }
+
+    protected boolean isStandardICCCharacterization(String name)
+    {
+        for (String iccStandard : listICC)
+        {
+            if (iccStandard.contains(name))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void validate(PreflightContext ctx) throws ValidationException
+    {
+        PDDocument pdfbox = ctx.getDocument();
+        this.catalog = pdfbox.getDocumentCatalog();
+
+        if (this.catalog == null)
+        {
+            new ValidationError(ERROR_SYNTAX_NOCATALOG, "There are no Catalog entry in the Document.");
+            throw new ValidationException("There are no Catalog entry in the Document.");
+        }
+
+        validateActions(ctx);
+        validateLang(ctx);
+        validateNames(ctx);
+        validateOCProperties(ctx);
+        validateOutputIntent(ctx);
+    }
+
+    /**
+     * This method validates if OpenAction entry contains forbidden action type. It checks too if an Additional Action
+     * is present.
+     * 
+     * @param ctx
+     * @throws ValidationException
+     *             Propagate the ActionManager exception
+     */
+    protected void validateActions(PreflightContext ctx) throws ValidationException
+    {
+        ContextHelper.validateElement(ctx, catalog.getCOSDictionary(), ACTIONS_PROCESS);
+        // AA entry if forbidden in PDF/A-1
+        COSBase aa = catalog.getCOSDictionary().getItem(DICTIONARY_KEY_ADDITIONAL_ACTION);
+        if (aa != null)
+        {
+            addValidationError(ctx, new ValidationError(ERROR_ACTION_FORBIDDEN_ADDITIONAL_ACTION,
+                    "The AA field is forbidden for the Catalog  when the PDF is a PDF/A"));
+        }
+    }
+
+    /**
+     * The Lang element is optional but it is recommended. This method check the Syntax of the Lang if this entry is
+     * present.
+     * 
+     * @param ctx
+     * @throws ValidationException
+     */
+    protected void validateLang(PreflightContext ctx) throws ValidationException
+    {
+        String lang = catalog.getLanguage();
+        if (lang != null && !"".equals(lang) && !lang.matches("[A-Za-z]{1,8}(-[A-Za-z]{1,8})*"))
+        {
+            addValidationError(ctx, new ValidationError(ERROR_SYNTAX_LANG_NOT_RFC1766));
+        }
+    }
+
+    /**
+     * A Catalog shall not contain the EmbeddedFiles entry.
+     * 
+     * @param ctx
+     * @throws ValidationException
+     */
+    protected void validateNames(PreflightContext ctx) throws ValidationException
+    {
+        PDDocumentNameDictionary names = catalog.getNames();
+        if (names != null)
+        {
+            PDEmbeddedFilesNameTreeNode efs = names.getEmbeddedFiles();
+            if (efs != null)
+            {
+                addValidationError(ctx, new ValidationError(ERROR_SYNTAX_TRAILER_CATALOG_EMBEDDEDFILES,
+                        "EmbeddedFile entry is present in the Names dictionary"));
+            }
+        }
+    }
+
+    /**
+     * A Catalog shall not contain the OCPProperties (Optional Content Properties) entry.
+     * 
+     * @param ctx
+     * @throws ValidationException
+     */
+    protected void validateOCProperties(PreflightContext ctx) throws ValidationException
+    {
+        if (catalog.getOCProperties() != null)
+        {
+            addValidationError(ctx, new ValidationError(ERROR_SYNTAX_TRAILER_CATALOG_OCPROPERTIES,
+                    "A Catalog shall not contain the OCPProperties entry."));
+        }
+    }
+
+    /**
+     * This method checks the content of each OutputIntent. The S entry must contain GTS_PDFA1. The DestOuputProfile
+     * must contain a valid ICC Profile Stream.
+     * 
+     * If there are more than one OutputIntent, they have to use the same ICC Profile.
+     * 
+     * This method returns a list of ValidationError. It is empty if no errors have been found.
+     * 
+     * @param ctx
+     * @throws ValidationException
+     */
+    public void validateOutputIntent(PreflightContext ctx) throws ValidationException
+    {
+        COSDocument cosDocument = ctx.getDocument().getDocument();
+        COSBase cBase = catalog.getCOSDictionary().getItem(COSName.getPDFName(DOCUMENT_DICTIONARY_KEY_OUTPUT_INTENTS));
+        COSArray outputIntents = COSUtils.getAsArray(cBase, cosDocument);
+
+        Map<COSObjectKey, Boolean> tmpDestOutputProfile = new HashMap<COSObjectKey, Boolean>();
+        for (int i = 0; outputIntents != null && i < outputIntents.size(); ++i)
+        {
+            COSDictionary outputIntentDict = COSUtils.getAsDictionary(outputIntents.get(i), cosDocument);
+
+            if (outputIntentDict == null)
+            {
+                addValidationError(ctx, new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_INVALID_ENTRY,
+                        "OutputIntent object is null or isn't a dictionary"));
+            }
+            else
+            {
+                // S entry is mandatory and must be equals to GTS_PDFA1
+                String sValue = outputIntentDict.getNameAsString(OUTPUT_INTENT_DICTIONARY_KEY_S);
+                if (!OUTPUT_INTENT_DICTIONARY_VALUE_GTS_PDFA1.equals(sValue))
+                {
+                    addValidationError(ctx, new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_S_VALUE_INVALID,
+                            "The S entry of the OutputIntent isn't GTS_PDFA1"));
+                    continue;
+                }
+
+                // OutputConditionIdentifier is a mandatory field
+                String outputConditionIdentifier = outputIntentDict
+                        .getString(OUTPUT_INTENT_DICTIONARY_KEY_OUTPUT_CONDITION_IDENTIFIER);
+                if (outputConditionIdentifier == null)
+                {// empty string is authorized (it may be an application specific value)
+                    addValidationError(ctx, new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_INVALID_ENTRY,
+                            "The OutputIntentCondition is missing"));
+                    continue;
+                }
+
+                /*
+                 * If OutputConditionIdentifier is "Custom" or a non Standard ICC Characterization : DestOutputProfile
+                 * and Info are mandatory DestOutputProfile must be a ICC Profile
+                 * 
+                 * Because of PDF/A conforming file needs to specify the color characteristics, the DestOutputProfile is
+                 * checked even if the OutputConditionIdentifier isn't "Custom"
+                 */
+                COSBase destOutputProfile = outputIntentDict.getItem(OUTPUT_INTENT_DICTIONARY_KEY_DEST_OUTPUT_PROFILE);
+                validateICCProfile(destOutputProfile, tmpDestOutputProfile, ctx);
+
+                PreflightConfiguration config = ctx.getConfig();
+                if (config.isLazyValidation() && !isStandardICCCharacterization(outputConditionIdentifier))
+                {
+                    String info = outputIntentDict.getString(COSName.getPDFName(OUTPUT_INTENT_DICTIONARY_KEY_INFO));
+                    if (info == null || "".equals(info))
+                    {
+                        ValidationError error = new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_INVALID_ENTRY,
+                                "The Info entry of a OutputIntent dictionary is missing");
+                        error.setWarning(true);
+                        addValidationError(ctx, error);
+                        continue;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * This method checks the destOutputProfile which must be a valid ICCProfile.
+     * 
+     * If an other ICCProfile exists in the mapDestOutputProfile, a ValdiationError
+     * (ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_MULTIPLE) is returned because of only one profile is authorized. If the
+     * ICCProfile already exist in the mapDestOutputProfile, the method returns null. If the destOutputProfile contains
+     * an invalid ICCProfile, a ValidationError (ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_INVALID) is returned If the
+     * destOutputProfile is an empty stream, a ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_INVALID_ENTRY) is returned.
+     * 
+     * If the destOutputFile is valid, mapDestOutputProfile is updated, the ICCProfile is added to the document ctx and
+     * null is returned.
+     * 
+     * @param destOutputProfile
+     * @param tmpDestOutputProfile
+     * @param ctx
+     * @return
+     * @throws ValidationException
+     */
+    protected void validateICCProfile(COSBase destOutputProfile, Map<COSObjectKey, Boolean> mapDestOutputProfile,
+            PreflightContext ctx) throws ValidationException
+    {
+        try
+        {
+            if (destOutputProfile == null)
+            {
+                return;
+            }
+
+            // destOutputProfile should be an instance of COSObject because of this is a object reference
+            if (destOutputProfile instanceof COSObject)
+            {
+                if (mapDestOutputProfile.containsKey(new COSObjectKey((COSObject) destOutputProfile)))
+                {
+                    // the profile is already checked. continue
+                    return;
+                }
+                else if (!mapDestOutputProfile.isEmpty())
+                {
+                    // A DestOutputProfile exits but it isn't the same, error
+                    addValidationError(ctx, new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_MULTIPLE,
+                            "More than one ICCProfile is defined"));
+                    return;
+                }
+                // else the profile will be kept in the tmpDestOutputProfile if it is valid
+            }
+
+            // keep reference to avoid multiple profile definition
+            mapDestOutputProfile.put(new COSObjectKey((COSObject) destOutputProfile), true);
+            COSDocument cosDocument = ctx.getDocument().getDocument();
+            PDStream stream = PDStream.createFromCOS(COSUtils.getAsStream(destOutputProfile, cosDocument));
+            if (stream == null)
+            {
+                addValidationError(ctx, new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_INVALID_ENTRY,
+                        "OutputIntent object uses a NULL Object"));
+                return;
+            }
+
+            ICC_Profile iccp = ICC_Profile.getInstance(stream.getByteArray());
+            PreflightConfiguration config = ctx.getConfig();
+            // check the ICC Profile version (6.2.2)
+            if (iccp.getMajorVersion() == 2)
+            {
+                if (iccp.getMinorVersion() > 0x40)
+                {
+                    // in PDF 1.4, max version is 02h.40h (meaning V 3.5)
+                    // see the ICCProfile specification (ICC.1:1998-09)page 13 - §6.1.3 :
+                    // The current profile version number is "2.4.0" (encoded as 02400000h")
+                    ValidationError error = new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_TOO_RECENT,
+                            "Invalid version of the ICCProfile");
+                    error.setWarning(config.isLazyValidation());
+                    addValidationError(ctx, error);
+                    return;
+                } // else OK
+            }
+            else if (iccp.getMajorVersion() > 2)
+            {
+                // in PDF 1.4, max version is 02h.40h (meaning V 3.5)
+                // see the ICCProfile specification (ICC.1:1998-09)page 13 - §6.1.3 :
+                // The current profile version number is "2.4.0" (encoded as 02400000h"
+                ValidationError error = new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_TOO_RECENT,
+                        "Invalid version of the ICCProfile");
+                error.setWarning(config.isLazyValidation());
+                addValidationError(ctx, error);
+                return;
+            } // else seems less than 2, so correct
+
+            if (ctx.getIccProfileWrapper() == null)
+            {
+                ctx.setIccProfileWrapper(new ICCProfileWrapper(iccp));
+            }
+
+        }
+        catch (IllegalArgumentException e)
+        {
+            // this is not a ICC_Profile
+            addValidationError(ctx, new ValidationError(ERROR_GRAPHIC_OUTPUT_INTENT_ICC_PROFILE_INVALID,
+                    "DestOutputProfile isn't a ICCProfile"));
+        }
+        catch (IOException e)
+        {
+            throw new ValidationException("Unable to parse the ICC Profile", e);
+        }
+    }
 }

Modified: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/EmptyValidationProcess.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/EmptyValidationProcess.java?rev=1453416&r1=1453415&r2=1453416&view=diff
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/EmptyValidationProcess.java (original)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/EmptyValidationProcess.java Wed Mar  6 16:46:35 2013
@@ -25,14 +25,15 @@ import org.apache.pdfbox.preflight.Prefl
 import org.apache.pdfbox.preflight.exception.ValidationException;
 
 /**
- * This class is used to return a non null ValidationProcess when a missing process is asked
- * to the ConfigurationBean only if the errorOnMissingProcess configuration attribute is set to false.
- *
+ * This class is used to return a non null ValidationProcess when a missing process is asked to the ConfigurationBean
+ * only if the errorOnMissingProcess configuration attribute is set to false.
+ * 
  */
-public class EmptyValidationProcess implements ValidationProcess {
-	
-	public void validate(PreflightContext context)
-	throws ValidationException {
-		// this class does nothing 
-	}
+public class EmptyValidationProcess implements ValidationProcess
+{
+
+    public void validate(PreflightContext context) throws ValidationException
+    {
+        // this class does nothing
+    }
 }

Modified: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/FileSpecificationValidationProcess.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/FileSpecificationValidationProcess.java?rev=1453416&r1=1453415&r2=1453416&view=diff
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/FileSpecificationValidationProcess.java (original)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/FileSpecificationValidationProcess.java Wed Mar  6 16:46:35 2013
@@ -37,51 +37,57 @@ import org.apache.pdfbox.pdmodel.PDDocum
 import org.apache.pdfbox.preflight.PreflightContext;
 import org.apache.pdfbox.preflight.ValidationResult.ValidationError;
 import org.apache.pdfbox.preflight.exception.ValidationException;
+
 /**
  * 
  * This validation process check that FileSpec dictionaries are confirming with the PDF/A-1b specification.
  */
-public class FileSpecificationValidationProcess extends AbstractProcess {
-
+public class FileSpecificationValidationProcess extends AbstractProcess
+{
 
-	public void validate(PreflightContext ctx) throws ValidationException {
-		PDDocument pdfDoc = ctx.getDocument();
-		COSDocument cDoc = pdfDoc.getDocument();
-
-		List<?> lCOSObj = cDoc.getObjects();
-		for (Object o : lCOSObj) {
-			COSBase cBase = ((COSObject) o).getObject();
-			if (cBase instanceof COSDictionary) {
-				COSDictionary dic = (COSDictionary) cBase;
-				String type = dic.getNameAsString(COSName.TYPE);
-				if (FILE_SPECIFICATION_VALUE_TYPE.equals(type)) {
-					// ---- It is a file specification
-					validateFileSpecification(ctx, dic);
-				}
-			}
-		}
-	}
-
-
-	/**
-	 * Validate a FileSpec dictionary, a FileSpec dictionary mustn't have the EF
-	 * (EmbeddedFile) entry.
-	 * 
-	 * @param ctx
-	 *          The document handler
-	 * @param cObj
-	 *          the FileSpec Dictionary
-	 * @return
-	 */
-	public List<ValidationError> validateFileSpecification(PreflightContext ctx, COSDictionary fileSpec) {
-		List<ValidationError> result = new ArrayList<ValidationError>(0);
-
-		// ---- Check dictionary entries
-		// ---- Only the EF entry is forbidden
-		if (fileSpec.getItem(COSName.getPDFName(FILE_SPECIFICATION_KEY_EMBEDDED_FILE)) != null) {
-			addValidationError(ctx, new ValidationError(ERROR_SYNTAX_EMBEDDED_FILES,"EmbeddedFile entry is present in a FileSpecification dictionary"));
-		}
+    public void validate(PreflightContext ctx) throws ValidationException
+    {
+        PDDocument pdfDoc = ctx.getDocument();
+        COSDocument cDoc = pdfDoc.getDocument();
+
+        List<?> lCOSObj = cDoc.getObjects();
+        for (Object o : lCOSObj)
+        {
+            COSBase cBase = ((COSObject) o).getObject();
+            if (cBase instanceof COSDictionary)
+            {
+                COSDictionary dic = (COSDictionary) cBase;
+                String type = dic.getNameAsString(COSName.TYPE);
+                if (FILE_SPECIFICATION_VALUE_TYPE.equals(type))
+                {
+                    // ---- It is a file specification
+                    validateFileSpecification(ctx, dic);
+                }
+            }
+        }
+    }
+
+    /**
+     * Validate a FileSpec dictionary, a FileSpec dictionary mustn't have the EF (EmbeddedFile) entry.
+     * 
+     * @param ctx
+     *            The document handler
+     * @param cObj
+     *            the FileSpec Dictionary
+     * @return
+     */
+    public List<ValidationError> validateFileSpecification(PreflightContext ctx, COSDictionary fileSpec)
+    {
+        List<ValidationError> result = new ArrayList<ValidationError>(0);
+
+        // ---- Check dictionary entries
+        // ---- Only the EF entry is forbidden
+        if (fileSpec.getItem(COSName.getPDFName(FILE_SPECIFICATION_KEY_EMBEDDED_FILE)) != null)
+        {
+            addValidationError(ctx, new ValidationError(ERROR_SYNTAX_EMBEDDED_FILES,
+                    "EmbeddedFile entry is present in a FileSpecification dictionary"));
+        }
 
-		return result;
-	}
+        return result;
+    }
 }

Modified: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/MetadataValidationProcess.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/MetadataValidationProcess.java?rev=1453416&r1=1453415&r2=1453416&view=diff
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/MetadataValidationProcess.java (original)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/MetadataValidationProcess.java Wed Mar  6 16:46:35 2013
@@ -50,139 +50,159 @@ import org.apache.xmpbox.xml.DomXmpParse
 import org.apache.xmpbox.xml.XmpParsingException;
 import org.apache.xmpbox.xml.XmpParsingException.ErrorType;
 
+public class MetadataValidationProcess extends AbstractProcess
+{
 
-public class MetadataValidationProcess extends AbstractProcess {
-
-
-	public void validate(PreflightContext ctx) throws ValidationException {
-		try {
-			PDDocument document = ctx.getDocument();
-
-			byte[] tmp = getXpacket(document.getDocument());
-			DomXmpParser builder;
-			builder = new DomXmpParser();
-			XMPMetadata metadata;
-			metadata = builder.parse(tmp);
-			ctx.setMetadata(metadata);
-
-			// 6.7.5 no deprecated attribute in xpacket processing instruction
-			if (metadata.getXpacketBytes() != null) {
-				addValidationError(ctx, new ValidationError(
-						PreflightConstants.ERROR_METADATA_XPACKET_DEPRECATED,
-						"bytes attribute is forbidden"));
-			}
-			if (metadata.getXpacketEncoding() != null) {
-				addValidationError(ctx, new ValidationError(
-						PreflightConstants.ERROR_METADATA_XPACKET_DEPRECATED,
-						"encoding attribute is forbidden"));
-			}
-
-			// Call metadata synchronization checking
-			addValidationErrors(ctx, new SynchronizedMetaDataValidation()
-			.validateMetadataSynchronization(document, metadata));
-
-			// Call PDF/A Identifier checking
-			addValidationErrors(ctx, new PDFAIdentificationValidation()
-			.validatePDFAIdentifer(metadata));
-
-			// Call rdf:about checking
-			try {
-				new RDFAboutAttributeConcordanceValidation().validateRDFAboutAttributes(metadata);
-			} catch (DifferentRDFAboutException e) {
-				addValidationError(ctx, new ValidationError(
-						PreflightConstants.ERROR_METADATA_RDF_ABOUT_ATTRIBUTE_INEQUAL_VALUE, e
-						.getMessage()));
-			}
-
-		} catch (XpacketParsingException e) {
-			if (e.getError() != null) {
-				addValidationError(ctx, e.getError());
-			} else {
-				addValidationError(ctx, new ValidationError(PreflightConstants.ERROR_METADATA_MAIN,
-						"Unexpected error"));
-			}
-		} catch (XmpParsingException e) {
-			if (e.getErrorType()==ErrorType.NoValueType) {
-				addValidationError(ctx, new ValidationError(
-						PreflightConstants.ERROR_METADATA_UNKNOWN_VALUETYPE, e
-						.getMessage()));
-			} else if (e.getErrorType()==ErrorType.RequiredProperty) {
-				addValidationError(ctx, new ValidationError(
-						PreflightConstants.ERROR_METADATA_PROPERTY_MISSING, e.getMessage()));
-			} else if (e.getErrorType()==ErrorType.InvalidPrefix) {
-				addValidationError(ctx, new ValidationError(
-						PreflightConstants.ERROR_METADATA_ABSENT_DESCRIPTION_SCHEMA, e
-						.getMessage()));
-			} else if (e.getErrorType()==ErrorType.InvalidType) {
-				addValidationError(ctx, new ValidationError(
-						PreflightConstants.ERROR_METADATA_PROPERTY_UNKNOWN, e.getMessage()));
-			} else if (e.getErrorType()==ErrorType.XpacketBadEnd) {
-				throw new ValidationException("Unable to parse font metadata due to : "
-						+ e.getMessage(), e);
-			} else if (e.getErrorType()==ErrorType.NoSchema) {
-				addValidationError(ctx, new ValidationError(
-						PreflightConstants.ERROR_METADATA_ABSENT_DESCRIPTION_SCHEMA, e
-						.getMessage()));
-			} else if (e.getErrorType()==ErrorType.InvalidPdfaSchema) {
-				addValidationError(ctx, new ValidationError(
-						PreflightConstants.ERROR_METADATA_WRONG_NS_URI, e.getMessage()));
-			} else {
-				addValidationError(ctx, new ValidationError(PreflightConstants.ERROR_METADATA_FORMAT, e
-						.getMessage()));
-			}
-		} catch (IOException e) {
-			throw new ValidationException("Failed while validating", e);
-		}
-	}
-
-	/**
-	 * Return the xpacket from the dictionary's stream
-	 */
-	public static byte[] getXpacket(COSDocument cdocument) throws IOException,
-	XpacketParsingException {
-		COSObject catalog = cdocument.getCatalog();
-		COSBase cb = catalog.getDictionaryObject(COSName.METADATA);
-		if (cb == null) {
-			// missing Metadata Key in catalog
-			ValidationError error = new ValidationError(
-					PreflightConstants.ERROR_METADATA_FORMAT,
-					"Missing Metadata Key in catalog");
-			throw new XpacketParsingException("Failed while retrieving xpacket",
-					error);
-		}
-		// no filter key
-		COSDictionary metadataDictionnary = COSUtils.getAsDictionary(cb, cdocument);
-		if (metadataDictionnary.getItem(COSName.FILTER) != null) {
-			// should not be defined
-			ValidationError error = new ValidationError(
-					PreflightConstants.ERROR_SYNTAX_STREAM_INVALID_FILTER,
-					"Filter specified in metadata dictionnary");
-			throw new XpacketParsingException("Failed while retrieving xpacket",
-					error);
-		}
-
-		PDStream stream = PDStream.createFromCOS(metadataDictionnary);
-		ByteArrayOutputStream bos = new ByteArrayOutputStream();
-		InputStream is = stream.createInputStream();
-		IOUtils.copy(is, bos);
-		is.close();
-		bos.close();
-		return bos.toByteArray();
-	}
-
-	/**
-	 * Check if metadata dictionary has no stream filter
-	 * 
-	 * @param doc
-	 * @return
-	 */
-	protected List<ValidationError> checkStreamFilterUsage(PDDocument doc) {
-		List<ValidationError> ve = new ArrayList<ValidationError>();
-		List<?> filters = doc.getDocumentCatalog().getMetadata().getFilters();
-		if (filters != null && !filters.isEmpty()) {
-			ve.add(new ValidationError(PreflightConstants.ERROR_METADATA_MAIN,
-					"Using stream filter on metadata dictionary is forbidden"));
-		}
-		return ve;
-	}
+    public void validate(PreflightContext ctx) throws ValidationException
+    {
+        try
+        {
+            PDDocument document = ctx.getDocument();
+
+            byte[] tmp = getXpacket(document.getDocument());
+            DomXmpParser builder;
+            builder = new DomXmpParser();
+            XMPMetadata metadata;
+            metadata = builder.parse(tmp);
+            ctx.setMetadata(metadata);
+
+            // 6.7.5 no deprecated attribute in xpacket processing instruction
+            if (metadata.getXpacketBytes() != null)
+            {
+                addValidationError(ctx, new ValidationError(PreflightConstants.ERROR_METADATA_XPACKET_DEPRECATED,
+                        "bytes attribute is forbidden"));
+            }
+            if (metadata.getXpacketEncoding() != null)
+            {
+                addValidationError(ctx, new ValidationError(PreflightConstants.ERROR_METADATA_XPACKET_DEPRECATED,
+                        "encoding attribute is forbidden"));
+            }
+
+            // Call metadata synchronization checking
+            addValidationErrors(ctx,
+                    new SynchronizedMetaDataValidation().validateMetadataSynchronization(document, metadata));
+
+            // Call PDF/A Identifier checking
+            addValidationErrors(ctx, new PDFAIdentificationValidation().validatePDFAIdentifer(metadata));
+
+            // Call rdf:about checking
+            try
+            {
+                new RDFAboutAttributeConcordanceValidation().validateRDFAboutAttributes(metadata);
+            }
+            catch (DifferentRDFAboutException e)
+            {
+                addValidationError(ctx, new ValidationError(
+                        PreflightConstants.ERROR_METADATA_RDF_ABOUT_ATTRIBUTE_INEQUAL_VALUE, e.getMessage()));
+            }
+
+        }
+        catch (XpacketParsingException e)
+        {
+            if (e.getError() != null)
+            {
+                addValidationError(ctx, e.getError());
+            }
+            else
+            {
+                addValidationError(ctx, new ValidationError(PreflightConstants.ERROR_METADATA_MAIN, "Unexpected error"));
+            }
+        }
+        catch (XmpParsingException e)
+        {
+            if (e.getErrorType() == ErrorType.NoValueType)
+            {
+                addValidationError(ctx,
+                        new ValidationError(PreflightConstants.ERROR_METADATA_UNKNOWN_VALUETYPE, e.getMessage()));
+            }
+            else if (e.getErrorType() == ErrorType.RequiredProperty)
+            {
+                addValidationError(ctx,
+                        new ValidationError(PreflightConstants.ERROR_METADATA_PROPERTY_MISSING, e.getMessage()));
+            }
+            else if (e.getErrorType() == ErrorType.InvalidPrefix)
+            {
+                addValidationError(ctx, new ValidationError(
+                        PreflightConstants.ERROR_METADATA_ABSENT_DESCRIPTION_SCHEMA, e.getMessage()));
+            }
+            else if (e.getErrorType() == ErrorType.InvalidType)
+            {
+                addValidationError(ctx,
+                        new ValidationError(PreflightConstants.ERROR_METADATA_PROPERTY_UNKNOWN, e.getMessage()));
+            }
+            else if (e.getErrorType() == ErrorType.XpacketBadEnd)
+            {
+                throw new ValidationException("Unable to parse font metadata due to : " + e.getMessage(), e);
+            }
+            else if (e.getErrorType() == ErrorType.NoSchema)
+            {
+                addValidationError(ctx, new ValidationError(
+                        PreflightConstants.ERROR_METADATA_ABSENT_DESCRIPTION_SCHEMA, e.getMessage()));
+            }
+            else if (e.getErrorType() == ErrorType.InvalidPdfaSchema)
+            {
+                addValidationError(ctx,
+                        new ValidationError(PreflightConstants.ERROR_METADATA_WRONG_NS_URI, e.getMessage()));
+            }
+            else
+            {
+                addValidationError(ctx, new ValidationError(PreflightConstants.ERROR_METADATA_FORMAT, e.getMessage()));
+            }
+        }
+        catch (IOException e)
+        {
+            throw new ValidationException("Failed while validating", e);
+        }
+    }
+
+    /**
+     * Return the xpacket from the dictionary's stream
+     */
+    public static byte[] getXpacket(COSDocument cdocument) throws IOException, XpacketParsingException
+    {
+        COSObject catalog = cdocument.getCatalog();
+        COSBase cb = catalog.getDictionaryObject(COSName.METADATA);
+        if (cb == null)
+        {
+            // missing Metadata Key in catalog
+            ValidationError error = new ValidationError(PreflightConstants.ERROR_METADATA_FORMAT,
+                    "Missing Metadata Key in catalog");
+            throw new XpacketParsingException("Failed while retrieving xpacket", error);
+        }
+        // no filter key
+        COSDictionary metadataDictionnary = COSUtils.getAsDictionary(cb, cdocument);
+        if (metadataDictionnary.getItem(COSName.FILTER) != null)
+        {
+            // should not be defined
+            ValidationError error = new ValidationError(PreflightConstants.ERROR_SYNTAX_STREAM_INVALID_FILTER,
+                    "Filter specified in metadata dictionnary");
+            throw new XpacketParsingException("Failed while retrieving xpacket", error);
+        }
+
+        PDStream stream = PDStream.createFromCOS(metadataDictionnary);
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        InputStream is = stream.createInputStream();
+        IOUtils.copy(is, bos);
+        is.close();
+        bos.close();
+        return bos.toByteArray();
+    }
+
+    /**
+     * Check if metadata dictionary has no stream filter
+     * 
+     * @param doc
+     * @return
+     */
+    protected List<ValidationError> checkStreamFilterUsage(PDDocument doc)
+    {
+        List<ValidationError> ve = new ArrayList<ValidationError>();
+        List<?> filters = doc.getDocumentCatalog().getMetadata().getFilters();
+        if (filters != null && !filters.isEmpty())
+        {
+            ve.add(new ValidationError(PreflightConstants.ERROR_METADATA_MAIN,
+                    "Using stream filter on metadata dictionary is forbidden"));
+        }
+        return ve;
+    }
 }

Modified: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/PageTreeValidationProcess.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/PageTreeValidationProcess.java?rev=1453416&r1=1453415&r2=1453416&view=diff
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/PageTreeValidationProcess.java (original)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/PageTreeValidationProcess.java Wed Mar  6 16:46:35 2013
@@ -31,21 +31,28 @@ import org.apache.pdfbox.preflight.Prefl
 import org.apache.pdfbox.preflight.exception.ValidationException;
 import org.apache.pdfbox.preflight.utils.ContextHelper;
 
-public class PageTreeValidationProcess extends AbstractProcess {
+public class PageTreeValidationProcess extends AbstractProcess
+{
 
-	public void validate(PreflightContext context) throws ValidationException {
-		PDDocumentCatalog catalog = context.getDocument().getDocumentCatalog();
-		if (catalog != null) {
-			List<?> pages = catalog.getAllPages();
-			for (int i = 0; i < pages.size(); ++i) {
-				validatePage(context,(PDPage) pages.get(i));
-			}
-		} else {
-			throw new ValidationException("There are no Catalog entry in the Document.");
-		}
-	}
+    public void validate(PreflightContext context) throws ValidationException
+    {
+        PDDocumentCatalog catalog = context.getDocument().getDocumentCatalog();
+        if (catalog != null)
+        {
+            List<?> pages = catalog.getAllPages();
+            for (int i = 0; i < pages.size(); ++i)
+            {
+                validatePage(context, (PDPage) pages.get(i));
+            }
+        }
+        else
+        {
+            throw new ValidationException("There are no Catalog entry in the Document.");
+        }
+    }
 
-	protected void validatePage(PreflightContext context, PDPage page) throws ValidationException {
-		ContextHelper.validateElement(context, page, PAGE_PROCESS);
-	}
+    protected void validatePage(PreflightContext context, PDPage page) throws ValidationException
+    {
+        ContextHelper.validateElement(context, page, PAGE_PROCESS);
+    }
 }

Modified: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/StreamValidationProcess.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/StreamValidationProcess.java?rev=1453416&r1=1453415&r2=1453416&view=diff
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/StreamValidationProcess.java (original)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/process/StreamValidationProcess.java Wed Mar  6 16:46:35 2013
@@ -45,249 +45,314 @@ import org.apache.pdfbox.preflight.excep
 import org.apache.pdfbox.preflight.utils.COSUtils;
 import org.apache.pdfbox.preflight.utils.FilterHelper;
 
-public class StreamValidationProcess extends AbstractProcess {
+public class StreamValidationProcess extends AbstractProcess
+{
 
-
-	public void validate(PreflightContext ctx)	throws ValidationException {
-		PDDocument pdfDoc = ctx.getDocument();
-		COSDocument cDoc = pdfDoc.getDocument();
-
-		List<?> lCOSObj = cDoc.getObjects();
-		for (Object o : lCOSObj) {
-			COSObject cObj = (COSObject) o;
-			/*
-			 *  If this object represents a Stream, the Dictionary must contain the Length key
-			 */
-			COSBase cBase = cObj.getObject();
-			if (cBase instanceof COSStream) {
-				validateStreamObject(ctx, cObj);
-			}
-		}
-	}
-
-	public void validateStreamObject(PreflightContext context, COSObject cObj) 
-	throws ValidationException {
-		COSStream streamObj = (COSStream) cObj.getObject();
-
-		// ---- Check dictionary entries
-		// ---- Only the Length entry is mandatory
-		// ---- In a PDF/A file, F, FFilter and FDecodeParms are forbidden
-		checkDictionaryEntries(context, streamObj);
-		// ---- check stream length
-		checkStreamLength(context, cObj);
-		// ---- Check the Filter value(s)
-		checkFilters(streamObj, context);
-	}
-
-	/**
-	 * This method checks if one of declared Filter is LZWdecode. If LZW is found,
-	 * the result list is updated with an error code.
-	 * 
-	 * @param stream
-	 * @param handler
-	 * @param result
-	 */
-	protected void checkFilters(COSStream stream, PreflightContext context) {
-		COSBase bFilter = stream.getItem(COSName.FILTER);
-		if (bFilter != null) {
-			COSDocument cosDocument = context.getDocument().getDocument();
-			if (COSUtils.isArray(bFilter, cosDocument)) {
-				COSArray afName = (COSArray) bFilter;
-				for (int i = 0; i < afName.size(); ++i) {
-					FilterHelper.isAuthorizedFilter(context, afName.getString(i));
-				}
-			} else if (bFilter instanceof COSName) {
-				String fName = ((COSName) bFilter).getName();
-				FilterHelper.isAuthorizedFilter(context, fName);
-			} else {
-				// ---- The filter type is invalid
-				addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_INVALID_FILTER,	"Filter should be a Name or an Array"));
-			}
-		} 
-		//  else Filter entry is optional
-	}
-
-	private boolean readUntilStream(InputStream ra) throws IOException {
-		boolean search = true;
-		//    String stream = "";
-		boolean maybe = false;
-		int lastChar = -1;
-		do {
-			int c = ra.read();
-			switch (c) {
-			case 's':
-				//      stream = "s";
-				maybe = true;
-				lastChar = c;
-				break;
-			case 't':
-				//      if (maybe && stream.endsWith("s")) {
-				if (maybe && lastChar == 's') {
-					//          stream = stream + "t";
-					lastChar = c;
-				} else {
-					maybe = false;
-					lastChar = -1;
-				}
-				break;
-			case 'r':
-				// if (maybe && stream.endsWith("t")) {
-				if (maybe && lastChar == 't') {
-					//        stream = stream + "r";
-					lastChar = c;
-				} else {
-					maybe = false;
-					lastChar = -1;
-				}
-				break;
-			case 'e':
-				//      if (maybe && stream.endsWith("r")) {
-				if (maybe && lastChar == 'r') {
-					lastChar = c;
-					//        stream = stream + "e";
-				} else {
-					maybe = false;
-				}
-				break;
-			case 'a':
-				//        if (maybe && stream.endsWith("e")) {
-				if (maybe && lastChar == 'e') {
-					lastChar = c;
-					//        stream = stream + "a";
-				} else {
-					maybe = false;
-				}
-				break;
-			case 'm':
-				//        if (maybe && stream.endsWith("a")) {
-				if (maybe && lastChar == 'a') {
-					return true;
-				} else {
-					maybe = false;
-				}
-				break;
-			case -1:
-				search = false;
-				break;
-			default:
-				maybe = false;
-				break;
-			}
-		} while (search);
-		return false;
-	}
-
-	protected void checkStreamLength(PreflightContext context, COSObject cObj) throws ValidationException {
-		COSStream streamObj = (COSStream) cObj.getObject();
-		int length = streamObj.getInt(COSName.LENGTH);
-		InputStream ra = null;
-		try {
-			ra = context.getSource().getInputStream();
-			Long offset = context.getDocument().getDocument()
-					.getXrefTable().get(new COSObjectKey(cObj));
-
-			// ---- go to the beginning of the object
-			long skipped = 0;
-			if (offset != null) {
-				while (skipped != offset) {
-					long curSkip = ra.skip(offset - skipped);
-					if (curSkip < 0) {
-						throw new ValidationException(
-								"Unable to skip bytes in the PDFFile to check stream length");
-					}
-					skipped += curSkip;
-				}
-
-				// ---- go to the stream key word
-				if (readUntilStream(ra)) {
-					int c = ra.read();
-					if (c == '\r') {
-						ra.read();
-					} // else c is '\n' no more character to read
-
-
-					// ---- Here is the true beginning of the Stream Content.
-					// ---- Read the given length of bytes and check the 10 next bytes
-					// ---- to see if there are endstream.
-					byte[] buffer = new byte[1024];
-					int nbBytesToRead = length;
-
-					do {
-						int cr = 0;
-						if (nbBytesToRead > 1024) {
-							cr = ra.read(buffer, 0, 1024);
-						} else {
-							cr = ra.read(buffer, 0, nbBytesToRead);
-						}
-						if (cr == -1) {
-							addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,	"Stream length is invalide"));
-							return;
-						} else {
-							nbBytesToRead = nbBytesToRead - cr;
-						}
-					} while (nbBytesToRead > 0);
-
-					int len = "endstream".length() + 2;
-					byte[] buffer2 = new byte[len];
-					for (int i = 0; i < len; ++i) {
-						buffer2[i] = (byte) ra.read();
-					}
-
-					// ---- check the content of 10 last characters
-					String endStream = new String(buffer2);
-					if (buffer2[0] == '\r' && buffer2[1] == '\n') {
-						if (!endStream.contains("endstream")) {
-							addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
-									"Stream length is invalide"));
-						}
-					} else if (buffer2[0] == '\r' && buffer2[1] == 'e') {
-						if (!endStream.contains("endstream")) {
-							addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
-									"Stream length is invalide"));
-						}
-					} else if (buffer2[0] == '\n' && buffer2[1] == 'e') {
-						if (!endStream.contains("endstream")) {
-							addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
-									"Stream length is invalide"));
-						}
-					} else {
-						addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
-								"Stream length is invalide"));
-					}
-
-				} else {
-					addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
-							"Stream length is invalide"));
-				}
-			} 
-		} catch (IOException e) {
-			throw new ValidationException("Unable to read a stream to validate it due to : " + e.getMessage(),	e);
-		} finally {
-			if ( ra != null) {
-				IOUtils.closeQuietly(ra);
-			}
-		}
-	}
-
-	/**
-	 * Check dictionary entries. Only the Length entry is mandatory. In a PDF/A
-	 * file, F, FFilter and FDecodeParms are forbidden
-	 * 
-	 * @param streamObj
-	 * @param result
-	 */
-	protected void checkDictionaryEntries(PreflightContext context, COSStream streamObj) {
-		boolean len = streamObj.containsKey(COSName.LENGTH);
-		boolean f = streamObj.containsKey(COSName.F);
-		boolean ffilter = streamObj.containsKey(COSName.F_FILTER);
-		boolean fdecParams = streamObj.containsKey(COSName.F_DECODE_PARMS);
-
-		if (!len) {
-			addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_MISSING,	"Stream length is missing"));
-		}
-
-		if (f || ffilter || fdecParams) {
-			addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_FX_KEYS, "F, FFilter or FDecodeParms keys are present in the stream dictionary"));
-		}
-	}
+    public void validate(PreflightContext ctx) throws ValidationException
+    {
+        PDDocument pdfDoc = ctx.getDocument();
+        COSDocument cDoc = pdfDoc.getDocument();
+
+        List<?> lCOSObj = cDoc.getObjects();
+        for (Object o : lCOSObj)
+        {
+            COSObject cObj = (COSObject) o;
+            /*
+             * If this object represents a Stream, the Dictionary must contain the Length key
+             */
+            COSBase cBase = cObj.getObject();
+            if (cBase instanceof COSStream)
+            {
+                validateStreamObject(ctx, cObj);
+            }
+        }
+    }
+
+    public void validateStreamObject(PreflightContext context, COSObject cObj) throws ValidationException
+    {
+        COSStream streamObj = (COSStream) cObj.getObject();
+
+        // ---- Check dictionary entries
+        // ---- Only the Length entry is mandatory
+        // ---- In a PDF/A file, F, FFilter and FDecodeParms are forbidden
+        checkDictionaryEntries(context, streamObj);
+        // ---- check stream length
+        checkStreamLength(context, cObj);
+        // ---- Check the Filter value(s)
+        checkFilters(streamObj, context);
+    }
+
+    /**
+     * This method checks if one of declared Filter is LZWdecode. If LZW is found, the result list is updated with an
+     * error code.
+     * 
+     * @param stream
+     * @param handler
+     * @param result
+     */
+    protected void checkFilters(COSStream stream, PreflightContext context)
+    {
+        COSBase bFilter = stream.getItem(COSName.FILTER);
+        if (bFilter != null)
+        {
+            COSDocument cosDocument = context.getDocument().getDocument();
+            if (COSUtils.isArray(bFilter, cosDocument))
+            {
+                COSArray afName = (COSArray) bFilter;
+                for (int i = 0; i < afName.size(); ++i)
+                {
+                    FilterHelper.isAuthorizedFilter(context, afName.getString(i));
+                }
+            }
+            else if (bFilter instanceof COSName)
+            {
+                String fName = ((COSName) bFilter).getName();
+                FilterHelper.isAuthorizedFilter(context, fName);
+            }
+            else
+            {
+                // ---- The filter type is invalid
+                addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_INVALID_FILTER,
+                        "Filter should be a Name or an Array"));
+            }
+        }
+        // else Filter entry is optional
+    }
+
+    private boolean readUntilStream(InputStream ra) throws IOException
+    {
+        boolean search = true;
+        // String stream = "";
+        boolean maybe = false;
+        int lastChar = -1;
+        do
+        {
+            int c = ra.read();
+            switch (c)
+            {
+            case 's':
+                // stream = "s";
+                maybe = true;
+                lastChar = c;
+                break;
+            case 't':
+                // if (maybe && stream.endsWith("s")) {
+                if (maybe && lastChar == 's')
+                {
+                    // stream = stream + "t";
+                    lastChar = c;
+                }
+                else
+                {
+                    maybe = false;
+                    lastChar = -1;
+                }
+                break;
+            case 'r':
+                // if (maybe && stream.endsWith("t")) {
+                if (maybe && lastChar == 't')
+                {
+                    // stream = stream + "r";
+                    lastChar = c;
+                }
+                else
+                {
+                    maybe = false;
+                    lastChar = -1;
+                }
+                break;
+            case 'e':
+                // if (maybe && stream.endsWith("r")) {
+                if (maybe && lastChar == 'r')
+                {
+                    lastChar = c;
+                    // stream = stream + "e";
+                }
+                else
+                {
+                    maybe = false;
+                }
+                break;
+            case 'a':
+                // if (maybe && stream.endsWith("e")) {
+                if (maybe && lastChar == 'e')
+                {
+                    lastChar = c;
+                    // stream = stream + "a";
+                }
+                else
+                {
+                    maybe = false;
+                }
+                break;
+            case 'm':
+                // if (maybe && stream.endsWith("a")) {
+                if (maybe && lastChar == 'a')
+                {
+                    return true;
+                }
+                else
+                {
+                    maybe = false;
+                }
+                break;
+            case -1:
+                search = false;
+                break;
+            default:
+                maybe = false;
+                break;
+            }
+        } while (search);
+        return false;
+    }
+
+    protected void checkStreamLength(PreflightContext context, COSObject cObj) throws ValidationException
+    {
+        COSStream streamObj = (COSStream) cObj.getObject();
+        int length = streamObj.getInt(COSName.LENGTH);
+        InputStream ra = null;
+        try
+        {
+            ra = context.getSource().getInputStream();
+            Long offset = context.getDocument().getDocument().getXrefTable().get(new COSObjectKey(cObj));
+
+            // ---- go to the beginning of the object
+            long skipped = 0;
+            if (offset != null)
+            {
+                while (skipped != offset)
+                {
+                    long curSkip = ra.skip(offset - skipped);
+                    if (curSkip < 0)
+                    {
+                        throw new ValidationException("Unable to skip bytes in the PDFFile to check stream length");
+                    }
+                    skipped += curSkip;
+                }
+
+                // ---- go to the stream key word
+                if (readUntilStream(ra))
+                {
+                    int c = ra.read();
+                    if (c == '\r')
+                    {
+                        ra.read();
+                    } // else c is '\n' no more character to read
+
+                    // ---- Here is the true beginning of the Stream Content.
+                    // ---- Read the given length of bytes and check the 10 next bytes
+                    // ---- to see if there are endstream.
+                    byte[] buffer = new byte[1024];
+                    int nbBytesToRead = length;
+
+                    do
+                    {
+                        int cr = 0;
+                        if (nbBytesToRead > 1024)
+                        {
+                            cr = ra.read(buffer, 0, 1024);
+                        }
+                        else
+                        {
+                            cr = ra.read(buffer, 0, nbBytesToRead);
+                        }
+                        if (cr == -1)
+                        {
+                            addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
+                                    "Stream length is invalide"));
+                            return;
+                        }
+                        else
+                        {
+                            nbBytesToRead = nbBytesToRead - cr;
+                        }
+                    } while (nbBytesToRead > 0);
+
+                    int len = "endstream".length() + 2;
+                    byte[] buffer2 = new byte[len];
+                    for (int i = 0; i < len; ++i)
+                    {
+                        buffer2[i] = (byte) ra.read();
+                    }
+
+                    // ---- check the content of 10 last characters
+                    String endStream = new String(buffer2);
+                    if (buffer2[0] == '\r' && buffer2[1] == '\n')
+                    {
+                        if (!endStream.contains("endstream"))
+                        {
+                            addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
+                                    "Stream length is invalide"));
+                        }
+                    }
+                    else if (buffer2[0] == '\r' && buffer2[1] == 'e')
+                    {
+                        if (!endStream.contains("endstream"))
+                        {
+                            addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
+                                    "Stream length is invalide"));
+                        }
+                    }
+                    else if (buffer2[0] == '\n' && buffer2[1] == 'e')
+                    {
+                        if (!endStream.contains("endstream"))
+                        {
+                            addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
+                                    "Stream length is invalide"));
+                        }
+                    }
+                    else
+                    {
+                        addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
+                                "Stream length is invalide"));
+                    }
+
+                }
+                else
+                {
+                    addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_INVALID,
+                            "Stream length is invalide"));
+                }
+            }
+        }
+        catch (IOException e)
+        {
+            throw new ValidationException("Unable to read a stream to validate it due to : " + e.getMessage(), e);
+        }
+        finally
+        {
+            if (ra != null)
+            {
+                IOUtils.closeQuietly(ra);
+            }
+        }
+    }
+
+    /**
+     * Check dictionary entries. Only the Length entry is mandatory. In a PDF/A file, F, FFilter and FDecodeParms are
+     * forbidden
+     * 
+     * @param streamObj
+     * @param result
+     */
+    protected void checkDictionaryEntries(PreflightContext context, COSStream streamObj)
+    {
+        boolean len = streamObj.containsKey(COSName.LENGTH);
+        boolean f = streamObj.containsKey(COSName.F);
+        boolean ffilter = streamObj.containsKey(COSName.F_FILTER);
+        boolean fdecParams = streamObj.containsKey(COSName.F_DECODE_PARMS);
+
+        if (!len)
+        {
+            addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_LENGTH_MISSING,
+                    "Stream length is missing"));
+        }
+
+        if (f || ffilter || fdecParams)
+        {
+            addValidationError(context, new ValidationError(ERROR_SYNTAX_STREAM_FX_KEYS,
+                    "F, FFilter or FDecodeParms keys are present in the stream dictionary"));
+        }
+    }
 }