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 }