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

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

There is a newer version: 20151002
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-2015 IDRsolutions and Contributors.
 *
 * This file is part of JPedal/JPDF2HTML5
 *
     This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * FontMappings.java
 * ---------------
 */
package org.jpedal.fonts;

import org.jpedal.PdfDecoderInt;
import org.jpedal.exception.PdfFontException;
import org.jpedal.parser.DecoderOptions;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.Strip;

import java.awt.*;
import java.io.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Holds Maps which are used to map font names onto actual fonts and files
 */
public class FontMappings {
    
    /**ensure fonts setup only once*/
    public static boolean fontsInitialised;
    
    /**
     * font to use in preference to Lucida
     */
    public static String defaultFont;
    
    /**
     * flag to show if there must be a mapping value (program exits if none
     * found)
     */
    public static boolean enforceFontSubstitution;
    
    /**
     * used to remap fonts onto truetype fonts (set internally)
     */
    public static Map fontSubstitutionTable;
    
    /**
     * hold details of all fonts
     */
    public static Map fontPropertiesTable;
    
    /**
     * used to ensure substituted fonts unique
     */
    public static Map fontPossDuplicates;
    
    /**
     * used to store number for subfonts in TTC
     */
    public static Map fontSubstitutionFontID;
    
    /**
     * used to remap fonts onto truetype fonts (set internally)
     */
    public static Map fontSubstitutionLocation = new ConcurrentHashMap();
    
    /**
     * used to remap fonts onto truetype fonts (set internally)
     */
    public static Map fontSubstitutionAliasTable = new ConcurrentHashMap();
    
    /**only upload all fonts once*/
    private static boolean fontsSet;
    
    private static final String separator = System.getProperty("file.separator");
    
    /**put fonts in variable so can be altered if needed by Client*/
    public static String[] defaultFontDirs= {"C:/windows/fonts/","C:/winNT/fonts/",
        "/Library/Fonts/",
        //"/System/Library/Fonts/",  ms 20111013 commented out as breaks forms with Zapf
        "/usr/share/fonts/truetype/msttcorefonts/",
        //"/usr/share/fonts/truetype/",
        //"/windows/D/Windows/Fonts/"
        "usr/local/Fonts/",
    };
    /**
     * determine how font substitution is done
     */
    private static int fontSubstitutionMode = PdfDecoderInt.SUBSTITUTE_FONT_USING_FILE_NAME;
    //private static int fontSubstitutionMode=PdfDecoderInt.SUBSTITUTE_FONT_USING_POSTSCRIPT_NAME;
    //private static int fontSubstitutionMode=PdfDecoderInt.SUBSTITUTE_FONT_USING_FULL_FONT_NAME;
    //private static int fontSubstitutionMode=PdfDecoderInt.SUBSTITUTE_FONT_USING_FAMILY_NAME;
    //private static int fontSubstitutionMode=PdfDecoderInt.SUBSTITUTE_FONT_USING_POSTSCRIPT_NAME_USE_FAMILY_NAME_IF_DUPLICATES;
    
    private FontMappings(){}
    
    /**
     * used internally to pick uo org.jpedal.fontmaps property and set
     */
    public static void initFonts(){
        
        // pick up D options and use settings
        
        try {
            final String fontMaps = System.getProperty("org.jpedal.fontmaps");
            
            if (fontMaps != null) {
                final StringTokenizer fontPaths = new StringTokenizer(fontMaps, ",");
                
                while (fontPaths.hasMoreTokens()) {
                    
                    final String fontPath = fontPaths.nextToken();
                    final StringTokenizer values = new StringTokenizer(fontPath, "=:");
                    
                    final int count = values.countTokens() - 1;
                    final String[] nameInPDF = new String[count];
                    final String key = values.nextToken();
                    for (int i = 0; i < count; i++) {
                        nameInPDF[i] = values.nextToken();
                    }
                    
                    setSubstitutedFontAliases(key, nameInPDF); //$NON-NLS-1$
                    
                }
            }
            
        } catch (final Exception e) {
            LogWriter.writeLog("Unable to read org.jpedal.fontmaps " + e.getMessage());
        }
        
        // pick up D options and use settings
        
        try {
            final String fontDirs = System.getProperty("org.jpedal.fontdirs");
            String failed = null;
            if (fontDirs != null) {
                failed = FontMappings.addFonts(fontDirs, failed);
            }
            if (failed != null) {
                LogWriter.writeLog("Could not find " + failed);
            }
        } catch (final Exception e) {
            LogWriter.writeLog("Unable to read FontDirs " + e.getMessage());
        }
    }
    
    
    /**
     * set mode to use when substituting fonts (default is to use Filename (ie arial.ttf)
     * Options are  SUBSTITUTE_* values from PdfDecoder
     */
    public static void setFontSubstitutionMode(final int mode) {
        fontSubstitutionMode = mode;
    }
    
