All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.fonts.StandardFonts Maven / Gradle / Ivy

There is a newer version: 7.15.25
Show newest version
/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/support/
 *
 * (C) Copyright 1997-2017 IDRsolutions and Contributors.
 *
 * This file is part of JPedal/JPDF2HTML5
 *
 @LICENSE@
 *
 * ---------------
 * StandardFonts.java
 * ---------------
 */
package org.jpedal.fonts;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

import org.jpedal.fonts.glyph.T1Glyphs;
import org.jpedal.fonts.objects.FontData;
import org.jpedal.fonts.tt.TTGlyphs;
import org.jpedal.utils.LogWriter;

public class StandardFonts {

    /**
     * holds names of every character
     */
    private static Map unicode_name_mapping_table = new HashMap();

    /**
     * holds lookup to map char values for decoding char into name
     */
    private static String[][] unicode_char_decoding_table = new String[7][335];

    public static final int PDF = 6;

    /**
     * flag used to identify ZAPF encoding
     */
    public static final int ZAPF = 5;

    /**
     * flag used to identify Symbol encoding
     */
    public static final int SYMBOL = 4;

    /**
     * flag used to identify MacExpert encoding
     */
    public static final int MACEXPERT = 3;

    /**
     * flag used to identify WIN encoding
     */
    public static final int WIN = 2;

    /**
     * flag used to identify STD encoding
     */
    public static final int STD = 1;

    /**
     * flag used to identify mac encoding
     */
    public static final int MAC = 0;

    /**
     * mapped onto CID0 or CID2
     */
    public static final int TYPE0 = 1228944676;

    public static final int TYPE1 = 1228944677;
    public static final int TRUETYPE = 1217103210;

    public static final int TYPE3 = 1228944679;
    public static final int CIDTYPE0 = -1684566726;
    public static final int CIDTYPE2 = -1684566724;

    public static final int OPENTYPE = 6;

    public static final int TRUETYPE_COLLECTION = 7;

    public static final int FONT_UNSUPPORTED = 8;

    /**
     * constant value for ellipsis
     */
    private static final String ellipsis = String.valueOf((char) Integer.parseInt("2026", 16));

    /**
     * must use windows encoding because files were edited on Windows
     */
    private static final String enc = "Cp1252";

    /**
     * lookup table to workout index from glyf
     */
    private static Map[] glyphToChar = new HashMap[7];

    /**
     * holds lookup to map char values for decoding NAME into encoded char
     */
    private static String[] MAC_char_encoding_table;

    /**
     * holds lookup to map char values for decoding NAME into encoded char
     */
    private static String[] WIN_char_encoding_table;

    /**
     * holds lookup to map char values for decoding NAME into encoded char
     */
    private static String[] STD_char_encoding_table;

    /**
     * holds lookup to map char values for decoding NAME into encoded char
     */
    private static String[] PDF_char_encoding_table;

    /**
     * holds lookup to map char values for decoding NAME into encoded char
     */
    private static String[] ZAPF_char_encoding_table;

    /**
     * holds lookup to map char values for decoding NAME into encoded char
     */
    private static String[] SYMBOL_char_encoding_table;

    /**
     * holds lookup to map char values for decoding NAME into encoded char
     */
    private static String[] MACEXPERT_char_encoding_table;

    /**
     * holds lookup to map unicode values onto their glyph name
     */
    private static final Map unicodeToName = new HashMap();


    /**
     * loader to load data from jar
     */
    private static ClassLoader loader = StandardFonts.class.getClassLoader();

    /**
     * list of standard fonts
     */
    private static Map standardFileList = new HashMap();

    /**
     * flag if standard font loaded
     */
    private static Map standardFontLoaded = new HashMap();

    /**
     * lookup for standard fonts which we read when object created
     * (deliberately switched back to hashtable - see case 13910)
     */
    private static Map widthTableStandard = new Hashtable();

    /**
     * lookup table for java fonts
     */
    protected static Map javaFontList = new HashMap();

    /**
     * java font versions of fonts
     */
    protected static String javaFonts[] =
            {"Courier",
                    "Courier-Bold",
                    "Courier",
                    "Courier-Bold",
                    "Arial",
                    "Arial-Bold",
                    "Arial",
                    "Arial-Italic",
                    "Symbol",
                    "Times New Roman",
                    "Times New Roman",
                    "Times New Roman",
                    "Times New Roman",
                    "Wingdings"};

