001: /*
002: * Copyright (c) 2001-2006 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.jvnet.substance.fonts;
032:
033: import java.awt.Font;
034: import java.awt.Toolkit;
035: import java.util.Locale;
036:
037: import org.jvnet.lafwidget.utils.LookUtils;
038: import org.jvnet.substance.SubstanceLookAndFeel;
039:
040: /**
041: * Provides static access to popular Windows fonts. The sizes of the font
042: * constants are specified in <em>typographic points</em>, approximately 1/72
043: * of an inch.
044: * <p>
045: *
046: * TODO: Consider changing the visibility of the package private methods to
047: * public. As an alternative we may provide a FontPolicy that can emulate the
048: * font choice on Windows XP/2000 and Windows Vista for different software
049: * resolutions (96dpi/120dpi) and desktop font size settings (Normal/Large/Extra
050: * Large).
051: *
052: * @author Karsten Lentzsch
053: * @version $Revision: 1.4 $
054: *
055: * @see FontSet
056: * @see FontSets
057: * @see FontPolicy
058: * @see FontPolicies
059: *
060: * @since 2.0
061: */
062: public final class Fonts {
063:
064: /**
065: * The name of the default dialog font on western Windows XP.
066: */
067: public static final String TAHOMA_NAME = "Tahoma";
068:
069: /**
070: * The name of the default dialog font on western Windows Vista.
071: */
072: public static final String SEGOE_UI_NAME = "Segoe UI";
073:
074: // Physical Fonts *********************************************************
075:
076: /**
077: * This is the default font on western XP with 96dpi and normal fonts.
078: * Ascent=11, descent=3, height=14, dbuX=6, dbuY=12, 14dluY=21px.
079: */
080: public static final Font TAHOMA_11PT = new Font(TAHOMA_NAME,
081: Font.PLAIN, 11);
082:
083: /**
084: * Ascent=13, descent=3, height=16, dbuX=8, dbuY=13, 14dluY=22.75px.
085: */
086: public static final Font TAHOMA_13PT = new Font(TAHOMA_NAME,
087: Font.PLAIN, 13);
088:
089: /**
090: * Ascent=14, descent=3, height=17, dbuX=8, dbuY=14, 14dluY=24.5px.
091: */
092: public static final Font TAHOMA_14PT = new Font(TAHOMA_NAME,
093: Font.PLAIN, 14);
094:
095: /**
096: * This is Segoe UI 9pt, the default font on western Vista with 96dpi.
097: * Ascent=13, descent=4, height=17, dbuX=7, dbuY=13, 13dluY=21.125px.
098: */
099: public static final Font SEGOE_UI_12PT = new Font(SEGOE_UI_NAME,
100: Font.PLAIN, 12);
101:
102: /**
103: * Ascent=14, descent=4, height=18, dbuX=8, dbuY=14, 13dluY=22.75px.
104: */
105: public static final Font SEGOE_UI_13PT = new Font(SEGOE_UI_NAME,
106: Font.PLAIN, 13);
107:
108: /**
109: * Ascent=16, descent=5, height=21, dbuX=9, dbuY=16, 13dluY=26px.
110: */
111: public static final Font SEGOE_UI_15PT = new Font(SEGOE_UI_NAME,
112: Font.PLAIN, 15);
113:
114: // Default Windows Fonts **************************************************
115:
116: /**
117: * The default icon font on western Windows XP with 96dpi and the dialog
118: * font desktop setting "Normal".
119: */
120: public static final Font WINDOWS_XP_96DPI_NORMAL = TAHOMA_11PT;
121:
122: /**
123: * The default GUI font on western Windows XP with 96dpi and the dialog font
124: * desktop setting "Normal".
125: */
126: public static final Font WINDOWS_XP_96DPI_DEFAULT_GUI = TAHOMA_11PT;
127:
128: /**
129: * The default icon font on western Windows XP with 96dpi and the dialog
130: * font desktop setting "Large".
131: */
132: public static final Font WINDOWS_XP_96DPI_LARGE = TAHOMA_13PT;
133:
134: /**
135: * The default icon font on western Windows XP with 120dpi and the dialog
136: * font desktop setting "Normal".
137: */
138: public static final Font WINDOWS_XP_120DPI_NORMAL = TAHOMA_14PT;
139:
140: /**
141: * The default GUI font on western Windows XP with 120dpi and the dialog
142: * font desktop setting "Normal".
143: */
144: public static final Font WINDOWS_XP_120DPI_DEFAULT_GUI = TAHOMA_13PT;
145:
146: /**
147: * The default icon font on western Windows Vista with 96dpi and the dialog
148: * font desktop setting "Normal".
149: */
150: public static final Font WINDOWS_VISTA_96DPI_NORMAL = SEGOE_UI_12PT;
151:
152: /**
153: * The default icon font on western Windows Vista with 96dpi and the dialog
154: * font desktop setting "Large".
155: */
156: public static final Font WINDOWS_VISTA_96DPI_LARGE = SEGOE_UI_15PT;
157:
158: /**
159: * The default icon font on western Windows Vista with 101dpi and the dialog
160: * font desktop setting "Normal".
161: * <P>
162: *
163: * TODO: Check if this shall be removed or not.
164: */
165: static final Font WINDOWS_VISTA_101DPI_NORMAL = SEGOE_UI_13PT;
166:
167: /**
168: * The default icon font on western Windows Vista with 120dpi and the dialog
169: * font desktop setting "Normal".
170: */
171: public static final Font WINDOWS_VISTA_120DPI_NORMAL = SEGOE_UI_15PT;
172:
173: // Desktop Property Font Keys *********************************************
174:
175: /**
176: * The desktop property key used to lookup the DEFAULTGUI font. This font
177: * scales with the software resolution only but works in western and
178: * non-western Windows environments.
179: *
180: * @see #getWindowsControlFont()
181: */
182: static final String WINDOWS_DEFAULT_GUI_FONT_KEY = "win.defaultGUI.font";
183:
184: /**
185: * The desktop property key used to lookup Windows' icon font. This font
186: * scales with the software resolution and the desktop font size setting
187: * (Normal/Large/Extra Large). However, in some non-western Windows
188: * environments this font cannot display the locale's glyphs.
189: * <p>
190: *
191: * Implementation Note: Windows uses the icon font to label icons in the
192: * Windows Explorer and other places. It seems to me that this works in
193: * non-western environments due to font chaining.
194: *
195: * @see #getWindowsControlFont()
196: */
197: static final String WINDOWS_ICON_FONT_KEY = "win.icon.font";
198:
199: // Instance Creation ******************************************************
200:
201: private Fonts() {
202: // Override default constructor; prevents instantation.
203: }
204:
205: // Font Lookup ************************************************************
206:
207: static Font getDefaultGUIFontWesternModernWindowsNormal() {
208: return LookUtils.IS_LOW_RESOLUTION ? WINDOWS_XP_96DPI_DEFAULT_GUI
209: : WINDOWS_XP_120DPI_DEFAULT_GUI;
210: }
211:
212: static Font getDefaultIconFontWesternModernWindowsNormal() {
213: return LookUtils.IS_LOW_RESOLUTION ? WINDOWS_XP_96DPI_NORMAL
214: : WINDOWS_XP_120DPI_NORMAL;
215: }
216:
217: static Font getDefaultIconFontWesternWindowsVistaNormal() {
218: return LookUtils.IS_LOW_RESOLUTION ? WINDOWS_VISTA_96DPI_NORMAL
219: : WINDOWS_VISTA_120DPI_NORMAL;
220: }
221:
222: /**
223: * Returns the Windows control font used by the JGoodies Looks version 1.x.
224: * It is intended for visual backward compatibility only. The font returned
225: * is the default GUI font that scales with the resolution (96dpi, 120dpi,
226: * etc) but not with the desktop font size settings (normal, large, extra
227: * large).
228: * <p>
229: *
230: * On Windows Vista, the font may be completely wrong.
231: *
232: * @return the Windows default GUI font that scales with the resolution, but
233: * not the desktop font size setting
234: *
235: * @throws UnsupportedOperationException
236: * on non-Windows platforms
237: */
238: static Font getLooks1xWindowsControlFont() {
239: if (!LookUtils.IS_OS_WINDOWS)
240: throw new UnsupportedOperationException();
241:
242: return getDesktopFont(WINDOWS_DEFAULT_GUI_FONT_KEY);
243: }
244:
245: /**
246: * Looks up and returns the Windows control font. Returns the Windows icon
247: * title font unless it is inappropriate for the Windows version, Java
248: * renderer, or locale.
249: * <p>
250: *
251: * The icon title font scales with the resolution (96dpi, 101dpi, 120dpi,
252: * etc) and the desktop font size settings (normal, large, extra large).
253: * Older versions may return a poor font. Also, since Java 1.4 and Java 5
254: * render the Windows Vista icon font Segoe UI poorly, we return the default
255: * GUI font in these environments.
256: * <p>
257: *
258: * The last check is, if the icon font can display text in the default
259: * locale. Therefore we test if the locale's localized display name can be
260: * displayed by the icon font. For example, Tahoma can display "English",
261: * "Deutsch", but not the display name for "Chinese" in Chinese.
262: *
263: * @return the Windows control font
264: *
265: * @throws UnsupportedOperationException
266: * on non-Windows platforms
267: */
268: public static Font getWindowsControlFont() {
269: if (!LookUtils.IS_OS_WINDOWS)
270: throw new UnsupportedOperationException();
271:
272: Font defaultGUIFont = getDefaultGUIFont();
273: // Return the default GUI font on older Windows versions.
274: if (LookUtils.IS_OS_WINDOWS_95 || LookUtils.IS_OS_WINDOWS_98
275: || LookUtils.IS_OS_WINDOWS_NT
276: || LookUtils.IS_OS_WINDOWS_ME)
277: return defaultGUIFont;
278:
279: // Java 1.4 and Java 5 raster the Segoe UI poorly,
280: // so we use the older Tahoma, if it can display the localized text.
281: if (LookUtils.IS_OS_WINDOWS_VISTA) {
282: // Substance-specific addition since version 4.2 - if the
283: // current text painter uses native text rendering, we
284: // allow using Segoe UI under JDK 5.0 as well.
285: if (LookUtils.IS_JAVA_1_4_OR_5
286: && !SubstanceLookAndFeel.getCurrentTextPainter()
287: .isNative()) {
288: Font tahoma = getDefaultGUIFontWesternModernWindowsNormal();
289: return Boolean.TRUE.equals(canDisplayLocalizedText(
290: tahoma, Locale.getDefault())) ? tahoma
291: : defaultGUIFont;
292: }
293: }
294:
295: Font iconFont = getDesktopFont(WINDOWS_ICON_FONT_KEY);
296: return Boolean.TRUE.equals(canDisplayLocalizedText(iconFont,
297: Locale.getDefault())) ? iconFont : defaultGUIFont;
298: }
299:
300: /**
301: * Looks up and returns the Windows defaultGUI font. Works around a bug with
302: * Java 1.4.2_11, 1.5.0_07, and 1.6 b89 in the Vista Beta2, where the
303: * win.defaultGUI.font desktop property returns null. In this case a logical
304: * "Dialog" font is used as fallback.
305: *
306: * @return the Windows defaultGUI font, or a dialog font as fallback.
307: */
308: private static Font getDefaultGUIFont() {
309: Font font = getDesktopFont(WINDOWS_DEFAULT_GUI_FONT_KEY);
310: if (font != null)
311: return font;
312: return new Font("Dialog", Font.PLAIN, 12);
313: }
314:
315: /**
316: * Checks and answers whether the given font can display text that is
317: * localized for the specified locale. Returns <code>null</code> if we
318: * can't test it.
319: * <p>
320: *
321: * First checks, if the locale's display language is available in localized
322: * form, for example "Deutsch" for the German locale. If so, we check if the
323: * given font can display the localized display language.
324: * <p>
325: *
326: * Otherwise we check some known combinations of fonts and locales and
327: * return the associated results. For all other combinations,
328: * <code>null</code> is returned to indicate that we don't know whether
329: * the font can display text in the given locale.
330: *
331: * @param font
332: * the font to be tested
333: * @param locale
334: * the locale to be used
335: * @return <code>Boolean.TRUE</code> if the font can display the locale's
336: * text, <code>Boolean.FALSE</code> if not, <code>null</code> if
337: * we don't know
338: *
339: * @since 2.0.4
340: */
341: public static Boolean canDisplayLocalizedText(Font font,
342: Locale locale) {
343: if (localeHasLocalizedDisplayLanguage(locale)) {
344: return Boolean.valueOf(canDisplayLocalizedDisplayLanguage(
345: font, locale));
346: }
347: String fontName = font.getName();
348: String language = locale.getLanguage();
349: if ("Tahoma".equals(fontName)) {
350: if ("hi".equals(language))
351: return Boolean.FALSE;
352: else if ("ja".equals(language))
353: return Boolean.FALSE;
354: else if ("ko".equals(language))
355: return Boolean.FALSE;
356: else if ("zh".equals(language))
357: return Boolean.FALSE;
358: }
359: if ("Microsoft Sans Serif".equals(fontName)) {
360: if ("ja".equals(language))
361: return Boolean.FALSE;
362: else if ("ko".equals(language))
363: return Boolean.FALSE;
364: else if ("zh".equals(language))
365: return Boolean.FALSE;
366: }
367: return null;
368: }
369:
370: /**
371: * Checks and answers if the given font can display the locale's localized
372: * display language, for example "English" for English, "Deutsch" for
373: * German, etc. The test invokes <code>Font#canDisplayUpTo</code> on the
374: * localized display language. In a Chinese locale this test will check if
375: * the font can display Chinese glyphs.
376: *
377: * @param font
378: * the font to be tested
379: * @param locale
380: * the locale to be used
381: * @return true if the font can display the locale's localized display
382: * language, false otherwise
383: */
384: private static boolean canDisplayLocalizedDisplayLanguage(
385: Font font, Locale locale) {
386: String testString = locale.getDisplayLanguage(locale);
387: int index = font.canDisplayUpTo(testString);
388: return index == -1;
389: }
390:
391: /**
392: * Checks and answers whether the locale's display language is available in
393: * a localized form, for example "Deutsch" for the German locale.
394: *
395: * @param locale
396: * the Locale to test
397: * @return true if the display language is localized, false if not
398: */
399: private static boolean localeHasLocalizedDisplayLanguage(
400: Locale locale) {
401: if (locale.getLanguage().equals(Locale.ENGLISH.getLanguage()))
402: return true;
403: String englishDisplayLanguage = locale
404: .getDisplayLanguage(Locale.ENGLISH);
405: String localizedDisplayLanguage = locale
406: .getDisplayLanguage(locale);
407: return !(englishDisplayLanguage
408: .equals(localizedDisplayLanguage));
409: }
410:
411: /**
412: * Looks up and returns a font using the default toolkit's desktop
413: * properties.
414: *
415: * @param fontName
416: * the name of the font to return
417: * @return the font
418: */
419: private static Font getDesktopFont(String fontName) {
420: Toolkit toolkit = Toolkit.getDefaultToolkit();
421: return (Font) toolkit.getDesktopProperty(fontName);
422: }
423:
424: }
|