/*
* Copyright 2006-2017 ICEsoft Technologies Canada Corp.
*
* 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.pobjects.Stream;
import org.icepdf.core.pobjects.fonts.ofont.OFont;
import org.icepdf.core.util.Defs;
import org.icepdf.core.util.Library;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Simple Factory for loading of font library if present.
*/
public class FontFactory {
private static final Logger logger =
Logger.getLogger(FontFactory.class.toString());
// allow scaling of large images to improve clarity on screen
private static boolean awtFontLoading;
// dynamic property to switch between font engine and awt font substitution.
private static boolean awtFontSubstitution;
static {
// turn on font file loading using awt, can cause the jvm to crash
// if the font file is corrupt.
awtFontLoading =
Defs.sysPropertyBoolean("org.icepdf.core.awtFontLoading",
false);
}
public static final int FONT_OPEN_TYPE = 5;
public static final int FONT_TRUE_TYPE = java.awt.Font.TRUETYPE_FONT;
public static final int FONT_TYPE_0 = 6;
public static final int FONT_TYPE_1 = java.awt.Font.TYPE1_FONT;
public static final int FONT_TYPE_3 = 7;
// Singleton instance of class
private static FontFactory fontFactory;
// NFont class path
private static final String FONT_CLASS =
"org.icepdf.core.pobjects.fonts.nfont.Font";
private static final String NFONT_CLASS =
"org.icepdf.core.pobjects.fonts.nfont.NFont";
private static final String NFONT_OPEN_TYPE =
"org.icepdf.core.pobjects.fonts.nfont.NFontOpenType";
private static final String NFONT_TRUE_TYPE =
"org.icepdf.core.pobjects.fonts.nfont.NFontTrueType";
private static final String NFONT_TRUE_TYPE_0 =
"org.icepdf.core.pobjects.fonts.nfont.NFontType0";
private static final String NFONT_TRUE_TYPE_1 =
"org.icepdf.core.pobjects.fonts.nfont.NFontType1";
private static final String NFONT_TRUE_TYPE_3 =
"org.icepdf.core.pobjects.fonts.nfont.NFontType3";
static {
// check class bath for NFont library, and declare results.
try {
Class.forName(NFONT_CLASS);
} catch (ClassNotFoundException e) {
logger.log(Level.FINE, "NFont font library was not found on the class path");
}
}
private static boolean foundNFont;
/**
* <p>Returns a static instance of the FontManager class.</p>
*
* @return instance of the FontManager.
*/
public static FontFactory getInstance() {
// make sure we have initialized the manager
if (fontFactory == null) {
fontFactory = new FontFactory();
}
return fontFactory;
}
private FontFactory() {
}
public Font getFont(Library library, HashMap entries) {
Font fontDictionary = null;
if (foundFontEngine()) {
// load each know file type reflectively.
try {
Class<?> fontClass = Class.forName(FONT_CLASS);
Class[] fontArgs = {Library.class, HashMap.class};
Constructor fontClassConstructor =
fontClass.getDeclaredConstructor(fontArgs);
Object[] fontUrl = {library, entries};
fontDictionary = (Font) fontClassConstructor.newInstance(fontUrl);
} catch (Throwable e) {
logger.log(Level.FINE, "Could not load font dictionary class", e);
}
} else {
// create OFont implementation.
fontDictionary =
new org.icepdf.core.pobjects.fonts.ofont.Font(library, entries);
}
return fontDictionary;
}
public FontFile createFontFile(Stream fontStream, int fontType, String fontSubType) {
FontFile fontFile = null;
if (foundFontEngine()) {
try {
Class<?> fontClass = getNFontClass(fontType);
if (fontClass != null) {
// convert the stream to byte[]
Class[] bytArrayArg = {byte[].class, String.class};
Constructor fontClassConstructor =
fontClass.getDeclaredConstructor(bytArrayArg);
byte[] data = fontStream.getDecodedStreamBytes(0);
Object[] fontStreamBytes = {data, fontSubType};
if (data.length > 0) {
fontFile = (FontFile) fontClassConstructor
.newInstance(fontStreamBytes);
}
}
} catch (Throwable e) {
logger.log(Level.FINE, "Could not create instance of font file " + fontType);
if (fontType == FONT_TRUE_TYPE) {
// we might have a very rare corner case where the file2 definition is actually a Open type font
if (logger.isLoggable(Level.FINE)) {
logger.fine("Trying to reload TrueType definition as OpenType.");
}
try {
// force a OpentType font load.
Class<?> fontClass = getNFontClass(FONT_OPEN_TYPE);
if (fontClass != null) {
// convert the stream to byte[]
Class[] bytArrayArg = {byte[].class, String.class};
Constructor fontClassConstructor =
fontClass.getDeclaredConstructor(bytArrayArg);
byte[] data = fontStream.getDecodedStreamBytes(0);
Object[] fontStreamBytes = {data, fontSubType};
if (data.length > 0) {
fontFile = (FontFile) fontClassConstructor
.newInstance(fontStreamBytes);
}
}
} catch (Exception ex) {
logger.log(Level.FINE, "Could not create instance of font file as OpenType." + fontType);
}
}
}
} else if (awtFontLoading) {
// see if the font file can be loaded with Java Fonts
InputStream in = null;
try {
in = fontStream.getDecodedByteArrayInputStream();
// make sure we try to load open type fonts as well, done as true type.
if (fontType == FONT_OPEN_TYPE) fontType = FONT_TRUE_TYPE;
java.awt.Font javaFont = java.awt.Font.createFont(fontType, in);
if (javaFont != null) {
// create instance of OFont.
fontFile = new OFont(javaFont);
if (logger.isLoggable(Level.FINE)) {
logger.fine("Successfully created embedded OFont: " + fontTypeToString(fontType));
}
try {
in.close();
} catch (IOException e) {
logger.log(Level.FINE, "Error closing font stream.", e);
}
}
} catch (Throwable e) {
logger.log(Level.FINE, "Error reading font file with ", e);
try {
if (in != null) in.close();
} catch (Throwable e1) {
logger.log(Level.FINE, "Error closing font stream.", e);
}
}
}
return fontFile;
}
public FontFile createFontFile(File file, int fontType, String fontSubType) {
try {
return createFontFile(file.toURI().toURL(), fontType, fontSubType);
} catch (Throwable e) {
logger.log(Level.FINE, "Could not create instance of font file " + fontType, e);
}
return null;
}
public FontFile createFontFile(URL url, int fontType, String fontSubType) {
FontFile fontFile = null;
if (foundFontEngine()) {
try {
Class<?> fontClass = getNFontClass(fontType);
if (fontClass != null) {
// convert the stream to byte[]
Class[] urlArg = {URL.class, String.class};
Constructor fontClassConstructor =
fontClass.getDeclaredConstructor(urlArg);
Object[] fontUrl = {url, fontSubType};
fontFile = (FontFile) fontClassConstructor.newInstance(fontUrl);
}
} catch (Throwable e) {
logger.log(Level.FINE, "Could not create instance of font file " + fontType, e);
}
} else {
// see if the font file can be loaded with Java Fonts
try {
// make sure we try to load open type fonts as well, done as true type.
if (fontType == FONT_OPEN_TYPE) fontType = FONT_TRUE_TYPE;
java.awt.Font javaFont = java.awt.Font.createFont(fontType, url.openStream());
if (javaFont != null) {
// create instance of OFont.
fontFile = new OFont(javaFont);
if (logger.isLoggable(Level.FINE)) {
logger.fine("Successfully loaded OFont: " + url);
}
}
} catch (Throwable e) {
logger.log(Level.FINE, "Error reading font file with ", e);
}
}
return fontFile;
}
public boolean isAwtFontSubstitution() {
return awtFontSubstitution;
}
public void setAwtFontSubstitution(boolean awtFontSubstitution) {
FontFactory.awtFontSubstitution = awtFontSubstitution;
}
public void toggleAwtFontSubstitution() {
FontFactory.awtFontSubstitution = !FontFactory.awtFontSubstitution;
}
private Class getNFontClass(int fontType) throws ClassNotFoundException {
Class fontClass = null;
if (FONT_OPEN_TYPE == fontType) {
fontClass = Class.forName(NFONT_OPEN_TYPE);
} else if (FONT_TRUE_TYPE == fontType) {
fontClass = Class.forName(NFONT_TRUE_TYPE);
} else if (FONT_TYPE_0 == fontType) {
fontClass = Class.forName(NFONT_TRUE_TYPE_0);
} else if (FONT_TYPE_1 == fontType) {
fontClass = Class.forName(NFONT_TRUE_TYPE_1);
} else if (FONT_TYPE_3 == fontType) {
fontClass = Class.forName(NFONT_TRUE_TYPE_3);
}
return fontClass;
}
private String fontTypeToString(int fontType) {
if (fontType == FONT_OPEN_TYPE) {
return "Open Type Font";
} else if (fontType == FONT_TRUE_TYPE) {
return "True Type Font";
} else if (fontType == FONT_TYPE_0) {
return "Type 0 Font";
} else if (fontType == FONT_TYPE_1) {
return "Type 1 Font";
} else if (fontType == FONT_TYPE_3) {
return "Type 3 Font";
} else {
return "unknown font type: " + fontType;
}
}
/**
* Test if font engine is available on the class path and it has been
* disabled with the property awtFontSubstitution.
*
* @return true if font engine was found, false otherwise.
*/
public boolean foundFontEngine() {
// check class bath for NFont library
try {
Class.forName(NFONT_CLASS);
foundNFont = true;
} catch (ClassNotFoundException e) {
// keep quiet
}
return foundNFont && !awtFontSubstitution;
}
}