    /**
     * names of 14 local fonts used in pdf
     */
    protected static String files_names[] =
            {
                    "Courier",
                    "Courier-Bold",
                    "Courier-BoldOblique",
                    "Courier-Oblique",
                    "Helvetica",
                    "Helvetica-Bold",
                    "Helvetica-BoldOblique",
                    "Helvetica-Oblique",
                    "Symbol",
                    "Times-Bold",
                    "Times-BoldItalic",
                    "Times-Italic",
                    "Times-Roman",
                    "ZapfDingbats"};

    /**
     * alternative names of 14 local fonts used in pdf
     */
    protected static String files_names_bis[] =
            {
                    "CourierNew",
                    "CourierNew,Bold",
                    "CourierNew,BoldItalic",
                    "CourierNew,Italic",
                    "Arial",
                    "Arial,Bold",
                    "Arial,BoldItalic",
                    "Arial,Italic",
                    "Symbol",
                    "TimesNewRoman,Bold",
                    "TimesNewRoman,BoldItalic",
                    "TimesNewRoman,Italic",
                    "TimesNewRoman",
                    "Wingdings"};

    /**
     * holds lookup values used by truetype font mapping
     */
    private static HashMap adobeMap;

    //hold bounds (deliberately switched back to hashtable - see case 13910)
    private static Map fontBounds = new Hashtable();

    /**
     * flag if standard_encoding loaded
     */
    public static boolean usesGlyphlist;

    public static String show() {
        String a = "";

        if (PDF_char_encoding_table != null) {
            a = a + "98=" + PDF_char_encoding_table[98];
        }

        return a;
    }

    public static void dispose() {

        unicode_name_mapping_table = null;

        unicode_char_decoding_table = null;

        glyphToChar = null;

        MAC_char_encoding_table = null;

        WIN_char_encoding_table = null;

        STD_char_encoding_table = null;

        PDF_char_encoding_table = null;

        ZAPF_char_encoding_table = null;

        SYMBOL_char_encoding_table = null;

        MACEXPERT_char_encoding_table = null;


        loader = null;

        standardFileList = null;

        standardFontLoaded = null;

        widthTableStandard = null;
        
        /*names of CID fonts supplied by Adobe*/
        //CIDFonts =null;
        
        /*lookup table for java fonts*/
        javaFontList = null;
        
        /*java font versions of fonts*/
        javaFonts = null;

        files_names = null;

        files_names_bis = null;

        adobeMap = null;

        fontBounds = null;
    }

    //////////////////////////////////////////////////
    /*
     * create lookup array so we can quickly test if
     * we have one of the 14 fonts so we can test quickly
     */
    static {
        // loop to read widths from default fonts
        for (int i = 0; i < files_names.length; i++) {
            standardFileList.put(files_names_bis[i], i);
            standardFileList.put(files_names[i], i);
        }

        loadAdobeMap();
    }

    /**
     * return font type based on file ending or FONT_UNSUPPORTED
     * if not recognised
     *
     * @param name
     * @return int - defined in StandardFonts (no value is FONT_UNSUPPORTED)
     */
    public static int getFontType(final String name) {

        int type = FONT_UNSUPPORTED;

        if (name.endsWith(".ttf")) {
            type = TRUETYPE;
        } else if (name.endsWith(".otf")) {
            type = OPENTYPE;
        } else if (name.endsWith(".ttc")) {
            type = TRUETYPE_COLLECTION;
            //else if(name.endsWith(".afm"))
            //  type=TYPE1;
        } else if (name.endsWith(".pfb")) {
            type = TYPE1;
        }

        return type;
    }

    /**
     * get FontBonds set in afm file
     */
    public static float[] getFontBounds(final String fontName) {
        return fontBounds.get(fontName);
    }


    public static String getUnicodeName(final String key) {
        return unicode_name_mapping_table.get(key);
    }

    public static String getUnicodeChar(final int i, final int key) {

        return unicode_char_decoding_table[i][key];
    }

    public static Float getStandardWidth(String font, final String key) {

        font = font.toLowerCase();

        Float value = widthTableStandard.get(font + key);
        if (value == null) {
            String altfont = font;
            final int p = altfont.indexOf(',');
            if (p != -1) {
                altfont = altfont.substring(0, p);
                value = widthTableStandard.get(altfont + key);
            }
        }

        return value;
    }

    //////////////////////////////////////////////////////////////////////////

