/* * @(#)Font.java 1.28 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package java.awt; import sun.awt.peer.FontPeer; import sun.awt.PeerBasedToolkit; import java.security.AccessController; import sun.security.action.GetPropertyAction; import java.awt.font.TextAttribute; import java.util.Locale; import java.util.Map; import java.util.Hashtable; import java.text.AttributedCharacterIterator.Attribute; /** * A class that produces font objects. * * @version 1.29, 07/01/98 * @author Sami Shaio * @author Arthur van Hoff * @author Jim Graham * @since JDK1.0 */ public class Font implements java.io.Serializable { /** * A map of font attributes available in this font. * Attributes include things like ligatures and glyph substitution. * * @serial * @see #getAttributes() */ private Hashtable fRequestedAttributes; private static final Map EMPTY_MAP = new Hashtable(5, (float)0.9); /* * Constants to be used for styles. Can be combined to mix * styles. */ /** * The plain style constant. This style can be combined with * the other style constants for mixed styles. * @since JDK1.0 */ public static final int PLAIN = 0; /** * The bold style constant. This style can be combined with the * other style constants for mixed styles. * @since JDK1.0 */ public static final int BOLD = 1; /** * The italicized style constant. This style can be combined * with the other style constants for mixed styles. * @since JDK1.0 */ public static final int ITALIC = 2; /** * Private data. */ transient private int pData; /** * The platform specific family name of this font. */ transient private String family; /** * The logical name of this font. * @since JDK1.0 */ protected String name; /** * The style of the font. This is the sum of the * constants <code>PLAIN</code>, <code>BOLD</code>, * or <code>ITALIC</code>. */ protected int style; /** * The point size of this font. * @since JDK1.0 */ protected int size; /** * The point size of this <code>Font</code> in <code>float</code>. * * @serial * @see #getSize() * @see #getSize2D() */ private float pointSize; /** * The platform specific font information. */ transient FontPeer peer; /* * JDK 1.1 serialVersionUID */ private static final long serialVersionUID = -4206021311591459213L; /** * Gets the peer of the font. * @return the peer of the font. * @since JDK1.1 */ /* public FontPeer getPeer(){ return peer; } */ /** * Creates a new font with the specified name, style and point size. * @param name the font name * @param style the constant style used * @param size the point size of the font * @see Toolkit#getFontList * @since JDK1.0 */ public Font(String name, int style, int size) { this(name, style, size, null); this.peer = ((PeerBasedToolkit) Toolkit.getDefaultToolkit()).getFontPeer(this); initializeFont(null); } /** Creates a font using the specified peer. This can be useful from native code where a Java Font object needs to be created from an existing native font. */ private Font(String name, int style, int size, FontPeer peer) { this.name = name; if (this.name == null) { this.name = "Default"; } this.style = PLAIN; if (style == BOLD || style == ITALIC || style == (BOLD | ITALIC)) { this.style = style; } this.size = size; this.peer = peer; setFamily(); initializeFont(null); } private void initializeFont(Hashtable attributes) { if (this.name == null) { this.name = "Default"; } if (attributes == null) { fRequestedAttributes = new Hashtable(5, (float)0.9); fRequestedAttributes.put(TextAttribute.FAMILY, name); fRequestedAttributes.put(TextAttribute.SIZE, new Float(size)); fRequestedAttributes.put(TextAttribute.WEIGHT, (style & BOLD) != 0 ? TextAttribute.WEIGHT_BOLD : TextAttribute.WEIGHT_REGULAR); fRequestedAttributes.put(TextAttribute.POSTURE, (style & ITALIC) != 0 ? TextAttribute.POSTURE_OBLIQUE : TextAttribute.POSTURE_REGULAR); } } /** * Creates a new <code>Font</code> with the specified attributes. * This <code>Font</code> only recognizes keys defined in * {@link TextAttribute} as attributes. If <code>attributes</code> * is <code>null</code>, a new <code>Font</code> is initialized * with default attributes. * @param attributes the attributes to assign to the new * <code>Font</code>, or <code>null</code> */ public Font(Map attributes){ initFromMap(attributes); setFamily(); } private void initFromMap(Map attributes) { this.name = "Dialog"; this.pointSize = 12; this.size = 12; if ((attributes != null) && (!attributes.equals(EMPTY_MAP))) { Object obj; fRequestedAttributes = new Hashtable(attributes); if ((obj = attributes.get(TextAttribute.FAMILY)) != null) { this.name = (String)obj; } if ((obj = attributes.get(TextAttribute.WEIGHT)) != null){ if(((Float)obj).compareTo(TextAttribute.WEIGHT_BOLD) >= 0) { this.style |= BOLD; } } if ((obj = attributes.get(TextAttribute.POSTURE)) != null){ if(obj.equals(TextAttribute.POSTURE_OBLIQUE)) { this.style |= ITALIC; } } if ((obj = attributes.get(TextAttribute.SIZE)) != null){ this.pointSize = ((Float)obj).floatValue(); this.size = (int)(this.pointSize + 0.5); } } initializeFont(fRequestedAttributes); } /** * Gets the platform specific family name of the font. Use the * <code>getName</code> method to get the logical name of the font. * @return a string, the platform specific family name. * @see java.awt.Font#getName * @since JDK1.0 */ public String getFamily() { return family; } /** * Gets the logical name of the font. * @return a string, the logical name of the font. * @see #getFamily * @since JDK1.0 */ public String getName() { return name; } /** * Gets the style of the font. * @return the style of this font. * @see #isPlain * @see #isBold * @see #isItalic * @since JDK1.0 */ public int getStyle() { return style; } /** * Gets the point size of the font. * @return the point size of this font. * @since JDK1.0 */ public int getSize() { return size; } /** * Indicates whether the font's style is plain. * @return <code>true</code> if the font is neither * bold nor italic; <code>false</code> otherwise. * @see java.awt.Font#getStyle * @since JDK1.0 */ public boolean isPlain() { return style == 0; } /** * Indicates whether the font's style is bold. * @return <code>true</code> if the font is bold; * <code>false</code> otherwise. * @see java.awt.Font#getStyle * @since JDK1.0 */ public boolean isBold() { return (style & BOLD) != 0; } /** * Indicates whether the font's style is italic. * @return <code>true</code> if the font is italic; * <code>false</code> otherwise. * @see java.awt.Font#getStyle * @since JDK1.0 */ public boolean isItalic() { return (style & ITALIC) != 0; } /** * Gets a font from the system properties list. * @param nm the property name * @see java.awt.Font#getFont(java.lang.String, java.awt.Font) * @since JDK1.0 */ public static Font getFont(String nm) { return getFont(nm, null); } /** * Returns a <code>Font</code> appropriate to this attribute set. * * @param attributes the attributes to assign to the new * <code>Font</code> * @return a new <code>Font</code> created with the specified * attributes * @since 1.2 * @see java.awt.font.TextAttribute */ public static Font getFont(Map attributes) { Font font = (Font)attributes.get(TextAttribute.FONT); if (font != null) { return font; } //For now, don't implement caching, because we do already for peers and // native fonts. So just create a new font. Later we may decided // to implement caching on this level return new Font(attributes); } /** * Returns the <code>Font</code> that the <code>str</code> * argument describes. * To ensure that this method returns the desired Font, * format the <code>str</code> parameter in * one of two ways: * <p> * "fontfamilyname-style-pointsize" or <br> * "fontfamilyname style pointsize"<p> * in which <i>style</i> is one of the three * case-insensitive strings: * <code>"BOLD"</code>, <code>"BOLDITALIC"</code>, or * <code>"ITALIC"</code>, and pointsize is a decimal * representation of the point size. * For example, if you want a font that is Arial, bold, and * a point size of 18, you would call this method with: * "Arial-BOLD-18". * <p> * The default size is 12 and the default style is PLAIN. * If you don't specify a valid size, the returned * <code>Font</code> has a size of 12. If you don't specify * a valid style, the returned Font has a style of PLAIN. * If you do not provide a valid font family name in * the <code>str</code> argument, this method still returns * a valid font with a family name of "dialog". * To determine what font family names are available on * your system, use the * {@link GraphicsEnvironment#getAvailableFontFamilyNames()} method. * If <code>str</code> is <code>null</code>, a new <code>Font</code> * is returned with the family name "dialog", a size of 12 and a * PLAIN style. If <code>str</code> is <code>null</code>, * a new <code>Font</code> is returned with the name "dialog", a * size of 12 and a PLAIN style. * @param str the name of the font, or <code>null</code> * @return the <code>Font</code> object that <code>str</code> * describes, or a new default <code>Font</code> if * <code>str</code> is <code>null</code>. * @see #getFamily * @since JDK1.1 */ public static Font decode(String str) { String fontName = str; String styleName = ""; int fontSize = 12; int fontStyle = Font.PLAIN; if (str == null) { return new Font("Dialog", fontStyle, fontSize); } int lastHyphen = str.lastIndexOf('-'); int lastSpace = str.lastIndexOf(' '); char sepChar = (lastHyphen > lastSpace) ? '-' : ' '; int sizeIndex = str.lastIndexOf(sepChar); int styleIndex = str.lastIndexOf(sepChar, sizeIndex-1); int strlen = str.length(); if (sizeIndex > 0 && sizeIndex+1 < strlen) { try { fontSize = Integer.valueOf(str.substring(sizeIndex+1)).intValue(); if (fontSize <= 0) { fontSize = 12; } } catch (NumberFormatException e) { /* It wasn't a valid size, if we didn't also find the * start of the style string perhaps this is the style */ styleIndex = sizeIndex; sizeIndex = strlen; if (str.charAt(sizeIndex-1) == sepChar) { sizeIndex--; } } } if (styleIndex >= 0 && styleIndex+1 < strlen) { styleName = str.substring(styleIndex+1, sizeIndex); styleName = styleName.toLowerCase(Locale.ENGLISH); if (styleName.equals("bolditalic")) { fontStyle = Font.BOLD | Font.ITALIC; } else if (styleName.equals("italic")) { fontStyle = Font.ITALIC; } else if (styleName.equals("bold")) { fontStyle = Font.BOLD; } else if (styleName.equals("plain")) { fontStyle = Font.PLAIN; } else { /* this string isn't any of the expected styles, so * assume its part of the font name */ styleIndex = sizeIndex; if (str.charAt(styleIndex-1) == sepChar) { styleIndex--; } } fontName = str.substring(0, styleIndex); } else { int fontEnd = strlen; if (styleIndex > 0) { fontEnd = styleIndex; } else if (sizeIndex > 0) { fontEnd = sizeIndex; } if (fontEnd > 0 && str.charAt(fontEnd-1) == sepChar) { fontEnd--; } fontName = str.substring(0, fontEnd); } return new Font(fontName, fontStyle, fontSize); } /** * Gets the specified font from the system properties list. * The first argument is treated as the name of a system property to * be obtained as if by the method <code>System.getProperty</code>. * The string value of this property is then interpreted as a font. * <p> * The property value should be one of the following forms: * <ul> * <li><em>fontname-style-pointsize</em> * <li><em>fontname-pointsize</em> * <li><em>fontname-style</em> * <li><em>fontname</em> * </ul> * where <i>style</i> is one of the three strings * <code>"BOLD"</code>, <code>"BOLDITALIC"</code>, or * <code>"ITALIC"</code>, and point size is a decimal * representation of the point size. * <p> * The default style is <code>PLAIN</code>. The default point size * is 12. * <p> * If the specified property is not found, the <code>font</code> * argument is returned instead. * @param nm the property name * @param font a default font to return if property <code>nm</code> * is not defined * @return the <code>Font</code> value of the property. * @since JDK1.0 */ public static Font getFont(String nm, Font font) { String str = null; try { str = System.getProperty(nm); } catch(SecurityException e) { } if (str == null) { return font; } return decode(str); } /** * Returns a hashcode for this font. * @return a hashcode value for this font. * @since JDK1.0 */ public int hashCode() { return name.hashCode() ^ style ^ size; } /** * Compares this object to the specifed object. * The result is <code>true</code> if and only if the argument is not * <code>null</code> and is a <code>Font</code> object with the same * name, style, and point size as this font. * @param obj the object to compare this font with. * @return <code>true</code> if the objects are equal; * <code>false</code> otherwise. * @since JDK1.0 */ public boolean equals(Object obj) { if (obj instanceof Font) { Font font = (Font) obj; return (size == font.size) && (style == font.style) && name.equals(font.name); } return false; } /** * Converts this object to a String representation. * @return a string representation of this object * @since JDK1.0 */ public String toString() { String strStyle; if (isBold()) { strStyle = isItalic() ? "bolditalic" : "bold"; } else { strStyle = isItalic() ? "italic" : "plain"; } return getClass().getName() + "[family=" + family + ",name=" + name + ",style=" + strStyle + ",size=" + size + "]"; } /* Serialization support. A readObject method is neccessary because * the constructor creates the fonts peer, and we can't serialize the * peer. Similarly the computed font "family" may be different * at readObject time than at writeObject time. An integer version is * written so that future versions of this class will be able to recognize * serialized output from this one. */ private int fontSerializedDataVersion = 1; private void writeObject(java.io.ObjectOutputStream s) throws java.lang.ClassNotFoundException, java.io.IOException { s.defaultWriteObject(); } private void readObject(java.io.ObjectInputStream s) throws java.lang.ClassNotFoundException, java.io.IOException { s.defaultReadObject(); initializeFont(fRequestedAttributes); setFamily(); this.peer = ((PeerBasedToolkit) Toolkit.getDefaultToolkit()).getFontPeer(this); } private void setFamily() { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] names = ge.getAvailableFontFamilyNames(); if (names.length == 0) { family = "Default"; } else { String defaultFamilyName = null; for (int i = 0; i < names.length; i++) { if (names[i].equalsIgnoreCase(name)) { family = names[i]; break; } /* * Font.decode() specifies the following: * * The default size is 12 and the default style is PLAIN. * If you don't specify a valid size, the returned * <code>Font</code> has a size of 12. If you don't specify * a valid style, the returned Font has a style of PLAIN. * If you do not provide a valid font family name in * the <code>str</code> argument, this method still returns * a valid font with a family name of "dialog". */ if (names[i].equalsIgnoreCase("Dialog")) { defaultFamilyName = names[i]; } } /* * Note that the five logical fonts: Serif, SansSerif, Monospaced, * Dialog, and DialogInput must be supported by the Java runtime * environment. But just in case they are missing, set the family * to the first name. */ if (family == null) { family = (defaultFamilyName != null) ? defaultFamilyName : names[0]; } } } /** * Returns a map of font attributes available in this * <code>Font</code>. Attributes include things like ligatures and * glyph substitution. * @return the attributes map of this <code>Font</code>. */ public Map getAttributes(){ return (Map)fRequestedAttributes.clone(); } /** * Returns the keys of all the attributes supported by this * <code>Font</code>. These attributes can be used to derive other * fonts. * @return an array containing the keys of all the attributes * supported by this <code>Font</code>. * @since 1.2 */ public Attribute[] getAvailableAttributes(){ Attribute attributes[] = { TextAttribute.FAMILY, TextAttribute.WEIGHT, TextAttribute.POSTURE, TextAttribute.SIZE }; return attributes; } }