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 [6/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/Type1FontContainer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1FontContainer.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1FontContainer.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1FontContainer.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,129 @@
+/*****************************************************************************
+ *
+ * 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.cff.CFFFont;
+import org.apache.fontbox.cff.CFFFont.Mapping;
+import org.apache.padaf.preflight.ValidationConstants;
+import org.apache.padaf.preflight.font.type1.Type1;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+
+public class Type1FontContainer 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 Type1Font data extracted by the
+ * Type1Parser object
+ */
+ private Type1 fontObject = null;
+
+ private List<CFFFont> cffFonts= null;
+
+ public Type1FontContainer(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 setFontObject(Type1 fontObject) {
+ this.fontObject = fontObject;
+ }
+
+ void setCFFFontObjects(List<CFFFont> fontObject) {
+ this.cffFonts = fontObject;
+ }
+
+ @Override
+ public void checkCID(int cid) throws GlyphException {
+ if (isAlreadyComputedCid(cid)) {
+ return;
+ }
+
+ int indexOfWidth = (cid - firstCharInWidthsArray);
+ float widthProvidedByPdfDictionary = this.defaultGlyphWidth;
+
+ int widthInFontProgram =0;
+ try {
+ if (this.fontObject != null) {
+ widthInFontProgram = this.fontObject.getWidthOfCID(cid);
+ } else {
+ // -- Retrieves the SID with the Character Name in the encoding map
+ // -- Need more PDF with a Type1C subfont to valid this implementation
+ String name = this.font.getFontEncoding().getName(cid);
+ for (CFFFont cff : cffFonts) {
+ int SID = cff.getEncoding().getSID(cid);
+ for (Mapping m : cff.getMappings() ){
+ if (m.getName().equals(name)) {
+ SID = m.getSID();
+ break;
+ }
+ }
+ widthInFontProgram = cff.getWidth(SID);
+ if (widthInFontProgram != defaultGlyphWidth) {
+ break;
+ }
+ }
+ }
+ } catch (GlyphException e) {
+ addKnownCidElement(new GlyphDetail(cid, e));
+ throw e;
+ } catch (IOException e) {
+ GlyphException ge = new GlyphException(ValidationConstants.ERROR_FONTS_DAMAGED, cid,
+ "Unable to get width of the CID/SID : " + cid);
+ addKnownCidElement(new GlyphDetail(cid, ge));
+ throw ge;
+ }
+
+ 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/Type1FontContainer.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1FontValidator.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1FontValidator.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1FontValidator.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1FontValidator.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,323 @@
+/*****************************************************************************
+ *
+ * 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.Font;
+import java.awt.FontFormatException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+
+import org.apache.commons.io.IOUtils;
+import org.apache.fontbox.cff.CFFFont;
+import org.apache.fontbox.cff.CFFParser;
+import org.apache.padaf.preflight.DocumentHandler;
+import org.apache.padaf.preflight.ValidationException;
+import org.apache.padaf.preflight.ValidationResult;
+import org.apache.padaf.preflight.ValidationResult.ValidationError;
+import org.apache.padaf.preflight.font.type1.Type1;
+import org.apache.padaf.preflight.font.type1.Type1Parser;
+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.COSArrayList;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+
+public class Type1FontValidator extends SimpleFontValidator {
+
+ public Type1FontValidator(DocumentHandler handler, COSObject obj)
+ throws ValidationException {
+ super(handler, obj);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @seenet.awl.edoc.pdfa.validation.font.SimpleFontValidator#
+ * checkSpecificMandatoryFields()
+ */
+ 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"));
+ 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
+ // ---- Event if the Font is one of the 14 standard Fonts, those keys are
+ // mandatory for a PDF/A
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * net.awl.edoc.pdfa.validation.font.SimpleFontValidator#checkEncoding(org
+ * .apache.pdfbox.cos.COSDocument)
+ */
+ protected boolean checkEncoding(COSDocument cDoc) {
+ if (COSUtils.isString(this.encoding, cDoc)) {
+ String encodingName = COSUtils.getAsString(this.encoding, cDoc);
+ if (!(encodingName.equals(FONT_DICTIONARY_VALUE_ENCODING_MAC)
+ || encodingName.equals(FONT_DICTIONARY_VALUE_ENCODING_MAC_EXP)
+ || encodingName.equals(FONT_DICTIONARY_VALUE_ENCODING_WIN)
+ || encodingName.equals(FONT_DICTIONARY_VALUE_ENCODING_PDFDOC) || encodingName
+ .equals(FONT_DICTIONARY_VALUE_ENCODING_STD))) {
+ this.fontContainer.addError(new ValidationError(ERROR_FONTS_ENCODING));
+ return false;
+ }
+ } else if (COSUtils.isDictionary(this.encoding, cDoc)) {
+ this.pFont.getFontEncoding();
+ } else if (this.encoding != null) {
+ this.fontContainer.addError(new ValidationError(ERROR_FONTS_ENCODING));
+ return false;
+ }
+ //else
+ // ---- According to PDF Reference, the encoding entry is optional.
+ // PDF/A specification only speaks of TrueType encoding
+
+ 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;
+ }
+
+ /**
+ * Check if the font name is present and if fontName equals to the baseName.
+ * If the validation fails, false is returned and the FontContainer is
+ * updated.
+ *
+ * @return
+ */
+ boolean checkFontName() {
+ String fontName = this.pFontDesc.getFontName();
+ String baseName = this.pFont.getBaseFont();
+
+ // For a Type1 Font, the FontName is the same as the BaseName.
+ if (fontName == null || (!fontName.equals(baseName))) {
+ 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 methods validates the Font Stream, if the font program is damaged or
+ * missing the FontContainer is updated and false is returned.
+ *
+ * @throws ValidationException
+ */
+ protected boolean checkFontFileElement() throws ValidationException {
+ // ---- if the this font is a Subset, the CharSet entry must be present in
+ // the FontDescriptor
+ if (isSubSet(pFontDesc.getFontName())) {
+ String charsetStr = pFontDesc.getCharSet();
+ if (charsetStr == null || "".equals(charsetStr)) {
+ this.fontContainer.addError(new ValidationResult.ValidationError(
+ ERROR_FONTS_CHARSET_MISSING_FOR_SUBSET,
+ "The Charset entry is missing for the Type1 Subset"));
+ return false;
+ }
+ }
+
+ // ---- FontFile Validation
+ 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 ((ff1 == null && (ff3 == null || !"Type1C".equals(((COSDictionary)ff3.getCOSObject()).getNameAsString(COSName.SUBTYPE)))) || !onlyOne) {
+ this.fontContainer.addError(new ValidationResult.ValidationError(
+ ERROR_FONTS_FONT_FILEX_INVALID, "The FontFile is invalid"));
+ return false;
+ }
+
+ if (ff1 != null) {
+ COSStream stream = ff1.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;
+ boolean hasLength2 = stream.getInt(COSName.getPDFName(FONT_DICTIONARY_KEY_LENGTH2)) > 0;
+ boolean hasLength3 = stream.getInt(COSName.getPDFName(FONT_DICTIONARY_KEY_LENGTH3)) > 0;
+ if (!(hasLength1 && hasLength2 && hasLength3)) {
+ 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
+ // ---- try to load the font using the java.awt.font object.
+ // ---- if the font is invalid, an exception will be thrown
+ ByteArrayInputStream bis = null;
+ try {
+ bis = new ByteArrayInputStream(ff1.getByteArray());
+ Font.createFont(Font.TYPE1_FONT, bis);
+ return checkFontMetricsDataAndFeedFontContainer(ff1) && checkFontFileMetaData(pFontDesc, ff1);
+ } catch (IOException e) {
+ this.fontContainer.addError(new ValidationResult.ValidationError(
+ ERROR_FONTS_TYPE1_DAMAGED, "The FontFile can't be read"));
+ return false;
+ } catch (FontFormatException e) {
+ this.fontContainer.addError(new ValidationResult.ValidationError(
+ ERROR_FONTS_TYPE1_DAMAGED, "The FontFile is damaged"));
+ return false;
+ } finally {
+ if (bis != null) {
+ IOUtils.closeQuietly(bis);
+ }
+ }
+ } else {
+ return checkCIDFontWidths(ff3) && checkFontFileMetaData(pFontDesc, ff3);
+ }
+ }
+
+ /**
+ * Type1C is a CFF font format, extract all CFFFont object from the stream
+ *
+ * @param fontStream
+ * @return
+ * @throws ValidationException
+ */
+ protected boolean checkCIDFontWidths(PDStream fontStream)
+ throws ValidationException {
+ try {
+ CFFParser cffParser = new CFFParser();
+ List<CFFFont> lCFonts = cffParser.parse(fontStream.getByteArray());
+
+ if (lCFonts == null || lCFonts.isEmpty()) {
+ this.fontContainer.addError(new ValidationResult.ValidationError(
+ ERROR_FONTS_CID_DAMAGED, "The FontFile can't be read"));
+ return false;
+ }
+
+ ((Type1FontContainer)this.fontContainer).setCFFFontObjects(lCFonts);
+
+
+ List<?> pdfWidths = this.pFont.getWidths();
+ int firstChar = pFont.getFirstChar();
+ float defaultGlyphWidth = pFontDesc.getMissingWidth();
+
+ COSArray widths = null;
+ if (pdfWidths instanceof COSArrayList) {
+ widths = ((COSArrayList) pdfWidths).toList();
+ } else {
+ widths = ((COSArray) pdfWidths);
+ }
+
+ ((Type1FontContainer)this.fontContainer).setWidthsArray(widths.toList());
+ ((Type1FontContainer)this.fontContainer).setFirstCharInWidthsArray(firstChar);
+ ((Type1FontContainer)this.fontContainer).setDefaultGlyphWidth(defaultGlyphWidth);
+
+ return true;
+ } 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 and adds the FontContainer in the
+ * DocumentHandler.
+ *
+ * @param fontStream
+ * @return
+ * @throws ValidationException
+ */
+ protected boolean checkFontMetricsDataAndFeedFontContainer(PDStream fontStream)
+ throws ValidationException {
+ try {
+
+ // ---- Parse the Type1 Font program
+ ByteArrayInputStream bis = new ByteArrayInputStream(fontStream
+ .getByteArray());
+ COSStream streamObj = fontStream.getStream();
+ int length1 = streamObj.getInt(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_LENGTH1));
+ int length2 = streamObj.getInt(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_LENGTH2));
+
+ Type1Parser parserForMetrics = Type1Parser.createParserWithEncodingObject(bis, length1, length2, pFont.getFontEncoding());
+ Type1 parsedData = parserForMetrics.parse();
+
+ ((Type1FontContainer)this.fontContainer).setFontObject(parsedData);
+
+ List<?> pdfWidths = this.pFont.getWidths();
+ int firstChar = pFont.getFirstChar();
+ float defaultGlyphWidth = pFontDesc.getMissingWidth();
+
+ COSArray widths = null;
+ if (pdfWidths instanceof COSArrayList) {
+ widths = ((COSArrayList) pdfWidths).toList();
+ } else {
+ widths = ((COSArray) pdfWidths);
+ }
+
+ ((Type1FontContainer)this.fontContainer).setWidthsArray(widths.toList());
+ ((Type1FontContainer)this.fontContainer).setFirstCharInWidthsArray(firstChar);
+ ((Type1FontContainer)this.fontContainer).setDefaultGlyphWidth(defaultGlyphWidth);
+
+ return true;
+ } catch (IOException e) {
+ throw new ValidationException("Unable to check Type1 metrics due to : "
+ + e.getMessage(), e);
+ }
+ }
+}
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1FontValidator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1MetricHelper.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1MetricHelper.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1MetricHelper.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1MetricHelper.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,1060 @@
+/*****************************************************************************
+ *
+ * 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.FONT_DICTIONARY_VALUE_ENCODING_MAC;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_ENCODING_MAC_EXP;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_ENCODING_PDFDOC;
+import static org.apache.padaf.preflight.ValidationConstants.FONT_DICTIONARY_VALUE_ENCODING_WIN;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+
+import org.apache.commons.io.IOUtils;
+import org.apache.fontbox.cff.CharStringCommand;
+import org.apache.fontbox.cff.Type1CharStringParser;
+import org.apache.fontbox.cff.Type1FontUtil;
+import org.apache.padaf.preflight.ValidationException;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.encoding.Encoding;
+import org.apache.pdfbox.encoding.MacRomanEncoding;
+import org.apache.pdfbox.encoding.PdfDocEncoding;
+import org.apache.pdfbox.encoding.StandardEncoding;
+import org.apache.pdfbox.encoding.WinAnsiEncoding;
+
+/**
+ * This class computes a Type1 font stream to extract Glyph Metrics. The given
+ * stream must be a valid type 1 stream.
+ *
+ * Remark : According to the PDF Reference only PostScript Type 1 binary fonts
+ * are allowed in a conforming PDF file so the encrypted "eexec" data are
+ * considered as binary data...
+ *
+ * This class is depreciated, now it is better to use the Type1Parser.
+ */
+@Deprecated
+public class Type1MetricHelper {
+ protected static final char NAME_START = '/';
+
+ protected static final int FULL_NAME_TOKEN = 1;
+ protected static final int FAMILY_NAME_TOKEN = 2;
+ protected static final int DUP_TOKEN = 3;
+ protected static final int FONT_NAME_TOKEN = 4;
+ protected static final int ENCODING_TOKEN = 5;
+ protected static final int READONLY_TOKEN = 6;
+
+ protected static final int LEN_IV_TOKEN = 7;
+ protected static final int CHARSTRINGS_TOKEN = 8;
+ protected static final int CHAR_LABEL_TOKEN = 9;
+
+ protected static final int OBJ_NAME_TOKEN = 10;
+
+ protected static final String NOTDEF = "/.notdef";
+
+ private static final String PS_STANDARD_ENCODING = "StandardEncoding";
+ private static final String PS_ISOLATIN_ENCODING = "ISOLatin1Encoding";
+
+ /**
+ * The PostScript font stream.
+ */
+ private InputStream font = null;
+ /**
+ * The length in bytes of the clear-text portion of the Type1 font program.
+ */
+ private int clearTextSize = 0;
+ /**
+ * The length in bytes of the eexec encrypted portion of the type1 font
+ * program.
+ */
+ private int eexecSize = 0;
+ /**
+ * This map links the character identifier to a internal font program label
+ * which is different from the standard Encoding
+ */
+ private Map<Integer, String> cidToLabel = new HashMap<Integer, String>(0);
+ /**
+ * This map links the character label to a character identifier which is
+ * different from the standard Encoding.
+ */
+ private Map<String, Integer> labelToCid = new HashMap<String, Integer>(0);
+ /**
+ * This map link the character label to a container containing Glyph
+ * description.
+ */
+ private Map<String, Type1GlyphDescription> labelToMetric = new HashMap<String, Type1GlyphDescription>(
+ 0);
+
+ /**
+ * The character encoding of the Font
+ */
+ private Encoding encoding = null;
+
+ /**
+ * The family name of the font
+ */
+ protected String familyName = null;
+ /**
+ * The full name of the font
+ */
+ protected String fullName = null;
+ /**
+ * The font name of the font
+ */
+ protected String fontName = null;
+
+ /**
+ *
+ * @param type1
+ * The unfiltered PostScript Type 1 Font stream.
+ * @param length1
+ * The length in bytes of the clear-text portion of the Type1 font
+ * program.
+ * @param length2
+ * The length in bytes of the eexec encrypted portion of the type1
+ * font program.
+ * @param encodingName
+ * the Encoding name, StandardEncoding is used for unknown name
+ */
+ public Type1MetricHelper(InputStream type1, int length1, int length2,
+ String encodingName) {
+ super();
+ this.font = type1;
+ this.clearTextSize = length1;
+ this.eexecSize = length2;
+ this.cidToLabel.put(-1, NOTDEF);
+ this.labelToCid.put(NOTDEF, -1);
+
+
+ // ---- Instantiate the Encoding Map
+ if (FONT_DICTIONARY_VALUE_ENCODING_MAC.equals(encodingName)) {
+ this.encoding = new MacRomanEncoding();
+ } else if (FONT_DICTIONARY_VALUE_ENCODING_MAC_EXP.equals(encodingName)) {
+ this.encoding = new MacRomanEncoding();
+ } else if (FONT_DICTIONARY_VALUE_ENCODING_WIN.equals(encodingName)) {
+ this.encoding = new WinAnsiEncoding();
+ } else if (FONT_DICTIONARY_VALUE_ENCODING_PDFDOC.equals(encodingName)) {
+ this.encoding = new PdfDocEncoding();
+ } else {
+ this.encoding = new StandardEncoding();
+ }
+ }
+
+ /**
+ *
+ * @param type1
+ * The unfiltered PostScript Type 1 Font stream.
+ * @param length1
+ * The length in bytes of the clear-text portion of the Type1 font
+ * program.
+ * @param length2
+ * The length in bytes of the eexec encrypted portion of the type1
+ * font program.
+ * @param enc
+ * The Encoding inherited Object
+ */
+ public Type1MetricHelper(InputStream type1, int length1, int length2,
+ Encoding enc) {
+ super();
+ this.font = type1;
+ this.clearTextSize = length1;
+ this.eexecSize = length2;
+ this.cidToLabel.put(-1, NOTDEF);
+ this.labelToCid.put(NOTDEF, -1);
+
+ // ---- Instantiate the Encoding Map
+ if (enc != null) {
+ this.encoding = enc;
+ } else {
+ this.encoding = new StandardEncoding();
+ }
+ }
+
+ /**
+ * Close the font stream
+ */
+ public void close() {
+ IOUtils.closeQuietly(this.font);
+ }
+
+ private void createStandardEncoding() {
+ this.labelToCid.put("/A", 0101);
+ this.labelToCid.put("/AE", 0341);
+ this.labelToCid.put("/B", 0102);
+ this.labelToCid.put("/C", 0103);
+ this.labelToCid.put("/D", 0104);
+ this.labelToCid.put("/E", 0105);
+ this.labelToCid.put("/F", 0106);
+ this.labelToCid.put("/G", 0107);
+ this.labelToCid.put("/H", 0110);
+ this.labelToCid.put("/I", 0111);
+ this.labelToCid.put("/J", 0112);
+ this.labelToCid.put("/K", 0113);
+ this.labelToCid.put("/L", 0114);
+ this.labelToCid.put("/Lslash", 0350);
+ this.labelToCid.put("/M", 0115);
+ this.labelToCid.put("/N", 0116);
+ this.labelToCid.put("/O", 0117);
+ this.labelToCid.put("/OE", 0352);
+ this.labelToCid.put("/Oslash", 0351);
+ this.labelToCid.put("/P", 0120);
+ this.labelToCid.put("/Q", 0121);
+ this.labelToCid.put("/R", 0122);
+ this.labelToCid.put("/S", 0123);
+ this.labelToCid.put("/T", 0124);
+ this.labelToCid.put("/U", 0125);
+ this.labelToCid.put("/V", 0126);
+ this.labelToCid.put("/W", 0127);
+ this.labelToCid.put("/X", 0130);
+ this.labelToCid.put("/Y", 0131);
+ this.labelToCid.put("/Z", 0132);
+ this.labelToCid.put("/a", 0141);
+ this.labelToCid.put("/acute", 0302);
+ this.labelToCid.put("/acute", 0302);
+ this.labelToCid.put("/ae", 0361);
+ this.labelToCid.put("/ampersand", 046);
+ this.labelToCid.put("/asciicircum", 0136);
+ this.labelToCid.put("/asciitilde", 0176);
+ this.labelToCid.put("/asterisk", 052);
+ this.labelToCid.put("/at", 0100);
+ this.labelToCid.put("/b", 0142);
+ this.labelToCid.put("/backslash", 0134);
+ this.labelToCid.put("/bar", 0174);
+ this.labelToCid.put("/braceleft", 0173);
+ this.labelToCid.put("/braceright", 0175);
+ this.labelToCid.put("/bracketleft", 0133);
+ this.labelToCid.put("/bracketright", 0135);
+ this.labelToCid.put("/breve", 0306);
+ this.labelToCid.put("/bullet", 0267);
+ this.labelToCid.put("/c", 0143);
+ this.labelToCid.put("/caron", 0317);
+ this.labelToCid.put("/cedilla", 0313);
+ this.labelToCid.put("/cent", 0242);
+ this.labelToCid.put("/circumflex", 0303);
+ this.labelToCid.put("/colon", 072);
+ this.labelToCid.put("/comma", 054);
+ this.labelToCid.put("/currency", 0250);
+ this.labelToCid.put("/d", 0144);
+ this.labelToCid.put("/dagger", 0262);
+ this.labelToCid.put("/daggerdbl", 0263);
+ this.labelToCid.put("/dieresis", 0310);
+ this.labelToCid.put("/dollar", 044);
+ this.labelToCid.put("/dotaccent", 0307);
+ this.labelToCid.put("/dotlessi", 0365);
+ this.labelToCid.put("/e", 0145);
+ this.labelToCid.put("/eight", 070);
+ this.labelToCid.put("/ellipsis", 274);
+ this.labelToCid.put("/emdash", 0320);
+ this.labelToCid.put("/endash", 0261);
+ this.labelToCid.put("/equal", 075);
+ this.labelToCid.put("/exclam", 041);
+ this.labelToCid.put("/exclamdown", 0241);
+ this.labelToCid.put("/f", 0146);
+ this.labelToCid.put("/fi", 0256);
+ this.labelToCid.put("/five", 0065);
+ this.labelToCid.put("/fl", 0257);
+ this.labelToCid.put("/florin", 0246);
+ this.labelToCid.put("/four", 064);
+ this.labelToCid.put("/fraction", 0244);
+ this.labelToCid.put("/g", 0147);
+ this.labelToCid.put("/germandbls", 0373);
+ this.labelToCid.put("/grave", 0301);
+ this.labelToCid.put("/greater", 0076);
+ this.labelToCid.put("/guillemotleft", 0253);
+ this.labelToCid.put("/guillemotright", 0273);
+ this.labelToCid.put("/guilsinglleft", 0254);
+ this.labelToCid.put("/guilsinglright", 0255);
+ this.labelToCid.put("/h", 0150);
+ this.labelToCid.put("/hungarumlaut", 0315);
+ this.labelToCid.put("/hyphen", 055);
+ this.labelToCid.put("/i", 0151);
+ this.labelToCid.put("/j", 0152);
+ this.labelToCid.put("/k", 0153);
+ this.labelToCid.put("/l", 0154);
+ this.labelToCid.put("/less", 0074);
+ this.labelToCid.put("/lslash", 0370);
+ this.labelToCid.put("/m", 0155);
+ this.labelToCid.put("/macron", 0305);
+ this.labelToCid.put("/n", 0156);
+ this.labelToCid.put("/nine", 071);
+ this.labelToCid.put("/numbersign", 043);
+ this.labelToCid.put("/o", 0157);
+ this.labelToCid.put("/oe", 0372);
+ this.labelToCid.put("/ogonek", 0316);
+ this.labelToCid.put("/one", 061);
+ this.labelToCid.put("/ordfeminine", 0343);
+ this.labelToCid.put("/ordmasculine", 0353);
+ this.labelToCid.put("/oslash", 0371);
+ this.labelToCid.put("/p", 0160);
+ this.labelToCid.put("/paragraph", 0266);
+ this.labelToCid.put("/parenleft", 050);
+ this.labelToCid.put("/parenright", 051);
+ this.labelToCid.put("/percent", 045);
+ this.labelToCid.put("/period", 056);
+ this.labelToCid.put("/periodcentered", 0264);
+ this.labelToCid.put("/perthousand", 0275);
+ this.labelToCid.put("/plus", 0053);
+ this.labelToCid.put("/q", 0161);
+ this.labelToCid.put("/question", 077);
+ this.labelToCid.put("/questiondown", 0277);
+ this.labelToCid.put("/quotedbl", 0042);
+ this.labelToCid.put("/quotedblbase", 0271);
+ this.labelToCid.put("/quotedblleft", 0252);
+ this.labelToCid.put("/quotedblright", 0272);
+ this.labelToCid.put("/quoteleft", 0140);
+ this.labelToCid.put("/quoteright", 047);
+ this.labelToCid.put("/quotesinglbase", 0270);
+ this.labelToCid.put("/quotesingle", 0251);
+ this.labelToCid.put("/r", 0162);
+ this.labelToCid.put("/ring", 0312);
+ this.labelToCid.put("/s", 0163);
+ this.labelToCid.put("/section", 0247);
+ this.labelToCid.put("/semicolon", 0073);
+ this.labelToCid.put("/seven", 0067);
+ this.labelToCid.put("/six", 066);
+ this.labelToCid.put("/slash", 057);
+ this.labelToCid.put("/space", 040);
+ this.labelToCid.put("/sterling", 0243);
+ this.labelToCid.put("/t", 0164);
+ this.labelToCid.put("/three", 063);
+ this.labelToCid.put("/tilde", 0304);
+ this.labelToCid.put("/two", 062);
+ this.labelToCid.put("/u", 0165);
+ this.labelToCid.put("/underscore", 0137);
+ this.labelToCid.put("/v", 0166);
+ this.labelToCid.put("/w", 0167);
+ this.labelToCid.put("/x", 0170);
+ this.labelToCid.put("/y", 0171);
+ this.labelToCid.put("/yen", 0245);
+ this.labelToCid.put("/z", 0172);
+ this.labelToCid.put("/zero", 060);
+ transafertLTOCinCTIL();
+ }
+
+ private void transafertLTOCinCTIL() {
+ for (Entry<String, Integer> entry : this.labelToCid.entrySet()) {
+ this.cidToLabel.put(entry.getValue(), entry.getKey());
+ }
+ }
+
+ private void createISOLatin1Encoding() {
+ this.labelToCid.put("/A", 0101);
+ this.labelToCid.put("/AE", 0306);
+ this.labelToCid.put("/Aacute", 0301);
+ this.labelToCid.put("/Acircumflex", 0302);
+ this.labelToCid.put("/Adieresis", 0304);
+ this.labelToCid.put("/Agrave", 0300);
+ this.labelToCid.put("/Aring", 0305);
+ this.labelToCid.put("/Atilde", 0303);
+ this.labelToCid.put("/B", 0102);
+ this.labelToCid.put("/C", 0103);
+ this.labelToCid.put("/Ccedilla", 0307);
+ this.labelToCid.put("/D", 0104);
+ this.labelToCid.put("/E", 0105);
+ this.labelToCid.put("/Eacute", 0311);
+ this.labelToCid.put("/Ecircumflex", 0312);
+ this.labelToCid.put("/Edieresis", 0313);
+ this.labelToCid.put("/Egrave", 0310);
+ this.labelToCid.put("/Eth", 0320);
+ this.labelToCid.put("/F", 0106);
+ this.labelToCid.put("/G", 0107);
+ this.labelToCid.put("/H", 0110);
+ this.labelToCid.put("/I", 0111);
+ this.labelToCid.put("/Iacute", 0315);
+ this.labelToCid.put("/Icircumflex", 0316);
+ this.labelToCid.put("/Idieresis", 0317);
+ this.labelToCid.put("/Igrave", 0314);
+ this.labelToCid.put("/J", 0112);
+ this.labelToCid.put("/K", 0113);
+ this.labelToCid.put("/L", 0114);
+ this.labelToCid.put("/M", 0115);
+ this.labelToCid.put("/N", 0116);
+ this.labelToCid.put("/Ntilde", 0321);
+ this.labelToCid.put("/O", 0117);
+ this.labelToCid.put("/Oacute", 0323);
+ this.labelToCid.put("/Ocircumflex", 0324);
+ this.labelToCid.put("/Odieresis", 0326);
+ this.labelToCid.put("/Ograve", 0322);
+ this.labelToCid.put("/Oslash", 0330);
+ this.labelToCid.put("/Otilde", 0325);
+ this.labelToCid.put("/P", 0120);
+ this.labelToCid.put("/Q", 0121);
+ this.labelToCid.put("/R", 0122);
+ this.labelToCid.put("/S", 0123);
+ this.labelToCid.put("/T", 0124);
+ this.labelToCid.put("/Thorn", 0336);
+ this.labelToCid.put("/U", 0125);
+ this.labelToCid.put("/Uacute", 0332);
+ this.labelToCid.put("/Ucircumflex", 0333);
+ this.labelToCid.put("/Udieresis", 0334);
+ this.labelToCid.put("/Ugrave", 0331);
+ this.labelToCid.put("/V", 0126);
+ this.labelToCid.put("/W", 0127);
+ this.labelToCid.put("/X", 0130);
+ this.labelToCid.put("/Y", 0131);
+ this.labelToCid.put("/Yacute", 0335);
+ this.labelToCid.put("/Z", 0132);
+ this.labelToCid.put("/a", 0141);
+ this.labelToCid.put("/aacute", 0341);
+ this.labelToCid.put("/acircumflex", 0342);
+ this.labelToCid.put("/acute", 0222);
+ this.labelToCid.put("/acute", 0264);
+ this.labelToCid.put("/adieresis", 0344);
+ this.labelToCid.put("/ae", 0346);
+ this.labelToCid.put("/agrave", 0340);
+ this.labelToCid.put("/ampersand", 0046);
+ this.labelToCid.put("/aring", 0345);
+ this.labelToCid.put("/asciicircum", 0136);
+ this.labelToCid.put("/asciitilde", 0176);
+ this.labelToCid.put("/asterisk", 0052);
+ this.labelToCid.put("/at", 0100);
+ this.labelToCid.put("/atilde", 0343);
+ this.labelToCid.put("/b", 0142);
+ this.labelToCid.put("/backslash", 0134);
+ this.labelToCid.put("/bar", 0174);
+ this.labelToCid.put("/braceleft", 0173);
+ this.labelToCid.put("/braceright", 0175);
+ this.labelToCid.put("/bracketleft", 0133);
+ this.labelToCid.put("/bracketright", 0135);
+ this.labelToCid.put("/breve", 0226);
+ this.labelToCid.put("/brokenbar", 0246);
+ this.labelToCid.put("/c", 0143);
+ this.labelToCid.put("/caron", 0237);
+ this.labelToCid.put("/ccedilla", 0347);
+ this.labelToCid.put("/cedilla", 0270);
+ this.labelToCid.put("/cent", 0242);
+ this.labelToCid.put("/circumflex", 0223);
+ this.labelToCid.put("/colon", 0072);
+ this.labelToCid.put("/comma", 0054);
+ this.labelToCid.put("/copyright", 0251);
+ this.labelToCid.put("/currency", 0244);
+ this.labelToCid.put("/d", 0144);
+ this.labelToCid.put("/degree", 0260);
+ this.labelToCid.put("/dieresis", 0250);
+ this.labelToCid.put("/divide", 0367);
+ this.labelToCid.put("/dollar", 0044);
+ this.labelToCid.put("/dotaccent", 0227);
+ this.labelToCid.put("/dotlessi", 0220);
+ this.labelToCid.put("/e", 0145);
+ this.labelToCid.put("/eacute", 0351);
+ this.labelToCid.put("/ecircumflex", 0352);
+ this.labelToCid.put("/edieresis", 0353);
+ this.labelToCid.put("/egrave", 0350);
+ this.labelToCid.put("/eight", 0070);
+ this.labelToCid.put("/equal", 0075);
+ this.labelToCid.put("/eth", 0360);
+ this.labelToCid.put("/exclam", 0041);
+ this.labelToCid.put("/exclamdown", 0241);
+ this.labelToCid.put("/f", 0146);
+ this.labelToCid.put("/five", 0065);
+ this.labelToCid.put("/four", 0064);
+ this.labelToCid.put("/g", 0147);
+ this.labelToCid.put("/germandbls", 0337);
+ this.labelToCid.put("/grave", 0221);
+ this.labelToCid.put("/greater", 0076);
+ this.labelToCid.put("/guillemotleft", 0253);
+ this.labelToCid.put("/guillemotright", 0273);
+ this.labelToCid.put("/h", 0150);
+ this.labelToCid.put("/hungarumlaut", 0235);
+ this.labelToCid.put("/hyphen", 0255);
+ this.labelToCid.put("/i", 0151);
+ this.labelToCid.put("/iacute", 0355);
+ this.labelToCid.put("/icircumflex", 0356);
+ this.labelToCid.put("/idieresis", 0357);
+ this.labelToCid.put("/igrave", 0354);
+ this.labelToCid.put("/j", 0152);
+ this.labelToCid.put("/k", 0153);
+ this.labelToCid.put("/l", 0154);
+ this.labelToCid.put("/less", 0074);
+ this.labelToCid.put("/logicalnot", 0254);
+ this.labelToCid.put("/m", 0155);
+ this.labelToCid.put("/macron", 0257);
+ this.labelToCid.put("/minus", 0055);
+ this.labelToCid.put("/mu", 0265);
+ this.labelToCid.put("/multiply", 0327);
+ this.labelToCid.put("/n", 0156);
+ this.labelToCid.put("/nine", 0071);
+ this.labelToCid.put("/ntilde", 0361);
+ this.labelToCid.put("/numbersign", 0043);
+ this.labelToCid.put("/o", 0157);
+ this.labelToCid.put("/oacute", 0363);
+ this.labelToCid.put("/ocircumflex", 0364);
+ this.labelToCid.put("/odieresis", 0366);
+ this.labelToCid.put("/ogonek", 0236);
+ this.labelToCid.put("/ograve", 0362);
+ this.labelToCid.put("/one", 0061);
+ this.labelToCid.put("/onehalf", 0275);
+ this.labelToCid.put("/onequarter", 0274);
+ this.labelToCid.put("/onesuperior", 0271);
+ this.labelToCid.put("/ordfeminine", 0252);
+ this.labelToCid.put("/ordmasculine", 0272);
+ this.labelToCid.put("/oslash", 0370);
+ this.labelToCid.put("/otilde", 0365);
+ this.labelToCid.put("/p", 0160);
+ this.labelToCid.put("/paragraph", 0266);
+ this.labelToCid.put("/parenleft", 0050);
+ this.labelToCid.put("/parenright", 0051);
+ this.labelToCid.put("/percent", 0045);
+ this.labelToCid.put("/period", 0056);
+ this.labelToCid.put("/periodcentered", 0267);
+ this.labelToCid.put("/plus", 0053);
+ this.labelToCid.put("/plusminus", 0261);
+ this.labelToCid.put("/q", 0161);
+ this.labelToCid.put("/question", 0077);
+ this.labelToCid.put("/questiondown", 0277);
+ this.labelToCid.put("/quotedbl", 0042);
+ this.labelToCid.put("/quoteleft", 0140);
+ this.labelToCid.put("/quoteright", 0047);
+ this.labelToCid.put("/r", 0162);
+ this.labelToCid.put("/registered", 0256);
+ this.labelToCid.put("/ring", 0232);
+ this.labelToCid.put("/s", 0163);
+ this.labelToCid.put("/section", 0247);
+ this.labelToCid.put("/semicolon", 0073);
+ this.labelToCid.put("/seven", 0067);
+ this.labelToCid.put("/six", 0066);
+ this.labelToCid.put("/slash", 0057);
+ this.labelToCid.put("/space", 0040);
+ this.labelToCid.put("/sterling", 0243);
+ this.labelToCid.put("/t", 0164);
+ this.labelToCid.put("/thorn", 0376);
+ this.labelToCid.put("/three", 0063);
+ this.labelToCid.put("/threequarters", 0276);
+ this.labelToCid.put("/threesuperior", 0263);
+ this.labelToCid.put("/tilde", 0224);
+ this.labelToCid.put("/two", 0062);
+ this.labelToCid.put("/twosuperior", 0262);
+ this.labelToCid.put("/u", 0165);
+ this.labelToCid.put("/uacute", 0372);
+ this.labelToCid.put("/ucircumflex", 0373);
+ this.labelToCid.put("/udieresis", 0374);
+ this.labelToCid.put("/ugrave", 0371);
+ this.labelToCid.put("/underscore", 0137);
+ this.labelToCid.put("/v", 0166);
+ this.labelToCid.put("/w", 0167);
+ this.labelToCid.put("/x", 0170);
+ this.labelToCid.put("/y", 0171);
+ this.labelToCid.put("/yacute", 0375);
+ this.labelToCid.put("/ydieresis", 0377);
+ this.labelToCid.put("/yen", 0245);
+ this.labelToCid.put("/z", 0172);
+ this.labelToCid.put("/zero", 0060);
+ transafertLTOCinCTIL();
+ }
+
+ /**
+ * Parse the font stream to feed cidToLabel and labelToMetric with Glyphs
+ * information.
+ *
+ * @throws ValidationException
+ * On unexpected error
+ */
+ public void parse() throws ValidationException {
+ readClearText();
+ computeEexec();
+ }
+
+ /**
+ * Read eexecSize in the font stream.
+ *
+ * @return
+ * @throws IOException
+ */
+ protected byte[] readEexec() throws IOException {
+ int BUFFER_SIZE = 1024;
+ byte[] buffer = new byte[BUFFER_SIZE];
+ ByteArrayOutputStream eexecPart = new ByteArrayOutputStream();
+ int lr = 0;
+ int total = 0;
+ do {
+ lr = this.font.read(buffer, 0, BUFFER_SIZE);
+ if (lr == BUFFER_SIZE && (total + BUFFER_SIZE < eexecSize)) {
+ eexecPart.write(buffer, 0, BUFFER_SIZE);
+ total += BUFFER_SIZE;
+ } else if (lr > 0 && (total + lr < eexecSize)) {
+ eexecPart.write(buffer, 0, lr);
+ total += lr;
+ } else if (lr > 0 && (total + lr >= eexecSize)) {
+ eexecPart.write(buffer, 0, eexecSize - total);
+ total += (eexecSize - total);
+ }
+ } while (eexecSize > total && lr > 0);
+ IOUtils.closeQuietly(eexecPart);
+ return eexecPart.toByteArray();
+ }
+
+ /**
+ * This method read eexecSize bytes. Read bytes are decoded using the
+ * Type1FontUtils class and FontMetrics are computed.
+ *
+ * @throws ValidationException
+ */
+ protected void computeEexec() throws ValidationException {
+ try {
+ byte[] eexec = readEexec();
+ byte[] decryptedEexec = decodeEexec(eexec);
+// // Uncomment this to see EExec as clear text
+// System.out.println("DECODED EEXEC : ");
+// System.out.println(new String(decryptedEexec));
+
+ parseDecodedEexec(decryptedEexec);
+ } catch (IOException e) {
+ throw new ValidationException("Unable to compute the eexec portion : "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Parse decoded eexec portion of the font program. Feeds the labelToMetric
+ * map.
+ *
+ * @param eexec
+ * @throws IOException
+ */
+ protected void parseDecodedEexec(byte[] eexec) throws IOException {
+ ByteArrayInputStream baisEexec = new ByteArrayInputStream(eexec);
+ boolean expectedCharString = false;
+ // ---- According to Type1 specification 4 is the default value
+ int lenIV = 4;
+ int nChars = 0;
+ for (;;) {
+
+ byte[] token = readToken(baisEexec);
+
+ switch (tokenIdentifier(token)) {
+ case DUP_TOKEN:
+ if (!expectedCharString) {
+ byte[] tokenChoice = readToken(baisEexec); // ---- numeric code
+ if (tokenIdentifier(tokenChoice) == CHARSTRINGS_TOKEN) {
+ byte[] n = readToken(baisEexec);
+ nChars = Integer.parseInt(new String(n, "US-ASCII"));
+ expectedCharString = true;
+ // ---- read the end of line "dict dup begin"
+ readToken(baisEexec);
+ readToken(baisEexec);
+ readToken(baisEexec);
+ break;
+ } else if (!"begin".equals(new String(tokenChoice, "US-ASCII"))) {
+ byte[] toskip = readToken(baisEexec); // ---- binary length
+ readToken(baisEexec); // ---- skip RD
+ int skip = Integer.parseInt(new String(toskip, "US-ASCII"));
+ readBytes(baisEexec, skip);
+ }
+ }
+ break;
+ case LEN_IV_TOKEN:
+ byte[] l = readToken(baisEexec);
+ lenIV = Integer.parseInt(new String(l, "US-ASCII"));
+ break;
+ case CHARSTRINGS_TOKEN:
+ byte[] n = readToken(baisEexec);
+ nChars = Integer.parseInt(new String(n, "US-ASCII"));
+ expectedCharString = true;
+ // ---- read the end of line "dict dup begin"
+ readToken(baisEexec);
+ readToken(baisEexec);
+ readToken(baisEexec);
+ break;
+ case OBJ_NAME_TOKEN:
+ /*
+ * ---- OBJ_NAME_TOKEN : Some character's label aren't defined in the
+ * Encoding object but they should be defined by the Encoding array in
+ * the font ---- program.
+ */
+ // break;
+ case CHAR_LABEL_TOKEN:
+ // case OBJ_NAME_TOKEN :
+ if (expectedCharString) {
+ String label = new String(token, "US-ASCII");
+ byte[] csl = readToken(baisEexec);
+ int length = Integer.parseInt(new String(csl, "US-ASCII"));
+ // ---- read the RD token
+ readToken(baisEexec);
+ this.labelToMetric.put(label, getGlyphDescription(baisEexec, length,
+ lenIV));
+ nChars--;
+ if (nChars == 0) {
+ // ---- no more character
+ return;
+ }
+ }
+ default:
+ // nothing to do
+ break;
+ }
+ }
+ }
+
+ /**
+ *
+ * @param is
+ * @param length
+ * @return
+ * @throws IOException
+ */
+ protected byte[] readBytes(InputStream is, int length) throws IOException {
+ byte[] charF = new byte[length];
+ for (int i = 0; i < length; i++) {
+ charF[i] = (byte) is.read();
+ }
+ return charF;
+ }
+
+ /**
+ * Read the CharString in the InputStream and decode it. The decoded
+ * CharString is used to create a GlyphDescription object.
+ *
+ * @param is
+ * the decoded eexec portion of the type 1 font program
+ * @param length
+ * the number of bytes to read
+ * @param rdBytes
+ * the number of padding bytes at the beginning of the decoded
+ * CharString
+ * @return
+ * @throws IOException
+ */
+ protected Type1GlyphDescription getGlyphDescription(InputStream is,
+ int length, int rdBytes) throws IOException {
+ byte[] charF = readBytes(is, length);
+ byte[] dcs = Type1FontUtil.charstringDecrypt(charF, rdBytes);
+
+ Type1CharStringParser t1p = new Type1CharStringParser();
+ List<Object> lSequence = t1p.parse(dcs);
+
+ return new Type1GlyphDescription(lSequence);
+ }
+
+ /**
+ * Call the Type1FontUtil.eexecDecrypt() method
+ *
+ * @param eexec
+ * @return the decrypted eexec portion of the font program
+ */
+ protected byte[] decodeEexec(byte[] eexec) {
+ return Type1FontUtil.eexecDecrypt(eexec);
+ }
+
+ /**
+ * Read the clear-text portion of the Type1 font program.
+ *
+ * If FamillyName, FullName and FontName exist in the font program,
+ * Type1MetricHelper updates its attributes. This method feeds the cidToLabel
+ * map.
+ *
+ * @throws ValidationException
+ */
+ protected void readClearText() throws ValidationException {
+ int readBytes = 0;
+
+ try {
+ boolean dupAuth = false;
+ while (clearTextSize - readBytes > 0) {
+
+ byte[] token = readToken(this.font);
+ switch (tokenIdentifier(token)) {
+ case FAMILY_NAME_TOKEN:
+ byte[] fname = readToken(this.font);
+ readBytes += (fname.length + 1);
+
+ this.familyName = new String(fname, "US-ASCII");
+ break;
+ case FULL_NAME_TOKEN:
+ byte[] fullname = readToken(this.font);
+ readBytes += (fullname.length + 1);
+
+ this.fullName = new String(fullname, "US-ASCII");
+ break;
+ case DUP_TOKEN:
+ if (dupAuth) {
+ byte[] cid = readToken(this.font);
+ readBytes += (cid.length + 1);
+
+ byte[] label = readToken(this.font);
+ readBytes += (label.length + 1);
+
+ int cl = Integer.parseInt(new String(cid, "US-ASCII"));
+ String lc = new String(label, "US-ASCII");
+ this.cidToLabel.put(cl, lc);
+ this.labelToCid.put(lc, cl);
+ }
+ break;
+ case ENCODING_TOKEN:
+ byte[] tmpTok = readToken(this.font);
+ readBytes += (tmpTok.length + 1);
+ String encoding = new String(tmpTok, "US-ASCII");
+ if (PS_STANDARD_ENCODING.equals(encoding)) {
+ createStandardEncoding();
+ } else if (PS_ISOLATIN_ENCODING.equals(encoding)) {
+ createISOLatin1Encoding();
+ } else {
+ dupAuth = true;
+ }
+ break;
+ case READONLY_TOKEN:
+ dupAuth = false;
+ break;
+ default:
+ // nothing to go
+ }
+
+ // ---- add the token and the Space character length
+ readBytes += (token.length + 1);
+ }
+
+ } catch (IOException e) {
+ throw new ValidationException("Unable to read the clear text : "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Returns an int value which represent the token.
+ *
+ * @param token
+ * @return -1 if the token must be ignored
+ * @throws IOException
+ */
+ protected int tokenIdentifier(byte[] token) throws IOException {
+
+ String tokenAsStr = new String(token, "US-ASCII");
+ if ("/FamilyName".equals(tokenAsStr)) {
+ return FAMILY_NAME_TOKEN;
+ }
+
+ if ("/FullName".equals(tokenAsStr)) {
+ return FULL_NAME_TOKEN;
+ }
+
+ if ("/FontName".equals(tokenAsStr)) {
+ return FONT_NAME_TOKEN;
+ }
+
+ if ("dup".equals(tokenAsStr)) {
+ return DUP_TOKEN;
+ }
+
+ if ("/Encoding".equals(tokenAsStr)) {
+ return ENCODING_TOKEN;
+ }
+
+ if ("readonly".equals(tokenAsStr)) {
+ return READONLY_TOKEN;
+ }
+
+ if ("/lenIV".equals(tokenAsStr)) {
+ return LEN_IV_TOKEN;
+ }
+
+ if ("/CharStrings".equals(tokenAsStr)) {
+ return CHARSTRINGS_TOKEN;
+ }
+
+ if (labelToCid.containsKey(tokenAsStr)
+ || this.encoding.getNameToCodeMap().containsKey(
+ COSName.getPDFName(tokenAsStr.replace("/", "")))) {
+ return CHAR_LABEL_TOKEN;
+ }
+
+ String regex = "/[^\\s\\(\\)\\[\\]\\{\\}/<>%]+";
+ if (tokenAsStr.matches(regex)) {
+ return OBJ_NAME_TOKEN;
+ }
+
+ return -1;
+ }
+
+ /**
+ * Read the stream until a space character or EOL is reached.
+ *
+ * @return byte array containing bytes read before the space character.
+ * @throws IOException
+ */
+ protected byte[] readToken(InputStream stream) throws IOException {
+ List<Integer> buffer = new ArrayList<Integer>();
+ int currentByte = -1;
+ do {
+ currentByte = stream.read();
+ // ---- Token is String literal
+ if (currentByte > 0 && currentByte == '(') {
+ int opened = 1;
+ buffer.add(currentByte);
+
+ while (opened != 0) {
+ currentByte = stream.read();
+ if (currentByte < 0) {
+ throw new IOException("Unexpected End Of File");
+ }
+
+ if (currentByte == '(') {
+ opened++;
+ } else if (currentByte == ')') {
+ opened--;
+ }
+
+ // ---- Add useful character
+ buffer.add(currentByte);
+ }
+ } else if (currentByte > 0 && currentByte == '[') {
+ // ---- token is an array
+ int opened = 1;
+ buffer.add(currentByte);
+
+ while (opened != 0) {
+ currentByte = stream.read();
+ if (currentByte < 0) {
+ throw new IOException("Unexpected End Of File");
+ }
+
+ if (currentByte == '[') {
+ opened++;
+ } else if (currentByte == ']') {
+ opened--;
+ }
+
+ // ---- Add useful character
+ buffer.add(currentByte);
+ }
+ } else if (currentByte > 0 && currentByte == '{') {
+ // ---- token is an dictionary
+ int opened = 1;
+ buffer.add(currentByte);
+
+ while (opened != 0) {
+ currentByte = stream.read();
+ if (currentByte < 0) {
+ throw new IOException("Unexpected End Of File");
+ }
+
+ if (currentByte == '{') {
+ opened++;
+ } else if (currentByte == '}') {
+ opened--;
+ }
+
+ // ---- Add useful character
+ buffer.add(currentByte);
+ }
+ } else if (currentByte > 0
+ && (currentByte != ' ' && currentByte != '\n' && currentByte != '\r')) {
+ // ---- Add useful character
+ buffer.add(currentByte);
+ } else if (currentByte < 0) {
+ throw new IOException("Unexpected End Of File");
+ } else {
+ break;
+ }
+ } while (true);
+
+ byte[] res = new byte[buffer.size()];
+ for (int i = 0; i < res.length; ++i) {
+ res[i] = buffer.get(i).byteValue();
+ }
+
+// System.out.println("### READ TOKEN : " + new String(res));
+// if ("/CharStrings".equals(new String(res))) {
+// System.err.println("POUET");
+// }
+ return res;
+ }
+
+ /**
+ * Returns the Character name as PDF Name Object. (Prefixed by '/'). If the
+ * name is missing from the cidToLabel map and missing from the encoding
+ * object, the "/.notdef" name is returned.
+ * The pdf encoding array is used before the cidToLabel map.
+ * @param cid
+ * @return
+ */
+ protected String getLabelAsName(int cid) {
+ String label = null;
+
+ try {
+ label = this.encoding.getName(cid);
+ } catch (IOException e) {
+ label = this.cidToLabel.get(cid);
+ }
+
+ if (label == null) {
+ label = NOTDEF;
+ }
+ return label.charAt(0) == NAME_START ? label : NAME_START + label;
+ }
+
+ /**
+ * Return the Glyph width according to the Character identifier.
+ *
+ * @param cid
+ * @return
+ */
+ public int getWidth(int cid) {
+ String label = getLabelAsName(cid);
+
+ Type1GlyphDescription glyph = this.labelToMetric.get(label);
+ if (glyph != null) {
+ return glyph.getWidth();
+ }
+
+ return 0;
+ }
+
+ /**
+ * Container which contains all CharString Command and Operand. Currently,
+ * only the Glyph width can be access through the "hsdw" operator.
+ */
+ public static class Type1GlyphDescription {
+ private List<Object> lSequence = null;
+
+ private Integer width = null;
+
+ public Type1GlyphDescription(List<Object> ls) {
+ this.lSequence = ls;
+ }
+
+ public int getWidth() {
+ if (width != null) {
+ return width;
+ }
+
+ for (int i = 0; lSequence != null && i < lSequence.size(); ++i) {
+ Object obj = lSequence.get(i);
+ if (obj instanceof CharStringCommand) {
+ CharStringCommand csCmd = (CharStringCommand) obj;
+ if ("hsbw".equals(CharStringCommand.TYPE1_VOCABULARY.get(csCmd
+ .getKey()))) {
+ width = (Integer) lSequence.get(i - 1);
+ return width;
+ }
+ }
+ }
+
+ return 0;
+ }
+ }
+}
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type1MetricHelper.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type3FontContainer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type3FontContainer.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type3FontContainer.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type3FontContainer.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,50 @@
+/*****************************************************************************
+ *
+ * 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.ValidationConstants;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+
+/**
+ * Because Type3 font program is an inner type of the PDF file,
+ * this font container is quite different from the other because
+ * all character/glyph are already checked.
+ *
+ */
+public class Type3FontContainer extends AbstractFontContainer {
+
+ public Type3FontContainer(PDFont fd) {
+ super(fd);
+ }
+
+ @Override
+ public void checkCID(int cid) throws GlyphException {
+ if (!isAlreadyComputedCid(cid)) {
+ // missing glyph
+ GlyphException e = new GlyphException(ValidationConstants.ERROR_FONTS_GLYPH_MISSING, cid,
+ "There are no glyph in the Type 3 font for the character \"" + cid + "\"");
+ addKnownCidElement(new GlyphDetail(cid, e));
+ throw e;
+ }
+ }
+}
\ No newline at end of file
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type3FontContainer.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type3FontValidator.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type3FontValidator.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type3FontValidator.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type3FontValidator.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,553 @@
+/*****************************************************************************
+ *
+ * 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 java.util.Set;
+
+
+import org.apache.padaf.preflight.DocumentHandler;
+import org.apache.padaf.preflight.ValidationException;
+import org.apache.padaf.preflight.ValidationResult.ValidationError;
+import org.apache.padaf.preflight.contentstream.ContentStreamException;
+import org.apache.padaf.preflight.font.AbstractFontContainer.State;
+import org.apache.padaf.preflight.graphics.ExtGStateContainer;
+import org.apache.padaf.preflight.graphics.ShadingPattern;
+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.cos.COSStream;
+import org.apache.pdfbox.encoding.DictionaryEncoding;
+import org.apache.pdfbox.encoding.Encoding;
+import org.apache.pdfbox.encoding.EncodingManager;
+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;
+
+public class Type3FontValidator extends AbstractFontValidator {
+ protected PDType3Font pdType3 = null;
+
+ protected COSBase fontBBox = null;
+ protected COSBase fontMatrix = null;
+ protected COSBase charProcs = null;
+ protected COSBase fontEncoding = null;
+ protected COSBase firstChar = null;
+ protected COSBase lastChar = null;
+ protected COSBase widths = null;
+ protected COSBase toUnicode = null;
+ protected COSBase resources = null;
+
+ protected Encoding type3Encoding = null;
+
+ public Type3FontValidator(DocumentHandler handler, COSObject obj)
+ throws ValidationException {
+ super(handler, obj);
+ this.pdType3 = (PDType3Font) this.pFont;
+ }
+
+ /**
+ * This methods stores in attributes all required element. We extract these
+ * elements because of the PDType3Font object returns sometime default value
+ * if the field is missing, so to avoid mistake during required field
+ * validation we store them.
+ */
+ private void extractFontDictionaryEntries() {
+ // ---- required elements
+ this.fontBBox = this.fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_FONTBBOX));
+ this.fontMatrix = this.fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_FONTMATRIX));
+ this.charProcs = this.fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_CHARPROCS));
+ this.fontEncoding = this.fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_ENCODING));
+ this.firstChar = this.fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_FIRSTCHAR));
+ this.lastChar = this.fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_LASTCHAR));
+ this.widths = this.fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_WIDTHS));
+
+ // ---- Optional elements
+ this.toUnicode = this.fDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_TOUNICODE));
+ this.resources = this.fDictionary.getItem(COSName
+ .getPDFName(DICTIONARY_KEY_RESOURCES));
+ }
+
+ /**
+ * Returns true if all required fields are present. Otherwise, this method
+ * returns false and the FontContainer is updated.
+ *
+ * @return
+ */
+ private boolean checkMandatoryFields() {
+ boolean all = (this.fontBBox != null);
+ all = all && (this.fontMatrix != null);
+ all = all && (this.charProcs != null);
+ all = all && (this.fontEncoding != null);
+ all = all && (this.firstChar != null);
+ all = all && (this.lastChar != null);
+ all = all && (this.widths != null);
+ if (!all) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID));
+ }
+
+ /*
+ * ---- Since PDF 1.5 : FontDescriptor is mandatory for Type3 font. However
+ * because of the FontDescriptor is optional in PDF-1.4 no specific checks
+ * are processed for PDF/A validation.
+ */
+ return all;
+ }
+
+ /**
+ * FontBBox and FontMatrix are required. This method checks the type and the
+ * content of the FontBBox and FontMatrix element (Array of 4/6 number). If a
+ * type is invalid, the FontContainer is updated and the method returns false.
+ *
+ * @return
+ */
+ private boolean checkFontBBoxMatrix() {
+ COSDocument cDoc = this.handler.getDocument().getDocument();
+
+ // ---- both elements are an array
+ if (!COSUtils.isArray(this.fontBBox, cDoc)) {
+ this.fontContainer
+ .addError(new ValidationError(ERROR_FONTS_DICTIONARY_INVALID,
+ "The FontBBox element isn't an array"));
+ return false;
+ }
+
+ if (!COSUtils.isArray(this.fontMatrix, cDoc)) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "The FontMatrix element isn't an array"));
+ return false;
+ }
+
+ // ---- check the content of the FontBBox.
+ // ---- Should be an array with 4 numbers
+ COSArray bbox = COSUtils.getAsArray(fontBBox, cDoc);
+ if (bbox.size() != 4) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID, "The FontBBox element is invalid"));
+ return false;
+ } else {
+ for (int i = 0; i < 4; i++) {
+ COSBase elt = bbox.get(i);
+ if (!(COSUtils.isFloat(elt, cDoc) || COSUtils.isInteger(elt, cDoc))) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "An element of FontBBox isn't a number"));
+ return false;
+ }
+ }
+ }
+
+ // ---- check the content of the FontMatrix.
+ // ---- Should be an array with 6 numbers
+ COSArray matrix = COSUtils.getAsArray(fontMatrix, cDoc);
+ if (matrix.size() != 6) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID, "The FontMatrix element is invalid"));
+ return false;
+ } else {
+ for (int i = 0; i < 6; i++) {
+ COSBase elt = matrix.get(i);
+ if (!(COSUtils.isFloat(elt, cDoc) || COSUtils.isInteger(elt, cDoc))) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "An element of FontMatrix isn't a number"));
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * 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 Encoding) 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 :
+ * <UL>
+ * <li>An existing encoding name.
+ * <li>A dictionary with an existing encoding name (the name is optional) and
+ * a well formed "Differences" array (the array is optional)
+ * </UL>
+ *
+ * @return
+ */
+ private boolean checkEncoding() {
+ COSDocument cDoc = this.handler.getDocument().getDocument();
+
+ EncodingManager emng = new EncodingManager();
+ if (COSUtils.isString(this.fontEncoding, cDoc)) {
+ // ---- Encoding is a Name, check if it is an Existing Encoding
+ String enc = COSUtils.getAsString(this.fontEncoding, cDoc);
+ try {
+ type3Encoding = emng.getEncoding(COSName.getPDFName(enc));
+ } catch (IOException e) {
+ // ---- the encoding doesn't exist
+ this.fontContainer.addError(new ValidationError(ERROR_FONTS_ENCODING));
+ return false;
+ }
+ } else if (COSUtils.isDictionary(this.fontEncoding, cDoc)) {
+ COSDictionary encodingDictionary = COSUtils.getAsDictionary(
+ this.fontEncoding, cDoc);
+ try {
+ type3Encoding = new DictionaryEncoding(encodingDictionary);
+ } catch (IOException e) {
+ // ---- the encoding doesn't exist
+ this.fontContainer.addError(new ValidationError(ERROR_FONTS_ENCODING));
+ return false;
+ }
+
+ COSBase diff = encodingDictionary.getItem(COSName
+ .getPDFName(FONT_DICTIONARY_KEY_DIFFERENCES));
+ if (diff != null) {
+ if (!COSUtils.isArray(diff, cDoc)) {
+ this.fontContainer
+ .addError(new ValidationError(ERROR_FONTS_TYPE3_DAMAGED,
+ "The differences element of the encoding dictionary isn't an array"));
+ return false;
+ }
+
+ // ---- The DictionaryEncoding object doesn't throw exception if the
+ // Differences isn't well formed.
+ // So check if the array has the right format.
+ COSArray differences = COSUtils.getAsArray(diff, cDoc);
+ for (int i = 0; i < differences.size(); ++i) {
+ COSBase item = differences.get(i);
+ if (!(item instanceof COSInteger || item instanceof COSName)) {
+ // ---- Error, the Differences array is invalid
+ this.fontContainer
+ .addError(new ValidationError(ERROR_FONTS_TYPE3_DAMAGED,
+ "Differences Array should contain COSInt or COSName, no other type"));
+ return false;
+ }
+ }
+ }
+ } else {
+ // ---- the encoding entry is invalid
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_TYPE3_DAMAGED,
+ "The Encoding entry doesn't have the right type"));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * 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 character code 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.
+ *
+ * @param errors
+ * @return
+ */
+ private boolean checkCharProcsAndMetrics() throws ValidationException {
+ COSDocument cDoc = this.handler.getDocument().getDocument();
+
+ // ---- 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;
+ }
+
+ COSDictionary charProcsDictionary = COSUtils.getAsDictionary(this.charProcs, cDoc);
+ if (charProcsDictionary == null) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "The CharProcs element isn't a dictionary"));
+ 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;
+ }
+
+ // ---- Check width consistency
+ for (int i = 0; i < expectedLength; i++) {
+ int cid = fc + i;
+ COSBase arrContent = wArr.get(i);
+ if (COSUtils.isNumeric(arrContent, cDoc)) {
+ float width = COSUtils.getAsFloat(arrContent, cDoc);
+
+ String charName = null;
+ try {
+ charName = this.type3Encoding.getName(cid);
+ } catch (IOException e) {
+ // shouldn't occur
+ throw new ValidationException("Unable to check Widths consistency", e);
+ }
+
+ COSBase item = charProcsDictionary.getItem(COSName.getPDFName(charName));
+ COSStream charStream = COSUtils.getAsStream(item, cDoc);
+ if (charStream == null && width != 0) {
+ GlyphException glyphEx = new GlyphException(ERROR_FONTS_METRICS, cid,
+ "The CharProcs \"" + charName
+ + "\" doesn't exist but the width is " + width);
+ GlyphDetail glyphDetail = new GlyphDetail(cid, glyphEx);
+ this.fontContainer.addKnownCidElement(glyphDetail);
+ } else {
+ try {
+ // --- Parse the Glyph description to obtain the Width
+ PDFAType3StreamParser parser = new PDFAType3StreamParser(
+ this.handler);
+ PDResources pRes = null;
+ if (this.resources != null) {
+ COSDictionary resAsDict = COSUtils.getAsDictionary(
+ this.resources, cDoc);
+ if (resAsDict != null) {
+ pRes = new PDResources(resAsDict);
+ }
+ }
+ parser.resetEngine();
+ parser.processSubStream(null, pRes, charStream);
+
+ if (width != parser.getWidth()) {
+ GlyphException glyphEx = new GlyphException(ERROR_FONTS_METRICS, cid,
+ "The CharProcs \"" + charName
+ + "\" should have a width equals to " + width);
+ GlyphDetail glyphDetail = new GlyphDetail(cid, glyphEx);
+ this.fontContainer.addKnownCidElement(glyphDetail);
+ } else {
+ // Glyph is OK, we keep the CID.
+ GlyphDetail glyphDetail = new GlyphDetail(cid);
+ this.fontContainer.addKnownCidElement(glyphDetail);
+ }
+ } catch (ContentStreamException e) {
+ this.fontContainer.addError(new ValidationError(e
+ .getValidationError()));
+ return false;
+ } catch (IOException e) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_TYPE3_DAMAGED,
+ "The CharProcs references an element which can't be read"));
+ return false;
+ }
+ }
+
+ } else {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "The Witdhs array is invalid. (some element aren't integer)"));
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * If the Resources entry is present, this method check its content. Only
+ * fonts and Images are checked because this resource describes glyphs. REMARK
+ * : The font and the image aren't validated because they will be validated by
+ * an other ValidationHelper.
+ *
+ * @return
+ */
+ private boolean checkResources() throws ValidationException {
+ if (this.resources == null) {
+ // ---- No resources dictionary.
+ return true;
+ }
+
+ COSDocument cDoc = this.handler.getDocument().getDocument();
+ COSDictionary dictionary = COSUtils.getAsDictionary(this.resources, cDoc);
+ if (dictionary == null) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "The Resources element isn't a dictionary"));
+ return false;
+ }
+
+ COSBase cbImg = dictionary.getItem(COSName
+ .getPDFName(DICTIONARY_KEY_XOBJECT));
+ COSBase cbFont = dictionary
+ .getItem(COSName.getPDFName(DICTIONARY_KEY_FONT));
+
+ if (cbImg == null && cbFont == null) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_TYPE3_DAMAGED,
+ "The Resources element doesn't have Glyph information"));
+ return false;
+ }
+
+ if (cbImg != null) {
+ // ---- the referenced objects must be present in the PDF file
+ COSDictionary dicImgs = COSUtils.getAsDictionary(cbImg, cDoc);
+ Set<COSName> keyList = dicImgs.keySet();
+ for (Object key : keyList) {
+
+ COSBase item = dictionary.getItem((COSName) key);
+ COSDictionary xObjImg = COSUtils.getAsDictionary(item, cDoc);
+ if (xObjImg == null) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "The Resources dictionary of type 3 font is invalid"));
+ return false;
+ }
+
+ if (!XOBJECT_DICTIONARY_VALUE_SUBTYPE_IMG.equals(xObjImg
+ .getString(COSName.getPDFName(DICTIONARY_KEY_SUBTYPE)))) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "The Resources dictionary of type 3 font is invalid"));
+ return false;
+ }
+ }
+ }
+
+ if (cbFont != null) {
+ // ---- the referenced object must be present in the PDF file
+ COSDictionary dicFonts = COSUtils.getAsDictionary(cbFont, cDoc);
+ Set<COSName> keyList = dicFonts.keySet();
+ for (Object key : keyList) {
+
+ COSBase item = dictionary.getItem((COSName) key);
+ COSDictionary xObjFont = COSUtils.getAsDictionary(item, cDoc);
+ if (xObjFont == null) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "The Resources dictionary of type 3 font is invalid"));
+ return false;
+ }
+
+ if (!FONT_DICTIONARY_VALUE_FONT.equals(xObjFont.getString(COSName
+ .getPDFName(DICTIONARY_KEY_TYPE)))) {
+ this.fontContainer.addError(new ValidationError(
+ ERROR_FONTS_DICTIONARY_INVALID,
+ "The Resources dictionary of type 3 font is invalid"));
+ return false;
+ }
+
+ try {
+ PDFont aFont = PDFontFactory.createFont(xObjFont);
+ // FontContainer aContainer = this.handler.retrieveFontContainer(aFont);
+ AbstractFontContainer aContainer = this.handler.getFont(aFont.getCOSObject());
+ // ---- another font is used in the Type3, check if the font is valid.
+ if (aContainer.isValid() != State.VALID) {
+ this.fontContainer
+ .addError(new ValidationError(ERROR_FONTS_TYPE3_DAMAGED,
+ "The Resources dictionary of type 3 font contains invalid font"));
+ return false;
+ }
+ } catch (IOException e) {
+ throw new ValidationException("Unable to valid the Type3 : "
+ + e.getMessage());
+ }
+ }
+ }
+
+ List<ValidationError> errors = new ArrayList<ValidationError>();
+ ExtGStateContainer extGStates = new ExtGStateContainer(dictionary, cDoc);
+ boolean res = extGStates.validateTransparencyRules(errors);
+ for (ValidationError err : errors) {
+ this.fontContainer.addError(err);
+ }
+
+ return res && validateShadingPattern(dictionary, errors);
+ }
+
+ /**
+ * This method check the Shading entry of the resource dictionary if exists.
+ *
+ * @param result
+ * @return
+ * @throws ValidationException
+ */
+ protected boolean validateShadingPattern(COSDictionary dictionary,
+ List<ValidationError> result) throws ValidationException {
+ boolean res = true;
+ if (dictionary != null) {
+ COSDictionary shadings = (COSDictionary) dictionary
+ .getDictionaryObject(PATTERN_KEY_SHADING);
+ if (shadings != null) {
+ for (COSName key : shadings.keySet()) {
+ COSDictionary aShading = (COSDictionary) shadings.getDictionaryObject(key);
+ ShadingPattern sp = new ShadingPattern(handler, aShading);
+ List<ValidationError> lErrors = sp.validate();
+ if (lErrors != null && !lErrors.isEmpty()) {
+ result.addAll(lErrors);
+ res = false;
+ }
+ }
+ }
+ }
+ return res;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.awl.edoc.pdfa.validation.font.FontValidator#validate()
+ */
+ public boolean validate() throws ValidationException {
+ extractFontDictionaryEntries();
+ boolean isValid = checkMandatoryFields();
+ isValid = isValid && checkFontBBoxMatrix();
+ isValid = isValid && checkEncoding();
+ isValid = isValid && checkCharProcsAndMetrics();
+ isValid = isValid && checkResources();
+ return isValid;
+ }
+}
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/Type3FontValidator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/UndefFontContainer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/UndefFontContainer.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/UndefFontContainer.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/UndefFontContainer.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,38 @@
+/*****************************************************************************
+ *
+ * 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.ValidationConstants;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+
+public class UndefFontContainer extends AbstractFontContainer {
+
+ public UndefFontContainer(PDFont fd) {
+ super(fd);
+ }
+
+ @Override
+ public void checkCID(int cid) throws GlyphException {
+ throw new GlyphException(ValidationConstants.ERROR_FONTS_UNKNOWN_FONT_REF, 0, "A text content is using a undefined font type.");
+ }
+}
\ No newline at end of file
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/UndefFontContainer.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/type1/GlyphDescription.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/type1/GlyphDescription.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/type1/GlyphDescription.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/type1/GlyphDescription.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ *
+ * 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.type1;
+
+import java.util.List;
+
+import org.apache.fontbox.cff.CharStringCommand;
+
+public class GlyphDescription {
+ private List<Object> operations = null;
+
+ private Integer glyphWidth = null;
+
+ GlyphDescription(List<Object> operations) {
+ this.operations = operations;
+ }
+
+ public int getGlyphWidth() {
+ if (this.glyphWidth != null) {
+ return glyphWidth;
+ }
+
+ this.glyphWidth = searchWidth();
+ return this.glyphWidth;
+ }
+
+ private int searchWidth() {
+ for (int i = 0; operations != null && i < operations.size(); ++i) {
+ Object obj = operations.get(i);
+ if (obj instanceof CharStringCommand) {
+ CharStringCommand csCmd = (CharStringCommand) obj;
+ if ("hsbw".equals(CharStringCommand.TYPE1_VOCABULARY.get(csCmd.getKey()))) {
+ return (Integer) operations.get(i - 1);
+ }
+ }
+ }
+ // 0 is the default value if glyph isn't found.
+ return 0;
+ }
+}
\ No newline at end of file
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/type1/GlyphDescription.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/type1/PeekInputStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/type1/PeekInputStream.java?rev=1150373&view=auto
==============================================================================
--- pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/type1/PeekInputStream.java (added)
+++ pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/type1/PeekInputStream.java Sun Jul 24 14:02:12 2011
@@ -0,0 +1,73 @@
+/*****************************************************************************
+ *
+ * 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.type1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+
+public class PeekInputStream extends InputStream {
+ private byte[] content = new byte[0];
+ private int position = 0;
+
+ public PeekInputStream(InputStream source) throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try {
+ IOUtils.copyLarge(source, bos);
+ content = bos.toByteArray();
+ } finally {
+ IOUtils.closeQuietly(source);
+ IOUtils.closeQuietly(bos);
+ }
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (position >= content.length) {
+ throw new IOException("No more content in this stream");
+ }
+
+ int currentByte = (content[position] & 0xFF);
+ ++position;
+ return currentByte;
+ }
+
+ public int peek() throws IOException {
+ if (position >= content.length) {
+ throw new IOException("No more content in this stream");
+ }
+
+ return (content[position] & 0xFF);
+ }
+
+ public byte[] peek(int numberOfBytes) throws IOException {
+ if ( numberOfBytes < 0 || (position + numberOfBytes) >= content.length) {
+ throw new IOException("No more content in this stream, can't return the next " + numberOfBytes + " bytes");
+ }
+
+ byte[] nextBytes = new byte[numberOfBytes];
+ System.arraycopy(this.content, this.position, nextBytes, 0, numberOfBytes);
+ return nextBytes;
+ }
+}
\ No newline at end of file
Propchange: pdfbox/trunk/preflight/src/main/java/org/apache/padaf/preflight/font/type1/PeekInputStream.java
------------------------------------------------------------------------------
svn:eol-style = native