    /**
     * create mapping tables for pdf values
     * for Zapf and Symbol (not fully implmented yet)
     */
    private static void readStandardMappingTable(
            final int key,
            final String file_name) {
        String char_value, NAME, VAL, line, hexVal;
        int value;
        BufferedReader input_stream = null;

        glyphToChar[key] = new HashMap();


        try {


            input_stream =
                    (file_name.equals("symbol.cfg"))
                            ? new BufferedReader(
                            new InputStreamReader(
                                    loader.getResourceAsStream(
                                            "org/jpedal/res/pdf/" + file_name),
                                    enc))
                            : new BufferedReader(
                            new InputStreamReader(
                                    loader.getResourceAsStream(
                                            "org/jpedal/res/pdf/" + file_name),
                                    "UTF-16"));

            // trap problems
            if (input_stream == null) {
                LogWriter.writeLog("Unable to open " + file_name + " to read standard encoding");
            }

            //read in lines and place in map tables for fast lookup
            while (true) {
                line = input_stream.readLine();
                if (line == null) {
                    break;
                }

                //write values to table, converting from Octal
                final StringTokenizer values = new StringTokenizer(line);

                final int tokenCount = values.countTokens();

                //trap for space and lines which cause problems in Zapf
                if (tokenCount > 1 && (!line.contains("space"))) {

                    switch (tokenCount) {
                        //ignore first as token but read as char
                        case 3:
                            char_value = values.nextToken();
                            NAME = values.nextToken();
                            VAL = values.nextToken();
                            break;

                        case 4:
                            hexVal = values.nextToken();

                            values.nextToken(); //ignore in this case
                            NAME = values.nextToken();
                            VAL = values.nextToken();

                            char_value = Character.toString((char) Integer.parseInt(hexVal, 16));
                            break;

                        case 2: //zapf values
                            char_value = " ";
                            NAME = values.nextToken();
                            VAL = values.nextToken();
                            break;

                        default:
                            char_value = values.nextToken();
                            NAME = values.nextToken();
                            VAL = values.nextToken();

                    }

                    if (tokenCount != 2) { //do not add forZapf
                        unicode_name_mapping_table.put(key + NAME, char_value);
                    }

                    glyphToChar[key].put(NAME, Integer.parseInt(VAL));

                    //20021104 added to make sure names in list as well
                    //if (file_name.equals("zapf.cfg"))
                    if (tokenCount != 2) { //do not add forZapf
                        unicode_name_mapping_table.put(NAME, char_value);
                    }

                    //convert if there is a value
                    if (Character.isDigit(VAL.charAt(0))) {

                        value = Integer.parseInt(VAL, 8);

                        if (key == ZAPF) {
                            ZAPF_char_encoding_table[value] = char_value;
                        } else if (key == SYMBOL) {
                            SYMBOL_char_encoding_table[value] = char_value;
                        } else if (key == MACEXPERT) {
                            MACEXPERT_char_encoding_table[value] = char_value;
                        }

                        unicode_char_decoding_table[key][value] = NAME;

                    }
                }
            }
        } catch (final Exception e) {
            LogWriter.writeLog("Exception " + e + " reading lookup table for pdf");
        }

        if (input_stream != null) {
            try {
                input_stream.close();
            } catch (final Exception e) {
                LogWriter.writeLog("Exception " + e + " reading lookup table for pdf  for abobe map");
            }
        }
    }
    //////////////////////////////////////////////

