/*
* Copyright 2006-2012 ICEsoft Technologies Inc.
*
* Licensed 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.icepdf.core.pobjects.fonts;
import org.icepdf.core.util.Defs;
import org.icepdf.core.util.FontUtil;
import java.io.File;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <p>The <code>FontManager</code> class is responsible for finding available
* fonts on the client operating system. This class by default checks the
* following directories when the readSystemFonts method is called without any
* parameters.</p>
* <p/>
* <p>The default font directories are as follows:</p>
* <ul>
* <li><b>Windows</b> - "c:\\windows\\fonts\\", "d:\\windows\\fonts\\", "e:\\windows\\fonts\\", "f:\\windows\\fonts\\", "c:\\winnt\\Fonts\\", "d:\\winnt\\Fonts\\"</li>
* <li><b>Macintosh</b> - "/Network/Library/Fonts/", "/System/Library/Fonts/", "/System Folder/Fonts"</li>
* <li><b>Linux/Unix</b> - /system/etc/fonts/","/usr/share/fonts/truetype/", "/usr/share/fonts/local/", "/etc/fonts/","/usr/lib/X11/fonts", "/usr/X11R6/lib/X11/fonts", "/usr/openwin/lib/X11/fonts", "/usr/openwin/lib/X11/fonts/misc/"</li>
* <li><b>Java default</b> - sysProp("java.home") + "/lib/fonts"</li>
* </ul>
* <p/>
* <p>It is possible to specify other directories to search for fonts via the
* readSystemFonts methods extraFontPaths parameter {@link #readSystemFonts}.
* Reading all of an operating systems font's can be time consuming. To help
* speed up this process the method getFontProperties exports font data via a
* Properties object. The font Properties object can then be saved to disk or
* be read back into the FontManager via the setFontProperties method. </p>
*
* @since 2.0
*/
public class FontManager {
private static final Logger logger =
Logger.getLogger(FontManager.class.toString());
// stores all font data
private static ArrayList<Object[]> fontList;
// flags for detecting font decorations
private static int PLAIN = 0xF0000001;
private static int BOLD = 0xF0000010;
private static int ITALIC = 0xF0000100;
private static int BOLD_ITALIC = 0xF0001000;
// Differences for type1 fonts which match adobe core14 metrics
private static final String TYPE1_FONT_DIFFS[][] =
{{"Bookman-Demi", "URWBookmanL-DemiBold", "Arial"},
{"Bookman-DemiItalic", "URWBookmanL-DemiBoldItal", "Arial"},
{"Bookman-Light", "URWBookmanL-Ligh", "Arial"},
{"Bookman-LightItalic", "URWBookmanL-LighItal", "Arial"},
{"Courier", "NimbusMonL-Regu", "Nimbus Mono L", "CourierNew", "CourierNewPSMT"},
{"Courier-Oblique", "NimbusMonL-ReguObli", "Nimbus Mono L", "Courier,Italic", "CourierNew-Italic", "CourierNew,Italic", "CourierNewPS-ItalicMT"},
{"Courier-Bold", "NimbusMonL-Bold", "Nimbus Mono L", "Courier,Bold", "CourierNew,Bold", "CourierNew-Bold", "CourierNewPS-BoldMT"},
{"Courier-BoldOblique", "NimbusMonL-BoldObli", "Nimbus Mono L", "Courier,BoldItalic", "CourierNew-BoldItalic", "CourierNew,BoldItalic", "CourierNewPS-BoldItalicMT"},
{"AvantGarde-Book", "URWGothicL-Book", "Arial"},
{"AvantGarde-BookOblique", "URWGothicL-BookObli", "Arial"},
{"AvantGarde-Demi", "URWGothicL-Demi", "Arial"},
{"AvantGarde-DemiOblique", "URWGothicL-DemiObli", "Arial"},
{"Helvetica", "Helvetica", "Arial", "ArialMT", "NimbusSanL-Regu", "Nimbus Sans L"},
// {"Helvetica", "NimbusSanL-Regu", "Nimbus Sans L", "Arial", "ArialMT"}, // known problem in Phelps nfont engine
{"Helvetica-Oblique", "NimbusSanL-ReguItal", "Nimbus Sans L", "Helvetica,Italic", "Helvetica-Italic", "Arial,Italic", "Arial-Italic", "Arial-ItalicMT"},
// {"Helvetica-Bold", "NimbusSanL-Bold", "Nimbus Sans L", "Helvetica-Black", "Helvetica,Bold", "Arial,Bold", "Arial-Bold", "Arial-BoldMT"}, // known problem in Phelps nfont engine
{"Helvetica-Bold", "Helvetica,Bold", "Arial,Bold", "Arial-Bold", "Arial-BoldMT", "NimbusSanL-Bold", "Nimbus Sans L"},
{"Helvetica-BoldOblique", "NimbusSanL-BoldItal", "Helvetica-BlackOblique", "Nimbus Sans L", "Helvetica,BoldItalic", "Helvetica-BoldItalic", "Arial,BoldItalic", "Arial-BoldItalic", "Arial-BoldItalicMT"},
{"Helvetica-Black", "Helvetica,Bold", "Arial,Bold", "Arial-Bold", "Arial-BoldMT", "NimbusSanL-Bold", "Nimbus Sans L"},
{"Helvetica-BlackOblique", "NimbusSanL-BoldItal", "Helvetica-BlackOblique", "Nimbus Sans L", "Helvetica,BoldItalic", "Helvetica-BoldItalic", "Arial,BoldItalic", "Arial-BoldItalic", "Arial-BoldItalicMT"},
{"Helvetica-Narrow", "NimbusSanL-ReguCond", "Nimbus Sans L"},
{"Helvetica-Narrow-Oblique", "NimbusSanL-ReguCondItal", "Nimbus Sans L"},
{"Helvetica-Narrow-Bold", "NimbusSanL-BoldCond", "Nimbus Sans L"},
{"Helvetica-Narrow-BoldOblique", "NimbusSanL-BoldCondItal", "Nimbus Sans L"},
{"Helvetica-Condensed", "NimbusSanL-ReguCond", "Nimbus Sans L"},
{"Helvetica-Condensed-Oblique", "NimbusSanL-ReguCondItal", "Nimbus Sans L"},
{"Helvetica-Condensed-Bold", "NimbusSanL-BoldCond", "Nimbus Sans L"},
{"Helvetica-Condensed-BoldOblique", "NimbusSanL-BoldCondItal", "Nimbus Sans L"},
{"Palatino-Roman", "URWPalladioL-Roma", "Arial"},
{"Palatino-Italic", "URWPalladioL-Ital", "Arial"},
{"Palatino-Bold", "URWPalladioL-Bold", "Arial"},
{"Palatino-BoldItalic", "URWPalladioL-BoldItal", "Arial"},
{"NewCenturySchlbk-Roman", "CenturySchL-Roma", "Arial"},
{"NewCenturySchlbk-Italic", "CenturySchL-Ital", "Arial"},
{"NewCenturySchlbk-Bold", "CenturySchL-Bold", "Arial"},
{"NewCenturySchlbk-BoldItalic", "CenturySchL-BoldItal", "Arial"},
{"Times-Roman", "NimbusRomNo9L-Regu", "Nimbus Roman No9 L", "TimesNewRoman", "TimesNewRomanPSMT", "TimesNewRomanPS"},
{"Times-Italic", "NimbusRomNo9L-ReguItal", "Nimbus Roman No9 L", "TimesNewRoman,Italic", "TimesNewRoman-Italic", "TimesNewRomanPS-Italic", "TimesNewRomanPS-ItalicMT"},
{"Times-Bold", "NimbusRomNo9L-Medi", "Nimbus Roman No9 L", "TimesNewRoman,Bold", "TimesNewRoman-Bold", "TimesNewRomanPS-Bold", "TimesNewRomanPS-BoldMT"},
{"Times-BoldItalic", "NimbusRomNo9L-MediItal", "Nimbus Roman No9 L", "TimesNewRoman,BoldItalic", "TimesNewRoman-BoldItalic", "TimesNewRomanPS-BoldItalic", "TimesNewRomanPS-BoldItalicMT"},
{"Symbol", "StandardSymL", "Standard Symbols L"},
{"ZapfChancery-MediumItalic", "URWChanceryL-MediItal", "Arial"},
{"ZapfDingbats", "Dingbats", "Zapf-Dingbats"}
};
private static final String[] JAPANESE_FONT_NAMES = {
"Arial Unicode MS", "PMingLiU", "MingLiU",
"MS PMincho", "MS Mincho", "Kochi Mincho", "Hiragino Mincho Pro",
"KozMinPro Regular Acro", "HeiseiMin W3 Acro", "Adobe Ming Std Acro"
};
private static final String[] CHINESE_SIMPLIFIED_FONT_NAMES = {
"Arial Unicode MS", "PMingLiU", "MingLiU",
"SimSun", "NSimSun", "Kochi Mincho", "STFangsong", "STSong Light Acro",
"Adobe Song Std Acro"
};
private static final String[] CHINESE_TRADITIONAL_FONT_NAMES = {
"Arial Unicode MS", "PMingLiU", "MingLiU",
"SimSun", "NSimSun", "Kochi Mincho", "BiauKai", "MSungStd Light Acro",
"Adobe Song Std Acro"
};
private static final String[] KOREAN_FONT_NAMES = {
"Arial Unicode MS", "Gulim", "Batang",
"BatangChe", "HYSMyeongJoStd Medium Acro", "Adobe Myungjo Std Acro"
};
// Default system directories to scan for font programs. This variable can
// not be declared final as it will hard wire the java.home font directory, which
// is bad, should be different for different environments.
private static String[] SYSTEM_FONT_PATHS =
new String[]{
// windows
"c:\\windows\\fonts\\",
"d:\\windows\\fonts\\",
"e:\\windows\\fonts\\",
"f:\\windows\\fonts\\",
"c:\\winnt\\Fonts\\",
"d:\\winnt\\Fonts\\",
"c:\\cygwin\\usr\\share\\ghostscript\\fonts\\",
"d:\\cygwin\\usr\\share\\ghostscript\\fonts\\",
// Mac
"/Network/Library/Fonts/",
"/System/Library/Fonts/",
"/System Folder/Fonts",
"/usr/local/share/ghostscript/",
"/Applications/GarageBand.app/Contents/Resources/",
"/Applications/NeoOffice.app/Contents/share/fonts/truetype/",
"/Library/Dictionaries/Shogakukan Daijisen.dictionary/Contents/",
"/Library/Dictionaries/Shogakukan Progressive English-Japanese Japanese-English Dictionary.dictionary/Contents/",
"/Library/Dictionaries/Shogakukan Ruigo Reikai Jiten.dictionary/Contents/",
"/Library/Fonts/",
"/Volumes/Untitled/WINDOWS/Fonts/",
"/usr/share/enscript/",
"/usr/share/groff/1.19.2/font/devps/generate/",
"/usr/X11/lib/X11/fonts/Type1/",
"/usr/X11/lib/X11/fonts/TrueType/",
"/usr/X11/lib/X11/fonts/",
// Linux
"/etc/fonts/",
"/system/etc/fonts/",
"/usr/lib/X11/fonts",
"/usr/share/a2ps/afm/",
"/usr/share/enscript/afm/",
"/usr/share/fonts/local/",
"/usr/share/fonts/truetype/",
"/usr/share/fonts/truetype/freefont/",
"/usr/share/fonts/truetype/msttcorefonts/",
"/usr/share/fonts/Type1/",
"/usr/share/fonts/type1/gsfonts/",
"/usr/share/fonts/X11/Type1/",
"/usr/share/ghostscript/fonts/",
"/usr/share/groff/1.18.1/font/devps/",
"/usr/share/groff/1.18.1/font/devps/generate/",
"/usr/share/libwmf/fonts/",
"/usr/share/ogonkify/afm/",
"/usr/X11R6/lib/X11/fonts/",
"/var/lib/defoma/gs.d/dirs/fonts/",
// solaris
"/usr/openwin/lib/locale/ar/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/euro_fonts/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/hi_IN.UTF-8/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/iso_8859_13/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/iso_8859_15/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/iso_8859_2/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/iso_8859_2/X11/fonts/Type1/afm/",
"/usr/openwin/lib/locale/iso_8859_4/X11/fonts/Type1/afm/",
"/usr/openwin/lib/locale/iso_8859_5/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/iso_8859_5/X11/fonts/Type1/afm/",
"/usr/openwin/lib/locale/iso_8859_7/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/iso_8859_7/X11/fonts/Type1/afm/",
"/usr/openwin/lib/locale/iso_8859_8/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/iso_8859_8/X11/fonts/Type1/",
"/usr/openwin/lib/locale/iso_8859_8/X11/fonts/Type1/afm/",
"/usr/openwin/lib/locale/iso_8859_9/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/iso_8859_9/X11/fonts/Type1/afm/",
"/usr/openwin/lib/locale/ja//X11/fonts/TrueType/",
"/usr/openwin/lib/locale/K0I8-R/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/ru.ansi-1251/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/th_TH/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/zh.GBK/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/zh/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/zh_CN.GB18030/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/zh_TW.BIG5/X11/fonts/TrueType/",
"/usr/openwin/lib/locale/zh_TW/X11/fonts/TrueType/",
"/usr/openwin/lib/X11/fonts/",
"/usr/openwin/lib/X11/fonts/F3/afm/",
"/usr/openwin/lib/X11/fonts/misc/",
"/usr/openwin/lib/X11/fonts/TrueType/",
"/usr/openwin/lib/X11/fonts/Type1/",
"/usr/openwin/lib/X11/fonts/Type1/afm/",
"/usr/openwin/lib/X11/fonts/Type1/outline/",
"/usr/openwin/lib/X11/fonts/Type1/sun/",
"/usr/openwin/lib/X11/fonts/Type1/sun/afm/",
"/usr/sfw/share/a2ps/afm/",
"/usr/sfw/share/ghostscript/fonts/",
"/usr/sfw/share/ghostscript/fonts/",
// Java default
Defs.sysProperty("java.home") + "/lib/fonts"
};
// Singleton instance of class
private static FontManager fontManager;
/**
* <p>Returns a static instance of the FontManager class.</p>
*
* @return instance of the FontManager.
*/
public static FontManager getInstance() {
// make sure we have initialized the manager
if (fontManager == null) {
fontManager = new FontManager();
}
return fontManager;
}
/**
* <p>Gets a Properties object containing font information for the operating
* system which the FontManager is running on. This Properties object
* can be saved to disk and read at a later time using the {@see #setFontProperties}
* method.</p>
*
* @return Properties object containing font data information.
*/
public Properties getFontProperties() {
Properties fontProperites;
// make sure we are initialized
if (fontList == null) {
readSystemFonts(null);
}
// copy all data from fontList into the properties file
fontProperites = new Properties();
Iterator fontIterator = fontList.iterator();
Object[] currentFont;
String name;
String family;
Integer decorations;
String path;
// Build the properties file using the font name as the key and
// the value is the family, decoration and path information
// separated by the "|" character.
while (fontIterator.hasNext()) {
currentFont = (Object[]) fontIterator.next();
name = (String) currentFont[0];
family = (String) currentFont[1];
decorations = (Integer) currentFont[2];
path = (String) currentFont[3];
// add the new entry
fontProperites.put(name, family + "|" + decorations + "|" + path);
}
return fontProperites;
}
/**
* <p>Reads font data from the Properties file. All name and key data replaces
* any existing font information.</p>
*
* @param fontProperties Properties object containing valid font information.
* @throws IllegalArgumentException thrown, if there is a problem parsing the
* Properties file. If thrown, the calling application should re-read
* the system fonts.
*/
public void setFontProperties(Properties fontProperties)
throws IllegalArgumentException {
String errorString = "Error parsing font properties ";
try {
fontList = new ArrayList<Object[]>(150);
Enumeration fonts = fontProperties.propertyNames();
String name;
String family;
Integer decorations;
String path;
StringTokenizer tokens;
// read in font information
while (fonts.hasMoreElements()) {
name = (String) fonts.nextElement();
tokens = new StringTokenizer((String) fontProperties.get(name), "|");
// get family, decoration and path tokens
family = tokens.nextToken();
decorations = new Integer(tokens.nextToken());
path = tokens.nextToken();
if (name != null && family != null && path != null) {
fontList.add(new Object[]{name, family, decorations, path});
} else {
throw new IllegalArgumentException(errorString);
}
}
sortFontListByName();
} catch (Throwable e) {
logger.log(Level.FINE, "Error setting font properties ", e);
throw new IllegalArgumentException(errorString);
}
}
/**
* Clears internal font list of items. Used to clean list while custructing
* a new list.
*/
public void clearFontList() {
if (fontList != null) {
fontList.clear();
fontList.trimToSize();
}
}
/**
* <p>Searches all default system font paths and any font paths
* specified by the extraFontPaths parameter, and records data about all
* found fonts. This font data is used to substitute fonts which are not
* embedded inside a PDF document.</p>
*
* @param extraFontPaths array String object where each entry represents
* a system directory path containing font programs.
*/
public void readSystemFonts(String[] extraFontPaths) {
// create a new font list if needed.
if (fontList == null) {
fontList = new ArrayList<Object[]>(150);
}
// Setup parameters
FontFile font;
String path;
StringBuilder fontPath;
String fontName;
String[] fontPaths;
File directory;
// Copy any extra font paths to the
String[] fontDirectories;
if (extraFontPaths == null) {
fontDirectories = SYSTEM_FONT_PATHS;
} else {
int length = SYSTEM_FONT_PATHS.length + extraFontPaths.length;
fontDirectories = new String[length];
// copy the static list into the new list
System.arraycopy(SYSTEM_FONT_PATHS, 0, fontDirectories, 0, SYSTEM_FONT_PATHS.length);
// added any paths specified by the user
System.arraycopy(extraFontPaths, 0, fontDirectories, SYSTEM_FONT_PATHS.length, extraFontPaths.length);
}
if (logger.isLoggable(Level.FINER)) {
logger.finer("Reading system fonts:");
}
// Iterate through SYSTEM_FONT_PATHS and load all readable fonts
for (int i = fontDirectories.length - 1; i >= 0; i--) {
path = fontDirectories[i];
// if the path is valid start reading fonts.
if (path != null) {
directory = new File(path);
if (directory.canRead()) {
fontPaths = directory.list();
for (int j = fontPaths.length - 1; j >= 0; j--) {
fontName = fontPaths[j];
fontPath = new StringBuilder(25);
fontPath.append(directory.getAbsolutePath()).append(
File.separatorChar).append(fontName);
if (logger.isLoggable(Level.FINER)) {
logger.finer("Trying to load font file: " + fontPath);
}
// try loading the font
font = buildFont(fontPath.toString());
// if a readable font was found
if (font != null) {
// normalize name
fontName = font.getName().toLowerCase();
// Add new font data to the font list
fontList.add(new Object[]{font.getName().toLowerCase(), // original PS name
FontUtil.normalizeString(font.getFamily()), // family name
guessFontStyle(fontName), // weight and decorations, mainly bold,italic
fontPath.toString()}); // path to font on OS
if (logger.isLoggable(Level.FINER)) {
logger.finer("Adding system font: " + font.getName() + " " + fontPath.toString());
}
}
}
}
}
}
sortFontListByName();
}
/**
* <p>Utility method for guessing a font family name from its base name.</p>
*
* @param name base name of font.
* @return guess of the base fonts name.
*/
public static String guessFamily(String name) {
String fam = name;
int inx;
// Family name usually precedes a common, ie. "Arial,BoldItalic"
if ((inx = fam.indexOf(',')) > 0)
fam = fam.substring(0, inx);
// Family name usually precedes a dash, example "Times-Bold",
if ((inx = fam.lastIndexOf('-')) > 0)
fam = fam.substring(0, inx);
return fam;
}
/**
* <p>Gets all available font names on the operating system.</p>
*
* @return font names of all found fonts.
*/
public String[] getAvailableNames() {
if (fontList != null) {
String[] availableNames = new String[fontList.size()];
Iterator nameIterator = fontList.iterator();
Object[] fontData;
for (int i = 0; nameIterator.hasNext(); i++) {
fontData = (Object[]) nameIterator.next();
availableNames[i] = fontData[0].toString();
}
return availableNames;
}
return null;
}
/**
* <p>Gets all available font family names on the operating system.</p>
*
* @return font family names of all found fonts.
*/
public String[] getAvailableFamilies() {
if (fontList != null) {
String[] availableNames = new String[fontList.size()];
Iterator nameIterator = fontList.iterator();
Object[] fontData;
for (int i = 0; nameIterator.hasNext(); i++) {
fontData = (Object[]) nameIterator.next();
availableNames[i] = fontData[1].toString();
}
return availableNames;
}
return null;
}
/**
* <p>Gets all available font styles on the operating system.</p>
*
* @return font style names of all found fonts.
*/
public String[] getAvailableStyle() {
if (fontList != null) {
String[] availableStyles = new String[fontList.size()];
Iterator nameIterator = fontList.iterator();
Object[] fontData;
int decorations;
String style = "";
for (int i = 0; nameIterator.hasNext(); i++) {
fontData = (Object[]) nameIterator.next();
decorations = (Integer) fontData[2];
if ((decorations & BOLD_ITALIC) == BOLD_ITALIC) {
style += " BoldItalic";
} else if ((decorations & BOLD) == BOLD) {
style += " Bold";
} else if ((decorations & ITALIC) == ITALIC) {
style += " Italic";
} else if ((decorations & PLAIN) == PLAIN) {
style += " Plain";
}
availableStyles[i] = style;
style = "";
}
return availableStyles;
}
return null;
}
public FontFile getJapaneseInstance(String name, int fontFlags) {
return getAsianInstance(name, JAPANESE_FONT_NAMES, fontFlags);
}
public FontFile getKoreanInstance(String name, int fontFlags) {
return getAsianInstance(name, KOREAN_FONT_NAMES, fontFlags);
}
public FontFile getChineseTraditionalInstance(String name, int fontFlags) {
return getAsianInstance(name, CHINESE_TRADITIONAL_FONT_NAMES, fontFlags);
}
public FontFile getChineseSimplifiedInstance(String name, int fontFlags) {
return getAsianInstance(name, CHINESE_SIMPLIFIED_FONT_NAMES, fontFlags);
}
private FontFile getAsianInstance(String name, String[] list, int flags) {
if (fontList == null) {
readSystemFonts(null);
}
FontFile font = null;
if (list != null) {
// search for know list of fonts
for (int i = list.length - 1; i >= 0; i--) {
// try and find an instance of the name and family from the font list
font = findFont(name, flags);
if (font != null) {
if (logger.isLoggable(Level.FINER)) {
logger.finer("Font Substitution: Found Asian font: " + font.getName() + " for named font " + name);
}
return font;
}
}
// lastly see if we can't a system font that matches the list names.
// search for know list of fonts
for (int i = list.length - 1; i >= 0; i--) {
// try and find an instance of the name and family from the font list
font = findFont(list[i], flags);
if (font != null) {
if (logger.isLoggable(Level.FINER)) {
logger.finer("Font Substitution: Found Asian font: " + font.getName() + " for named font " + name);
}
return font;
}
}
}
return font;
}
/**
* <p>Get an instance of a NFont from the given font name and flag decoration
* information.</p>
*
* @param name base name of font.
* @param flags flags used to describe font.
* @return a new instance of NFont which best approximates the font described
* by the name and flags attribute.
*/
public FontFile getInstance(String name, int flags) {
if (fontList == null) {
readSystemFonts(null);
}
FontFile font;
// try and find equivalent type1 font
font = getType1Fonts(name, flags);
if (font != null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Font Substitution: Found type1 font: " + font.getName() + " for named font " + name);
}
return font;
}
// try and find an instance of the name and family from the font list
font = findFont(name, flags);
if (font != null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Font Substitution: Found system font: " + font.getName() + " for named font " + name);
}
return font;
}
// try and find an equivalent java font
font = getCoreJavaFont(name, flags);
if (font != null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Font Substitution: Found java font: " + font.getName() + " for named font " + name);
}
return font;
}
// if all else fails return first font in fontList with matching style,
// this should never happen, but just in case.
if (fontList.size() > 0) {
Object[] fontData;
boolean found = false;
int decorations = guessFontStyle(name);
int style;
// get first font that has a matching style
for (int i = fontList.size() - 1; i >= 0; i--) {
fontData = fontList.get(i);
style = (Integer) fontData[2];
if (((decorations & BOLD_ITALIC) == BOLD_ITALIC) &&
((style & BOLD_ITALIC) == BOLD_ITALIC)) {
found = true;
} else if (((decorations & BOLD) == BOLD) &&
((style & BOLD) == BOLD)) {
found = true;
} else if (((decorations & ITALIC) == ITALIC) &&
((style & ITALIC) == ITALIC)) {
found = true;
} else if (((decorations & PLAIN) == PLAIN) &&
((style & PLAIN) == PLAIN)) {
found = true;
}
if (found){
font = buildFont((String) fontData[3]);
break;
}
}
if (!found){
fontData = fontList.get(0);
font = buildFont((String) fontData[3]);
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("Font Substitution: Found failed " + name + " " + font.getName());
}
}
if (font == null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("No Fonts can be found on your system. ");
}
}
return font;
}
/**
* Utility method for search the fontList array for an particular font name
* that has the specified sytle.
*
* @param fontName fontname with any decoration information still appended to name.
* @param flags flags from content parser, to help guess style.
* @return a valid font if found, null otherwise
*/
private FontFile findFont(String fontName, int flags) {
FontFile font = null;
// references for system font list.
Object[] fontData;
String baseName;
String familyName;
// normalize the fontName we are trying to find a match for
int decorations = guessFontStyle(fontName);
String name = FontUtil.normalizeString(fontName);
int style;
if (fontList != null) {
for (int i = fontList.size() - 1; i >= 0; i--) {
fontData = fontList.get(i);
baseName = (String) fontData[0];
familyName = (String) fontData[1];
if (logger.isLoggable(Level.FINEST)) {
logger.finest(baseName + " : " + familyName + " : " + name);
}
if (name.indexOf(familyName) >= 0 ||
fontName.toLowerCase().indexOf(baseName) >= 0) {
style = (Integer) fontData[2];
boolean found = false;
// ignore this font, as the cid mapping are not correct, or ther is
// just look and feel issues with them.
if (baseName.equals("opensymbol") ||
baseName.equals("starsymbol")
|| baseName.equals("arial-black")
|| baseName.equals("arial-blackitalic")
|| baseName.equals("new")
// mapping issue with standard ascii, not sure why, TimesNewRomanPSMT is ok.
|| baseName.equals("timesnewromanps")
) {
//found = false;
} else if (((decorations & BOLD_ITALIC) == BOLD_ITALIC) &&
((style & BOLD_ITALIC) == BOLD_ITALIC)) {
found = true;
} else if (((decorations & BOLD) == BOLD) &&
((style & BOLD) == BOLD)) {
found = true;
} else if (((decorations & ITALIC) == ITALIC) &&
((style & ITALIC) == ITALIC)) {
found = true;
} else if (((decorations & PLAIN) == PLAIN) &&
((style & PLAIN) == PLAIN)) {
found = true;
}
// symbol type fonts don't have an associated style, so
// no point trying to match them based on style.
else if (baseName.indexOf("wingdings") >= 0 ||
baseName.indexOf("zapfdingbats") >= 0 ||
baseName.indexOf("symbol") >= 0) {
found = true;
}
if (found) {
if (logger.isLoggable(Level.FINER)) {
logger.finer("----> Found font: " + baseName +
" family: " + getFontSytle(style, 0) +
" for: " + fontName);
}
font = buildFont((String) fontData[3]);
// make sure the font does indeed exist
if (font != null) {
break;
}
}
}
}
}
return font;
}
/**
* Loads a font specified by the fontpath parameter. If font path is invalid
* or the file can not be loaded, null is returned.
*
* @param fontPath font path of font program to laod
* @return a valid font if loadable, null otherwise
*/
private FontFile buildFont(String fontPath) {
FontFile font = null;
try {
File file = new File(fontPath);
if (!file.canRead()) {
return null;
}
FontFactory fontFactory = FontFactory.getInstance();
// found true type font
if ((fontPath.endsWith(".ttf") || fontPath.endsWith(".TTF")) ||
(fontPath.endsWith(".dfont") || fontPath.endsWith(".DFONT")) ||
(fontPath.endsWith(".ttc") || fontPath.endsWith(".TTC"))) {
font = fontFactory.createFontFile(file, FontFactory.FONT_TRUE_TYPE);
}
// found Type 1 font
else if ((fontPath.endsWith(".pfa") || fontPath.endsWith(".PFA")) ||
(fontPath.endsWith(".pfb") || fontPath.endsWith(".PFB"))) {
font = fontFactory.createFontFile(file, FontFactory.FONT_TYPE_1);
}
// found OpenType font
else if ((fontPath.endsWith(".otf") || fontPath.endsWith(".OTF")) ||
(fontPath.endsWith(".otc") || fontPath.endsWith(".OTC"))) {
font = fontFactory.createFontFile(file, FontFactory.FONT_OPEN_TYPE);
}
} catch (Throwable e) {
logger.log(Level.FINE, "Error reading font program.", e);
}
return font;
}
/**
* Gets a NFont instance by matching against font sytle commonalities in the
* Java Cores libraries.
*
* @param fontName font name to search for
* @param flags style flags
* @return a valid NFont if a match is found, null otherwise.
*/
private FontFile getCoreJavaFont(String fontName, int flags) {
int decorations = guessFontStyle(fontName);
fontName = FontUtil.normalizeString(fontName);
FontFile font;
// If no name are found then match against the core java font names
// "Serif", java equivalent is "Lucida Bright"
if ((fontName.indexOf("timesnewroman") >= 0 ||
fontName.indexOf("bodoni") >= 0 ||
fontName.indexOf("garamond") >= 0 ||
fontName.indexOf("minionweb") >= 0 ||
fontName.indexOf("stoneserif") >= 0 ||
fontName.indexOf("georgia") >= 0 ||
fontName.indexOf("bitstreamcyberbit") >= 0)) {
// important, add style information
font = findFont("lucidabright-" + getFontSytle(decorations, flags), 0);
}
// see if we working with a monospaced font, we sub "Sans Serif",
// java equivalent is "Lucida Sans"
else if ((fontName.indexOf("helvetica") != -1 ||
fontName.indexOf("arial") != -1 ||
fontName.indexOf("trebuchet") != -1 ||
fontName.indexOf("avantgardegothic") != -1 ||
fontName.indexOf("verdana") != -1 ||
fontName.indexOf("univers") != -1 ||
fontName.indexOf("futura") != -1 ||
fontName.indexOf("stonesans") != -1 ||
fontName.indexOf("gillsans") != -1 ||
fontName.indexOf("akzidenz") != -1 ||
fontName.indexOf("frutiger") != -1 ||
fontName.indexOf("grotesk") != -1)) {
// important, add style information
font = findFont("lucidasans-" + getFontSytle(decorations, flags), 0);
}
// see if we working with a mono spaced font "Mono Spaced"
// java equivalent is "Lucida Sans Typewriter"
else if ((fontName.indexOf("courier") != -1 ||
fontName.indexOf("couriernew") != -1 ||
fontName.indexOf("prestige") != -1 ||
fontName.indexOf("eversonmono") != -1)) {
// important, add style information
font = findFont("lucidasanstypewriter-" + getFontSytle(decorations, flags), 0);
}
// first try get the first match based on the style type and finally on failure
// failure go with the serif as it is the most common font family
else {
// System.out.println("Decorations " + decorations);
// System.out.println("flags " + flags);
// System.out.println("sytel " + getFontSytle(decorations, flags));
font = findFont("lucidabright-" + getFontSytle(decorations, flags), 0);
}
return font;
}
/**
* Gets a NFont instance by matching against font sytle commonalities in the
* of know type1 fonts
*
* @param fontName font name to search for
* @param flags style flags
* @return a valid NFont if a match is found, null otherwise.
*/
private FontFile getType1Fonts(String fontName, int flags) {
FontFile font = null;
boolean found = false;
boolean isType1Available = true;
// find a match for family in the type 1 nfont table
for (int i = 0, max = TYPE1_FONT_DIFFS.length; i < max; i++) {
for (int j = 0, max2 = TYPE1_FONT_DIFFS[i].length; j < max2; j++) {
// first check to see font name matches any elements
if (TYPE1_FONT_DIFFS[i][0].indexOf(fontName) >= 0) {
// next see if know type1 fonts are installed
if (isType1Available) {
font = findFont(TYPE1_FONT_DIFFS[i][1], flags);
if (font != null) {
found = true;
break;
} else {
isType1Available = false;
}
}
// do a full search for possible matches.
font = findFont(TYPE1_FONT_DIFFS[i][j], flags);
if (font != null) {
found = true;
break;
}
}
}
// break out of second loop
if (found) break;
}
return font;
}
/**
* Utitility method which maps know sytle strings to an integer value which
* is used later for effeciant font searching.
* todo: move out to FontUtil and use awt contants
*
* @param name base name of font.
* @return integer representing dffs
*/
private static int guessFontStyle(String name) {
name = name.toLowerCase();
int decorations = 0;
if ((name.indexOf("boldital") > 0) || (name.indexOf("demiital") > 0)) {
decorations |= BOLD_ITALIC;
} else if (name.indexOf("bold") > 0 || name.indexOf("black") > 0
|| name.indexOf("demi") > 0) {
decorations |= BOLD;
} else if (name.indexOf("ital") > 0 || name.indexOf("obli") > 0) {
decorations |= ITALIC;
} else {
decorations |= PLAIN;
}
return decorations;
}
/**
* Returns the string representation of a font style specified by the
* decoration and flags integers.
*
* @param sytle style specified by known offsets
* @param flags flags from pdf dictionary
* @return string representation of styles specified by the two integers.
*/
private String getFontSytle(int sytle, int flags) {
// Get any useful data from the flags integer.
String style = "";
if ((sytle & BOLD_ITALIC) == BOLD_ITALIC) {
style += " BoldItalic";
} else if ((sytle & BOLD) == BOLD) {
style += " Bold";
} else if ((sytle & ITALIC) == ITALIC) {
style += " Italic";
} else if ((sytle & PLAIN) == PLAIN) {
style += " Plain";
}
return style;
}
/**
* Sorts the fontList of system fonts by font name or the first element
* int the object[] store.
*/
private static void sortFontListByName() {
Collections.sort(fontList, new Comparator<Object[]>() {
public int compare(Object[] o1, Object[] o2) {
return ((String) o2[0]).compareTo((String) o1[0]);
}
});
}
}