Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*****************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
package org.apache.pdfbox.preflight.font;
import static org.apache.pdfbox.preflight.PreflightConfiguration.RESOURCES_PROCESS;
import static org.apache.pdfbox.preflight.PreflightConstants.ERROR_FONTS_DICTIONARY_INVALID;
import static org.apache.pdfbox.preflight.PreflightConstants.ERROR_FONTS_METRICS;
import static org.apache.pdfbox.preflight.PreflightConstants.ERROR_FONTS_TYPE3_DAMAGED;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSFloat;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.font.PDType3CharProc;
import org.apache.pdfbox.pdmodel.font.encoding.Encoding;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDFontFactory;
import org.apache.pdfbox.pdmodel.font.PDType3Font;
import org.apache.pdfbox.preflight.PreflightConstants;
import org.apache.pdfbox.preflight.PreflightContext;
import org.apache.pdfbox.preflight.PreflightPath;
import org.apache.pdfbox.preflight.ValidationResult.ValidationError;
import org.apache.pdfbox.preflight.content.ContentStreamException;
import org.apache.pdfbox.preflight.exception.ValidationException;
import org.apache.pdfbox.preflight.font.container.FontContainer;
import org.apache.pdfbox.preflight.font.container.Type3Container;
import org.apache.pdfbox.preflight.font.util.GlyphException;
import org.apache.pdfbox.preflight.font.util.PreflightType3Stream;
import org.apache.pdfbox.preflight.utils.ContextHelper;
public class Type3FontValidator extends FontValidator
{
protected final PDType3Font font;
protected final COSDictionary fontDictionary;
public Type3FontValidator(PreflightContext context, PDType3Font font)
{
super(context, font.getCOSObject(), new Type3Container(font));
this.fontDictionary = font.getCOSObject();
this.font = font;
}
@Override
public void validate() throws ValidationException
{
checkMandatoryField();
checkFontBBox();
checkFontMatrix();
checkEncoding();
checkResources();
checkCharProcsAndMetrics();
checkToUnicode();
}
protected void checkMandatoryField()
{
boolean areFieldsPResent = fontDictionary.containsKey(COSName.FONT_BBOX);
areFieldsPResent &= fontDictionary.containsKey(COSName.FONT_MATRIX);
areFieldsPResent &= fontDictionary.containsKey(COSName.CHAR_PROCS);
areFieldsPResent &= fontDictionary.containsKey(COSName.ENCODING);
areFieldsPResent &= fontDictionary.containsKey(COSName.FIRST_CHAR);
areFieldsPResent &= fontDictionary.containsKey(COSName.LAST_CHAR);
areFieldsPResent &= fontDictionary.containsKey(COSName.WIDTHS);
if (!areFieldsPResent)
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": Some required fields are missing from the Font dictionary."));
}
}
/**
* Check that the FontBBox element has the right format as declared in the
* PDF reference document.
*/
private void checkFontBBox()
{
COSBase fontBBox = fontDictionary.getDictionaryObject(COSName.FONT_BBOX);
if (!(fontBBox instanceof COSArray))
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": The FontBBox element isn't an array"));
return;
}
/*
* check the content of the FontBBox. Should be an array with 4 numbers
*/
COSArray bbox = (COSArray) fontBBox;
if (bbox.size() != 4)
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": The FontBBox element is invalid"));
return;
}
for (int i = 0; i < 4; i++)
{
COSBase elt = bbox.get(i);
if (!(elt instanceof COSFloat || elt instanceof COSInteger))
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": An element of FontBBox isn't a number"));
return;
}
}
}
/**
* Check that the FontMatrix element has the right format as declared in the PDF reference document.
*/
private void checkFontMatrix()
{
COSBase fontMatrix = fontDictionary.getDictionaryObject(COSName.FONT_MATRIX);
if (!(fontMatrix instanceof COSArray))
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": The FontMatrix element isn't an array"));
return;
}
/*
* Check the content of the FontMatrix. Should be an array with 6 numbers
*/
COSArray matrix = (COSArray) fontMatrix;
if (matrix.size() != 6)
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": The FontMatrix element is invalid"));
return;
}
for (int i = 0; i < 6; i++)
{
COSBase elt = matrix.get(i);
if (!(elt instanceof COSFloat || elt instanceof COSInteger))
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": An element of FontMatrix isn't a number"));
return;
}
}
}
/**
* For a Type3 font, the mapping between the Character Code and the
* Character name is entirely defined in the /Encoding entry. The /Encoding
* Entry can be a Name (For the 5 predefined encodings) or a dictionary. If
* it is a dictionary, the /Differences array contains the correspondence
* between a character code and a set of character name which are different
* from the encoding entry of the dictionary.
*
* This method checks that the encoding is :
*
*
An existing encoding name.
*
A dictionary with an existing encoding name (the name is optional)
* and a well formed "Differences" array (the array is optional)
*
*
*/
@Override
protected void checkEncoding()
{
COSBase fontEncoding = fontDictionary.getDictionaryObject(COSName.ENCODING);
if (fontEncoding instanceof COSName)
{
checkEncodingAsString(((COSName) fontEncoding).getName());
}
else if (fontEncoding instanceof COSDictionary)
{
checkEncodingAsDictionary(fontDictionary);
}
else
{
// the encoding entry is invalid
this.fontContainer.push(new ValidationError(ERROR_FONTS_TYPE3_DAMAGED,
font.getName() + ": The Encoding entry doesn't have the right type"));
}
}
/**
* This method is called by the CheckEncoding method if the /Encoding entry is a String. In this case, the String
* must be an existing encoding name. (WinAnsi, MacRoman...)
*
* @param enc The name of the encoding.
*/
private void checkEncodingAsString(String enc)
{
// Encoding is a name, check if it is an existing encoding
Encoding encodingInstance = Encoding.getInstance(COSName.getPDFName(enc));
if (encodingInstance == null)
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_TYPE3_DAMAGED,
"The encoding '" + enc + "' doesn't exist"));
}
}
/**
* This method is called by the CheckEncoding method if the /Encoding entry
* is an instance of COSDictionary. If that dictionary has a /BaseEncoding
* entry, the name is checked. In any case, the /Differences array is
* validated.
*
* @param encodingDictionary the encoding dictionary.
*/
private void checkEncodingAsDictionary(COSDictionary encodingDictionary)
{
if (encodingDictionary.containsKey(COSName.BASE_ENCODING))
{
checkEncodingAsString(encodingDictionary.getString(COSName.BASE_ENCODING));
}
COSBase diff = encodingDictionary.getDictionaryObject(COSName.DIFFERENCES);
if (diff != null)
{
if (!(diff instanceof COSArray))
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_TYPE3_DAMAGED,
"The differences element of the encoding dictionary isn't an array"));
return;
}
// ---- The DictionaryEncoding object doesn't throw exception if the
// Differences isn't well formed.
// So check if the array has the right format.
COSArray diffArray = (COSArray) diff;
for (int i = 0; i < diffArray.size(); ++i)
{
COSBase item = diffArray.get(i);
if (!(item instanceof COSInteger || item instanceof COSName))
{
// ---- Error, the Differences array is invalid
this.fontContainer.push(new ValidationError(ERROR_FONTS_TYPE3_DAMAGED,
"Differences Array should contain COSInt or COSName, no other type"));
return;
}
}
}
}
/**
* CharProcs is a dictionary where the key is a character name and the value is a Stream which contains the glyph
* representation of the key.
*
* This method checks that all characters codes defined in the Widths Array exist in the CharProcs dictionary. If
* the CharProcs doesn't know the Character, it is mapped with the .notdef one.
*
* For each character, the Glyph width must be the same as the Width value declared in the Widths array.
*/
private void checkCharProcsAndMetrics()
{
List widths = getWidths(font);
if (widths == null || widths.isEmpty())
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": The Widths array is unreachable"));
return;
}
COSDictionary charProcs = fontDictionary.getCOSDictionary(COSName.CHAR_PROCS);
if (charProcs == null)
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": The CharProcs element isn't a dictionary"));
return;
}
int fc = font.getCOSObject().getInt(COSName.FIRST_CHAR, -1);
int lc = font.getCOSObject().getInt(COSName.LAST_CHAR, -1);
/*
* 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 (widths.size() != expectedLength)
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": The length of Widths array is invalid. Expected : \"" + expectedLength + "\" Current : \""
+ widths.size() + "\""));
return;
}
// Check width consistency
for (int i = 0; i < expectedLength; i++)
{
int code = fc + i;
float width = widths.get(i);
PDType3CharProc charProc = getCharProc(code);
if (charProc != null)
{
try
{
float fontProgramWidth = getWidthFromCharProc(charProc);
if (Math.abs(width - fontProgramWidth) < 0.001f)
{
// Glyph is OK, we keep the CID.
// PDF/A-1b only states that the width "shall be consistent".
// For PDF/A-2,3 the description has been enhanced and is now requesting
// "consistent is defined to be a difference of no more than 1/1000 unit"
// We interpret this as clarification of the PDF/A-1b requirement.
this.fontContainer.markAsValid(code);
}
else
{
GlyphException glyphEx = new GlyphException(ERROR_FONTS_METRICS, code,
font.getName() + ": The character with CID " + code
+ " should have a width equals to " + width + ", but has "
+ fontProgramWidth);
this.fontContainer.markAsInvalid(code, glyphEx);
}
}
catch (ContentStreamException e)
{
// TODO spaces/isartor-6-2-3-3-t02-fail-h.pdf --> si ajout de l'erreur dans le container le test
// echoue... pourquoi si la font est utilisée ca devrait planter???
this.context.addValidationError(new ValidationError(e.getErrorCode(), e.getMessage(), e));
return;
}
catch (IOException e)
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_TYPE3_DAMAGED,
font.getName() + ": The CharProcs references an element which can't be read", e));
return;
}
}
}
}
public List getWidths(PDFont font)
{
COSArray array = (COSArray) font.getCOSObject().getDictionaryObject(COSName.WIDTHS);
if (array != null)
{
return array.toCOSNumberFloatList();
}
return Collections.emptyList();
}
private PDType3CharProc getCharProc(int code)
{
PDType3CharProc charProc = font.getCharProc(code);
if (charProc == null)
{
// There are no character description, we declare the Glyph as Invalid. If the character
// is used in a Stream, the GlyphDetail will throw an exception.
GlyphException glyphEx = new GlyphException(ERROR_FONTS_METRICS, code,
font.getName() + ": The CharProcs \"" + font.getEncoding().getName(code) + "\" doesn't exist");
this.fontContainer.markAsInvalid(code, glyphEx);
}
return charProc;
}
/**
* Parse the Glyph description to obtain the Width
*
* @return the width of the character
*/
private float getWidthFromCharProc(PDType3CharProc charProc) throws IOException
{
PreflightPath vPath = context.getValidationPath();
PreflightType3Stream parser = new PreflightType3Stream(context, vPath.getClosestPathElement(PDPage.class), charProc);
parser.showType3Character(charProc);
return parser.getWidth();
}
/**
* If the Resources entry is present, this method checks its content. Only fonts and images are checked because this
* resource describes glyphs.
*/
private void checkResources() throws ValidationException
{
COSBase resources = this.fontDictionary.getDictionaryObject(COSName.RESOURCES);
if (resources != null)
{
if (!(resources instanceof COSDictionary))
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
font.getName() + ": The Resources element isn't a dictionary"));
return;
}
COSDictionary dictionary = (COSDictionary) resources;
// process Fonts and XObjects
ContextHelper.validateElement(context, new PDResources(dictionary),
RESOURCES_PROCESS);
COSDictionary dicFonts = dictionary.getCOSDictionary(COSName.FONT);
if (dicFonts != null)
{
/*
* Check that all referenced object are present in the PDF file
*/
Set keyList = dicFonts.keySet();
for (COSName key : keyList)
{
COSDictionary xObjFont = dicFonts.getCOSDictionary(key);
try
{
PDFont aFont = PDFontFactory.createFont(xObjFont);
FontContainer aContainer = this.context.getFontContainer(aFont.getCOSObject());
// another font is used in the Type3, check if the font is valid.
if (!aContainer.isValid())
{
this.fontContainer.push(new ValidationError(ERROR_FONTS_TYPE3_DAMAGED,
font.getName() + ": The Resources dictionary of type 3 font contains invalid font"));
}
}
catch (IOException e)
{
context.addValidationError(new ValidationError(PreflightConstants.ERROR_FONTS_DAMAGED,
font.getName() + ": Unable to valid the Type3 : " + e.getMessage(), e));
}
}
}
}
}
}