    /**
     * create mapping tables for 'standard' pdf values
     * by reading in data from files (which are tables
     * taken from Adobe's standard documentation
     */
    private static void readStandardMappingTable(final int idx) {

        String char_value, NAME, STD_value, MAC_value, WIN_value, PDF_value, raw;
        int mac_value, win_value, std_value;
        String line;
        BufferedReader input_stream = null;

        //needed for comparison table
        if (idx == MAC) {
            checkLoaded(WIN);
        }


        try {

            //initialise inverse lookup
            glyphToChar[idx] = new HashMap();

            input_stream =
                    new BufferedReader(
                            new InputStreamReader(
                                    loader.getResourceAsStream(
                                            "org/jpedal/res/pdf/standard_encoding.cfg"), enc));

            usesGlyphlist = true;

            // trap problems
            if (input_stream == null) {
                LogWriter.writeLog("Unable to open standard_encoding.cfg from jar");
            }

            //read in lines and place in map tables for fast lookup
            while (true) {
                line = input_stream.readLine();
                if (line == null) {
                    break;
                }

                //write values to table, converting from Octal
                final StringTokenizer values = new StringTokenizer(line);
                final int count = values.countTokens();

                //format is NAME, STD,MAC,WIN,PDF, unicode value (as hex) char from PDF reference ignored) or
                //NAME, STD,MAC,WIN,PDF,  char (used for fi,fl and other double values)
                //ignore first as token but read as char

                NAME = values.nextToken();
                STD_value = values.nextToken();
                MAC_value = values.nextToken();
                WIN_value = values.nextToken();
                PDF_value = values.nextToken();
                raw = values.nextToken();

                if (count == 7) {
                    char_value = Character.toString((char) Integer.parseInt(raw, 16));
                } else {
                    char_value = raw;
                }

                unicodeToName.put(char_value, NAME);


                //convert if possible
                if ((idx == MAC) && (Character.isDigit(MAC_value.charAt(0)))) {
                    mac_value = Integer.parseInt(MAC_value, 8);

                    //substitute ellipsis
                    if (mac_value == 201) {
                        char_value = ellipsis;
                    }

                    MAC_char_encoding_table[mac_value] = char_value;
                    unicode_char_decoding_table[MAC][mac_value] = NAME;

                    glyphToChar[MAC].put(NAME, mac_value);

                    //build a comparison table to test encoding
                    //if (Character.isDigit(WIN_value.charAt(0)))
                    //	win_value = Integer.parseInt(WIN_value, 8);

                } else if ((idx == STD) && (Character.isDigit(STD_value.charAt(0)))) {
                    std_value = Integer.parseInt(STD_value, 8);

                    //substitute ellipsis
                    if (std_value == 188) {
                        char_value = ellipsis;
                    }

                    STD_char_encoding_table[std_value] = char_value;
                    unicode_char_decoding_table[STD][std_value] = NAME;

                    glyphToChar[STD].put(NAME, std_value);

                } else if ((idx == PDF) && (Character.isDigit(PDF_value.charAt(0)))) {
                    std_value = Integer.parseInt(PDF_value, 8);

                    //substitute ellipsis
                    if (std_value == 131) {
                        char_value = ellipsis;
                    }

                    PDF_char_encoding_table[std_value] = char_value;
                    unicode_char_decoding_table[PDF][std_value] = NAME;

                } else if (idx == WIN && Character.isDigit(WIN_value.charAt(0))) {
                    win_value = Integer.parseInt(WIN_value, 8);

                    //substitute ellipsis
                    if (win_value == 133) {
                        char_value = ellipsis;
                    }

                    WIN_char_encoding_table[win_value] = char_value;
                    unicode_char_decoding_table[WIN][win_value] = NAME;

                    glyphToChar[WIN].put(NAME, win_value);


                }

                //save details for later
                unicode_name_mapping_table.put(NAME, char_value);


            }

            //add in alternative MAC space   312 octal == space
            if (idx == MAC) {
                MAC_char_encoding_table[202] = " ";
            }

            //add in alternative WIN values
            if (idx == WIN) {
                WIN_char_encoding_table[160] = " ";
                WIN_char_encoding_table[255] = "-";

                unicode_char_decoding_table[WIN][160] = "space";

            }

        } catch (final Exception e) {
            LogWriter.writeLog("Exception " + e + " reading lookup table for pdf  for " + idx);
        }

        if (input_stream != null) {
            try {
                input_stream.close();
            } catch (final Exception e) {
                LogWriter.writeLog("Exception " + e + " reading lookup table for pdf  for abobe map");
            }
        }
    }
    
    /*used internally when we needed to convert bytes to MacROman to build new tables*/
    /*private static String byteToEncodedString(String value,String enc) throws Exception{
    
     String s=null;

     //Create the encoder and decoder for ISO-8859-1
     Charset charset = Charset.forName(enc);
     CharsetDecoder decoder = charset.newDecoder();
     CharsetEncoder encoder = charset.newEncoder();

     // Convert a string to ISO-LATIN-1 bytes in a ByteBuffer
     // The new ByteBuffer is ready to be read.
     java.nio.ByteBuffer bbuf = encoder.encode(CharBuffer.wrap("a"));
     bbuf.clear();
     //java.nio.ByteBuffer bbuf=new ByteBuffer();
     bbuf.put(0,(byte) Integer.parseInt(value,16));
     // Convert ISO-LATIN-1 bytes in a ByteBuffer to a character ByteBuffer and then to a string.
     // The new ByteBuffer is ready to be read.
     CharBuffer cbuf = decoder.decode(bbuf);
     s = cbuf.toString();

     System.out.println(Integer.toHexString((int)s.charAt(0)));

     return s;
     }*/

