001 /* 002 * Copyright 2007 - 2008 JEuclid, http://jeuclid.sf.net 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 /* $Id: DefaultFontFactory.java,v 2bab6eb875e8 2010/08/11 16:45:50 max $ */ 018 019 package net.sourceforge.jeuclid.font; 020 021 import java.awt.Font; 022 import java.awt.FontFormatException; 023 import java.awt.GraphicsEnvironment; 024 import java.io.File; 025 import java.io.IOException; 026 import java.io.InputStream; 027 import java.net.URL; 028 import java.util.Collection; 029 import java.util.HashMap; 030 import java.util.HashSet; 031 import java.util.List; 032 import java.util.Locale; 033 import java.util.Map; 034 import java.util.Set; 035 036 import javax.annotation.concurrent.ThreadSafe; 037 038 import org.apache.commons.logging.Log; 039 import org.apache.commons.logging.LogFactory; 040 import org.apache.xmlgraphics.util.ClasspathResource; 041 042 /** 043 * Concrete FontFactory implementation that does simple caching of Fonts 044 * loaded via {@link Font#createFont(int, File)} APIs. 045 * 046 * @version $Revision: 2bab6eb875e8 $ 047 */ 048 @ThreadSafe 049 public class DefaultFontFactory extends FontFactory { 050 051 private static final int CACHE_FONT_SIZE = 12; 052 053 private static final int NUM_STYLES = 4; 054 055 /** 056 * Logger for this class 057 */ 058 private static final Log LOGGER = LogFactory 059 .getLog(DefaultFontFactory.class); 060 061 private final Map<String, Font[]> fontCache = new HashMap<String, Font[]>(); 062 063 DefaultFontFactory() { 064 this.autoloadFontsFromAWT(); 065 this.autoloadFontsFromClasspath(); 066 } 067 068 private void autoloadFontsFromAWT() { 069 final String[] fam = GraphicsEnvironment 070 .getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); 071 for (final String element : fam) { 072 for (int i = 0; i < DefaultFontFactory.NUM_STYLES; i++) { 073 final Font f = new Font(element, i, 074 DefaultFontFactory.CACHE_FONT_SIZE); 075 this.cacheFont(f); 076 } 077 } 078 } 079 080 @SuppressWarnings("unchecked") 081 private void autoloadFontsFromClasspath() { 082 final List<URL> fonts = ClasspathResource.getInstance() 083 .listResourcesOfMimeType("application/x-font"); 084 for (final URL u : fonts) { 085 try { 086 try { 087 this.cacheFont(Font.createFont(Font.TRUETYPE_FONT, u 088 .openStream())); 089 } catch (final FontFormatException e) { 090 try { 091 this.cacheFont(Font.createFont(Font.TYPE1_FONT, u 092 .openStream())); 093 } catch (final FontFormatException e1) { 094 DefaultFontFactory.LOGGER.warn(e.getMessage()); 095 } 096 } 097 } catch (final IOException e) { 098 DefaultFontFactory.LOGGER.warn(e.getMessage()); 099 } 100 } 101 102 } 103 104 /** 105 * Create a font object with specified properties. Font name may refer to 106 * either 'built-in' or loaded externally and 'cached' font. 107 * 108 * @param name 109 * font name or font family name 110 * @param style 111 * font style 112 * @param size 113 * font size 114 * @return Font instance 115 * @see java.awt.Font#Font(String, int, int) 116 */ 117 @Override 118 public Font getFont(final String name, final int style, final float size) { 119 Font font; 120 synchronized (this.fontCache) { 121 final Font[] fonts = this.fontCache.get(name 122 .toLowerCase(Locale.ENGLISH)); 123 if (fonts == null) { 124 font = this.cacheFont( 125 new Font(name, Font.PLAIN, 126 DefaultFontFactory.CACHE_FONT_SIZE)) 127 .deriveFont(style, size); 128 } else { 129 font = fonts[style]; 130 if (font == null) { 131 font = fonts[0].deriveFont(style, size); 132 fonts[style] = font; 133 } else { 134 font = font.deriveFont(size); 135 } 136 } 137 } 138 return font; 139 } 140 141 /** {@inheritDoc} */ 142 @Override 143 public Font getFont(final List<String> preferredFonts, 144 final int codepoint, final int style, final float size) { 145 Font font = this.searchFontList(preferredFonts, codepoint, style, 146 size); 147 if (font == null) { 148 font = this.searchFontList(this.fontCache.keySet(), codepoint, 149 style, size); 150 } 151 return font; 152 } 153 154 private Font searchFontList(final Collection<String> fontList, 155 final int codepoint, final int style, final float size) { 156 for (final String fontName : fontList) { 157 final Font font = this.getFont(fontName, style, size); 158 final String desiredFont = fontName.trim(); 159 if (((font.getFamily().equalsIgnoreCase(desiredFont)) || (font 160 .getFontName().equalsIgnoreCase(desiredFont))) 161 && (font.canDisplay(codepoint))) { 162 return font; 163 } 164 } 165 return null; 166 } 167 168 /** 169 * Load an external font from a file and 'register' (aka 'cache') it for 170 * future use. 171 * 172 * @param format 173 * font format (TTF or TYPE_1 currently supported by the 174 * platform) 175 * @param fontFile 176 * file which contains the font 177 * @return The newly created Font instance 178 * @throws FontFormatException 179 * if font contained in the file doesn't match the specified 180 * format 181 * @throws IOException 182 * in case of problem while reading the file 183 * @see java.awt.Font#createFont(int, File) 184 */ 185 @Override 186 public Font registerFont(final int format, final File fontFile) 187 throws IOException, FontFormatException { 188 189 return this.cacheFont(Font.createFont(format, fontFile)); 190 } 191 192 /** 193 * Load an external font from a stream and 'register' (aka 'cache') it for 194 * future use. 195 * 196 * @param format 197 * font format (TTF or TYPE_1 currently supported by the 198 * platform) 199 * @param fontStream 200 * file which contains the font 201 * @return The newly created Font instance 202 * @throws FontFormatException 203 * if font contained in the stream doesn't match the specified 204 * format 205 * @throws IOException 206 * in case of problem while reading the stream 207 * @see java.awt.Font#createFont(int, InputStream) 208 */ 209 @Override 210 public Font registerFont(final int format, final InputStream fontStream) 211 throws IOException, FontFormatException { 212 213 return this.cacheFont(Font.createFont(format, fontStream)); 214 } 215 216 /** 217 * Actually stores a font in the cache. Uses font name and font family as 218 * keys. 219 * 220 * @param font 221 * Font instance to cache 222 * @return the font instance that was cached 223 */ 224 private Font cacheFont(final Font font) { 225 final String family = font.getFamily().trim().toLowerCase( 226 Locale.ENGLISH); 227 final String fontname = font.getName().trim().toLowerCase( 228 Locale.ENGLISH); 229 int style = font.getStyle(); 230 if (fontname.contains("italic")) { 231 style |= Font.ITALIC; 232 } 233 if (fontname.contains("oblique")) { 234 style |= Font.ITALIC; 235 } 236 if (fontname.contains("bold")) { 237 style |= Font.BOLD; 238 } 239 this.cacheFontWithStyle(font, family, style); 240 this.cacheFontWithStyle(font, fontname, style); 241 return font; 242 } 243 244 private void cacheFontWithStyle(final Font font, final String cacheName, 245 final int style) { 246 synchronized (this.fontCache) { 247 Font[] fonts = this.fontCache.get(cacheName); 248 if (fonts == null) { 249 fonts = new Font[DefaultFontFactory.NUM_STYLES]; 250 this.fontCache.put(cacheName, fonts); 251 fonts[0] = font; 252 } 253 fonts[style] = font; 254 } 255 } 256 257 /** {@inheritDoc} */ 258 @Override 259 public Set<String> listFontNames() { 260 final Set<String> retVal; 261 synchronized (this.fontCache) { 262 retVal = new HashSet<String>(this.fontCache.keySet()); 263 } 264 return retVal; 265 } 266 }