View Javadoc

1   /*
2    * Copyright 2007 - 2008 JEuclid, http://jeuclid.sf.net
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /* $Id: DefaultFontFactory.java,v 2bab6eb875e8 2010/08/11 16:45:50 max $ */
18  
19  package net.sourceforge.jeuclid.font;
20  
21  import java.awt.Font;
22  import java.awt.FontFormatException;
23  import java.awt.GraphicsEnvironment;
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.net.URL;
28  import java.util.Collection;
29  import java.util.HashMap;
30  import java.util.HashSet;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.Map;
34  import java.util.Set;
35  
36  import javax.annotation.concurrent.ThreadSafe;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.xmlgraphics.util.ClasspathResource;
41  
42  /**
43   * Concrete FontFactory implementation that does simple caching of Fonts
44   * loaded via {@link Font#createFont(int, File)} APIs.
45   * 
46   * @version $Revision: 2bab6eb875e8 $
47   */
48  @ThreadSafe
49  public class DefaultFontFactory extends FontFactory {
50  
51      private static final int CACHE_FONT_SIZE = 12;
52  
53      private static final int NUM_STYLES = 4;
54  
55      /**
56       * Logger for this class
57       */
58      private static final Log LOGGER = LogFactory
59              .getLog(DefaultFontFactory.class);
60  
61      private final Map<String, Font[]> fontCache = new HashMap<String, Font[]>();
62  
63      DefaultFontFactory() {
64          this.autoloadFontsFromAWT();
65          this.autoloadFontsFromClasspath();
66      }
67  
68      private void autoloadFontsFromAWT() {
69          final String[] fam = GraphicsEnvironment
70                  .getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
71          for (final String element : fam) {
72              for (int i = 0; i < DefaultFontFactory.NUM_STYLES; i++) {
73                  final Font f = new Font(element, i,
74                          DefaultFontFactory.CACHE_FONT_SIZE);
75                  this.cacheFont(f);
76              }
77          }
78      }
79  
80      @SuppressWarnings("unchecked")
81      private void autoloadFontsFromClasspath() {
82          final List<URL> fonts = ClasspathResource.getInstance()
83                  .listResourcesOfMimeType("application/x-font");
84          for (final URL u : fonts) {
85              try {
86                  try {
87                      this.cacheFont(Font.createFont(Font.TRUETYPE_FONT, u
88                              .openStream()));
89                  } catch (final FontFormatException e) {
90                      try {
91                          this.cacheFont(Font.createFont(Font.TYPE1_FONT, u
92                                  .openStream()));
93                      } catch (final FontFormatException e1) {
94                          DefaultFontFactory.LOGGER.warn(e.getMessage());
95                      }
96                  }
97              } catch (final IOException e) {
98                  DefaultFontFactory.LOGGER.warn(e.getMessage());
99              }
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 }