    /**
     * Returns the appropriate glyph name for a supplied unicode value.
     *
     * @param unicode The character in unicode
     * @return The glyph name
     */
    public static String getNameFromUnicode(final String unicode) {
        return unicodeToName.get(unicode);
    }

    public static String getEncodedChar(final int font_encoding, final int char_int) {

        String return_character = null;

        switch (font_encoding) {
            case WIN:
                return_character = WIN_char_encoding_table[char_int];
                break;
            case STD:
                return_character = STD_char_encoding_table[char_int];
                break;
            case MAC:
                return_character = MAC_char_encoding_table[char_int];
                break;
            case PDF:
                return_character = PDF_char_encoding_table[char_int];
                break;
            case ZAPF:
                return_character = ZAPF_char_encoding_table[char_int];
                break;
            case SYMBOL:
                return_character = SYMBOL_char_encoding_table[char_int];
                break;
            case MACEXPERT:
                return_character = MACEXPERT_char_encoding_table[char_int];
                break;
            default:
                break;
        }

        if (return_character == null) {
            return_character = "&#" + char_int + ';';
        }

        return return_character;
    }

    /**
     * load required mappings
     */
    public static void checkLoaded(final int enc) {
        
        
        /*load mapping if we need it and initialise storage*/
        if ((enc == MAC) && (MAC_char_encoding_table == null)) {

            MAC_char_encoding_table = new String[335];
            readStandardMappingTable(enc);

        } else if ((enc == WIN) && (WIN_char_encoding_table == null)) {

            WIN_char_encoding_table = new String[335];
            readStandardMappingTable(enc);

        } else if ((enc == STD) && (STD_char_encoding_table == null)) {

            STD_char_encoding_table = new String[335];
            readStandardMappingTable(enc);

        } else if ((enc == PDF) && (PDF_char_encoding_table == null)) {

            PDF_char_encoding_table = new String[335];
            readStandardMappingTable(enc);

        } else if ((enc == SYMBOL) && (SYMBOL_char_encoding_table == null)) {

            SYMBOL_char_encoding_table = new String[335];
            readStandardMappingTable(SYMBOL, "symbol.cfg");

        } else if ((enc == ZAPF) && (ZAPF_char_encoding_table == null)) {

            ZAPF_char_encoding_table = new String[335];
            readStandardMappingTable(ZAPF, "zapf.cfg");

        } else if ((enc == MACEXPERT) && (MACEXPERT_char_encoding_table == null)) {

            MACEXPERT_char_encoding_table = new String[335];
            readStandardMappingTable(MACEXPERT, "mac_expert.cfg");

        }
    }

    /////////////////////////////////////////////////////////////////////////

    /**
     * read default widths for 14 standard fonts supplied
     * by Adobe
     */
    static final synchronized void loadStandardFont(final int i) throws IOException {
        String line, next_command, char_name = "";
        final BufferedReader input_stream;
        float width = 200;
        //int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
        //int b_x1 = 0, b_y1 = 0, b_x2 = 0, b_y2 = 0;

        //allow for 2 calls on thread with second call stalling this so
        //we should exit
        if (standardFontLoaded.get(i) != null) {
            return;
        }

        // loop to read widths from default fonts
        {
            //open the file
            input_stream =
                    new BufferedReader(
                            new InputStreamReader(
                                    loader.getResourceAsStream(
                                            "org/jpedal/res/pdf/defaults/"
                                                    + files_names[i]
                                                    + ".afm"),
                                    enc));

            boolean char_mapping_table = false;
            //flag if in correct part of file
            while (true) { //read the lines and extract width info
                line = input_stream.readLine();

                if (line == null) {
                    break;
                }
                if (line.startsWith("EndCharMetrics")) {
                    char_mapping_table = false;
                }

                //extract bounding box
                if (line.startsWith("FontBBox")) {

                    final float[] fontBBox = new float[4];
                    final StringTokenizer values = new StringTokenizer(line);
                    //drop FontBBox
                    values.nextToken();

                    for (int a = 0; a < 4; a++) {
                        fontBBox[a] = Integer.parseInt(values.nextToken());
                    }

                    fontBounds.put(files_names[i], fontBBox);


                }

                if (char_mapping_table) { //extract info from the line
                    final StringTokenizer values = new StringTokenizer(line, " ;");

                    //extract values
                    while (values.hasMoreTokens()) {
                        next_command = values.nextToken();
                        if (next_command.equals("WX")) {
                            width = Float.parseFloat(values.nextToken()) / 1000;
                        } else if (next_command.equals("N")) {
                            char_name = values.nextToken();
                        }
                    }

                    //store width
                    widthTableStandard.put(files_names_bis[i].toLowerCase() + char_name, width);
                    widthTableStandard.put(files_names[i].toLowerCase() + char_name, width);

                }
                if (line.startsWith("StartCharMetrics")) {
                    char_mapping_table = true;
                }
            }
        }

        if (input_stream != null) {
            try {
                input_stream.close();
            } catch (final Exception e) {
                LogWriter.writeLog("Exception " + e + " reading lookup table for pdf  for abobe map");
            }
        }

        //flag value is set
        standardFontLoaded.put(i, "x");
    }

