/*****************************************************************************
*
* 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;
}
}