/* ******************************************************************************
*
* Copyright 2008-2010 Hans Dijkema
*
* JRichTextEditor 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 3 of
* the License, or (at your option) any later version.
*
* JRichTextEditor 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 JRichTextEditor. If not, see <http://www.gnu.org/licenses/>.
*
* ******************************************************************************/
package nl.dykema.jxmlnote.report.pdf;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
// THIS IS AN INTERNAL API OF SUN!
//import sun.font.FontManager;
import nl.dykema.jxmlnote.exceptions.DefaultXMLNoteErrorHandler;
import nl.dykema.jxmlnote.exceptions.FontPathException;
import nl.dykema.jxmlnote.internationalization.DefaultXMLNoteTranslator;
import nl.dykema.jxmlnote.internationalization.XMLNoteTranslator;
import com.lowagie.text.pdf.FontMapper;
/**
* This class provides a font mapper for iText that inserts all font directories from the font path
* of the sun.font.FontMapper. If it cannot insert them, it yields a Warning via XMLNoteErrorHandler
* and returns the default FontMapper of iText.
* <p>
* This class works via a static method. The constructor is protected.
*
* @author Hans Dijkema
*
*/
public class PdfFontMapper extends MyDefaultFontMapper {
enum OS { WINDOWS, UNIX, MACOSX, UNKNOWN };
static public OS getOS() {
String name_os=System.getProperty("os.name").trim().toLowerCase();
if (name_os.contains("windows")) {
return OS.WINDOWS;
} else if (name_os.contains("linux")) {
return OS.UNIX;
} else if (name_os.contains("unix")) {
return OS.UNIX;
} else if (name_os.contains("aix")) {
return OS.UNIX;
} else if (name_os.contains("solaris")) {
return OS.UNIX;
} else if (name_os.contains("sunos")) {
return OS.UNIX;
} else if (name_os.contains("mac")) {
return OS.MACOSX;
} else {
return OS.UNKNOWN;
}
}
Hashtable<String,Object[]> _cachedNames;
Hashtable<String,Integer> _cachedCounts;
Set<String> _paths;
public void insertNames(Object[] names,String path) {
super.insertNames(names,path);
_cachedNames.put(path,names);
}
public int insertFile(File fontFile,int count) {
String path=fontFile.getPath();
Object[] names=_cachedNames.get(path);
if (names!=null) {
insertNames(names,path);
_paths.add(path);
count+=_cachedCounts.get(path);
return count;
} else {
count=super.insertFile(fontFile, count);
_cachedCounts.put(path, new Integer(count));
_paths.add(path);
return count;
}
}
private XMLNoteTranslator _tr;
private static FontMapper _fontmapper=null;
/**
* This method creates an instance of this fontmapper, or reuses the existing one if
* it has already been created.
*
* @return the fontmapper for iText.
*/
public static synchronized FontMapper createPdfFontMapper(PdfReport rep,File fontmapcache) {
if (_fontmapper==null) {
_fontmapper=new PdfFontMapper(rep,fontmapcache);
}
return _fontmapper;
}
/**
* This method creates an instance of this fontmapper, or reuses the existing on if
* it has already been created. A font cache is read from in (which may be null) and
* written to out (which may not be null).
*
* @param rep
* @param in
* @param out
* @return
* @throws IOException
*/
public static synchronized FontMapper createPdfFontMapper(PdfReport rep,InputStream in,OutputStream out) throws IOException {
if (_fontmapper!=null) { return _fontmapper; }
File f=File.createTempFile("PdfFontMapper", "cache");
byte []b=new byte[1024];
int l;
if (in!=null) {
FileOutputStream fout=new FileOutputStream(f);
while ((l=in.read(b))>0) {
fout.write(b,0,l);
}
fout.close();
}
FontMapper fm=createPdfFontMapper(rep,f);
FileInputStream fin=new FileInputStream(f);
while ((l=fin.read(b))>0) {
out.write(b,0,l);
}
fin.close();
return fm;
}
private String expandPath1(File part) {
FileFilter f=new FileFilter() {
public boolean accept(File pathname) {
return pathname.isDirectory();
}
};
File [] dirs=part.listFiles(f);
if (dirs==null) { return null; }
String path=null;
for(File d : dirs) {
if (path==null) { path=d.getAbsolutePath(); }
else { path=path.concat(":").concat(d.getAbsolutePath()); }
String subs=expandPath1(d);
if (subs!=null) {
path=path.concat(":").concat(subs);
}
}
return path;
}
private String expandPath(String parts) {
String[] pathParts=parts.split(":");
String path=null;
for(String p : pathParts) {
if (path==null) { path=p; }
else { path=path.concat(":").concat(p); }
String ep=expandPath1(new File(p));
if (ep!=null) {
path=path.concat(":").concat(ep);
}
}
return path;
}
private static String _fontPath=null;
/**
* This function also looks for fonts in OS dependent places.
* It also looks to the environment variable 'FONTPATH'. If this
* environment variable is set (<path>[:<path>]*), it is prepended.
*
* @return
*/
private String[] getFontPath() {
// java 1.7
// We're not going to make us dependent on java 1.7 specific constructions.
// So we're introducing some new dependencies here, and some knowledge about
// font locations, depending on the operating system we're on.
String fontPath="";
if (_fontPath==null) {
OS os=getOS();
String env_where=System.getenv("FONTPATH");
if (env_where!=null) {
env_where=env_where.trim();
if (env_where.equals("")) { env_where=null; }
}
if (os.equals(OS.WINDOWS)) {
String where=System.getenv("SYSTEMROOT").concat("/fonts");
if (env_where!=null) { where=env_where.concat(":").concat(where); }
fontPath=expandPath(where);
} else if (os.equals(OS.UNIX)) {
String where="/usr/share/fonts";
if (env_where!=null) { where=env_where.concat(":").concat(where); }
fontPath=expandPath(where);
} else if (os.equals(OS.MACOSX)) {
String where=System.getenv("HOME").concat("/Libraries/fonts").concat(":/Library/Fonts:/System/Library/Fonts");
if (env_where!=null) { where=env_where.concat(":").concat(where); }
fontPath=expandPath(where);
}
_fontPath=fontPath;
} else {
fontPath=_fontPath;
}
// java 1.7 end
// java 1.6
//String fontPath=FontManager.getFontPath(true);
// java 1.6 end
if (fontPath==null) { return null; }
else if (fontPath.indexOf(':')>=0) {
String[] pathParts=fontPath.split(":");
return pathParts;
} else if (fontPath.indexOf(';')>=0) {
String[] pathParts=fontPath.split(";");
return pathParts;
} else {
String []pathPaths={ fontPath };
return pathPaths;
}
}
private void readInCache(File f) {
long l=f.length();
if (f.canRead() && f.isFile() && f.length()>0) {
try {
ObjectInputStream in=new ObjectInputStream(new FileInputStream(f));
readInCache(in);
in.close();
} catch (Exception e) {
DefaultXMLNoteErrorHandler.exception(e);
}
}
}
private void readInCache(ObjectInputStream in) {
_cachedNames.clear();
_cachedCounts.clear();
try {
int size=(Integer) in.readObject();
for (int i=0;i<size;i++) {
String path=(String) in.readObject();
Object[] names=(Object []) in.readObject();
_cachedNames.put(path,names);
}
size=(Integer) in.readObject();
for (int i=0;i<size;i++) {
String path=(String) in.readObject();
Integer count=(Integer) in.readObject();
_cachedCounts.put(path, count);
}
} catch (Exception e) {
DefaultXMLNoteErrorHandler.exception(e);
}
}
private void writeOutCache(File f) {
try {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(f));
writeOutCache(out);
out.close();
} catch(Exception e) {
DefaultXMLNoteErrorHandler.exception(e);
}
}
private void writeOutCache(ObjectOutputStream out) {
try {
Set<String> keys=_cachedNames.keySet();
Iterator<String> it=keys.iterator();
out.writeObject(keys.size());
while (it.hasNext()) {
String path=it.next();
out.writeObject(path);
Object [] names=_cachedNames.get(path);
out.writeObject(names);
}
keys=_cachedCounts.keySet();
it=keys.iterator();
out.writeObject(keys.size());
while (it.hasNext()) {
String path=it.next();
out.writeObject(path);
Integer count=_cachedCounts.get(path);
out.writeObject(count);
}
} catch(Exception e) {
DefaultXMLNoteErrorHandler.exception(e);
}
}
private void initializeFonts(PdfReport rep,File persistedCacheWithFontMapInfo) throws FontPathException {
if (persistedCacheWithFontMapInfo!=null) { readInCache(persistedCacheWithFontMapInfo); }
_paths.clear();
String[] paths=getFontPath();
if (paths==null) {
throw new FontPathException(_tr._("There seems to be no font path configured for this platform"));
} else {
String prev=null;
if (rep!=null) {
prev=rep.informStatus(_tr._("Initializing PDF Fonts"));
}
float progr=0.0f;
float step=100.0f/((float) paths.length);
int prevp=0;
if (rep!=null) {
prevp=rep.informProgress((int) progr);
}
for(String path : paths) {
progr+=step;
if (progr>100.0f) { progr=100.0f; }
if (rep!=null) { rep.informProgress((int) progr); }
super.insertDirectory(path);
}
if (rep!=null) {
rep.informStatus(prev);
rep.informProgress(prevp);
}
}
// clear out cached fonts that have not been read in
Set<String> cachedPaths=_cachedNames.keySet();
Iterator<String> it=cachedPaths.iterator();
Vector<String> clearPaths=new Vector<String>();
while (it.hasNext()) {
String p=it.next();
if (!_paths.contains(p)) {
clearPaths.add(p);
}
}
it=clearPaths.iterator();
while (it.hasNext()) {
String p=it.next();
_cachedNames.remove(p);
_cachedCounts.remove(p);
}
// persist new cache
if (persistedCacheWithFontMapInfo!=null) { writeOutCache(persistedCacheWithFontMapInfo); }
}
protected PdfFontMapper(PdfReport rep,File persistedCacheWithFontMapInfo) {
_tr=new DefaultXMLNoteTranslator();
_cachedNames=new Hashtable<String,Object[]>();
_cachedCounts=new Hashtable<String,Integer>();
_paths=new HashSet<String>();
try {
initializeFonts(rep,persistedCacheWithFontMapInfo);
} catch (FontPathException e) {
DefaultXMLNoteErrorHandler.warning(e, -1, e.getMessage());
}
}
}