    /**
     * check if one of 14 standard fonts and if so load widths
     */
    protected static void loadStandardFontWidth(final String fontName) {

        //get name of font if standard
        final Integer fileNumber = standardFileList.get(fontName);


        if (fileNumber != null && standardFontLoaded.get(fileNumber) == null) {

            try {
                loadStandardFont(fileNumber);

            } catch (final Exception e) {
                LogWriter.writeLog("[PDF] " + e + " problem reading lookup table for pdf font " + fontName + ' ' + fontName);
            }
        }
    }

    /**
     * converts glyph into character index
     */
    public static int lookupCharacterIndex(final String glyph, final int idx) {

        final Object value = glyphToChar[idx].get(glyph);
        if (value == null) {
            return 0;
        } else {
            return (Integer) value;
        }
    }

    /**
     * load the adobe unicode mapping table for truetype fonts
     */
    private static void loadAdobeMap() {

        BufferedReader input_stream = null;
        
        /*load if not already loaded*/
        if (adobeMap == null) {
            try {
                //initialise
                adobeMap = new HashMap();

                input_stream = new BufferedReader(
                        new InputStreamReader(
                                loader.getResourceAsStream(
                                        "org/jpedal/res/pdf/glyphlist.cfg"),
                                enc));

                // trap problems
                if (input_stream == null) {
                    LogWriter.writeLog("Unable to open glyphlist.cfg from jar");
                }

                //read in lines and place in map tables for fast lookup
                while (true) {
                    final String line = input_stream.readLine();
                    if (line == null) {
                        break;
                    }

                    if ((!line.startsWith("#")) && (line.indexOf(';') != -1)) {

                        final StringTokenizer vals = new StringTokenizer(line, ";");
                        final String key = vals.nextToken();
                        String operand = vals.nextToken();
                        final int space = operand.indexOf(' ');
                        if (space != -1) {
                            operand = operand.substring(0, space);
                        }
                        final int opVal = Integer.parseInt(operand, 16);
                        adobeMap.put(key, opVal);

                        unicode_name_mapping_table.put(key, Character.toString((char) opVal));

                    }
                }
            } catch (final Exception e) {
                LogWriter.writeLog("Exception " + e + " reading lookup table for pdf  for abobe map");
            }
        }

        if (input_stream != null) {
            try {
                input_stream.close();
            } catch (final Exception e) {
                LogWriter.writeLog("Exception " + e + " reading lookup table for pdf  for abobe map");
            }
        }
    }

    /**
     * @return Returns the adobe mapping for truetype case 3,1
     */
    public static int getAdobeMap(final String key) {

        final Integer value = adobeMap.get(key);
        if (value == null) {
            return -1;
        } else {
            return value;
        }

    }

    /**
     * @return Returns a boolean if in Adobe map
     */
    public static boolean isValidGlyphName(final String key) {

        if (key == null) {
            return false;
        } else {
            return (adobeMap.get(key) != null);
        }

    }


    /**
     * see if a standard font (ie Arial, Helvetica)
     */
    public static boolean isStandardFont(final String fontName, final boolean excludeWeights) {

        boolean isStandard = (standardFileList.get(fontName) != null);

        if (!isStandard && excludeWeights) {

            final char[] valuesToTest = {'-', ','};
            for (final char valueToTest : valuesToTest) {

                isStandard = checkSubFontName(fontName, valueToTest);
                if (isStandard) {
                    break;
                }
            }
        }
        return isStandard;
    }

    private static boolean checkSubFontName(final String fontName, final char charToTest) {

        boolean isStandard = false;

        final int ptr = fontName.indexOf(charToTest);
        if (ptr != -1) {
            isStandard = (standardFileList.get(fontName.substring(0, ptr)) != null);
        }
        return isStandard;
    }