    /**
     * set mode to use when substituting fonts (default is to use Filename (ie arial.ttf)
     * Options are  SUBSTITUTE_* values from PdfDecoder
     */
    public static int getFontSubstitutionMode() {
        return  fontSubstitutionMode;
    }
    
    /**
     * allows a number of fonts to be mapped onto an actual font and provides a
     * way around slightly differing font naming when substituting fonts - So if
     * arialMT existed on the target machine and the PDF contained arial and
     * helvetica (which you wished to replace with arialmt), you would use the
     * following code -
     * 
* String[] aliases={"arial","helvetica"}; * currentPdfDecoder.setSubstitutedFontAliases("arialmt",aliases); - *
* comparison is case-insensitive and file type/ending should not be * included - For use in conjunction with -Dorg.jpedal.fontdirs options which allows * user to pass a set of comma separated directories with Truetype fonts * (directories do not need to exist so can be multi-platform setting) */ public static void setSubstitutedFontAliases(final String fontFileName, final String[] aliases) { if (aliases != null) { final String name = fontFileName.toLowerCase(); String alias; for (final String aliase : aliases) { alias = aliase.toLowerCase(); if (!alias.equals(name)) { fontSubstitutionAliasTable.put(alias, name); } } } } /** * takes a comma separated list of font directories and add to substitution */ public static String addFonts(final String fontDirs, final String failed) { final StringTokenizer fontPaths = new StringTokenizer(fontDirs, ","); while (fontPaths.hasMoreTokens()) { String fontPath = fontPaths.nextToken(); if (!fontPath.endsWith("/") && !fontPath.endsWith("\\")) { fontPath += separator; } //LogWriter.writeLog("Looking in " + fontPath + " for TT fonts"); addTTDir(fontPath, failed); } return failed; } public static void dispose(){ fontSubstitutionTable=null; fontPropertiesTable=null; fontPossDuplicates=null; fontSubstitutionFontID = null; fontSubstitutionLocation = null; fontSubstitutionAliasTable = null; } /** * add a truetype font directory and contents to substitution */ public static String addTTDir(final String fontPath, String failed) { if ( fontSubstitutionTable == null) { fontSubstitutionTable = new ConcurrentHashMap(); fontSubstitutionFontID = new ConcurrentHashMap(); fontPossDuplicates = new ConcurrentHashMap(); fontPropertiesTable = new ConcurrentHashMap(); } final File currentDir = new File(fontPath); if ((currentDir.exists()) && (currentDir.isDirectory())) { final String[] files = currentDir.list(); if (files != null) { for (final String currentFont : files) { addFontFile(currentFont, fontPath); } } } else { if (failed == null) { failed = fontPath; } else { failed = failed + ',' + fontPath; } } return failed; } /** * add a list of settings to map common fonts which can be substituted onto correct platform settings for Windows/MAC/Linux so JPedal * will try to use the fonts on the computer if possible to produce most accurate display. */ public static void setFontReplacements() { //this is where we setup specific font mapping to use fonts on local machines //note different settigns for Win, linux, MAC //general final String[] aliases6={/**"AcArial"};//,/**/"acarialunicodems__cn"};//,"acarial,bold"}; setSubstitutedFontAliases("adobeheitistd-regular",aliases6); //platform settings if(DecoderOptions.isRunningOnMac){ //Courier (CourierNew) both on Mac and different setSubstitutedFontAliases("Courier italic",new String[]{"Courier-Oblique"}); setSubstitutedFontAliases("Courier bold",new String[]{"Courier-Bold"}); setSubstitutedFontAliases("Courier bold italic",new String[]{"Courier-BoldOblique"}); setSubstitutedFontAliases("Courier new italic",new String[]{"CourierNew,italic","CourierStd-Oblique","CourierNewPS-ItalicMT"}); setSubstitutedFontAliases("Courier new bold",new String[]{"CourierNew,Bold","Courier-Bold","CourierStd-Bold","CourierNewPS-BoldMT"}); setSubstitutedFontAliases("Courier new bold italic",new String[]{"CourierNew-BoldOblique","CourierStd-BoldOblique","CourierNewPS-BoldItalicMT"}); setSubstitutedFontAliases("Courier new",new String[]{"CourierNew","Courier","CourierStd","CourierNewPSMT"}); //Helvetica (Arial) setSubstitutedFontAliases("arial",new String[]{"Helvetica","arialmt"}); setSubstitutedFontAliases("arial italic",new String[]{"arial-italic", "arial-italicmt","Helvetica-Oblique","Arial,Italic"}); setSubstitutedFontAliases("arial bold",new String[]{"arial-boldmt,bold","arial-boldmt","Helvetica-Bold","Arial,bold"}); setSubstitutedFontAliases("arial bold italic",new String[]{"Arial-BoldItalicMT","Helvetica-BoldOblique"}); //Arial Narrow - not actually one of fonts but very common so added setSubstitutedFontAliases("arial Narrow",new String[]{"ArialNarrow",}); //called ArialNarrow in PDF, needs to be arialn for Windows setSubstitutedFontAliases("arial Narrow italic",new String[]{"ArialNarrow-italic"}); setSubstitutedFontAliases("arial Narrow bold",new String[]{"ArialNarrow-bold","ArialNarrow,Bold"}); setSubstitutedFontAliases("arial Narrow bold italic",new String[]{"ArialNarrow-bolditalic"}); //Times/TimesNewRoman setSubstitutedFontAliases("times new roman bold",new String[] {"Times-Bold","TimesNewRoman,Bold","TimesNewRomanPS-BoldMT"}); setSubstitutedFontAliases("times new roman bold italic",new String[] {"Times-BoldItalic","TimesNewRoman,BoldItalic","TimesNewRomanPS-BoldItalicMT"}); setSubstitutedFontAliases("times new roman italic",new String[] {"Times-Italic","TimesNewRoman,Italic","TimesNewRomanPS-ItalicMT"}); setSubstitutedFontAliases("times new roman",new String[] {"Times-Roman","TimesNewRoman","Times","TimesNewRomanPSMT"}); setSubstitutedFontAliases("wingdings",new String[] {"ZapfDingbats","ZaDb"}); //default at present for others as well }else {//if(PdfDecoder.isRunningOnWindows){ //Courier (CourierNew) setSubstitutedFontAliases("Couri",new String[]{"Courier-Oblique", "CourierNew,italic","CourierStd-Oblique","CourierNewPS-ItalicMT"}); setSubstitutedFontAliases("Courbd",new String[]{"Courier-Bold","CourierNew,Bold","CourierStd-Bold","CourierNewPS-BoldMT"}); setSubstitutedFontAliases("Courbi",new String[]{"Courier-BoldOblique","CourierNew-BoldOblique","CourierStd-BoldOblique","CourierNewPS-BoldItalicMT"}); setSubstitutedFontAliases("Cour",new String[]{"CourierNew","Courier","CourierStd","CourierNewPSMT","CourierNewPSMT"}); //Helvetica (Arial) setSubstitutedFontAliases("arial",new String[]{"Helvetica","arialmt","ArialNarrow"}); setSubstitutedFontAliases("ariali",new String[]{"arial-italic", "arial-italicmt","Helvetica-Oblique","Arial,Italic", "ArialNarrow-Italic"}); setSubstitutedFontAliases("arialbd",new String[]{"arial-boldmt,bold","arial-boldmt","Helvetica-Bold","Arial,bold","arial bold", "ArialNarrow-Bold"}); setSubstitutedFontAliases("arialbi",new String[]{"Arial-BoldItalicMT","Helvetica-BoldOblique", "ArialNarrow-BoldItalic"}); //Font doesn't work in generic Windows 8 (commented out by Mark) 14/11/2013 //Arial Narrow - not actually one of fonts but very common so added //setSubstitutedFontAliases("arialn",new String[]{"ArialNarrow",}); //called ArialNarrow in PDF, needs to be arialn for Windows //setSubstitutedFontAliases("arialni",new String[]{"ArialNarrow-italic"}); //setSubstitutedFontAliases("arialnb",new String[]{"ArialNarrow-bold","ArialNarrow,Bold"}); //setSubstitutedFontAliases("arialnbi",new String[]{"ArialNarrow-bolditalic"}); //Times/TimesNewRoman setSubstitutedFontAliases("timesbd",new String[] {"Times-Bold","TimesNewRoman,Bold","TimesNewRomanPS-BoldMT"}); setSubstitutedFontAliases("timesi",new String[] {"Times-BoldItalic","TimesNewRoman,BoldItalic"}); setSubstitutedFontAliases("timesbi",new String[] {"Times-Italic","TimesNewRoman,Italic"}); setSubstitutedFontAliases("times",new String[] {"Times-Roman","TimesNewRoman","Times","TimesNewRomanPSMT"}); setSubstitutedFontAliases("wingdings",new String[] {"ZapfDingbats","ZaDb"}); } setSubstitutedFontAliases("AdobeSongStd-Light",new String[] {"STSong-Light"}); // setSubstitutedFontAliases("AdobeSongStd-Light",new String[] {"MHei-Medium"}); // setSubstitutedFontAliases("AdobeSongStd-Light",new String[] {"MSung-Light"}); // setSubstitutedFontAliases("AdobeSongStd-Light",new String[] {"HeiseiKakuGo-W5"}); // setSubstitutedFontAliases("AdobeSongStd-Light",new String[] {"HeiseiMin-W3"}); // setSubstitutedFontAliases("AdobeSongStd-Light",new String[] {"HYGoThic-Medium"}); // setSubstitutedFontAliases("AdobeSongStd-Light",new String[] {"HYSMyeongJo-Medium"}); //set general mappings for non-embedded fonts (assumes names the same) - do first time used if(!fontsSet){ fontsSet=true; //now in public static variable so can be altered setFontDirs(defaultFontDirs); /**check for any windows fonts lurking in Adobe folders as well*/ if(DecoderOptions.isRunningOnWindows){ final File adobeFonts=new File("C:\\Program Files\\Adobe\\"); if(adobeFonts.exists()){ final String[] subdirs=adobeFonts.list(); for(final String path : subdirs){ final String adobePath="C:\\Program Files\\Adobe\\"+path+"\\Resource\\CIDFont"; final File testAdobe=new File(adobePath); //add if it exists if(testAdobe.exists()){ addTTDir(adobePath, ""); } } } } } } /** * takes a String[] of font directories and adds to substitution - Can just * be called for each JVM - Should be called before file opened - this * offers an alternative to the call -DFontDirs - Passing a null value * flushes all settings * * @return String which will be null or list of directories it could not * find */ public static String setFontDirs(final String[] fontDirs) { String failed = null; if (FontMappings.fontSubstitutionTable == null) { fontSubstitutionTable = new ConcurrentHashMap(); fontSubstitutionFontID = new ConcurrentHashMap(); fontPossDuplicates = new ConcurrentHashMap(); fontPropertiesTable = new ConcurrentHashMap(); } try { if (fontDirs == null) { // idiot safety test LogWriter.writeLog("Null font parameter passed"); fontSubstitutionAliasTable.clear(); fontSubstitutionLocation.clear(); fontSubstitutionTable.clear(); fontSubstitutionFontID.clear(); fontPossDuplicates.clear(); fontPropertiesTable.clear(); fontsSet=false; } else { for (final String fontDir : fontDirs) { String fontPath = fontDir; // allow for 'wrong' separator if (!fontPath.endsWith("/") && !fontPath.endsWith("\\")) { fontPath += separator; } failed = addTTDir(fontPath, failed); } } } catch (final Exception e) { LogWriter.writeLog("Unable to run setFontDirs " + e.getMessage()); } return failed; } /** * This routine allows the user to add truetype, * type1 or type1C fonts which will be used to disalay the fonts in PDF * rendering and substitution as if the fonts were embedded in the PDF
* This is very useful for clients looking to keep down the size of PDFs * transmitted and control display quality - *
* Thanks to Peter for the idea/code - *
* How to set it up - *
* JPedal will look for the existence of the directory fontPath (ie * com/myCompany/Fonts) - *
* If this exists, Jpedal will look for 3 possible directories (tt,t1c,t1) * and make a note of any fonts if these directories exist - *
* When fonts are resolved, this option will be tested first and if a font * if found, it will be used to display the font (the effect will be the * same as if the font was embedded) - *
* If the enforceMapping is true, JPedal assumes there must be a match and * will throw a PdfFontException - *
* Otherwise Jpedal will look in the java font path for a match or * approximate with Lucida - *
* The Format is defined as follows: - *
* fontname = filename *
* Type1/Type1C Font names exclude any prefix so /OEGPNB+FGHeavyItalic is * resolved to FGHeavyItalic - *
* Each font have the same name as the font it replaces (so Arial will * require a font file such as Arial.ttf) and it must be unique (there * cannot be an Arial font in each sub-directory) - *
* So to use this functionality, place the fonts in a jar or add to the * JPedal jar and call this method after instancing PdfDecoder - JPedal will * do the rest * * @param fontPath - * root directory for fonts * @param enforceMapping - * tell JPedal if all fonts should be in this directory * @return flag (true if fonts added) */ public static boolean addSubstituteFonts(String fontPath, final boolean enforceMapping) { boolean hasFonts = false; InputStream in=null, dir=null; try { final String[] dirs = {"tt", "t1c", "t1"}; final String[] types = {"/TrueType", "/Type1C", "/Type1"}; // check fontpath ends with separator - we may need to check this. // if((!fontPath.endsWith("/"))&(!fontPath.endsWith("\\"))) // fontPath=fontPath=fontPath+separator; enforceFontSubstitution = enforceMapping; final ClassLoader loader = FontMappings.class.getClass().getClassLoader(); // see if root dir exists dir = loader.getResourceAsStream(fontPath); LogWriter.writeLog("Looking for root " + fontPath); // if it does, look for sub-directories if (in != null) { LogWriter.writeLog("Adding fonts fonts found in tt,t1c,t1 sub-directories of "+ fontPath); hasFonts = true; for (int i = 0; i < dirs.length; i++) { if (!fontPath.endsWith("/")) { fontPath += '/'; } final String path = fontPath + dirs[i] + '/'; // see if it exists in = loader.getResourceAsStream(path); // if it does read its contents and store if (in != null) { System.out.println("Found " + path + ' ' + in); final ArrayList fonts; try { // works with IDE or jar if (in instanceof ByteArrayInputStream) { fonts = readIndirectValues(in); } else { fonts = getDirectoryMatches(path); } String value, fontName; // now assign the fonts for (final Object font : fonts) { value = (String) font; if (value == null) { break; } final int pointer = value.indexOf('.'); if (pointer == -1) { fontName = value.toLowerCase(); } else { fontName = value.substring(0, pointer).toLowerCase(); } fontSubstitutionTable.put(fontName, types[i]); fontSubstitutionLocation.put(fontName, path + value); } } catch (final Exception e) { LogWriter.writeLog("Exception " + e+ " reading substitute fonts"); }finally { if(in!=null){ try { in.close(); } catch (final IOException e) { LogWriter.writeLog("Exception: "+e.getMessage()); } } } } } } else { LogWriter.writeLog("No fonts found at " + fontPath); } } catch (final Exception e) { LogWriter.writeLog("Exception adding substitute fonts "+ e.getMessage()); }finally { //close streams if open if(in!=null){ try { in.close(); } catch (final IOException e) { LogWriter.writeLog("Exception: "+e.getMessage()); } } if(dir!=null){ try { dir.close(); } catch (final IOException e) { LogWriter.writeLog("Exception: "+e.getMessage()); } } } return hasFonts; } /** * method to add a single file to the PDF renderer * * @param currentFont - actual font name we use to identify * @param fontPath - full path to font file used for this font */ public static void addFontFile(final String currentFont, String fontPath) { if ( fontSubstitutionTable == null) { fontSubstitutionTable = new ConcurrentHashMap(); fontSubstitutionFontID = new ConcurrentHashMap(); fontPossDuplicates = new ConcurrentHashMap(); fontPropertiesTable = new ConcurrentHashMap(); } //add separator if needed if (fontPath != null && !fontPath.endsWith("/") && !fontPath.endsWith("\\")) { fontPath += separator; } final String name = currentFont.toLowerCase(); //decide font type final int type = StandardFonts.getFontType(name); InputStream in = null; if (type != StandardFonts.FONT_UNSUPPORTED && new File(fontPath + currentFont).exists()) { // see if root dir exists boolean failed=false; try { in = new FileInputStream(fontPath + currentFont); } catch (final Exception e) { LogWriter.writeLog("Exception: "+e.getMessage()); failed=true; } catch (final Error err) { LogWriter.writeLog("Error: "+err.getMessage()); failed=true; } // if it does, add if (!failed) { final String fontName; //name from file final int pointer = currentFont.indexOf('.'); if (pointer == -1) { fontName = currentFont.toLowerCase(); } else { fontName = currentFont.substring(0, pointer).toLowerCase(); } //choose filename or over-ride if OpenType if (fontSubstitutionMode == PdfDecoderInt.SUBSTITUTE_FONT_USING_FILE_NAME|| type == StandardFonts.OPENTYPE) { if(type==StandardFonts.TYPE1) { fontSubstitutionTable.put(fontName, "/Type1"); } else { //TT or OTF fontSubstitutionTable.put(fontName, "/TrueType"); } fontSubstitutionLocation.put(fontName, fontPath + currentFont); //store details under file fontPropertiesTable.put(fontName+"_type", type); fontPropertiesTable.put(fontName+"_path",fontPath + currentFont); } else if (type == StandardFonts.TRUETYPE_COLLECTION || type == StandardFonts.TRUETYPE) { if(fontSubstitutionMode== PdfDecoderInt.SUBSTITUTE_FONT_USING_POSTSCRIPT_NAME_USE_FAMILY_NAME_IF_DUPLICATES){ //get both possible values String[] postscriptNames=null; try { postscriptNames = StandardFonts.readNamesFromFont(type, fontPath + currentFont, PdfDecoderInt.SUBSTITUTE_FONT_USING_POSTSCRIPT_NAME); } catch (final Exception e) { LogWriter.writeLog("Exception: "+e.getMessage()); } String[] familyNames =null; try { familyNames = StandardFonts.readNamesFromFont(type, fontPath + currentFont, PdfDecoderInt.SUBSTITUTE_FONT_USING_FAMILY_NAME); } catch (final Exception e) { LogWriter.writeLog("Exception: "+e.getMessage()); } int fontCount=0; if(postscriptNames!=null) { fontCount=postscriptNames.length; } for(int ii=0;ii




© 2015 - 2024 Weber Informatics LLC | Privacy Policy