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