    /**
     * open font , read postscript, and return Map with font details
     */
    public static Map getFontDetails(final int type, final String subFont) {

        final Map fontDetails = new HashMap();
        
        /*read in font data*/
        if (type == TRUETYPE || type == TRUETYPE_COLLECTION) {

            //FontData closed in routine
            TTGlyphs.addStringValues(new FontData(subFont), fontDetails);

        }

        return fontDetails;
    }

    /**
     * open font , read postscript, family or full names and return array
     */
    public static String[] readNamesFromFont(final int type, final String subFont, final int mode) throws Exception {

        String[] fontNames = new String[1];
        fontNames[0] = "";
        
        
        /*read in font data*/
        if (type == TRUETYPE || type == TRUETYPE_COLLECTION) {

            //FontData closed in routine
            fontNames = TTGlyphs.readFontNames(new FontData(subFont), mode);

        } else if (type == TYPE1) {

            //FontData closed in routine
            fontNames = T1Glyphs.readFontNames(new FontData(subFont));

        }

        return fontNames;
    }

    //allow for number value as well as glyph name (ie 68 rather than D)
    public static String convertNumberToGlyph(String mappedChar, final boolean containsHexNumbers, final boolean allowSingleValue) {

        int charCount = mappedChar.length();

        boolean isNumber = true; //assume true and disprove

        //System.out.println(mappedChar+" "+containsHexNumbers);
        
        /**/
        boolean hasHex = false;
        if (charCount == 2 || charCount == 3 || (allowSingleValue && charCount == 1)) {
            for (int ii = 0; ii < charCount; ii++) { //test all values to see if number
                final char c = mappedChar.charAt(ii);
                if (c >= '0' && c <= '9') {
                } else if (containsHexNumbers && c >= 'A' && c <= 'F') {
                    hasHex = true;
                } else {  //fail on first and exit loop
                    isNumber = false;
                    ii = charCount;
                }
            }

            if (isNumber) {

                if (charCount == 3 || !containsHexNumbers) {
                    if (!hasHex) { //stop spurious match (ie DC1)
                        mappedChar = String.valueOf((char) Integer.parseInt(mappedChar));
                    }
                } else {
                    mappedChar = String.valueOf((char) Integer.parseInt(mappedChar, 16));
                }
            }
        } else { //check any hash values to fix odd bug in file where #2323 truncates to #23
            boolean hasHash = false, hasText = false;

            for (int ii = 0; ii < charCount; ii++) { //test all values to see if number
                final char c = mappedChar.charAt(ii);
                if (c == '#') {
                    hasHash = true;
                    //ii=charCount;
                }
                if (c >= 'A' && c <= 'Z') {
                    hasText = true;
                }
            }

            if (hasHash) {

                final StringBuilder buf = new StringBuilder(mappedChar);

                //if text as well replicate what seems to happen in Acrobat and remove number value
                if (hasText) {

                    try {
                        //System.out.println("before="+buf.toString());

                        for (int ii = 0; ii < charCount; ii++) { //delete any excess values
                            final char c = buf.charAt(ii);
                            if ((c == '#') && (ii < charCount)) {
                                for (int aa = 0; aa < 2; aa++) {
                                    buf.deleteCharAt(ii + 1);
                                    //System.out.println("del"+buf.);
                                    charCount--;
                                }


                                //if(ii>=charCount)
                                //     break;
                            }
                        }

                    } catch (final Exception e) {
                        LogWriter.writeLog("Exception: " + e.getMessage());
                    }
                } else {
                    for (int ii = 0; ii < charCount; ii++) { //delete any excess values
                        final char c = buf.charAt(ii);
                        if (c == '#') {
                            ii += 3;
                            if (ii < charCount) {
                                char c2 = buf.charAt(ii);
                                while (c2 >= '0' && c2 <= '9') {
                                    buf.deleteCharAt(ii);
                                    charCount--;

                                    if (ii >= charCount) {
                                        break;
                                    }

                                    c2 = buf.charAt(ii);
                                }
                            }
                            ii--;
                        }
                    }
                }

                mappedChar = buf.toString();

            }
        }

        return mappedChar;
    }

    /**
     * turn hashed key value into String
     */
    public static String getFontypeAsString(final int fontType) {
        switch (fontType) {

            case TRUETYPE:
                return "TrueType";
            case TYPE1:
                return "Type1";
            case TYPE3:
                return "Type3";

            case CIDTYPE0:
                return "CIDFontType0";
            case CIDTYPE2:
                return "CIDFontType2";

            default:
                return "Unknown";
        }
    }


