Package org.apache.padaf.preflight.font

Source Code of org.apache.padaf.preflight.font.Type1MetricHelper$Type1GlyphDescription

/*****************************************************************************
*
* 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();
    } else {
      glyph = this.labelToMetric.get(NOTDEF);
      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;
    }
  }
}
TOP

Related Classes of org.apache.padaf.preflight.font.Type1MetricHelper$Type1GlyphDescription

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.