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 2011/07/24 16:02:33 UTC
svn commit: r1150373 [5/12] - in /pdfbox/trunk/preflight: ./ src/ src/main/
src/main/java/ src/main/java/org/ src/main/java/org/apache/
src/main/java/org/apache/padaf/ src/main/java/org/apache/padaf/preflight/
src/main/java/org/apache/padaf/preflight/a...
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/CompositeFontValidator.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/CompositeFontValidator.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/CompositeFontValidator.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/CompositeFontValidator.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,883 @@
+/*****************************************************************************
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+package org.apache.padaf.preflight.font;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+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.padaf.preflight.DocumentHandler;
+import org.apache.padaf.preflight.ValidationConstants;
+import org.apache.padaf.preflight.ValidationException;
+import org.apache.padaf.preflight.ValidationResult;
+import org.apache.padaf.preflight.ValidationResult.ValidationError;
+import org.apache.padaf.preflight.utils.COSUtils;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSDocument;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.font.PDFontDescriptorDictionary;
+
+public class CompositeFontValidator extends AbstractFontValidator {
+ protected String basefont;
+ protected COSBase descendantFonts;
+ protected COSDictionary cidFont;
+ protected COSBase encoding;
+ protected COSStream cmap;
+ protected COSBase toUnicode;
+
+ protected CMap cidToGidMap = null;
+
+ protected boolean isIdentityCMap = false;
+
+ public CompositeFontValidator(DocumentHandler handler, COSObject obj)
+ throws ValidationException {
+ super(handler, obj);
+ }
+
+ /**
+ * 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() {
+ String type = fDictionary.getNameAsString(COSName
+ .getPDFName(DICTIONARY_KEY_TYPE));
+ String subtype = fDictionary.getNameAsString(COSName
+ .getPDFName(DICTIONARY_KEY_SUBTYPE));
+
+ // ---- 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 = fDictionary.getNameAsString(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_BASEFONT));
+ this.descendantFonts = fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_DESCENDANT_FONTS));
+ this.encoding = fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_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
+ .getPDFName(FONT_DICTIONARY_KEY_TOUNICODE));
+ 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
+ COSDocument cDoc = this.handler.getDocument().getDocument();
+ COSArray array = COSUtils.getAsArray(descendantFonts, cDoc);
+
+ 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), cDoc);
+ 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
+ .getPDFName(DICTIONARY_KEY_TYPE));
+ String subtype = cidFont.getNameAsString(COSName
+ .getPDFName(DICTIONARY_KEY_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, cDoc);
+ if (isT0) {
+ result = result && checkCIDToGIDMap(cidToGid, cDoc, false);
+ result = result && processCIDFontType0(fontDesc);
+ } else {
+ result = result && checkCIDToGIDMap(cidToGid, cDoc, 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
+ * @param cDoc
+ * @return
+ */
+ private boolean checkCIDSystemInfo(COSBase sysinfo, COSDocument cDoc) {
+ COSDictionary cidSysInfo = COSUtils.getAsDictionary(sysinfo, cDoc);
+ 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, cDoc) && COSUtils.isString(ord, cDoc) && COSUtils
+ .isInteger(sup, cDoc))) {
+ 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 cDoc
+ * @param mandatory
+ * @return
+ */
+ private boolean checkCIDToGIDMap(COSBase ctog, COSDocument cDoc,
+ boolean mandatory) {
+ if (COSUtils.isString(ctog, cDoc)) {
+ // ---- valid only if the string is Identity
+ String ctogStr = COSUtils.getAsString(ctog, cDoc);
+ 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, cDoc)) {
+ try {
+ COSStream ctogMap = COSUtils.getAsStream(ctog, cDoc);
+ this.cidToGidMap = new CMapParser().parse(null, ctogMap
+ .getUnfilteredStream());
+ } 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) {
+ COSDocument cDoc = this.handler.getDocument().getDocument();
+
+ if (COSUtils.isString(aEncoding, cDoc)) {
+ // ---- if encoding is a string, only 2 values are allowed
+ String str = COSUtils.getAsString(aEncoding, cDoc);
+ 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, cDoc)) {
+ // ---- 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, cDoc));
+ } 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) {
+ COSDocument cDoc = handler.getDocument().getDocument();
+
+ String type = aCMap
+ .getNameAsString(COSName.getPDFName(DICTIONARY_KEY_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));
+ 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, cDoc)) {
+ 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(); //getCmapEntry("CMapName");
+
+
+ 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() {
+ COSDocument cDoc = handler.getDocument().getDocument();
+ if (!isIdentityCMap) {
+ COSDictionary cmsi = COSUtils.getAsDictionary(this.cmap.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_CID_SYSINFO)), cDoc);
+ COSDictionary cfsi = COSUtils.getAsDictionary(this.cidFont
+ .getItem(COSName.getPDFName(FONT_DICTIONARY_KEY_CID_SYSINFO)), cDoc);
+
+ String regCM = COSUtils.getAsString(cmsi.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_SYSINFO_REGISTRY)), cDoc);
+ String ordCM = COSUtils.getAsString(cmsi.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_SYSINFO_ORDERING)), cDoc);
+
+ String regCF = COSUtils.getAsString(cfsi.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_SYSINFO_REGISTRY)), cDoc);
+ String ordCF = COSUtils.getAsString(cfsi.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_SYSINFO_ORDERING)), cDoc);
+
+ 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 boolean validate() throws ValidationException {
+ boolean result = checkMandatoryFields();
+ result = result && checkCIDFont();
+ result = result && checkCMap(encoding);
+ if (result) {
+ this.cmap = COSUtils.getAsStream(encoding, handler.getDocument()
+ .getDocument());
+ }
+ result = result && compareCIDSystemInfo();
+ return result;
+ }
+
+ /**
+ * Check if all required fields are present in the PDF file to describe the
+ * Font Descriptor.
+ *
+ * @param handler
+ * @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(
+ ValidationConstants.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, handler
+ .getDocument().getDocument());
+ 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, true);
+ 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
+ * @param checkConsistency
+ * true if the font name must be consistent with the BaseName of the
+ * Font dictionary
+ * @return
+ */
+ protected boolean checkCIDKeyedFontName(
+ PDFontDescriptorDictionary pfDescriptor, boolean checkConsistency) {
+ String fontName = pfDescriptor.getFontName();
+ String baseName = this.pFont.getBaseFont();
+
+ if (fontName == null) {
+ this.fontContainer.addError(new ValidationResult.ValidationError(
+ ERROR_FONTS_DESCRIPTOR_INVALID,
+ "The FontName in font descriptor is missing"));
+ return false;
+ }
+
+ if (checkConsistency
+ && !(fontName.equals(baseName) || fontName.contains(baseName) || baseName
+ .contains(fontName))) {
+ this.fontContainer
+ .addError(new ValidationResult.ValidationError(
+ ERROR_FONTS_DESCRIPTOR_INVALID,
+ "The FontName in font descriptor isn't the same as the BaseFont in the Font dictionary"));
+ 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
+ .getPDFName(DICTIONARY_KEY_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 {
+ LinkedHashMap<Integer, Integer> widths = getWidthsArray();
+ int defaultWidth = this.cidFont.getInt("DW", 1000);
+ 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.setPdfWidths(widths);
+ type2FontContainer.setCmap(this.cidToGidMap);
+ type2FontContainer.setDefaultGlyphWidth(defaultWidth);
+ 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
+ LinkedHashMap<Integer, Integer> widths = getWidthsArray();
+ int defaultWidth = this.cidFont.getInt("DW", 1000);
+ CFFType0FontContainer type0FontContainer = ((CompositeFontContainer)this.fontContainer).getCFFType0();
+ type0FontContainer.setFontObject(lCFonts);
+ type0FontContainer.setDefaultGlyphWidth(defaultWidth);
+ type0FontContainer.setWidthsArray(widths);
+
+ 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;
+ }
+
+ boolean hasLength1 = stream.getInt(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_LENGTH1)) > 0;
+ if (!hasLength1) {
+ this.fontContainer.addError(new ValidationResult.ValidationError(
+ ValidationConstants.ERROR_FONTS_FONT_FILEX_INVALID,
+ "The FontFile is invalid"));
+ 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);
+ }
+
+ /**
+ * For a CIDFont the width array, there are two formats of width array :
+ * <UL>
+ * <li>C [W1...Wn] : C is an integer specifying a starting CID value and the
+ * array of n numbers specify widths for n consecutive CIDs.
+ * <li>Cf Cl W : Defines the same width W for the range Cf to Cl
+ * </UL>
+ * This method gets a linked hash map of width where the key is a CID and the
+ * value is the Width.
+ *
+ * @return
+ * @throws ValidationException
+ */
+ protected LinkedHashMap<Integer, Integer> getWidthsArray()
+ throws ValidationException {
+ LinkedHashMap<Integer, Integer> widthsMap = new LinkedHashMap<Integer, Integer>();
+ COSDocument cDoc = handler.getDocument().getDocument();
+ COSBase cBase = this.cidFont.getItem(COSName.getPDFName("W"));
+ COSArray wArr = COSUtils.getAsArray(cBase, cDoc);
+
+ for (int i = 0; i < wArr.size();) {
+
+ int firstCid = wArr.getInt(i);
+
+ if (i + 1 >= wArr.size()) {
+ throw new ValidationException("Invalid format of the W entry");
+ }
+
+ COSBase cb = wArr.getObject(i + 1);
+ if (COSUtils.isArray(cb, cDoc)) {
+
+ // ---- First Format
+ COSArray seqWidths = COSUtils.getAsArray(cb, cDoc);
+ widthsMap.put(firstCid, seqWidths.getInt(0));
+ for (int jw = 1; jw < seqWidths.size(); jw++) {
+ widthsMap.put((firstCid + jw), seqWidths.getInt(jw));
+ }
+
+ i = i + 2;
+
+ } else {
+
+ // ---- Second Format
+ if (i + 2 >= wArr.size()) {
+ throw new ValidationException("Invalid format of the W entry");
+ }
+
+ int lastCid = wArr.getInt(i + 1);
+ int commonWidth = wArr.getInt(i + 2);
+ for (int jw = firstCid; jw <= lastCid; ++jw) {
+ widthsMap.put((firstCid + jw), commonWidth);
+ }
+
+ i = i + 3;
+
+ }
+
+ }
+
+ return widthsMap;
+ }
+
+ /**
+ * 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, this.handler.getDocument()
+ .getDocument())) {
+ 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, handler
+ .getDocument().getDocument());
+ 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, false);
+ isValid = isValid && checkFontFileElement_CIDFontType2(pfDescriptor);
+ isValid = isValid && checkCIDSet(pfDescriptor);
+ return isValid;
+ }
+}
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/CompositeFontValidator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontMetaDataValidation.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontMetaDataValidation.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontMetaDataValidation.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontMetaDataValidation.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,219 @@
+/*****************************************************************************
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+package org.apache.padaf.preflight.font;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+import org.apache.padaf.preflight.ValidationConstants;
+import org.apache.padaf.preflight.ValidationException;
+import org.apache.padaf.preflight.ValidationResult.ValidationError;
+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;
+
+/**
+ * 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.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(
+ ValidationConstants.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(
+ ValidationConstants.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(
+ ValidationConstants.ERROR_METADATA_MISMATCH, sb.toString()));
+ }
+ return false;
+ }
+ }
+
+ // ---- dc:title is required
+ ve.add(new ValidationError(
+ ValidationConstants.ERROR_METADATA_PROPERTY_MISSING,
+ "dc:title is missing from the FontFile MetaData"));
+ return false;
+ }
+
+ /**
+ * 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();
+ ComplexProperty copyrights = dc.getRights();
+ if (copyrights == null || copyrights.getContainer() == null
+ || copyrights.getContainer().getAllProperties().isEmpty()) {
+ ve
+ .add(new ValidationError(
+ ValidationConstants.ERROR_METADATA_PROPERTY_MISSING,
+ "CopyRights is missing from the XMP information (dc:rights) of the Font File Stream."));
+ return false;
+ }
+
+ XMPRightsManagementSchema rights = metadata.getXMPRightsManagementSchema();
+ BooleanType marked = rights.getMarked();
+ if (marked == null) {
+ ve
+ .add(new ValidationError(
+ ValidationConstants.ERROR_METADATA_PROPERTY_MISSING,
+ "the XMP information (xmpRights:Marked) missing for the Font File Stream."));
+ return false;
+ } else if (!marked.getValue()) {
+ ve
+ .add(new ValidationError(
+ ValidationConstants.ERROR_METADATA_PROPERTY_MISSING,
+ "the XMP information (xmpRights:Marked) is invalid for the Font File Stream."));
+ return false;
+ }
+
+ ComplexProperty usage = rights.getUsageTerms();
+ if (usage == null || usage.getContainer() == null
+ || usage.getContainer().getAllProperties().isEmpty()) {
+ ve
+ .add(new ValidationError(
+ ValidationConstants.ERROR_METADATA_PROPERTY_MISSING,
+ "Usage Terms are missing from the XMP information (xmpRights:UsageTerms) of the Font File Stream."));
+ return false;
+ }
+
+ List<String> owners = rights.getOwnerValue();
+ if (owners == null || owners.isEmpty()) {
+ ve.add(new ValidationError(
+ ValidationConstants.ERROR_METADATA_PROPERTY_MISSING,
+ "Owner missing from the XMP information (xmpRights:Owner) of the Font File Stream."));
+ return false;
+ } else {
+ boolean allEmpty = true;
+ for (String owner : owners) {
+ if (!"".equals(owner)) {
+ allEmpty = false;
+ }
+ }
+ if (allEmpty) {
+ ve.add(new ValidationError(
+ ValidationConstants.ERROR_METADATA_PROPERTY_MISSING,
+ "Owner missing from the XMP information (xmpRights:Owner) of the Font File Stream."));
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontMetaDataValidation.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontValidator.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontValidator.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontValidator.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontValidator.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,62 @@
+/*****************************************************************************
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+package org.apache.padaf.preflight.font;
+
+import java.util.List;
+
+import org.apache.padaf.preflight.ValidationException;
+import org.apache.padaf.preflight.ValidationResult.ValidationError;
+import org.apache.padaf.preflight.font.AbstractFontContainer.State;
+
+
+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 boolean 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();
+
+ /**
+ * Return all validation errors.
+ *
+ * @return
+ */
+ public List<ValidationError> getValdiationErrors();
+}
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontValidator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontValidatorFactory.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontValidatorFactory.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontValidatorFactory.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontValidatorFactory.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,81 @@
+/*****************************************************************************
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+package org.apache.padaf.preflight.font;
+
+import static org.apache.padaf.preflight.ValidationConstants.DICTIONARY_KEY_SUBTYPE;
+import static org.apache.padaf.preflight.ValidationConstants.DICTIONARY_KEY_TYPE;
+import static org.apache.padaf.preflight.ValidationConstants.ERROR_FONTS_DICTIONARY_INVALID;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_COMPOSITE;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_MMTYPE;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_TRUETYPE;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_TYPE0;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_TYPE0C;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_TYPE1;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_TYPE1C;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_TYPE2;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_TYPE3;
+
+import org.apache.padaf.preflight.DocumentHandler;
+import org.apache.padaf.preflight.ValidationException;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
+
+/**
+ * This class returns a FontValidator object according to the Font dictionary.
+ */
+public class FontValidatorFactory {
+
+ public FontValidator getFontValidator(COSObject cObj,
+ DocumentHandler handler) throws ValidationException {
+ COSDictionary dic = (COSDictionary) cObj.getObject();
+ String type = dic.getNameAsString(COSName.getPDFName(DICTIONARY_KEY_TYPE));
+ String subtype = dic.getNameAsString(COSName
+ .getPDFName(DICTIONARY_KEY_SUBTYPE));
+
+ if ((type == null || "".equals(type))
+ || (subtype == null || "".equals(subtype))) {
+ throw new ValidationException("Type and/or Subtype keys are missing : "
+ + ERROR_FONTS_DICTIONARY_INVALID);
+ } else {
+ if (FONT_DICTIONARY_VALUE_TRUETYPE.equals(subtype)) {
+ return new TrueTypeFontValidator(handler, cObj);
+ } else if (FONT_DICTIONARY_VALUE_MMTYPE.equals(subtype)
+ || FONT_DICTIONARY_VALUE_TYPE1.equals(subtype)) {
+ return new Type1FontValidator(handler, cObj);
+ } else if (FONT_DICTIONARY_VALUE_TYPE3.equals(subtype)) {
+ return new Type3FontValidator(handler, cObj);
+ } else if (FONT_DICTIONARY_VALUE_COMPOSITE.equals(subtype)) {
+ return new CompositeFontValidator(handler, cObj);
+ } else if (FONT_DICTIONARY_VALUE_TYPE2.equals(subtype)
+ || FONT_DICTIONARY_VALUE_TYPE1C.equals(subtype)
+ || FONT_DICTIONARY_VALUE_TYPE0C.equals(subtype)
+ || FONT_DICTIONARY_VALUE_TYPE0.equals(subtype)) {
+ // ---- Font managed by a Composite font.
+ // this dictionary will be checked by a CompositeFontValidator
+ return null;
+ } else {
+ throw new ValidationException("Unknown font type : " + subtype);
+ }
+ }
+ }
+}
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/FontValidatorFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/GlyphDetail.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/GlyphDetail.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/GlyphDetail.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/GlyphDetail.java Sun Jul 24 14:02:12 2011
@@ -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.padaf.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/padaf/preflight/font/GlyphDetail.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/GlyphException.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/GlyphException.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/GlyphException.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/GlyphException.java Sun Jul 24 14:02:12 2011
@@ -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.padaf.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/padaf/preflight/font/GlyphException.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/PDFAType3StreamParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/PDFAType3StreamParser.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/PDFAType3StreamParser.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/PDFAType3StreamParser.java Sun Jul 24 14:02:12 2011
@@ -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.padaf.preflight.font;
+
+import java.awt.Image;
+import java.io.IOException;
+import java.util.List;
+
+
+import org.apache.fontbox.util.BoundingBox;
+import org.apache.padaf.preflight.DocumentHandler;
+import org.apache.padaf.preflight.utils.ContentStreamEngine;
+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.graphics.xobject.PDInlinedImage;
+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(DocumentHandler handler) {
+ super(handler);
+ }
+
+ /**
+ * 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/padaf/preflight/font/PDFAType3StreamParser.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/SimpleFontValidator.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/SimpleFontValidator.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/SimpleFontValidator.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/SimpleFontValidator.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,269 @@
+/*****************************************************************************
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+package org.apache.padaf.preflight.font;
+
+
+import org.apache.padaf.preflight.DocumentHandler;
+import org.apache.padaf.preflight.ValidationConstants;
+import org.apache.padaf.preflight.ValidationException;
+import org.apache.padaf.preflight.ValidationResult;
+import org.apache.padaf.preflight.ValidationResult.ValidationError;
+import org.apache.padaf.preflight.utils.COSUtils;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSDocument;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
+import org.apache.pdfbox.pdmodel.font.PDFontDescriptorDictionary;
+
+public abstract class SimpleFontValidator extends AbstractFontValidator {
+ protected String basefont;
+ protected COSBase firstChar;
+ protected COSBase lastChar;
+ protected COSBase widths;
+ protected COSBase encoding;
+ protected COSBase toUnicode;
+ /**
+ * The PdfBox font descriptor dictionary wrapper.
+ */
+ protected PDFontDescriptorDictionary pFontDesc = null;
+ /**
+ * The font descriptor dictionary linked with the font dictionary
+ */
+ protected COSDictionary fDescriptor = null;
+
+ public SimpleFontValidator(DocumentHandler handler, COSObject obj)
+ throws ValidationException {
+ super(handler, obj);
+ COSBase tmpFontDesc = fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_FONT_DESC));
+ this.fDescriptor = COSUtils.getAsDictionary(tmpFontDesc, handler
+ .getDocument().getDocument());
+ if (this.fDescriptor != null) {
+ this.pFontDesc = new PDFontDescriptorDictionary(this.fDescriptor);
+ }
+ }
+
+ /**
+ * Extract element from the COSObject to avoid useless access to this object.
+ */
+ private void extractElementsToCheck() {
+ // ---- Here is required elements
+ this.basefont = fDictionary.getNameAsString(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_BASEFONT));
+ this.firstChar = fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_FIRSTCHAR));
+ this.lastChar = fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_LASTCHAR));
+ this.widths = fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_WIDTHS));
+ // ---- Here is optional elements
+ this.encoding = fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_ENCODING));
+ this.toUnicode = fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_TOUNICODE));
+ }
+
+ /**
+ * 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 = fDictionary.getNameAsString(COSName
+ .getPDFName(DICTIONARY_KEY_TYPE));
+ String subtype = fDictionary.getNameAsString(COSName
+ .getPDFName(DICTIONARY_KEY_SUBTYPE));
+
+ if (this.fDescriptor == 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) {
+ // ---- the Widths value can be a reference to an object
+ // ---- Access the object using the COSkey
+ COSArray wArr = COSUtils.getAsArray(this.widths, cDoc);
+
+ if (wArr == null) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID, "The Witdhs array is unreachable"));
+ return false;
+ }
+
+ // ---- firstChar and lastChar must be integer.
+ int fc = ((COSInteger) this.firstChar).intValue();
+ int lc = ((COSInteger) this.lastChar).intValue();
+
+ // ---- wArr length = (lc - fc) +1 and it is an array of int.
+ // ---- If FirstChar is greater than LastChar, the validation will fail
+ // because of
+ // ---- the array will have an expected size <= 0.
+ int expectedLength = (lc - fc) + 1;
+ if (wArr.size() != expectedLength) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "The length of Witdhs array is invalid. Expected : \""
+ + expectedLength + "\" Current : \"" + wArr.size() + "\""));
+ return false;
+ }
+
+ for (Object arrContent : wArr.toList()) {
+ boolean isInt = false;
+ if (arrContent instanceof COSBase) {
+ isInt = COSUtils.isInteger((COSBase) arrContent, cDoc);
+ }
+
+ if (!isInt) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "The Witdhs array is invalid. (some element aren't integer)"));
+ 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() {
+ boolean fname = false, flags = false, itangle = false, cheight = false;
+ boolean fbbox = false, asc = false, desc = false, stemv = false;
+
+ for (Object key : this.fDescriptor.keySet()) {
+ if (!(key instanceof COSName)) {
+ this.fontContainer.addError(new ValidationResult.ValidationError(
+ ValidationConstants.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;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * net.awl.edoc.pdfa.validation.font.FontValidator#validate(java.util.List)
+ */
+ public boolean validate() throws ValidationException {
+ COSDocument cDoc = handler.getDocument().getDocument();
+ if (!checkMandatoryFields()) {
+ return false;
+ }
+
+ boolean result = true;
+ result = result && checkWidthsArray(cDoc);
+ result = result && checkFontDescriptor();
+ result = result && checkEncoding(cDoc);
+ result = result && checkToUnicode(cDoc);
+ return result;
+ }
+}
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/SimpleFontValidator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/TrueTypeFontContainer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/TrueTypeFontContainer.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/TrueTypeFontContainer.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/TrueTypeFontContainer.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,175 @@
+/*****************************************************************************
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+package org.apache.padaf.preflight.font;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+import org.apache.fontbox.ttf.CMAPEncodingEntry;
+import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.padaf.preflight.ValidationConstants;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.encoding.Encoding;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+
+public class TrueTypeFontContainer extends AbstractFontContainer {
+ private List<?> widthsArray = new ArrayList(0);
+ private int firstCharInWidthsArray = 0;
+
+ /**
+ * Represent the missingWidth value of the FontDescriptor dictionary.
+ * According to the PDF Reference, if this value is missing, the default
+ * one is 0.
+ */
+ private float defaultGlyphWidth = 0;
+ /**
+ * Object which contains the TrueType font data extracted by the
+ * TrueTypeParser object
+ */
+ private TrueTypeFont fontObject = null;
+ private CMAPEncodingEntry cmap = null;
+
+ private int numberOfLongHorMetrics;
+ private int unitsPerEm;
+ private int[] glyphWidths;
+
+ public TrueTypeFontContainer(PDFont fd) {
+ super(fd);
+ }
+
+ void setWidthsArray(List<?> widthsArray) {
+ this.widthsArray = widthsArray;
+ }
+
+ void setFirstCharInWidthsArray(int firstCharInWidthsArray) {
+ this.firstCharInWidthsArray = firstCharInWidthsArray;
+ }
+
+ void setDefaultGlyphWidth(float defaultGlyphWidth) {
+ this.defaultGlyphWidth = defaultGlyphWidth;
+ }
+
+ 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 cmap) {
+ this.cmap = cmap;
+ }
+
+ @Override
+ public void checkCID(int cid) throws GlyphException {
+ if (isAlreadyComputedCid(cid)) {
+ return;
+ }
+
+ int indexOfWidth = (cid - firstCharInWidthsArray);
+ float widthProvidedByPdfDictionary = this.defaultGlyphWidth;
+ 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) {
+ GlyphException e = new GlyphException(ValidationConstants.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;
+ }
+
+ 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) {
+ GlyphException e = new GlyphException(ValidationConstants.ERROR_FONTS_GLYPH_MISSING,
+ cid,
+ "A glyph for the character \"" + cid
+ + "\" in the font program \""
+ + this.font.getBaseFont()
+ + "\"is missing. There are " + characterArray.length + " glyph used by this character...");
+ addKnownCidElement(new GlyphDetail(cid, e));
+ throw e;
+ }
+ }
+ }
+ } catch (IOException ioe) {
+ GlyphException e = new GlyphException(ValidationConstants.ERROR_FONTS_ENCODING_IO,
+ cid,
+ "Unable to get the encoding object from the PDFont object during the validation of cid \"" + cid
+ + "\" in the font program \""
+ + this.font.getBaseFont()
+ + "\".");
+ addKnownCidElement(new GlyphDetail(cid, e));
+ throw e;
+ }
+ }
+
+ // search glyph
+ int glyphId = cmap.getGlyphId(innerFontCid);
+ if (glyphId == 0) {
+ GlyphException e = new GlyphException(ValidationConstants.ERROR_FONTS_GLYPH_MISSING,
+ cid,
+ "Glyph for the character \"" + cid
+ + "\" in the font program \""
+ + this.font.getBaseFont()
+ + "\"is missing.");
+ addKnownCidElement(new GlyphDetail(cid, e));
+ throw e;
+ }
+
+ // compute glyph width
+ float glypdWidth = glyphWidths[numberOfLongHorMetrics - 1];
+ if (glyphId < numberOfLongHorMetrics) {
+ glypdWidth = glyphWidths[glyphId];
+ }
+ widthInFontProgram = ((glypdWidth * 1000) / unitsPerEm);
+
+ // search width in the PDF file
+ if (indexOfWidth >= 0 && indexOfWidth < this.widthsArray.size()) {
+ COSInteger w = (COSInteger)this.widthsArray.get(indexOfWidth);
+ widthProvidedByPdfDictionary = w.intValue();
+ }
+
+ checkWidthsConsistency(cid, widthProvidedByPdfDictionary, widthInFontProgram);
+ addKnownCidElement(new GlyphDetail(cid));
+ }
+
+}
\ No newline at end of file
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/TrueTypeFontContainer.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/TrueTypeFontValidator.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/TrueTypeFontValidator.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/TrueTypeFontValidator.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/TrueTypeFontValidator.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,296 @@
+/*****************************************************************************
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+package org.apache.padaf.preflight.font;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+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.padaf.preflight.DocumentHandler;
+import org.apache.padaf.preflight.ValidationConstants;
+import org.apache.padaf.preflight.ValidationException;
+import org.apache.padaf.preflight.ValidationResult;
+import org.apache.padaf.preflight.ValidationResult.ValidationError;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
+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.COSArrayList;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+
+public class TrueTypeFontValidator extends SimpleFontValidator {
+ /**
+ * @param handler
+ * @param obj
+ */
+ public TrueTypeFontValidator(DocumentHandler handler, COSObject obj)
+ throws ValidationException {
+ super(handler, obj);
+ }
+
+ /**
+ * 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 != null && lastChar != null && 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(
+ ValidationConstants.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 {
+ PDStream ff1 = pFontDesc.getFontFile();
+ PDStream ff2 = pFontDesc.getFontFile2();
+ PDStream ff3 = 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(
+ ValidationConstants.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(
+ ValidationConstants.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(
+ ValidationConstants.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(
+ ValidationConstants.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.fDictionary.getCOSObject()).getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_ENCODING)) != null) {
+ this.fontContainer.addError(new ValidationResult.ValidationError(
+ ValidationConstants.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(
+ ValidationConstants.ERROR_FONTS_ENCODING,
+ "The Encoding should be missing for the Symbolic TTF"));
+ return false;
+ }
+
+ return checkFontMetrics(ttf) && checkFontFileMetaData(pFontDesc, ff2);
+ } catch (IOException e) {
+ this.fontContainer.addError(new ValidationResult.ValidationError(
+ ValidationConstants.ERROR_FONTS_TRUETYPE_DAMAGED,
+ "The FontFile can't be read"));
+ return false;
+ } finally {
+ if (bis != null) {
+ IOUtils.closeQuietly(bis);
+ }
+ }
+ }
+
+ /**
+ * This method checks the metric consistency. If the validation fails, the
+ * FontContainer is updated. If the validation is a success, the
+ * FontContainer.cidKnownByFont map is updated.
+ *
+ * @param ttf
+ * @return
+ * @throws IOException
+ */
+ protected boolean checkFontMetrics(TrueTypeFont ttf) throws IOException,
+ ValidationException {
+
+ int firstChar = pFont.getFirstChar();
+ float defaultGlyphWidth = this.pFontDesc.getMissingWidth();
+
+ List<?> pdfWidths = this.pFont.getWidths();
+ COSArray widths = null;
+ if (pdfWidths instanceof COSArrayList) {
+ widths = ((COSArrayList) pdfWidths).toList();
+ } else {
+ widths = ((COSArray) pdfWidths);
+ }
+
+ ((TrueTypeFontContainer)this.fontContainer).setWidthsArray(widths.toList());
+ ((TrueTypeFontContainer)this.fontContainer).setFirstCharInWidthsArray(firstChar);
+ ((TrueTypeFontContainer)this.fontContainer).setDefaultGlyphWidth(defaultGlyphWidth);
+ ((TrueTypeFontContainer)this.fontContainer).setFontObjectAndInitializeInnerFields(ttf);
+ ((TrueTypeFontContainer)this.fontContainer).setCMap(getCMapOfFontProgram(ttf));
+
+ return true;
+ }
+
+ /**
+ * 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()[0];
+ } else {
+ if (this.pFont.getFontEncoding() instanceof WinAnsiEncoding) {
+ for (CMAPEncodingEntry cmapEntry : cmap.getCmaps()) {
+ // ---- Returns the WinAnsiEncoding CMap
+ if ((cmapEntry.getPlatformId() == 3)
+ && (cmapEntry.getPlatformEncodingId() == 1)) {
+ return cmapEntry;
+ }
+ }
+ } else {
+ // ---- Returns the MacRomanEncoding CMap
+ for (CMAPEncodingEntry cmapEntry : cmap.getCmaps()) {
+ if ((cmapEntry.getPlatformId() == 1)
+ && (cmapEntry.getPlatformEncodingId() == 0)) {
+ return cmapEntry;
+ }
+ }
+ }
+ }
+
+ throw new ValidationException("CMap not found in the TrueType FontProgam");
+ }
+}
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/TrueTypeFontValidator.java
------------------------------------------------------------------------------
svn:eol-style = native