Source code: com/port80/eclipse/util/FontFactory.java
1 package com.port80.eclipse.util;
2
3 import java.util.Collection;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.StringTokenizer;
10
11 import org.eclipse.swt.SWT;
12 import org.eclipse.swt.custom.ScrolledComposite;
13 import org.eclipse.swt.events.DisposeEvent;
14 import org.eclipse.swt.events.DisposeListener;
15 import org.eclipse.swt.graphics.Color;
16 import org.eclipse.swt.graphics.Font;
17 import org.eclipse.swt.graphics.FontData;
18 import org.eclipse.swt.layout.FillLayout;
19 import org.eclipse.swt.layout.RowLayout;
20 import org.eclipse.swt.widgets.Composite;
21 import org.eclipse.swt.widgets.Display;
22 import org.eclipse.swt.widgets.Label;
23 import org.eclipse.swt.widgets.Shell;
24
25 import com.port80.util.Debug;
26 import com.port80.util.attr.IAttrFactory;
27
28 /**
29 * SWT Font factory and cache.
30 */
31 public class FontFactory implements IAttrFactory, DisposeListener {
32
33 ////////////////////////////////////////////////////////////////////////
34
35 private static final String NAME = "FontFactory";
36 private static final boolean DEBUG = true;
37 private static boolean VERBOSE = Debug.isVerbose();
38
39 private static final int DEFAULT_SIZE = 12;
40
41 private static FontFactory fInstance = null;
42 private static Display fDisplay;
43
44 ////////////////////////////////////////////////////////////////////////
45
46 private Map fFontTable;
47
48 ////////////////////////////////////////////////////////////////////////
49
50 public FontFactory() {
51 fDisplay = Display.getDefault();
52 fFontTable = new HashMap();
53 }
54
55 ////////////////////////////////////////////////////////////////////////
56
57 public static FontFactory getDefault() {
58 if (fInstance == null)
59 fInstance = new FontFactory();
60 return fInstance;
61 }
62
63 ////////////////////////////////////////////////////////////////////////
64
65 /**
66 * Create or get the specified font from cache if already created.
67 * String representation of a font spec., accepted format:
68 * eg. "comic sans-bold italic-12"
69 */
70 public Font create(String spec) {
71 if (fInstance == null)
72 fInstance = new FontFactory();
73 if (DEBUG)
74 System.err.println(NAME + ".create(String): spec=" + spec);
75 int index=spec.indexOf(';');
76 if(index>=0) spec=spec.substring(0,index);
77 spec = spec.toLowerCase();
78 if (spec.startsWith("1|"))
79 spec = convertFontSpecFormat(spec);
80 Font ret = (Font) fFontTable.get(spec);
81 if (ret != null)
82 return ret;
83 FontData fontdata = parseFontSpec(spec);
84 ret = new Font(fDisplay, fontdata);
85 fFontTable.put(spec, ret);
86 return ret;
87 }
88
89 public Font create(String fontname, int size, int style) {
90 if (fInstance == null)
91 fInstance = new FontFactory();
92 if (DEBUG)
93 System.err.println(NAME + ".create(): fontname=" + fontname + ", size=" + size + ", style=" + style);
94 String spec = getFontSpec(fontname, size, style);
95 Font ret = (Font) fFontTable.get(spec);
96 if (ret != null)
97 return ret;
98 ret = new Font(fDisplay, fontname, size, style);
99 if (ret == null && VERBOSE)
100 System.err.println("Error creating font: " + fontname + ", size=" + size + ", style=" + style);
101 fFontTable.put(spec, ret);
102 return ret;
103 }
104
105 /** Derive a new Font from the given Font and new size. */
106 public Font deriveFont(Font font, int size) {
107 //FIXME: Not work for multiple FontData.
108 return deriveFont(font, size, font.getFontData()[0].getStyle());
109 }
110
111 /** Derive a new Font from the given Font and new size and style. */
112 public Font deriveFont(Font font, int size, int style) {
113 FontData data = font.getFontData()[0];
114 if (size < 0)
115 size = data.getHeight();
116 return create(data.getName(), size, style);
117 }
118
119 public void dispose(String spec) {
120 Font font = (Font) fFontTable.get(spec);
121 if (font != null) {
122 font.dispose();
123 }
124 if (VERBOSE)
125 System.err.println(NAME + ": disposed " + fFontTable.size() + " fonts.");
126 fFontTable.clear();
127 }
128
129 ////////////////////////////////////////////////////////////////////////
130
131 /**
132 * Dispose cached font resource.
133 * Typically this should be listening to the main window DisposeEvent.
134 */
135 public void widgetDisposed(DisposeEvent e) {
136 dispose();
137 }
138
139 public void dispose() {
140 Collection values = fFontTable.values();
141 Font font;
142 for (Iterator it = values.iterator(); it.hasNext();) {
143 font = (Font) it.next();
144 font.dispose();
145 }
146 if (VERBOSE)
147 System.err.println(NAME + ".dispose(): " + fFontTable.size() + " fonts.");
148 if (DEBUG) {
149 for (Iterator it = fFontTable.keySet().iterator(); it.hasNext();) {
150 String name = (String) it.next();
151 System.err.println("\t" + name);
152 }
153 }
154 fFontTable.clear();
155 }
156
157 // IAttrFactory interface //////////////////////////////////////////////
158 //
159
160 /**
161 * Convert String representation of an attribute value to the
162 * appropriate object for storing in AttrTable.
163 *
164 * String representation of a font spec., accepted format:
165 * eg. "comic sans-bold italic-12"
166 *
167 * Note: Case is insignificant in the font spec. but spaces in fontname is significant.
168 * Spaces and order in font style is not significant. Size must be an integer.
169 */
170 public Object createObject(String attrvalue) {
171 return create(attrvalue);
172 }
173
174 public boolean isValid(Object a) {
175 return (a instanceof Font);
176 }
177
178 public Class getObjectClass() {
179 return Font.class;
180 }
181
182 /** The String representation of an attribute value. */
183 public String toString(Object attr) {
184 return attr.toString();
185 }
186
187 /** The String representation of attribute type itself. */
188 public String toString() {
189 return NAME;
190 }
191
192 /** Prompt user and present a user interface to obtain an
193 * attribute value from user. */
194 public Object promptUser(String prompt) {
195 return null;
196 }
197
198 ////////////////////////////////////////////////////////////////////////
199
200 /**
201 * Convert FontData serialized format font spec. to foundry-family-style-size format.
202 */
203 public static String convertFontSpecFormat(String spec) {
204 // Assume FontData serailized format.
205 // 1|arial black|10|0|motif|1|-monotype-arial black-medium-r-normal--14-100-100-100-p-77-iso8859-15;
206 // Apparently, new FontData(spec) don't work, use new FontData(name, size, style).
207 String sizestr, stylestr, foundry;
208 int index, style;
209 index = spec.indexOf("|");
210 spec = spec.substring(index + 1);
211 index = spec.indexOf('|');
212 sizestr = spec.substring(index + 1);
213 spec = spec.substring(0, index);
214 // spec=arial black, sizestr=10|0|...
215 index = sizestr.indexOf('|');
216 stylestr = sizestr.substring(index + 1);
217 sizestr = sizestr.substring(0, index);
218 index = stylestr.indexOf('|');
219 foundry=stylestr.substring(index+1);
220 stylestr = stylestr.substring(0, index);
221 index=foundry.indexOf('-');
222 foundry=foundry.substring(index+1);
223 index=foundry.indexOf('-');
224 foundry=foundry.substring(0, index);
225 //
226 style = SWT.NORMAL;
227 if (stylestr != null && stylestr.length() > 0) {
228 try {
229 style = Integer.parseInt(stylestr);
230 } catch (Exception e) {
231 }
232 }
233 if ((style & SWT.BOLD) != 0)
234 stylestr = "bold";
235 else
236 stylestr = "";
237 if ((style & SWT.ITALIC) != 0)
238 if (stylestr.length() > 0)
239 stylestr += " italic";
240 else
241 stylestr = "italic";
242 return foundry+'-'+spec + '-' + stylestr + '-' + sizestr;
243 }
244
245 ////////////////////////////////////////////////////////////////////////
246
247 /** Parse a font spec. string. */
248 public static FontData parseFontSpec(String spec) {
249 String stylestr = null;
250 String sizestr = null;
251 int index;
252 int size;
253 int style;
254 index = spec.lastIndexOf('-');
255 if (index >= 0) {
256 sizestr = spec.substring(index + 1);
257 spec = spec.substring(0, index);
258 }
259 index = spec.lastIndexOf('-');
260 if (index >= 0) {
261 stylestr = spec.substring(index + 1);
262 spec = spec.substring(0, index);
263 }
264 //
265 style = SWT.NORMAL;
266 if (stylestr != null && stylestr.length() > 0) {
267 StringTokenizer st = new StringTokenizer(stylestr, " \t\n\r");
268 while (st.hasMoreTokens()) {
269 String s = st.nextToken();
270 if (s.equalsIgnoreCase("bold")) {
271 style |= SWT.BOLD;
272 } else if (s.equalsIgnoreCase("italic")) {
273 style |= SWT.ITALIC;
274 }
275 }
276 }
277 if (DEBUG)
278 System.err.println(
279 NAME
280 + ".create(String): fontname="
281 + spec
282 + ", stylestr="
283 + stylestr
284 + ", sizestr="
285 + sizestr);
286 //
287 size = DEFAULT_SIZE;
288 if (sizestr != null && sizestr.length() > 0) {
289 size = Integer.parseInt(sizestr);
290 }
291 return new FontData(spec, size, style);
292 }
293
294 /**
295 * Create a clean formatted font spec.
296 */
297 private static String getFontSpec(String fontname, int size, int style) {
298 StringBuffer ret = new StringBuffer(fontname + "-");
299 if ((style & SWT.BOLD) != 0)
300 ret.append("bold");
301 if ((style & SWT.ITALIC) != 0) {
302 if (ret.charAt(ret.length() - 1) == '-')
303 ret.append("italic");
304 else
305 ret.append(" italic");
306 }
307 ret.append("-" + size);
308 return ret.toString();
309 }
310
311 private static String getFontSpec(Font font) {
312 FontData fontdata = font.getFontData()[0];
313 return getFontSpec(fontdata.getName(), fontdata.getHeight(), fontdata.getStyle());
314 }
315
316 ////////////////////////////////////////////////////////////////////////
317
318 public static void main(String[] args) {
319 Display display = new Display();
320 Shell shell = new Shell(display);
321 shell.setText(NAME);
322 shell.addDisposeListener(FontFactory.getDefault());
323 shell.addDisposeListener(ColorFactory.getDefault());
324 //NOTE: Must have this to show the correct sized content.
325 shell.setLayout(new FillLayout());
326 ScrolledComposite scrolled = new ScrolledComposite(shell, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
327 //scrolled.setExpandHorizontal(true);
328 //scrolled.setExpandVertical(true);
329 Composite top = new Composite(scrolled, SWT.NONE);
330 scrolled.setContent(top);
331 // Composite top = new Composite(shell, SWT.V_SCROLL | SWT.H_SCROLL);
332 // Set up the image canvas scroll bars.
333 // ScrollBar horizontal = top.getHorizontalBar();
334 // horizontal.setVisible(true);
335 // horizontal.setMinimum(0);
336 // horizontal.setEnabled(false);
337 // ScrollBar vertical = top.getVerticalBar();
338 // vertical.setVisible(true);
339 // vertical.setMinimum(0);
340 // vertical.setEnabled(false);
341 top.setLayout(new RowLayout(SWT.VERTICAL));
342 //
343 Label label;
344 Font font;
345 //
346 // User selected fonts
347 //
348 System.err.println("\n###\n### Specified fonts:\n###\n");
349 Color bg = ColorFactory.getDefault().create("lightyellow");
350 for (int i = 0; i < args.length; ++i) {
351 label = new Label(top, SWT.NONE);
352 System.err.println("Spec " + i + "=" + args[i]);
353 font = FontFactory.getDefault().create(args[i]);
354 label.setFont(font);
355 label.setBackground(bg);
356 label.setText("Font " + i + ": " + args[i] + ": abcdefgABCDEFG1234567");
357 //
358 label = new Label(top, SWT.NONE);
359 System.err.println("Derive " + i + "=" + args[i] + " size=14");
360 font = FontFactory.getDefault().deriveFont(font, 14);
361 label.setFont(font);
362 label.setBackground(bg);
363 label.setText("Font " + i + ": " + getFontSpec(font) + ": abcdefgABCDEFG1234567");
364 }
365 //
366 // All fonts.
367 //
368 System.err.println("\n###\n### All fonts:\n###\n");
369 FontData[] fonts;
370 FontData fontdata;
371 String fontname;
372 fonts = fDisplay.getFontList(null, true);
373 bg = ColorFactory.getDefault().create("yellow");
374 if (fonts != null) {
375 Set visited = new HashSet();
376 for (int i = 0; i < fonts.length; ++i) {
377 fontdata = fonts[i];
378 fontname = fontdata.getName();
379 if (visited.contains(fontname))
380 continue;
381 visited.add(fontname);
382 font =
383 FontFactory.getDefault().create(
384 fontname,
385 fontdata.getHeight(),
386 fontdata.getStyle());
387 label = new Label(top, SWT.NONE);
388 label.setBackground(bg);
389 label.setFont(font);
390 fontname = getFontSpec(fontname, fontdata.getHeight(), fontdata.getStyle());
391 label.setText(
392 "Font "
393 + i
394 + "="
395 + fontname
396 + ": abcdefgABCDEFG1234567"
397 + ": "
398 + fontdata.toString());
399 }
400 }
401 //NOTE: Must have this to show the ScrolledComposite and its scrollbars correctly.
402 top.setSize(top.computeSize(SWT.DEFAULT, SWT.DEFAULT));
403 shell.pack();
404 shell.open();
405 while (!shell.isDisposed())
406 if (!display.readAndDispatch())
407 display.sleep();
408 display.dispose();
409 }
410 ////////////////////////////////////////////////////////////////////////
411 }