You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2012/06/29 12:25:39 UTC

svn commit: r1355309 [5/11] - in /pdfbox/trunk/preflight/src: main/java/org/apache/pdfbox/ main/java/org/apache/pdfbox/preflight/ main/java/org/apache/pdfbox/preflight/action/ main/java/org/apache/pdfbox/preflight/annotation/ main/java/org/apache/pdfbo...

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CFFType0FontContainer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CFFType0FontContainer.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CFFType0FontContainer.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CFFType0FontContainer.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,105 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.fontbox.cff.CFFFont;
+import org.apache.fontbox.cff.CFFFont.Mapping;
+import org.apache.pdfbox.preflight.PreflightConstants;
+
+
+public class CFFType0FontContainer extends AbstractFontContainer {
+	/**
+	 * Object which contains the CFF data extracted by the
+	 * CFFParser object
+	 */
+	private List<CFFFont> fontObject = null;
+
+	public CFFType0FontContainer(CompositeFontContainer container) {
+		super(container.getFont());
+		this.cidKnownByFont.putAll(container.cidKnownByFont);
+		this.isFontProgramEmbedded = container.isFontProgramEmbedded;
+		this.errors.addAll(container.errors);
+	}
+
+	@Override
+	public void checkCID(int cid) throws GlyphException {
+	  if (isAlreadyComputedCid(cid)) {
+		  return;
+	  } 
+
+		// ---- build the font container and keep it in the Handler.
+	  boolean cidFound = false;
+		for (CFFFont font : this.fontObject) {
+			Collection<Mapping> cMapping = font.getMappings();
+			for (Mapping mapping : cMapping) {
+				// -- REMARK : May be this code must be changed like the Type1FontContainer to Map the SID with the character name?
+				// -- Not enough PDF with this kind of Font to test the current implementation
+				if (mapping.getSID()==cid) {
+					cidFound = true;
+				}
+			}
+		}
+
+		if (!cidFound && cid != 0) { 
+			// Cid 0 is commonly used as the NotDef Glyph. this glyph can be used as Space.
+			// IN PDF/A-1 the Notdef glyph can be used as space. Not in PDF/A-2 
+			GlyphException ge = new GlyphException(PreflightConstants.ERROR_FONTS_GLYPH_MISSING, cid, 
+																							"CID " + cid + " is missing from the Composite Font format \"" 
+																							+ this.font.getBaseFont()+"\""	);
+		  addKnownCidElement(new GlyphDetail(cid, ge));
+			throw ge;
+		}
+
+		final float widthProvidedByPdfDictionary = this.font.getFontWidth(cid);
+		float defaultGlyphWidth = 0;
+		if (this.font.getFontDescriptor() != null) {
+			defaultGlyphWidth = this.font.getFontDescriptor().getMissingWidth();
+		}
+		
+		float widthInFontProgram = 0;
+		try {
+			// ---- Search the CID in all CFFFont in the FontProgram
+			for (CFFFont cff : fontObject) {
+				widthInFontProgram = cff.getWidth(cid);
+				if (widthInFontProgram != defaultGlyphWidth) {
+					break;
+				}
+			}
+		} catch (IOException e) {
+			GlyphException ge = new GlyphException(PreflightConstants.ERROR_FONTS_DAMAGED, cid, 
+					"Unable to get width of the CID/SID : " + cid);
+			addKnownCidElement(new GlyphDetail(cid, ge));
+			throw ge;
+		}
+
+	  checkWidthsConsistency(cid, widthProvidedByPdfDictionary, widthInFontProgram);
+	  addKnownCidElement(new GlyphDetail(cid));
+	}
+
+	void setFontObject(List<CFFFont> fontObject) {
+		this.fontObject = fontObject;
+	}
+}
\ No newline at end of file

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CFFType0FontContainer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CFFType2FontContainer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CFFType2FontContainer.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CFFType2FontContainer.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CFFType2FontContainer.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,120 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.pdfbox.preflight.PreflightConstants;
+
+
+public class CFFType2FontContainer extends AbstractFontContainer {
+	/**
+	 * Object which contains the TrueType font data (used in the CFFType2 font) 
+	 * extracted by the TrueTypeParser object
+	 */
+	private TrueTypeFont fontObject = null;
+	private CIDToGIDMap cidToGidMap = null;
+
+	private int numberOfLongHorMetrics;
+	private int unitsPerEm;
+	private int[] glyphWidths;
+
+	public CFFType2FontContainer(CompositeFontContainer container) {
+		super(container.getFont());
+		this.cidKnownByFont.putAll(container.cidKnownByFont);
+		this.isFontProgramEmbedded = container.isFontProgramEmbedded;
+		this.errors.addAll(container.errors);
+	}
+
+	@Override
+	public void checkCID(int cid) throws GlyphException {
+		if (isAlreadyComputedCid(cid)) {
+			return;
+		}
+
+		final float widthProvidedByPdfDictionary = this.font.getFontWidth(cid);
+
+		final int glyphIndex = getGlyphIndex(cid);
+
+		if(this.fontObject.getGlyph().getGlyphs().length <= glyphIndex) {
+			GlyphException ge = new GlyphException(PreflightConstants.ERROR_FONTS_GLYPH_MISSING, cid, 
+					"CID " + cid + " is missing from font \"" + this.font.getBaseFont() + "\"");
+			addKnownCidElement(new GlyphDetail(cid, ge));
+			throw ge;
+		}
+
+		// glyph exists we can check the width
+		float glypdWidth = glyphWidths[numberOfLongHorMetrics - 1];
+		if (glyphIndex < numberOfLongHorMetrics) {
+			glypdWidth = glyphWidths[glyphIndex];
+		}
+		float widthInFontProgram = ((glypdWidth * 1000) / unitsPerEm);
+
+		checkWidthsConsistency(cid, widthProvidedByPdfDictionary, widthInFontProgram);
+		addKnownCidElement(new GlyphDetail(cid));
+	}
+
+	/**
+	 * If CIDToGID map is Identity, the GID equals to the CID.
+	 * Otherwise the conversion is done by the CIDToGID map
+	 * @param cid
+	 * @return
+	 * @throws GlyphException
+	 */
+	private int getGlyphIndex(int cid) throws GlyphException {
+		int glyphIndex = cid;
+
+		if (this.cidToGidMap != null) {
+			glyphIndex = cidToGidMap.getGID(cid);
+			if (glyphIndex==cidToGidMap.NOTDEF_GLYPH_INDEX) {
+				GlyphException ge = new GlyphException(PreflightConstants.ERROR_FONTS_GLYPH_MISSING, cid, 
+						"CID " + cid + " can't be found in the cidToGid map");
+				addKnownCidElement(new GlyphDetail(cid, ge));
+				throw ge;
+			}
+		}
+		return glyphIndex;
+	}
+
+	void setFontObject(TrueTypeFont fontObject) {
+		this.fontObject = fontObject;
+	}
+
+	void setCmap(CIDToGIDMap cmap) {
+		this.cidToGidMap = cmap;
+	}
+
+	void setNumberOfLongHorMetrics(int numberOfLongHorMetrics) {
+		this.numberOfLongHorMetrics = numberOfLongHorMetrics;
+	}
+
+	void setUnitsPerEm(int unitsPerEm) {
+		this.unitsPerEm = unitsPerEm;
+	}
+
+	void setGlyphWidths(int[] _glyphWidths) {
+		this.glyphWidths = new int[_glyphWidths.length];
+		for( int i =0 ; i < _glyphWidths.length; ++i) {
+			this.glyphWidths[i] = _glyphWidths[i];
+		}
+	}
+
+}

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CFFType2FontContainer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CIDToGIDMap.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CIDToGIDMap.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CIDToGIDMap.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CIDToGIDMap.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,66 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.pdfbox.cos.COSStream;
+
+public class CIDToGIDMap {
+
+	public final int NOTDEF_GLYPH_INDEX = 0;
+	private byte[] map = null;
+	
+	/**
+	 * Copy the unfiltered stream content in a byte array.
+	 * @param stream
+	 * @throws IOException if the stream can't be copied
+	 */
+	public void parseStream(COSStream stream) throws IOException {
+		InputStream is = stream.getUnfilteredStream();
+		ByteArrayOutputStream os = new ByteArrayOutputStream();
+		
+		try {
+			IOUtils.copy(stream.getUnfilteredStream(), os);
+			map  = os.toByteArray();
+		} catch (IOException e) {
+			IOUtils.closeQuietly(is);
+			IOUtils.closeQuietly(os);
+		}
+	}
+	
+	/**
+	 * Return the glyph index according to the CID.
+	 * @param cid
+	 * @return
+	 */
+	public int getGID(int cid) {
+		if (map == null || (cid*2+1) >= map.length) {
+			return NOTDEF_GLYPH_INDEX;
+		}
+		int index = cid*2;
+		return ((map[index]&0xFF)<<8)^(map[index+1]&0xFF);
+	}
+}
\ No newline at end of file

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CIDToGIDMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CompositeFontContainer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CompositeFontContainer.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CompositeFontContainer.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CompositeFontContainer.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,51 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+import org.apache.pdfbox.pdmodel.font.PDFont;
+
+public class CompositeFontContainer extends AbstractFontContainer {
+	private AbstractFontContainer delegatedContainer = null;
+
+  public CompositeFontContainer(PDFont fd) {
+    super(fd);
+  }
+
+  @Override
+  public void checkCID(int cid) throws GlyphException {
+    this.delegatedContainer.checkCID(cid);
+  }
+
+  CFFType0FontContainer getCFFType0() {
+  	if (delegatedContainer == null) {
+  		delegatedContainer = new CFFType0FontContainer(this);
+  	}
+  	return (CFFType0FontContainer)this.delegatedContainer;
+  }
+
+  CFFType2FontContainer getCFFType2() {
+  	if (delegatedContainer == null) {
+  		delegatedContainer = new CFFType2FontContainer(this);
+  	}
+  	return (CFFType2FontContainer)this.delegatedContainer;
+  }
+}

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CompositeFontContainer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CompositeFontValidator.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CompositeFontValidator.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CompositeFontValidator.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CompositeFontValidator.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,742 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.fontbox.cff.CFFFont;
+import org.apache.fontbox.cff.CFFParser;
+import org.apache.fontbox.cmap.CMap;
+import org.apache.fontbox.cmap.CMapParser;
+import org.apache.fontbox.ttf.CIDFontType2Parser;
+import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDFontDescriptorDictionary;
+import org.apache.pdfbox.preflight.PreflightConstants;
+import org.apache.pdfbox.preflight.PreflightContext;
+import org.apache.pdfbox.preflight.ValidationResult;
+import org.apache.pdfbox.preflight.ValidationResult.ValidationError;
+import org.apache.pdfbox.preflight.exception.ValidationException;
+import org.apache.pdfbox.preflight.utils.COSUtils;
+
+public class CompositeFontValidator extends AbstractFontValidator {
+	protected String basefont;
+	protected COSBase descendantFonts;
+	protected COSDictionary cidFont;
+	protected COSBase encoding;
+	protected COSStream cmap;
+	protected COSBase toUnicode;
+
+	protected CIDToGIDMap cidToGidMap = null;
+
+	protected boolean isIdentityCMap = false;
+
+	public CompositeFontValidator(PreflightContext context, PDFont font)
+	throws ValidationException {		
+		super(context, font);
+	}
+
+	/**
+	 * This methods extracts from the Font dictionary all mandatory fields. If a
+	 * mandatory field is missing, the list of ValidationError in the
+	 * FontContainer is updated. On error, the method returns false.
+	 * 
+	 * @return
+	 */
+	protected boolean checkMandatoryFields() {
+		COSDictionary fDictionary = (COSDictionary)this.pFont.getCOSObject();
+		String type = this.pFont.getType();
+		String subtype = this.pFont.getSubType();
+
+		// just check if they are present because of the Helper has already checked them.
+		if ((type == null || "".equals(type)) || (subtype == null || "".equals(subtype))) {
+			this.fontContainer.addError(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
+					"Type and/or Subtype keys are missing"));
+			return false;
+		}
+		
+		// Check presence of baseFont, CMap and CIDFont
+		this.basefont = this.pFont.getBaseFont();
+		this.descendantFonts = fDictionary.getItem(COSName.DESCENDANT_FONTS);
+		this.encoding = fDictionary.getItem(COSName.ENCODING);
+
+		if ((basefont == null || "".equals(basefont)) || descendantFonts == null || encoding == null) {
+			// baseFont syntax isn't checked because of it is a convention not a rule
+			this.fontContainer.addError(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
+					"BaseFont, Encoding or DescendantFonts keys are missing"));
+			return false;
+		}
+
+		// toUnicode is optional, but keep the value if present.
+		this.toUnicode = fDictionary.getItem(COSName.TO_UNICODE);
+		return true;
+	}
+
+	/**
+	 * This method validates the CIDFont dictionary.
+	 * 
+	 * This method returns false and updates the list of errors in the
+	 * FontContainer if some mandatory fields are missing.
+	 * 
+	 * This method calls the processCIDFontTypeX method to check if the font is
+	 * damaged or not. If the font is damaged, the errors list is updated and the
+	 * method return false.
+	 * 
+	 * @return
+	 * @throws ValidationException
+	 */
+	protected boolean checkCIDFont() throws ValidationException {
+
+		// a CIDFont is contained in the DescendantFonts array
+		COSArray array = COSUtils.getAsArray(descendantFonts, cosDocument);
+		if (array == null) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_CIDKEYED_INVALID,
+					"CIDFont is missing from the DescendantFonts array"));
+			return false;
+		}
+		/*
+		 * in PDF 1.4, this array must contain only one element,
+		 * because of a PDF/A should be a PDF 1.4, this method 
+		 * returns an error if the array has more than one element.
+		 */
+		if (array.size() != 1) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_CIDKEYED_INVALID,
+					"The DescendantFonts array should have one element."));
+			return false;
+		}
+
+		this.cidFont = COSUtils.getAsDictionary(array.get(0), cosDocument);
+		if (this.cidFont == null) {
+			this.fontContainer
+			.addError(new ValidationError(ERROR_FONTS_CIDKEYED_INVALID,
+					"The DescendantFonts array should have one element with is a dictionary."));
+			return false;
+		}
+
+		String type = cidFont.getNameAsString(COSName.TYPE);
+		String subtype = cidFont.getNameAsString(COSName.SUBTYPE);
+
+		if ((type == null || "".equals(type))
+				|| (subtype == null || "".equals(subtype))) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_DICTIONARY_INVALID,
+					"Type and/or Subtype keys are missing"));
+			return false;
+		}
+
+		boolean isT0 = FONT_DICTIONARY_VALUE_TYPE0.equals(subtype);
+		boolean isT2 = FONT_DICTIONARY_VALUE_TYPE2.equals(subtype);
+		// ---- Even if these entries are present, values must be checked.
+		if (!FONT_DICTIONARY_VALUE_FONT.equals(type) || !(isT0 || isT2)) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_DICTIONARY_INVALID,
+					"Type and/or Subtype keys are missing"));
+			return false;
+		}
+
+		// ---- BaseFont is mandatory
+		String bf = cidFont.getNameAsString(COSName
+				.getPDFName(FONT_DICTIONARY_KEY_BASEFONT));
+		if (bf == null || "".equals(bf)) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_DICTIONARY_INVALID, "BaseFont is missing"));
+			return false;
+		}
+
+		// ---- checks others mandatory fields
+		COSBase sysinfo = cidFont.getItem(COSName
+				.getPDFName(FONT_DICTIONARY_KEY_CID_SYSINFO));
+		COSBase fontDesc = cidFont.getItem(COSName
+				.getPDFName(FONT_DICTIONARY_KEY_FONT_DESC));
+		COSBase cidToGid = cidFont.getItem(COSName
+				.getPDFName(FONT_DICTIONARY_KEY_CID_GIDMAP));
+
+		boolean result = checkCIDSystemInfo(sysinfo);
+		if (isT0) {
+			result = result && checkCIDToGIDMap(cidToGid, false);
+			result = result && processCIDFontType0(fontDesc);
+		} else {
+			result = result && checkCIDToGIDMap(cidToGid, true);
+			result = result && processCIDFontType2(fontDesc);
+		}
+		return result;
+	}
+
+	/**
+	 * Check the content of the CIDSystemInfo dictionary. A CIDSystemInfo
+	 * dictionary must contain :
+	 * <UL>
+	 * <li>a Name - Registry
+	 * <li>a Name - Ordering
+	 * <li>a Integer - Supplement
+	 * </UL>
+	 * 
+	 * @param sysinfo
+	 * @return
+	 */
+	private boolean checkCIDSystemInfo(COSBase sysinfo) {
+		COSDictionary cidSysInfo = COSUtils.getAsDictionary(sysinfo, cosDocument);
+		if (cidSysInfo == null) {
+			this.fontContainer.addError(new ValidationError(ERROR_FONTS_CIDKEYED_SYSINFO));
+			return false;
+		}
+
+		COSBase reg = cidSysInfo.getItem(COSName
+				.getPDFName(FONT_DICTIONARY_KEY_SYSINFO_REGISTRY));
+		COSBase ord = cidSysInfo.getItem(COSName
+				.getPDFName(FONT_DICTIONARY_KEY_SYSINFO_ORDERING));
+		COSBase sup = cidSysInfo.getItem(COSName
+				.getPDFName(FONT_DICTIONARY_KEY_SYSINFO_SUPPLEMENT));
+
+		if (!(COSUtils.isString(reg, cosDocument) && COSUtils.isString(ord, cosDocument) && COSUtils.isInteger(sup, cosDocument))) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_CIDKEYED_SYSINFO));
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * This method checks the CIDtoGIDMap entry of the Font dictionary. This
+	 * element must be a Stream or a Name. If it is a name, it must be "Identity"
+	 * otherwise, the PDF file isn't a PDF/A-1b.
+	 * 
+	 * If the validation fails, the method returns false and the list of error in
+	 * the FontContainer is updated.
+	 * 
+	 * If the CIDtoGIDMap is a Stream, it is parsed as a CMap and the result is
+	 * kept in the cidToGidMap attribute.
+	 * 
+	 * @param ctog
+	 * @param mandatory
+	 * @return
+	 */
+	private boolean checkCIDToGIDMap(COSBase ctog,boolean mandatory) {
+		if (COSUtils.isString(ctog, cosDocument)) {
+			// ---- valid only if the string is Identity
+			String ctogStr = COSUtils.getAsString(ctog, cosDocument);
+			if (!FONT_DICTIONARY_VALUE_CMAP_IDENTITY.equals(ctogStr)) {
+				this.fontContainer.addError(new ValidationError(ERROR_FONTS_CIDKEYED_CIDTOGID,"The CIDToGID entry is invalid"));
+				return false;
+			}
+		} else if (COSUtils.isStream(ctog, cosDocument)) {
+			try {
+				COSStream ctogMap = COSUtils.getAsStream(ctog, cosDocument);
+				this.cidToGidMap = new CIDToGIDMap();
+				this.cidToGidMap.parseStream(ctogMap);
+			} catch (IOException e) {
+				// ---- map can be invalid, return a Validation Error
+				this.fontContainer.addError(new ValidationError(ERROR_FONTS_CIDKEYED_CIDTOGID));
+				return false;
+			}
+		} else if (mandatory) {
+			this.fontContainer.addError(new ValidationError(ERROR_FONTS_CIDKEYED_CIDTOGID));
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Check the CMap entry.
+	 * 
+	 * The CMap entry must be a dictionary in a PDF/A. This entry can be a String
+	 * only if the String value is Identity-H or Identity-V
+	 * 
+	 * @param errors
+	 * @return
+	 */
+	protected boolean checkCMap(COSBase aEncoding) {
+
+		if (COSUtils.isString(aEncoding, cosDocument)) {
+			// ---- if encoding is a string, only 2 values are allowed
+			String str = COSUtils.getAsString(aEncoding, cosDocument);
+			if (!(FONT_DICTIONARY_VALUE_CMAP_IDENTITY_V.equals(str) || FONT_DICTIONARY_VALUE_CMAP_IDENTITY_H
+					.equals(str))) {
+				this.fontContainer.addError(new ValidationError(
+						ERROR_FONTS_CIDKEYED_INVALID,
+						"The CMap is a string but it isn't an Identity-H/V"));
+				return false;
+			}
+			isIdentityCMap = true;
+		} else if (COSUtils.isStream(aEncoding, cosDocument)) {
+			// ---- If the CMap is a stream, some fields are mandatory
+			// and the CIDSytemInfo must be compared with the CIDSystemInfo
+			// entry of the CIDFont.
+			return processCMapAsStream(COSUtils.getAsStream(aEncoding, cosDocument));
+		} else {
+			// ---- CMap type is invalid
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_CIDKEYED_CMAP_INVALID_OR_MISSING,
+					"The CMap type is invalid"));
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Standard information of a stream element will be checked by the
+	 * StreamHelper.
+	 * 
+	 * This method checks mandatory fields of the CMap stream. This method checks
+	 * too if the CMap stream is damaged using the CMapParser of the fontbox api.
+	 * 
+	 * @param aCMap
+	 * @return
+	 */
+	private boolean processCMapAsStream(COSStream aCMap) {
+
+		String type = aCMap.getNameAsString(COSName.TYPE);
+		String cmapName = aCMap.getNameAsString(COSName.getPDFName(FONT_DICTIONARY_KEY_CMAP_NAME));
+		COSBase sysinfo = aCMap.getItem(COSName.getPDFName(FONT_DICTIONARY_KEY_CID_SYSINFO));
+		int wmode = aCMap.getInt(COSName.getPDFName(FONT_DICTIONARY_KEY_CMAP_WMODE));
+		if (wmode == -1) {
+			/*
+			 * According to the getInt javadoc, -1 is returned if there are no result.
+			 * In the PDF Reference v1.7 p449, we can read that Default value is 0.
+			 */
+			wmode = FONT_DICTIONARY_DEFAULT_CMAP_WMODE;
+		}
+		COSBase cmapUsed = aCMap.getItem(COSName.getPDFName(FONT_DICTIONARY_KEY_CMAP_USECMAP));
+
+		if (!FONT_DICTIONARY_VALUE_TYPE_CMAP.equals(type)) {
+			// ---- CMap type is invalid
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_CIDKEYED_CMAP_INVALID_OR_MISSING,
+					"The CMap type is invalid"));
+			return false;
+		}
+
+		// ---- check the content of the CIDSystemInfo
+		if (!checkCIDSystemInfo(sysinfo)) {
+			return false;
+		}
+
+		if (cmapName == null || "".equals(cmapName) || wmode > 1) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_CIDKEYED_CMAP_INVALID_OR_MISSING,
+					"Some elements in the CMap dictionary are missing or invalid"));
+			return false;
+		}
+
+		try {
+
+			CMap fontboxCMap = new CMapParser().parse(null, aCMap.getUnfilteredStream());
+			int wmValue = fontboxCMap.getWMode();
+			String cmnValue = fontboxCMap.getName();
+
+
+			if (wmValue != wmode) {
+
+				this.fontContainer.addError(new ValidationError(
+						ERROR_FONTS_CIDKEYED_CMAP_INVALID_OR_MISSING,
+						"WMode is inconsistent"));
+				return false;
+			}
+
+			if (!cmnValue.equals(cmapName)) {
+
+				this.fontContainer.addError(new ValidationError(
+						ERROR_FONTS_CIDKEYED_CMAP_INVALID_OR_MISSING,
+						"CMapName is inconsistent"));
+				return false;
+			}
+
+		} catch (IOException e) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_CID_CMAP_DAMAGED, "The CMap type is damaged"));
+			return false;
+		}
+
+		if (cmapUsed != null) {
+			return checkCMap(cmapUsed);
+		}
+
+		return true;
+	}
+
+	/**
+	 * The CIDSystemInfo must have the same Registry and Ordering for CMap and
+	 * CIDFont. This control is useless if CMap is Identity-H or Identity-V so
+	 * this method is called by the checkCMap method.
+	 * 
+	 * @param errors
+	 * @return
+	 */
+	private boolean compareCIDSystemInfo() {
+		if (!isIdentityCMap) {
+			COSDictionary cmsi = COSUtils.getAsDictionary(this.cmap.getItem(COSName
+					.getPDFName(FONT_DICTIONARY_KEY_CID_SYSINFO)), cosDocument);
+			COSDictionary cfsi = COSUtils.getAsDictionary(this.cidFont
+					.getItem(COSName.getPDFName(FONT_DICTIONARY_KEY_CID_SYSINFO)), cosDocument);
+			String regCM = COSUtils.getAsString(cmsi.getItem(COSName
+					.getPDFName(FONT_DICTIONARY_KEY_SYSINFO_REGISTRY)), cosDocument);
+			String ordCM = COSUtils.getAsString(cmsi.getItem(COSName
+					.getPDFName(FONT_DICTIONARY_KEY_SYSINFO_ORDERING)), cosDocument);
+			String regCF = COSUtils.getAsString(cfsi.getItem(COSName
+					.getPDFName(FONT_DICTIONARY_KEY_SYSINFO_REGISTRY)), cosDocument);
+			String ordCF = COSUtils.getAsString(cfsi.getItem(COSName
+					.getPDFName(FONT_DICTIONARY_KEY_SYSINFO_ORDERING)), cosDocument);
+
+			if (!regCF.equals(regCM) || !ordCF.equals(ordCM)) {
+				this.fontContainer.addError(new ValidationError(
+						ERROR_FONTS_CIDKEYED_SYSINFO, "The CIDSystemInfo is inconsistent"));
+				return false;
+			}
+		} // else cmap is null because it is a Identity-H/V
+		return true;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * net.awl.edoc.pdfa.validation.font.FontValidator#validate(java.util.List)
+	 */
+	public void validate() throws ValidationException {
+		boolean result = checkMandatoryFields();
+		result = result && checkCIDFont();
+		result = result && checkCMap(encoding);
+		if (result) {
+			this.cmap = COSUtils.getAsStream(encoding, cosDocument);
+		}
+		result = result && compareCIDSystemInfo();
+	}
+
+	/**
+	 * Check if all required fields are present in the PDF file to describe the
+	 * Font Descriptor.
+	 * 
+	 * @param context
+	 * @param fontDescriptor
+	 * @param result
+	 */
+	protected boolean checkFontDescriptorMandatoryFields(
+			PDFontDescriptorDictionary pdFontDesc) {
+		boolean fname = false, flags = false, itangle = false, cheight = false;
+		boolean fbbox = false, asc = false, desc = false, stemv = false;
+
+		COSDictionary fontDescDictionary = pdFontDesc.getCOSDictionary();
+		for (Object key : fontDescDictionary.keySet()) {
+
+			if (!(key instanceof COSName)) {
+				this.fontContainer.addError(new ValidationResult.ValidationError(
+						PreflightConstants.ERROR_SYNTAX_DICTIONARY_KEY_INVALID,
+						"Invalid key in The font descriptor"));
+				return false;
+			}
+
+			String cosName = ((COSName) key).getName();
+			if (cosName.equals(FONT_DICTIONARY_KEY_FONTNAME)) {
+				fname = true;
+			}
+			if (cosName.equals(FONT_DICTIONARY_KEY_FLAGS)) {
+				flags = true;
+			}
+			if (cosName.equals(FONT_DICTIONARY_KEY_ITALICANGLE)) {
+				itangle = true;
+			}
+			if (cosName.equals(FONT_DICTIONARY_KEY_CAPHEIGHT)) {
+				cheight = true;
+			}
+			if (cosName.equals(FONT_DICTIONARY_KEY_FONTBBOX)) {
+				fbbox = true;
+			}
+			if (cosName.equals(FONT_DICTIONARY_KEY_ASCENT)) {
+				asc = true;
+			}
+			if (cosName.equals(FONT_DICTIONARY_KEY_DESCENT)) {
+				desc = true;
+			}
+			if (cosName.equals(FONT_DICTIONARY_KEY_STEMV)) {
+				stemv = true;
+			}
+
+		}
+
+		if (!(fname && flags && itangle && cheight && fbbox && asc && desc && stemv)) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_DESCRIPTOR_INVALID, "Some mandatory fields are missing"));
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Process the CIDFontType0 validation.
+	 * 
+	 * @param fontDesc
+	 *          The FontDescriptor which contains the Font Program
+	 * @return
+	 * @throws ValidationException
+	 */
+	protected boolean processCIDFontType0(COSBase fontDesc)	throws ValidationException {
+		COSDictionary fontDescDic = COSUtils.getAsDictionary(fontDesc, cosDocument);
+		if (fontDescDic == null) {
+			throw new ValidationException(
+					"Unable to process CIDFontType0 because of the font descriptor is invalid.");
+		}
+		PDFontDescriptorDictionary pfDescriptor = new PDFontDescriptorDictionary(fontDescDic);
+		boolean isValid = checkFontDescriptorMandatoryFields(pfDescriptor);
+		isValid = isValid && checkCIDKeyedFontName(pfDescriptor);
+		isValid = isValid && checkFontFileElement_CIDFontType0(pfDescriptor);
+		isValid = isValid && checkCIDSet(pfDescriptor);
+		return isValid;
+	}
+
+	/**
+	 * Checks if the FontName contained in the FontDescriptor dictionary of the
+	 * CID-Keyed Font is present. (The FontName is mandatory according to the PDF
+	 * Reference.) If the consistency must be checked, the FontName contained in
+	 * the FontDescriptor is consistent with the BaseName of the font dictionary.
+	 * If font name is invalid, the list of validation errors in the FontContainer
+	 * is updated.
+	 * 
+	 * @param pfDescriptor
+	 *          The FontDescriptor dictionary which contains the FontName to check
+	 * @return
+	 */
+	protected boolean checkCIDKeyedFontName(PDFontDescriptorDictionary pfDescriptor) {
+		String fontName = pfDescriptor.getFontName();
+		if (fontName == null) {
+			this.fontContainer.addError(new ValidationResult.ValidationError(
+					ERROR_FONTS_DESCRIPTOR_INVALID,
+					"The FontName in font descriptor is missing"));
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * This method return false and updates the FontContainer if the Composite
+	 * Font TYPE 0 or TYPE1C is damaged. This method checks the Widths
+	 * consistency.
+	 * 
+	 * @param pfDescriptor
+	 * @return
+	 */
+	boolean checkFontFileElement_CIDFontType0(PDFontDescriptorDictionary pfDescriptor) 
+	throws ValidationException {
+		// ---- FontFile Validation
+		PDStream ff1 = pfDescriptor.getFontFile();
+		PDStream ff2 = pfDescriptor.getFontFile2();
+		PDStream ff3 = pfDescriptor.getFontFile3();
+
+		boolean onlyOne = (ff1 != null && ff2 == null && ff3 == null)
+				|| (ff1 == null && ff2 != null && ff3 == null)
+				|| (ff1 == null && ff2 == null && ff3 != null);
+
+		if ((ff3 == null) || !onlyOne) {
+			this.fontContainer.addError(new ValidationResult.ValidationError(
+					ERROR_FONTS_FONT_FILEX_INVALID, "The FontFile is invalid"));
+			return false;
+		}
+
+		// ---- Stream validation should be done by the StreamValidateHelper.
+		// ---- Process font specific check
+		COSStream stream = ff3.getStream();
+		if (stream == null) {
+			this.fontContainer.addError(new ValidationResult.ValidationError(
+					ERROR_FONTS_FONT_FILEX_INVALID, "The FontFile is missing"));
+			this.fontContainer.setFontProgramEmbedded(false);
+			return false;
+		}
+
+		// ---- Lengthx aren't mandatory for this type of font
+		// ---- But the Subtype is a mandatory field with specific values
+		String st = stream.getNameAsString(COSName.SUBTYPE);
+		if (!(FONT_DICTIONARY_VALUE_TYPE0C.equals(st) || FONT_DICTIONARY_VALUE_TYPE1C.equals(st))) {
+			this.fontContainer.addError(new ValidationResult.ValidationError(
+					ERROR_FONTS_FONT_FILEX_INVALID,
+					"The FontFile3 stream doesn't have the right Subtype"));
+			return false;
+		}
+
+		// ---- try to load the font using the java.awt.font object.
+		// ---- if the font is invalid, an exception will be thrown
+		try {
+			CFFParser cffParser = new CFFParser();
+			List<CFFFont> lCFonts = cffParser.parse(ff3.getByteArray());
+
+			if (lCFonts == null || lCFonts.isEmpty()) {
+				this.fontContainer.addError(new ValidationResult.ValidationError(
+						ERROR_FONTS_CID_DAMAGED, "The FontFile can't be read"));
+				return false;
+			}
+
+			return checkCIDFontWidths(lCFonts)
+					&& checkFontFileMetaData(pfDescriptor, ff3);
+		} catch (IOException e) {
+			this.fontContainer.addError(new ValidationResult.ValidationError(
+					ERROR_FONTS_CID_DAMAGED, "The FontFile can't be read"));
+			return false;
+		}
+	}
+
+	/**
+	 * This method checks the metric consistency of a CIDFontType2.
+	 * 
+	 * @param ttf
+	 *          The TrueTypeFont object which represent the CIDFontType2 Font
+	 *          Program.
+	 * @return
+	 * @throws ValidationException
+	 */
+	protected boolean checkTTFontMetrics(TrueTypeFont ttf)	throws ValidationException {
+		int unitsPerEm = ttf.getHeader().getUnitsPerEm();
+		int[] glyphWidths = ttf.getHorizontalMetrics().getAdvanceWidth();
+		/* In a Mono space font program, the length of the AdvanceWidth array must be one.
+		 * According to the TrueType font specification, the Last Value of the AdvanceWidth array
+		 * is apply to the subsequent glyphs. So if the GlyphId is greater than the length of the array
+		 * the last entry is used.
+		 */
+		int numberOfLongHorMetrics = ttf.getHorizontalHeader().getNumberOfHMetrics();
+		CFFType2FontContainer type2FontContainer = ((CompositeFontContainer)this.fontContainer).getCFFType2();
+		type2FontContainer.setCmap(this.cidToGidMap);
+		type2FontContainer.setFontObject(ttf);
+		type2FontContainer.setGlyphWidths(glyphWidths);
+		type2FontContainer.setNumberOfLongHorMetrics(numberOfLongHorMetrics);
+		type2FontContainer.setUnitsPerEm(unitsPerEm);
+
+		return true;
+	}
+
+	/**
+	 * This method check Metrics consistency of the CIDFontType0.
+	 * 
+	 * @param lCFonts
+	 * @return
+	 * @throws ValidationException
+	 */
+	protected boolean checkCIDFontWidths(List<CFFFont> lCFonts)	throws ValidationException {
+		// ---- Extract Widths and default Width from the CIDFont dictionary
+		CFFType0FontContainer type0FontContainer = ((CompositeFontContainer)this.fontContainer).getCFFType0();
+		type0FontContainer.setFontObject(lCFonts);
+		return true;
+	}
+
+	/**
+	 * This method return false and updates the FontContainer if the Composite
+	 * Font TYPE 2 is damaged or missing.
+	 * 
+	 * @param pfDescriptor
+	 * @return
+	 */
+	boolean checkFontFileElement_CIDFontType2(PDFontDescriptorDictionary pfDescriptor) throws ValidationException {
+
+		// ---- FontFile Validation
+		PDStream ff1 = pfDescriptor.getFontFile();
+		PDStream ff2 = pfDescriptor.getFontFile2();
+		PDStream ff3 = pfDescriptor.getFontFile3();
+
+		boolean onlyOne = (ff1 != null && ff2 == null && ff3 == null)
+				|| (ff1 == null && ff2 != null && ff3 == null)
+				|| (ff1 == null && ff2 == null && ff3 != null);
+
+		if ((ff2 == null) || !onlyOne) {
+			this.fontContainer.addError(new ValidationResult.ValidationError(
+					ERROR_FONTS_FONT_FILEX_INVALID, "The FontFile is invalid"));
+			return false;
+		}
+
+		// ---- Stream validation should be done by the StreamValidateHelper.
+		// ---- Process font specific check
+		COSStream stream = ff2.getStream();
+		if (stream == null) {
+			this.fontContainer.addError(new ValidationResult.ValidationError(
+					ERROR_FONTS_FONT_FILEX_INVALID, "The FontFile is missing"));
+			this.fontContainer.setFontProgramEmbedded(false);
+			return false;
+		}
+
+		// ---- try to load the font using the java.awt.font object.
+		// ---- if the font is invalid, an exception will be thrown
+		TrueTypeFont ttf = null;
+		try {
+			// ---- According to PDF Reference, CIDFontType2 is a TrueType font.
+			// ---- Remark : Java.awt.Font throws exception when a CIDFontType2 is
+			// parsed even if it is valid.
+			ttf = new CIDFontType2Parser(true).parseTTF(new ByteArrayInputStream(ff2.getByteArray()));
+		} catch (Exception e) {
+			// ---- Exceptionally, Exception is catched Here because of damaged font
+			// can throw NullPointer Exception...
+			this.fontContainer.addError(new ValidationResult.ValidationError(
+					ERROR_FONTS_CID_DAMAGED, "The FontFile can't be read"));
+			return false;
+		}
+
+		return checkTTFontMetrics(ttf) && checkFontFileMetaData(pfDescriptor, ff2);
+	}
+
+	/**
+	 * If the embedded font is a subset, the CIDSet entry is mandatory and must be
+	 * a Stream. This method returns true if the CIDSet entry respects conditions,
+	 * otherwise the method returns false and the FontContainer is updated.
+	 * 
+	 * @param pfDescriptor
+	 * @return
+	 */
+	protected boolean checkCIDSet(PDFontDescriptorDictionary pfDescriptor) {
+		if (isSubSet(pfDescriptor.getFontName())) {
+			COSBase cidset = pfDescriptor.getCOSDictionary().getItem(
+					COSName.getPDFName(FONT_DICTIONARY_KEY_CIDSET));
+			if (cidset == null	|| !COSUtils.isStream(cidset, cosDocument)) {
+				this.fontContainer.addError(new ValidationResult.ValidationError(
+						ERROR_FONTS_CIDSET_MISSING_FOR_SUBSET,
+						"The CIDSet entry is missing for the Composite Subset"));
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * 
+	 * @param fontDesc
+	 * @return
+	 * @throws ValidationException
+	 */
+	protected boolean processCIDFontType2(COSBase fontDesc)	throws ValidationException {
+		COSDictionary fontDescDic = COSUtils.getAsDictionary(fontDesc, cosDocument);
+		if (fontDescDic == null) {
+			throw new ValidationException(
+					"Unable to process CIDFontType2 because of the font descriptor is invalid.");
+		}
+		PDFontDescriptorDictionary pfDescriptor = new PDFontDescriptorDictionary(fontDescDic);
+		boolean isValid = checkFontDescriptorMandatoryFields(pfDescriptor);
+		isValid = isValid && checkCIDKeyedFontName(pfDescriptor);
+		isValid = isValid && checkFontFileElement_CIDFontType2(pfDescriptor);
+		isValid = isValid && checkCIDSet(pfDescriptor);
+		return isValid;
+	}
+}

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/CompositeFontValidator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/FontMetaDataValidation.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/FontMetaDataValidation.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/FontMetaDataValidation.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/FontMetaDataValidation.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,188 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+import org.apache.padaf.xmpbox.XMPMetadata;
+import org.apache.padaf.xmpbox.schema.DublinCoreSchema;
+import org.apache.padaf.xmpbox.schema.XMPRightsManagementSchema;
+import org.apache.padaf.xmpbox.type.AbstractField;
+import org.apache.padaf.xmpbox.type.BooleanType;
+import org.apache.padaf.xmpbox.type.ComplexProperty;
+import org.apache.padaf.xmpbox.type.TextType;
+import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
+import org.apache.pdfbox.preflight.PreflightConstants;
+import org.apache.pdfbox.preflight.ValidationResult.ValidationError;
+import org.apache.pdfbox.preflight.exception.ValidationException;
+
+/**
+ * Class used to validate the MetaData entry of the Font File Stream dictionary.
+ */
+public class FontMetaDataValidation {
+
+	public List<ValidationError> validatePDFAIdentifer(XMPMetadata metadata,
+			PDFontDescriptor fontDesc) throws ValidationException {
+		List<ValidationError> ve = new ArrayList<ValidationError>();
+
+		analyseFontName(metadata, fontDesc, ve);
+		analyseRights(metadata, fontDesc, ve);
+
+		return ve;
+	}
+
+	/**
+	 * Value of the dc:title must be the same as the FontName in the font
+	 * descriptor.
+	 * 
+	 * @param metadata
+	 *          XMPMetaData of the Font File Stream
+	 * @param fontDesc
+	 *          The FontDescriptor dictionary
+	 * @param ve
+	 *          the list of validation error to update if the validation fails
+	 * @throws ValidationException
+	 */
+	public boolean analyseFontName(XMPMetadata metadata,
+			PDFontDescriptor fontDesc, List<ValidationError> ve)
+	throws ValidationException {
+		String fontName = fontDesc.getFontName();
+		String noSubSetName = fontName;
+		if (AbstractFontValidator.isSubSet(fontName)) {
+			noSubSetName = fontName.split(AbstractFontValidator
+					.getSubSetPatternDelimiter())[1];
+		}
+
+		DublinCoreSchema dc = metadata.getDublinCoreSchema();
+		if (dc != null) {
+			if (dc.getTitle() != null) {
+				String defaultTitle = dc.getTitleValue("x-default");
+				if (defaultTitle != null) {
+
+					if (!defaultTitle.equals(fontName)
+							&& (noSubSetName != null && !defaultTitle.equals(noSubSetName))) {
+						StringBuilder sb = new StringBuilder(80);
+						sb
+						.append("FontName")
+						.append(
+						" present in the FontDescriptor dictionary doesn't match with XMP information dc:title of the Font File Stream.");
+						ve.add(new ValidationError(
+								PreflightConstants.ERROR_METADATA_MISMATCH, sb.toString()));
+						return false;
+					}
+
+					// --- default value is the right one
+					return true;
+				} else {
+					Iterator<AbstractField> it = dc.getTitle().getContainer()
+					.getAllProperties().iterator();
+					boolean empty = true;
+					while (it.hasNext()) {
+						empty = false;
+						AbstractField tmp = it.next();
+						if (tmp != null && tmp instanceof TextType) {
+							if (((TextType) tmp).getStringValue().equals(fontName)
+									|| (noSubSetName != null && ((TextType) tmp).getStringValue()
+											.equals(noSubSetName))) {
+								// value found, return
+								return true;
+							}
+						}
+					}
+
+					// title doesn't match, it is an error.
+					StringBuilder sb = new StringBuilder(80);
+					sb.append("FontName");
+					if (empty) {
+						sb
+						.append(" present in the FontDescriptor dictionary can't be found in XMP information the Font File Stream.");
+						ve.add(new ValidationError(
+								PreflightConstants.ERROR_METADATA_PROPERTY_MISSING, sb
+								.toString()));
+					} else {
+						sb
+						.append(" present in the FontDescriptor dictionary doesn't match with XMP information dc:title of the Font File Stream.");
+						ve.add(new ValidationError(
+								PreflightConstants.ERROR_METADATA_MISMATCH, sb.toString()));
+					}
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * If XMP MetaData are present, they must have followings information :
+	 * <UL>
+	 * <li>dc:rights
+	 * <li>Marked (with the value true)
+	 * <li>Owner
+	 * <li>UsageTerms
+	 * </UL>
+	 * 
+	 * @param metadata
+	 *          XMPMetaData of the Font File Stream
+	 * @param fontDesc
+	 *          The FontDescriptor dictionary
+	 * @param ve
+	 *          the list of validation error to update if the validation fails
+	 * @throws ValidationException
+	 */
+	public boolean analyseRights(XMPMetadata metadata, PDFontDescriptor fontDesc, List<ValidationError> ve)
+	throws ValidationException {
+
+		DublinCoreSchema dc = metadata.getDublinCoreSchema();
+		if (dc != null) {
+			ComplexProperty copyrights = dc.getRights();
+			if (copyrights == null || copyrights.getContainer() == null
+					|| copyrights.getContainer().getAllProperties().isEmpty()) {
+				ve
+				.add(new ValidationError(
+						PreflightConstants.ERROR_METADATA_PROPERTY_MISSING,
+				"CopyRights is missing from the XMP information (dc:rights) of the Font File Stream."));
+				return false;
+			}
+		}
+
+		XMPRightsManagementSchema rights = metadata.getXMPRightsManagementSchema();
+		if (rights != null) {
+			BooleanType marked = rights.getMarked();
+			if (marked != null && !marked.getValue()) {
+				ve
+				.add(new ValidationError(
+						PreflightConstants.ERROR_METADATA_PROPERTY_MISSING,
+				"the XMP information (xmpRights:Marked) is invalid for the Font File Stream."));
+				return false;
+			}
+
+			/*
+			 * rights.getUsageTerms() & rights.getOwnerValue() should be present but it is only a 
+			 * recommendation : may be it should be useful to append a Warning if these entries are missing.  
+			 */
+		}
+		return true;
+	}
+}

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/FontMetaDataValidation.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/FontValidator.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/FontValidator.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/FontValidator.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/FontValidator.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,53 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+import org.apache.pdfbox.preflight.font.AbstractFontContainer.State;
+import org.apache.pdfbox.preflight.exception.ValidationException;
+
+
+public interface FontValidator {
+
+  /**
+   * Call this method to validate the font wrapped by this interface
+   * implementation. Return true if the validation succeed, false otherwise. If
+   * the validation failed, the error is updated in the FontContainer with the
+   * right error code.
+   * 
+   * @return
+   */
+  public abstract void validate() throws ValidationException;
+
+  /**
+   * Return the State of the Font Validation. Three values are possible :
+   * <UL>
+   * <li>VALID : there are no errors
+   * <li>MAYBE : Metrics aren't consistent of the FontProgram isn't embedded,
+   * but it can be valid.
+   * <li>INVALID : the validation fails
+   * </UL>
+   * 
+   * @return
+   */
+  public State getState();
+
+}

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/FontValidator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/GlyphDetail.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/GlyphDetail.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/GlyphDetail.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/GlyphDetail.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,46 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+public class GlyphDetail {
+	private GlyphException invalidGlyphError = null;
+	private int charecterIdentifier = 0;
+	
+	public GlyphDetail(int cid) {
+		this.charecterIdentifier = cid;
+	}
+
+	public GlyphDetail(int cid, GlyphException error) {
+		this.charecterIdentifier = cid;
+		this.invalidGlyphError = error;
+	}
+
+	public void throwExceptionIfNotValid() throws GlyphException {
+		if (this.invalidGlyphError != null) {
+			throw this.invalidGlyphError;
+		}
+	}
+
+	public int getCID() {
+		return this.charecterIdentifier;
+	}
+}

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/GlyphDetail.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/GlyphException.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/GlyphException.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/GlyphException.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/GlyphException.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,48 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+public class GlyphException extends Exception {
+	private String errorCode;
+	private int invalidCid;
+	
+	public GlyphException(String code, int cid) {
+		super();
+		this.errorCode = code;
+		this.invalidCid = cid;
+	}
+
+	public GlyphException(String code, int cid, String message) {
+		super(message);
+		this.errorCode = code;
+		this.invalidCid = cid;
+	}
+	
+	public String getErrorCode() {
+		return errorCode;
+	}
+
+	public int getInvalidCid() {
+		return invalidCid;
+	}
+
+}

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/GlyphException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/PDFAType3StreamParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/PDFAType3StreamParser.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/PDFAType3StreamParser.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/PDFAType3StreamParser.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,168 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+import java.awt.Image;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.fontbox.util.BoundingBox;
+import org.apache.pdfbox.cos.COSFloat;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.cos.COSNumber;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDInlinedImage;
+import org.apache.pdfbox.preflight.PreflightContext;
+import org.apache.pdfbox.preflight.content.ContentStreamEngine;
+import org.apache.pdfbox.util.ImageParameters;
+import org.apache.pdfbox.util.PDFOperator;
+
+/**
+ * This class is used to parse a glyph of a Type3 font program. If the glyph is
+ * parsed without error, the width of the glyph is accessible through the
+ * getWidth method.
+ */
+public class PDFAType3StreamParser extends ContentStreamEngine {
+  private boolean firstOperator = true;
+  private float width = 0;
+
+  private PDInlinedImage image = null;
+  private BoundingBox box = null;
+
+  public PDFAType3StreamParser(PreflightContext context, PDPage page) {
+    super(context, page);
+  }
+
+  /**
+   * This will parse a type3 stream and create an image from it.
+   * 
+   * @param type3Stream
+   *          The stream containing the operators to draw the image.
+   * 
+   * @return The image that was created.
+   * 
+   * @throws IOException
+   *           If there is an error processing the stream.
+   */
+  public Image createImage(COSStream type3Stream) throws IOException {
+  	resetEngine();
+    processSubStream(null, null, type3Stream);
+    return image.createImage();
+  }
+
+  /**
+   * This is used to handle an operation.
+   * 
+   * @param operator
+   *          The operation to perform.
+   * @param arguments
+   *          The list of arguments.
+   * 
+   * @throws IOException
+   *           If there is an error processing the operation.
+   */
+  protected void processOperator(PDFOperator operator, List arguments)
+      throws IOException {
+    super.processOperator(operator, arguments);
+    String operation = operator.getOperation();
+
+    if (operation.equals("BI")) {
+      ImageParameters params = operator.getImageParameters();
+      image = new PDInlinedImage();
+      image.setImageParameters(params);
+      image.setImageData(operator.getImageData());
+
+      validImageFilter(operator);
+      validImageColorSpace(operator);
+    }
+
+    if (operation.equals("d0")) {
+      // set glyph with for a type3 font
+      // COSNumber horizontalWidth = (COSNumber)arguments.get( 0 );
+      // COSNumber verticalWidth = (COSNumber)arguments.get( 1 );
+      // width = horizontalWidth.intValue();
+      // height = verticalWidth.intValue();
+
+      checkType3FirstOperator(arguments);
+
+    } else if (operation.equals("d1")) {
+      // set glyph with and bounding box for type 3 font
+      // COSNumber horizontalWidth = (COSNumber)arguments.get( 0 );
+      // COSNumber verticalWidth = (COSNumber)arguments.get( 1 );
+      COSNumber llx = (COSNumber) arguments.get(2);
+      COSNumber lly = (COSNumber) arguments.get(3);
+      COSNumber urx = (COSNumber) arguments.get(4);
+      COSNumber ury = (COSNumber) arguments.get(5);
+
+      // width = horizontalWidth.intValue();
+      // height = verticalWidth.intValue();
+      box = new BoundingBox();
+      box.setLowerLeftX(llx.floatValue());
+      box.setLowerLeftY(lly.floatValue());
+      box.setUpperRightX(urx.floatValue());
+      box.setUpperRightY(ury.floatValue());
+
+      checkType3FirstOperator(arguments);
+    }
+
+    checkColorOperators(operation);
+    validRenderingIntent(operator, arguments);
+    checkSetColorSpaceOperators(operator, arguments);
+    validNumberOfGraphicStates(operator);
+    firstOperator = false;
+  }
+
+  /**
+   * According to the PDF Reference, the first operator in a CharProc of a Type3
+   * font must be "d0" or "d1". This method process this validation. This method
+   * is called by the processOperator method.
+   * 
+   * @param arguments
+   * @throws IOException
+   */
+  private void checkType3FirstOperator(List arguments) throws IOException {
+    if (!firstOperator) {
+      throw new IOException("Type3 CharProc : First operator must be d0 or d1");
+    }
+
+    Object obj = arguments.get(0);
+    if (obj instanceof Number) {
+      width = ((Number) obj).intValue();
+    } else if (obj instanceof COSInteger) {
+      width = ((COSInteger) obj).floatValue();
+    } else if (obj instanceof COSFloat) {
+    	width = ((COSFloat)obj).floatValue();
+    } else {
+      throw new IOException(
+          "Unexpected argument type. Expected : COSInteger or Number / Received : "
+              + obj.getClass().getName());
+    }
+  }
+
+  /**
+   * @return the width of the CharProc glyph description
+   */
+  public float getWidth() {
+    return this.width;
+  }
+}

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/PDFAType3StreamParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/SimpleFontValidator.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/SimpleFontValidator.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/SimpleFontValidator.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/SimpleFontValidator.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,226 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+
+import java.util.List;
+
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSDocument;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
+import org.apache.pdfbox.pdmodel.font.PDFontDescriptorAFM;
+import org.apache.pdfbox.pdmodel.font.PDFontDescriptorDictionary;
+import org.apache.pdfbox.preflight.PreflightConstants;
+import org.apache.pdfbox.preflight.PreflightContext;
+import org.apache.pdfbox.preflight.ValidationResult;
+import org.apache.pdfbox.preflight.ValidationResult.ValidationError;
+import org.apache.pdfbox.preflight.exception.ValidationException;
+
+public abstract class SimpleFontValidator extends AbstractFontValidator {
+	protected String basefont;
+	protected int firstChar;
+	protected int lastChar;
+	protected List<Float> widths;
+	/**
+	 * The PdfBox font descriptor dictionary wrapper.
+	 */
+	protected PDFontDescriptor pFontDesc = null;
+
+	public SimpleFontValidator(PreflightContext context, PDFont font)
+			throws ValidationException {
+		super(context, font);
+		this.pFontDesc = font.getFontDescriptor();
+	}
+
+	/**
+	 * Extract element from the COSObject to avoid useless access to this object.
+	 */
+	private void extractElementsToCheck() {
+		// here is required elements
+		this.basefont = pFont.getBaseFont();
+		this.firstChar = pFont.getFirstChar();
+		this.lastChar = pFont.getLastChar();
+		this.widths = pFont.getWidths();
+	}
+
+	/**
+	 * Check if All required fields of a Font Dictionary are present. If there are
+	 * some missing fields, this method returns false and the FontContainer is
+	 * updated.
+	 * 
+	 * @return
+	 */
+	protected boolean checkMandatoryFields() {
+		String type = pFont.getBaseFont();
+		String subtype = pFont.getSubType();
+
+		if (this.pFontDesc == null) {
+			this.fontContainer
+			.addError(new ValidationError(ERROR_FONTS_FONT_FILEX_INVALID,
+					"The FontDescriptor is missing, so the Font Program isn't embedded."));
+			this.fontContainer.setFontProgramEmbedded(false);
+			return false;
+		}
+
+		if ((type == null || "".equals(type))
+				|| (subtype == null || "".equals(subtype))) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_DICTIONARY_INVALID,
+					"Type and/or Subtype keys are missing"));
+			return false;
+		} else {
+			extractElementsToCheck();
+			// ---- according to the subtype, the validation process isn't the same.
+			return checkSpecificMandatoryFields();
+		}
+	}
+
+	/**
+	 * This method checks the presence of some fields according to the Font type
+	 * 
+	 * @return
+	 */
+	protected abstract boolean checkSpecificMandatoryFields();
+
+	/**
+	 * Check if the widths array contains integer and if its length is valid. If
+	 * the validation fails, the FontContainer is updated.
+	 * 
+	 * @param cDoc
+	 */
+	protected boolean checkWidthsArray(COSDocument cDoc) {
+		if (widths == null) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_DICTIONARY_INVALID, "The Widths array is unreachable"));
+			return false;
+		}
+
+		int expectedLength = (lastChar - firstChar) + 1;
+		if (widths.size() != expectedLength) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_DICTIONARY_INVALID,
+					"The length of Witdhs array is invalid. Expected : \""
+							+ expectedLength + "\" Current : \"" + widths.size() + "\""));
+			return false;
+		}
+		return true;
+	}
+
+	protected boolean checkEncoding(COSDocument cDoc) {
+		return true;
+	}
+
+	protected boolean checkToUnicode(COSDocument cDoc) {
+		// Check the toUnicode -- Useless for PDF/A 1-b
+		return true;
+	}
+
+	/**
+	 * This method checks the font descriptor dictionary and embedded font files.
+	 * If the FontDescriptor validation fails, the FontContainer is updated.
+	 * 
+	 * @return
+	 */
+	protected abstract boolean checkFontDescriptor() throws ValidationException;
+
+	/**
+	 * Check if all required fields are present in the PDF file to describe the
+	 * Font Descriptor. If validation fails, FontConatiner is updated and false is
+	 * returned.
+	 */
+	protected boolean checkFontDescriptorMandatoryFields() throws ValidationException {
+		boolean fname = false, flags = false, itangle = false, cheight = false;
+		boolean fbbox = false, asc = false, desc = false, stemv = false;
+
+		if(pFontDesc instanceof PDFontDescriptorDictionary) {
+			COSDictionary fDescriptor = ((PDFontDescriptorDictionary) pFontDesc).getCOSDictionary();
+			for (Object key : fDescriptor.keySet()) {
+				if (!(key instanceof COSName)) {
+					this.fontContainer.addError(new ValidationResult.ValidationError(
+							PreflightConstants.ERROR_SYNTAX_DICTIONARY_KEY_INVALID,
+							"Invalid key in The font descriptor"));
+					return false;
+				}
+
+				String cosName = ((COSName) key).getName();
+				if (cosName.equals(FONT_DICTIONARY_KEY_FONTNAME)) {
+					fname = true;
+				}
+				if (cosName.equals(FONT_DICTIONARY_KEY_FLAGS)) {
+					flags = true;
+				}
+				if (cosName.equals(FONT_DICTIONARY_KEY_ITALICANGLE)) {
+					itangle = true;
+				}
+				if (cosName.equals(FONT_DICTIONARY_KEY_CAPHEIGHT)) {
+					cheight = true;
+				}
+				if (cosName.equals(FONT_DICTIONARY_KEY_FONTBBOX)) {
+					fbbox = true;
+				}
+				if (cosName.equals(FONT_DICTIONARY_KEY_ASCENT)) {
+					asc = true;
+				}
+				if (cosName.equals(FONT_DICTIONARY_KEY_DESCENT)) {
+					desc = true;
+				}
+				if (cosName.equals(FONT_DICTIONARY_KEY_STEMV)) {
+					stemv = true;
+				}
+			}
+
+			if (!(fname && flags && itangle && cheight && fbbox && asc && desc && stemv)) {
+				this.fontContainer.addError(new ValidationError(
+						ERROR_FONTS_DESCRIPTOR_INVALID, "Some mandatory fields are missing"));
+				return false;
+			}
+		} else if(pFontDesc instanceof PDFontDescriptorAFM) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_DESCRIPTOR_INVALID, "Font Descriptor is missing"));
+			return false;
+		} else {
+			throw new ValidationException("Invalid FontDescription object, expected PDFontDescriptorDictionary");
+		}
+		return true;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * net.awl.edoc.pdfa.validation.font.FontValidator#validate(java.util.List)
+	 */
+	public void validate() throws ValidationException {
+		COSDocument cDoc = context.getDocument().getDocument();
+		if (!checkMandatoryFields()) {
+			return ;
+		}
+
+		boolean result = true;
+		result = result && checkWidthsArray(cDoc);
+		result = result && checkFontDescriptor();
+		result = result && checkEncoding(cDoc);
+		result = result && checkToUnicode(cDoc);
+	}
+}

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/SimpleFontValidator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/TrueTypeFontContainer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/TrueTypeFontContainer.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/TrueTypeFontContainer.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/TrueTypeFontContainer.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,134 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+import java.io.IOException;
+
+import org.apache.fontbox.ttf.CMAPEncodingEntry;
+import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.pdfbox.encoding.Encoding;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.preflight.PreflightConstants;
+
+public class TrueTypeFontContainer extends AbstractFontContainer {
+	/**
+	 * Object which contains the TrueType font data extracted by the
+	 * TrueTypeParser object
+	 */
+	private TrueTypeFont fontObject = null;
+	private CMAPEncodingEntry[] cmaps = null;
+
+	private int numberOfLongHorMetrics;
+	private int unitsPerEm;
+	private int[] glyphWidths;
+
+	public TrueTypeFontContainer(PDFont fd) {
+		super(fd);
+	}
+
+	void setFontObjectAndInitializeInnerFields(TrueTypeFont fontObject) {
+		this.fontObject = fontObject;
+		this.unitsPerEm = this.fontObject.getHeader().getUnitsPerEm();
+		this.glyphWidths = this.fontObject.getHorizontalMetrics().getAdvanceWidth();
+		// ---- In a Mono space font program, the length of the AdvanceWidth array must be one.
+		// ---- According to the TrueType font specification, the Last Value of the AdvanceWidth array
+		// ---- is apply to the subsequent glyphs. So if the GlyphId is greater than the length of the array
+		// ---- the last entry is used.
+		this.numberOfLongHorMetrics = this.fontObject.getHorizontalHeader().getNumberOfHMetrics();
+	}
+
+	void setCMap(CMAPEncodingEntry[] cmaps) {
+		this.cmaps = cmaps;
+	}
+
+  @Override
+  public void checkCID(int cid) throws GlyphException {
+      if (isAlreadyComputedCid(cid)) {
+          return;
+      }
+      for (CMAPEncodingEntry entry : cmaps) {
+          if (checkCID(cid,entry)) {
+              return;
+          }
+      }
+      GlyphException e = new GlyphException(PreflightConstants.ERROR_FONTS_GLYPH_MISSING, 
+      		cid, 
+      		"The character \"" + cid 
+      		+ "\" in the font program \""
+      		+ this.font.getBaseFont() 
+      		+ "\"is missing from the Charater Encoding.");
+      addKnownCidElement(new GlyphDetail(cid, e));
+      throw e;	
+  }
+
+  public boolean checkCID(int cid, CMAPEncodingEntry cmap) throws GlyphException {
+
+      final float widthProvidedByPdfDictionary = this.font.getFontWidth(cid);
+      float widthInFontProgram ;
+
+      int innerFontCid = cid;
+
+
+      if (cmap.getPlatformEncodingId() == 1 && cmap.getPlatformId() == 3) {
+          try {
+              Encoding fontEncoding = this.font.getFontEncoding();
+              String character = fontEncoding.getCharacter(cid);
+              if (character == null) {
+                  return false;
+              }
+
+              char[] characterArray = character.toCharArray();
+              if (characterArray.length == 1 ) {
+                  innerFontCid = (int)characterArray[0];
+              } else {
+                  // TODO OD-PDFA-87 A faire?
+                  innerFontCid = (int)characterArray[0];
+                  for (int i = 1; i < characterArray.length; ++i) {
+                      if (cmap.getGlyphId((int)characterArray[i]) == 0) {
+                          return false;
+                      }
+                  }
+              }
+          } catch (IOException ioe) {
+              return false;
+          }
+      }
+
+      // search glyph
+      int glyphId = cmap.getGlyphId(innerFontCid);
+      if (glyphId == 0) {
+          return false;
+      }
+
+      // compute glyph width
+      float glypdWidth = glyphWidths[numberOfLongHorMetrics - 1];
+      if (glyphId < numberOfLongHorMetrics) {
+          glypdWidth = glyphWidths[glyphId];
+      }
+
+      widthInFontProgram = ((glypdWidth * 1000) / unitsPerEm);
+      checkWidthsConsistency(cid, widthProvidedByPdfDictionary, widthInFontProgram);
+      addKnownCidElement(new GlyphDetail(cid));
+      return true;
+  }
+
+}
\ No newline at end of file

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/TrueTypeFontContainer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/TrueTypeFontValidator.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/TrueTypeFontValidator.java?rev=1355309&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/TrueTypeFontValidator.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/TrueTypeFontValidator.java Fri Jun 29 10:25:22 2012
@@ -0,0 +1,265 @@
+/*****************************************************************************
+ * 
+ * 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.pdfbox.preflight.font;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.fontbox.ttf.CMAPEncodingEntry;
+import org.apache.fontbox.ttf.CMAPTable;
+import org.apache.fontbox.ttf.TTFParser;
+import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.encoding.Encoding;
+import org.apache.pdfbox.encoding.MacRomanEncoding;
+import org.apache.pdfbox.encoding.WinAnsiEncoding;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDFontDescriptorDictionary;
+import org.apache.pdfbox.preflight.PreflightConstants;
+import org.apache.pdfbox.preflight.PreflightContext;
+import org.apache.pdfbox.preflight.ValidationResult;
+import org.apache.pdfbox.preflight.ValidationResult.ValidationError;
+import org.apache.pdfbox.preflight.exception.ValidationException;
+
+public class TrueTypeFontValidator extends SimpleFontValidator {
+
+
+	public TrueTypeFontValidator(PreflightContext context, PDFont font) 
+			throws ValidationException {
+		super(context, font);
+	}
+
+	/**
+	 * Check if mandatory fields are present. Return false if a field is missing,
+	 * true otherwise. If validation fails, the FontContainer is updated.
+	 */
+	protected boolean checkSpecificMandatoryFields() {
+		// ---- name is required only in a PDF-1.0.
+		// ---- Currently our grammar matches only with PDF-1.[1-4]
+		// ---- BaseFont is required and is usually the FontName
+		if (basefont == null || "".equals(basefont)) {
+			this.fontContainer.addError(new ValidationError(
+					ERROR_FONTS_DICTIONARY_INVALID, "BaseFont is missing"));
+			// continue to process this font dictionary is useless
+			return false;
+		}
+
+		boolean allPresent = (firstChar >=  0 && lastChar >=  0 && widths != null);
+		if (!allPresent) {
+			this.fontContainer.addError(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID, "Required keys are missing"));
+			return false;
+		} // else  ok
+
+
+		return true;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * net.awl.edoc.pdfa.validation.font.SimpleFontValidator#checkFontDescriptor()
+	 */
+	@Override
+	protected boolean checkFontDescriptor() throws ValidationException {
+		boolean res = checkFontDescriptorMandatoryFields();
+		res = res && checkFontName();
+		res = res && checkFontFileElement();
+		return res;
+	}
+
+	/**
+	 * If the FontName is missing from the FontDescriptor dictionary, this method
+	 * returns false and the FontContainer is updated.
+	 * 
+	 * @return
+	 */
+	protected boolean checkFontName() {
+		String fontName = this.pFontDesc.getFontName();
+		if (fontName == null) {
+			this.fontContainer.addError(new ValidationResult.ValidationError(
+					PreflightConstants.ERROR_FONTS_DESCRIPTOR_INVALID,
+					"The FontName in font descriptor is null"));
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * This methods validates the Font Stream. If the font is damaged or missing
+	 * the FontContainer is updated and false is returned. Moreover, this method
+	 * checks the Encoding property of the FontDescriptor dictionary.
+	 * 
+	 * @return
+	 */
+	protected boolean checkFontFileElement() throws ValidationException {
+		if (pFontDesc instanceof PDFontDescriptorDictionary) {
+			PDStream ff1 = ((PDFontDescriptorDictionary)pFontDesc).getFontFile();
+			PDStream ff2 = ((PDFontDescriptorDictionary)pFontDesc).getFontFile2();
+			PDStream ff3 = ((PDFontDescriptorDictionary)pFontDesc).getFontFile3();
+			boolean onlyOne = (ff1 != null && ff2 == null && ff3 == null)
+					|| (ff1 == null && ff2 != null && ff3 == null)
+					|| (ff1 == null && ff2 == null && ff3 != null);
+
+			if (ff2 == null || !onlyOne) {
+				this.fontContainer.addError(new ValidationResult.ValidationError(
+						PreflightConstants.ERROR_FONTS_FONT_FILEX_INVALID,
+						"The FontFile2 is invalid"));
+				return false;
+			}
+
+			// ---- Stream validation should be done by the StreamValidateHelper.
+			// ---- Process font specific check
+			COSStream stream = ff2.getStream();
+			if (stream == null) {
+				this.fontContainer.addError(new ValidationResult.ValidationError(
+						PreflightConstants.ERROR_FONTS_FONT_FILEX_INVALID,
+						"The FontFile is missing"));
+				this.fontContainer.setFontProgramEmbedded(false);
+				return false;
+			}
+
+			boolean hasLength1 = stream.getInt(COSName.getPDFName(FONT_DICTIONARY_KEY_LENGTH1)) > 0;
+			if (!hasLength1) {
+				this.fontContainer.addError(new ValidationResult.ValidationError(
+						PreflightConstants.ERROR_FONTS_FONT_FILEX_INVALID,
+						"The FontFile is invalid"));
+				return false;
+			}
+
+			// ---- check the encoding part.
+			if (pFontDesc.isNonSymbolic()) {
+				// ---- only MacRomanEncoding or WinAnsiEncoding are allowed for a non
+				// symbolic font
+				Encoding encodingValue = this.pFont.getFontEncoding();
+				if (encodingValue == null
+						|| !(encodingValue instanceof MacRomanEncoding || encodingValue instanceof WinAnsiEncoding)) {
+					this.fontContainer.addError(new ValidationResult.ValidationError(
+							PreflightConstants.ERROR_FONTS_ENCODING,
+							"The Encoding is invalid for the NonSymbolic TTF"));
+					return false;
+				}
+			} else if (pFontDesc.isSymbolic()) {
+				// ---- For symbolic font, no encoding entry is allowed and only one
+				// encoding entry is expected into the FontFile CMap
+				if (((COSDictionary) this.pFont.getCOSObject()).getItem(COSName
+						.getPDFName(FONT_DICTIONARY_KEY_ENCODING)) != null) {
+					this.fontContainer.addError(new ValidationResult.ValidationError(
+							PreflightConstants.ERROR_FONTS_ENCODING,
+							"The Encoding should be missing for the Symbolic TTF"));
+					return false;
+				} // else check the content of the Font CMap (see below)
+
+			} else {
+				// ----- should never happen
+				return true;
+			}
+
+			/*
+			 * ---- try to load the font using the TTFParser object. If the font is
+			 * invalid, an exception will be thrown. Because of it is a Embedded Font
+			 * Program, some tables are required and other are optional see PDF
+			 * Reference (§5.8)
+			 */
+			ByteArrayInputStream bis = null;
+			try {
+				bis = new ByteArrayInputStream(ff2.getByteArray());
+				TrueTypeFont ttf = new TTFParser(true).parseTTF(bis);
+				if (pFontDesc.isSymbolic() && ttf.getCMAP().getCmaps().length != 1) {
+					this.fontContainer.addError(new ValidationResult.ValidationError(
+							PreflightConstants.ERROR_FONTS_ENCODING,
+							"The Encoding should be missing for the Symbolic TTF"));
+					return false;
+				}
+
+				((TrueTypeFontContainer)this.fontContainer).setFontObjectAndInitializeInnerFields(ttf);
+				((TrueTypeFontContainer)this.fontContainer).setCMap(getCMapOfFontProgram(ttf));
+
+				return checkFontFileMetaData(pFontDesc, ff2);
+			} catch (IOException e) {
+				this.fontContainer.addError(new ValidationResult.ValidationError(
+						PreflightConstants.ERROR_FONTS_TRUETYPE_DAMAGED,
+						"The FontFile can't be read"));
+				return false;
+			} finally {
+				if (bis != null) {
+					IOUtils.closeQuietly(bis);
+				}
+			}
+		} else {
+			throw new ValidationException("Invalid FontDescription object, expected PDFontDescriptorDictionary");
+		}
+	}
+
+	/**
+	 * Return the CMap encoding entry to use. This CMap belong to the TrueType
+	 * Font Program.
+	 * 
+	 * Here the selection rules :
+	 * <UL>
+	 * <li>For a Symbolic TrueType, the Font Program has only one CMap (Checked in
+	 * the checkFontFileElement method)
+	 * <li>For a Non-Symbolic TrueType, only two CMap can be used (WinAnsi
+	 * (plateformId : 3 / encodingId : 1) or MacRoman (plateformId : 1 /
+	 * encodingId : 0) ). This CMap returns the CMap which corresponds to the
+	 * Encoding value of the FontDescriptor dictionary.
+	 * </UL>
+	 * 
+	 * @param ttf
+	 *          The FontBox object which manages a TrueType Font program.
+	 * @return
+	 * @throws ValidationException
+	 *           if the FontProgram doesn't have the expected CMap
+	 */
+	protected CMAPEncodingEntry[] getCMapOfFontProgram(TrueTypeFont ttf)
+			throws ValidationException {
+		CMAPTable cmap = ttf.getCMAP();
+		if (this.pFontDesc.isSymbolic()) {
+			return cmap.getCmaps();
+		} else {
+			List<CMAPEncodingEntry> res = new ArrayList<CMAPEncodingEntry>();
+			boolean firstIs31 = false;
+			for (CMAPEncodingEntry cmapEntry : cmap.getCmaps()) {
+				// ---- Returns the WinAnsiEncoding CMap
+				if ((cmapEntry.getPlatformId() == 3) && (cmapEntry.getPlatformEncodingId() == 1)) {
+					res.add(0,cmapEntry);
+					firstIs31 = true;
+				} else if ((cmapEntry.getPlatformId() == 1)&& (cmapEntry.getPlatformEncodingId() == 0)) {
+					if (firstIs31) {
+						res.add(1, cmapEntry);
+					} else {
+						res.add(0, cmapEntry);
+					}
+				} else {
+					res.add(cmapEntry);
+				}
+			}
+			return res.toArray(new CMAPEncodingEntry[res.size()]);
+		}
+	}
+}

Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/pdfbox/preflight/font/TrueTypeFontValidator.java
------------------------------------------------------------------------------
    svn:eol-style = native