    private static final HashMap> mappedCharacters = new HashMap>();
    private static final HashMap> takenChars = new HashMap>();
    private static final int MAX_CHAR_CODE = 0xD800;

    private static void blockForbiddenRanges(final ArrayList ids) {
        int i;

        for (i = 0x0; i <= 0x1f; i++) {
            ids.add(i);
        }

        for (i = 0x7f; i <= 0xa0; i++) {
            ids.add(i);
        }

        for (i = 0x200c; i <= 0x200f; i++) {
            ids.add(i);
        }

    }

    /**
     * Find an appropriate value to use for identity CID fonts as unicode values are unrelated to actual values and many
     * potential values are blocked by browsers
     *
     * @param fontName Name of the font to use
     * @param cid      The cid value to map from
     * @return The value mapped onto
     */
    public static int mapCIDToValidUnicode(final String fontName, final int cid) {

        HashMap cidMap = mappedCharacters.get(fontName);
        ArrayList taken = takenChars.get(fontName);

        //Create structures for this font if they don't exist yet
        if (cidMap == null) {
            cidMap = new HashMap();
            mappedCharacters.put(fontName, cidMap);

            taken = new ArrayList();
            takenChars.put(fontName, taken);

            blockForbiddenRanges(taken);
        }

        //Fetch mapping and return if present
        final Integer result = cidMap.get(cid);
        if (result != null) {
            return result;
        }

        //First try existing CID
        int newCid = cid;

        //If under 0x20 shift by 0x20 to try to get reasonable extraction
        if (cid < 0x20) {
            newCid += 0x20;
        }

        //Otherwise use first available CID
        while (taken.contains(newCid) && taken.size() < MAX_CHAR_CODE) {
            newCid = (newCid + 1) % MAX_CHAR_CODE;
        }

        //Store for later lookup
        cidMap.put(cid, newCid);
        taken.add(newCid);

        return newCid;
    }


    public static int getIDForGlyphName(final String fontName, String glyphName) {
        boolean base10 = false;
        final int uc = getAdobeMap(glyphName);
        if (uc >= 0) {
            return uc;
        } else if (glyphName.startsWith("uni")) {
            glyphName = glyphName.substring(3);
        } else if (glyphName.charAt(0) == 'u' || glyphName.charAt(0) == 'G') {
            glyphName = glyphName.substring(1);
        } else {
            base10 = true;
        }

        if (glyphName != null) {
            try {
                if (base10 && glyphName.matches("[0-9]+")) {
                    final int num = Integer.parseInt(glyphName, 10);
                    return mapCIDToValidUnicode(fontName, num);
                } else if (glyphName.matches("[0-9A-F]+")) {
                    return Integer.parseInt(glyphName, 16);
                }
            } catch (final NumberFormatException e) {
                LogWriter.writeLog("Exception in handling cid id " + e);
            }
        }

        return -1;
    }

    /**
     * convert common abbreviations of standard fonts names
     */
    public static String expandName(String name) {

        final String testName = name.toLowerCase();

        if (testName.equals("cour")) {
            name = "Courier";
        } else if (testName.equals("helv")) {
            name = "Helvetica";
        } else if (testName.equals("hebo")) {
            name = "Helvetica-BOLD";
        } else if (testName.equals("zadb")) {
            name = "ZapfDingbats";
        } else if (testName.equals("tiro")) {
            name = "Times";
        }

        return name;
    }

    public static String[] CMAP;

    public static void readCMAP() {

        //initialise first time and use as flag to show read
        CMAP = new String[65536];

        String line;
        int rawVal, unicodeVal;
        final BufferedReader input_stream;

        try {

            input_stream = new BufferedReader(
                    new InputStreamReader(loader.getResourceAsStream("org/jpedal/res/pdf/jis.cfg")));


            //read in lines and place in map tables for fast lookup
            while (true) {
                line = input_stream.readLine();
                if (line == null) {
                    break;
                }

                if (line.startsWith("0") && line.contains("#")) {


                    //write values to table, converting from Octal
                    final StringTokenizer values = new StringTokenizer(line);
                    //System.out.println(line);
                    final String xx = values.nextToken().substring(2);

                    rawVal = Integer.parseInt(xx, 16); //raw hex
                    //values.nextToken(); //ignore
                    unicodeVal = Integer.parseInt(values.nextToken().substring(2), 16); //unicode
                    CMAP[rawVal] = String.valueOf((char) unicodeVal);
                }
            }

            input_stream.close();

        } catch (final Exception e) {
            LogWriter.writeLog("Exception: " + e.getMessage());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy