public synchronized void init() {
        // flag for initiated fonts
        if (inited) {
            return;
        }

        // re-initialize the char mapping array based on the encoding of the font
        if (encoding != null) {
            for (char i = 0; i < 256; i++) {
                cMap[i] = encoding.get(i);
            }
        }

        // ToUnicode indicates that we now have CMap stream that need to be parsed
        Object objectUnicode = library.getObject(entries, "ToUnicode");
        if (objectUnicode != null && objectUnicode instanceof Stream) {
            toUnicodeCMap = new CMap(library, new Hashtable(), (Stream) objectUnicode);
            toUnicodeCMap.init();
        }

        // Find any special encoding information, not used very often
        Object o = library.getObject(entries, "Encoding");
        if (o != null) {
            if (o instanceof Hashtable) {
                Hashtable encoding = (Hashtable) o;
                setBaseEncoding(library.getName(encoding, "BaseEncoding"));
                Vector differences = (Vector) library.getObject(encoding, "Differences");
                if (differences != null) {
                    int c = 0;
                    for (Object oo : differences) {
                        if (oo instanceof Number) {
                            c = ((Number) oo).intValue();
                        } else if (oo instanceof Name) {
                            String n = oo.toString();
                            int c1 = Encoding.getUV(n);
                            if (c1 == -1) {
                                if (n.charAt(0) == 'a') {
                                    n = n.substring(1);
                                    try {
                                        c1 = Integer.parseInt(n);
                                    } catch (Exception ex) {
                                        logger.log(Level.FINE, "Error parings font differences");
                                    }
                                }
                            }
                            cMap[c] = (char) c1;
                            c++;
                        }
                    }
                }
            } else if (o instanceof Name) {
                setBaseEncoding(((Name) o).getName());
            }
        }

        // An array of (LastChar ? FirstChar + 1) widths, each element being the
        // glyph width for the character code that equals FirstChar plus the array index.
        widths = (Vector) (library.getObject(entries, "Widths"));
        if (widths != null) {

            // Assigns the First character code defined in the font's Widths array
            o = library.getObject(entries, "FirstChar");
            if (o != null) {
                firstchar = (int) (library.getFloat(entries, "FirstChar"));
            }
        }
        // check of a cid font
        else if (library.getObject(entries, "W") != null) {
            // calculate CID widths
            cidWidths = calculateCIDWidths();
            // first char is likely 1.
            firstchar = 0;
            // cid fonts are not afm...
            isAFMFont = false;
        }
        // afm fonts don't have widths.
        else {
            isAFMFont = false;
        }


        // Assign the font descriptor
        Object of = library.getObject(entries, "FontDescriptor");
        if (of instanceof FontDescriptor) {
            fontDescriptor = (FontDescriptor) of;
            fontDescriptor.init();
        }

        // If there is no FontDescriptor then we most likely have a core afm
        // font and we should get the matrix so that we can derive the correct
        // font.
        if (fontDescriptor == null && basefont != null) {
            // see if the baseFont name matches one of the AFM names
            Object afm = AFM.AFMs.get(basefont.toLowerCase());
            if (afm != null && afm instanceof AFM) {
                AFM fontMetrix = (AFM) afm;
                // finally create a fontDescriptor based on AFM data.
                fontDescriptor = FontDescriptor.createDescriptor(library, fontMetrix);
                fontDescriptor.init();
            }
        }

        // assign font name for descriptor
        if (fontDescriptor != null) {
            String name = fontDescriptor.getFontName();
            if (name != null && name.length() > 0){
                basefont = cleanFontName(name);
            }
        }

        // checking flags for set bits.
        if (fontDescriptor != null && (fontDescriptor.getFlags() & 64) != 0
                && encoding == null) {
            encodingName = "standard";
            encoding = Encoding.getStandard();
        }

        // this is a test to basic CIDFont support.  The current font class
        // is not setup to deal with this type of font,  however we can still
        // located the descendant font described by the CIDFont's data and try
        // and cMap the properties over to the type1 font
        Object desendantFont = library.getObject(entries, "DescendantFonts");
        if (desendantFont != null) {
            Vector tmp = (Vector) desendantFont;
            if (tmp.elementAt(0) instanceof Reference) {
                // locate the font reference
                Object fontReference = library.getObject((Reference) tmp.elementAt(0));
                if (fontReference instanceof Font) {
                    // create and initiate the font based on the stream data
                    Font desendant = (Font) fontReference;
                    desendant.toUnicodeCMap = this.toUnicodeCMap;
                    desendant.init();
                    this.cidWidths = desendant.cidWidths;
                    // point the DescendantFont font Descriptor to this Font object
                    // this may help improve the display of some fonts.
                    if (fontDescriptor == null) {
                        fontDescriptor = desendant.fontDescriptor;
                        String name = fontDescriptor.getFontName();
                        if (name != null ){
                            basefont = cleanFontName(name);
                        }
                    }
                }
            }
        }

        // check to see if the the font subtype is Type 1 and see if it matches
        // one of the base 14 types included with the document.
        if (subtype.equals("Type1")) {
            AFM a = AFM.AFMs.get(basefont.toLowerCase());
            if (a != null && a.getFontName() != null) {
                afm = a;
            }
        }
        // Create a new true type font based on the named basefont.
        if (subtype.equals("Type1")) {
            for (String[] aType1Diff : type1Diff) {
                if (basefont.equals(aType1Diff[0])) {
                    java.awt.Font f =
                            new java.awt.Font(
                                    aType1Diff[1],
                                    style,
                                    12);
                    if (f.getFamily().equals(aType1Diff[2])) {
                        basefont = aType1Diff[1];
                        break;
                    }
                }
            }
        }

        // font substitution found flag
        isFontSubstitution = true;
//        isAFMFont = true;

        // get most types of embedded fonts from here
        if (fontDescriptor != null && fontDescriptor.getEmbeddedFont() != null) {
            font = fontDescriptor.getEmbeddedFont();
            isFontSubstitution = false;
            isAFMFont = false;
        }

        // look at all PS font names and try and find a match
        if (font == null && basefont != null) {
            // Check to see if any of the system fonts match the basefont name
            for (java.awt.Font font1 : fonts) {

                // remove white space
                StringTokenizer st = new StringTokenizer(font1.getPSName(), " ", false);
                String fontName = "";
                while (st.hasMoreElements()) fontName += st.nextElement();

                // if a match is found assign it as the real font
                if (fontName.equalsIgnoreCase(basefont)) {
                    font = new OFont(new java.awt.Font(font1.getFamily(), style, 1));
                    basefont = font1.getPSName();
                    isFontSubstitution = true;
                    break;
                }
            }
        }

        // look at font family name matches against system fonts
        if (font == null && basefont != null) {

            // clean the base name so that is has just the font family
            String fontFamily = FontUtil.guessFamily(basefont);

            for (java.awt.Font font1 : fonts) {
                // find font family match
                if(FontUtil.normalizeString(
                        font1.getFamily()).equalsIgnoreCase(fontFamily)) {
                    // create new font with font family name and style
                    font = new OFont(new java.awt.Font(font1.getFamily(), style, 1));
                    basefont = font1.getFontName();
                    isFontSubstitution = true;
                    break;
                }
            }
        }
        // if the font is still null decode the name, which will try and find
        // the basefont, if it fails it will assign a default dialog font
        if (font == null && basefont != null && basefont.indexOf("-") != -1) {
            font = new OFont(java.awt.Font.decode(basefont));
            basefont = font.getName();
        }
        // if still null, shouldn't be, assigned the basefont name
        if (font == null) {
            try{
                font = new OFont(java.awt.Font.getFont(basefont,
                        new java.awt.Font(basefont,
                                style,
                                12)));
                basefont = font.getName();
            }catch(Exception e){
                if (logger.isLoggable(Level.WARNING)) {
                    logger.warning("Error creating awt.font for: " + entries);
                }
            }
        }
        // If the font substitutions failed then we want to try and pick the proper
        // font family based on what the font name best matches up with none
        // font family font names.  if all else fails use serif as it is the most'
        // common font.
        if (!isFontSubstitution && font != null &&
                font.getName().toLowerCase().indexOf(font.getFamily().toLowerCase()) < 0) {
            // see if we working with a sans serif font
            if ((font.getName().toLowerCase().indexOf("times new roman") != -1 ||
                    font.getName().toLowerCase().indexOf("timesnewroman") != -1 ||
                    font.getName().toLowerCase().indexOf("bodoni") != -1 ||
                    font.getName().toLowerCase().indexOf("garamond") != -1 ||
                    font.getName().toLowerCase().indexOf("minion web") != -1 ||
                    font.getName().toLowerCase().indexOf("stone serif") != -1 ||
                    font.getName().toLowerCase().indexOf("stoneserif") != -1 ||
                    font.getName().toLowerCase().indexOf("georgia") != -1 ||
                    font.getName().toLowerCase().indexOf("bitstream cyberbit") != -1)) {
                font = new OFont(new java.awt.Font("serif",
                        font.getStyle(), (int) font.getSize()));
                basefont = "serif";
            }
            // see if we working with a monospaced font
            else if ((font.getName().toLowerCase().indexOf("helvetica") != -1 ||
                    font.getName().toLowerCase().indexOf("arial") != -1 ||
                    font.getName().toLowerCase().indexOf("trebuchet") != -1 ||
                    font.getName().toLowerCase().indexOf("avant garde gothic") != -1 ||
                    font.getName().toLowerCase().indexOf("avantgardegothic") != -1 ||
                    font.getName().toLowerCase().indexOf("verdana") != -1 ||
                    font.getName().toLowerCase().indexOf("univers") != -1 ||
                    font.getName().toLowerCase().indexOf("futura") != -1 ||
                    font.getName().toLowerCase().indexOf("stone sans") != -1 ||
                    font.getName().toLowerCase().indexOf("stonesans") != -1 ||
                    font.getName().toLowerCase().indexOf("gill sans") != -1 ||
                    font.getName().toLowerCase().indexOf("gillsans") != -1 ||
                    font.getName().toLowerCase().indexOf("akzidenz") != -1 ||
                    font.getName().toLowerCase().indexOf("grotesk") != -1)) {
                font = new OFont(new java.awt.Font("sansserif",
                        font.getStyle(), (int) font.getSize()));
                basefont = "sansserif";
            }
            // see if we working with a mono spaced font
            else if ((font.getName().toLowerCase().indexOf("courier") != -1 ||
                    font.getName().toLowerCase().indexOf("courier new") != -1 ||
                    font.getName().toLowerCase().indexOf("couriernew") != -1 ||
                    font.getName().toLowerCase().indexOf("prestige") != -1 ||
                    font.getName().toLowerCase().indexOf("eversonmono") != -1 ||
                    font.getName().toLowerCase().indexOf("Everson Mono") != -1)) {
                font = new OFont(new java.awt.Font("monospaced",
                        font.getStyle(), (int) font.getSize()));
                basefont = "monospaced";
            }
            // if all else fails go with the serif as it is the most common font family
            else {
                font = new OFont(new java.awt.Font("serif",
                        font.getStyle(), (int) font.getSize()));
                basefont = "serif";
            }
        }
        // finally if we have an empty font then we default to serif so that
        // we can try and render the character codes.
        if (font == null){
            font = new OFont(new java.awt.Font("serif", style, 12));
            basefont = "serif";
        }

        // setup encoding and widths.
        setWidth();
        font = font.deriveFont(encoding, toUnicodeCMap);

        if (logger.isLoggable(Level.FINE)) {
            logger.fine(name + " - " + encodingName + " " + basefont + " " +
                    font.toString() + " " + isFontSubstitution);
        }

        inited = true;
    }