echopointng.util.FontKit Maven / Gradle / Ivy
Show all versions of ibis-echo2 Show documentation
package echopointng.util;
/*
* This file is part of the Echo Point Project. This project is a collection
* of Components that have extended the Echo Web Application Framework.
*
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*/
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import nextapp.echo2.app.Component;
import nextapp.echo2.app.Extent;
import nextapp.echo2.app.Font;
import echopointng.util.collections.ConcurrentReaderHashMap;
/**
* A utility to class to help with Font manipulation
*
* @author Brad Baker
*/
public class FontKit {
/* our static cache of Font objects by Font String representation */
private static Map fontMap = new ConcurrentReaderHashMap();
private static final Map FONTSTYLE_TEXT_TO_CONSTANT;
static {
Map constantMap = new HashMap();
constantMap.put("PLAIN", new Integer(Font.PLAIN));
constantMap.put("FONT.PLAIN", new Integer(Font.PLAIN));
constantMap.put("BOLD", new Integer(Font.BOLD));
constantMap.put("FONT.BOLD", new Integer(Font.BOLD));
constantMap.put("ITALIC", new Integer(Font.ITALIC));
constantMap.put("FONT.ITALIC", new Integer(Font.ITALIC));
constantMap.put("LINETHROUGH", new Integer(Font.LINE_THROUGH));
constantMap.put("FONT.LINETHROUGH", new Integer(Font.LINE_THROUGH));
constantMap.put("OVERLINE", new Integer(Font.OVERLINE));
constantMap.put("FONT.OVERLINE", new Integer(Font.OVERLINE));
constantMap.put("UNDERLINE", new Integer(Font.UNDERLINE));
constantMap.put("FONT.UNDERLINE", new Integer(Font.UNDERLINE));
FONTSTYLE_TEXT_TO_CONSTANT = Collections.unmodifiableMap(constantMap);
}
private static final Map TYPEFACE_TEXT_TO_CONSTANT;
static {
Map constantMap = new HashMap();
constantMap.put("HELVETICA", Font.HELVETICA);
constantMap.put("ARIAL", Font.ARIAL);
constantMap.put("VERDANA", Font.VERDANA);
constantMap.put("TIMES", Font.TIMES);
constantMap.put("TIMES ROMAN", Font.TIMES_ROMAN);
constantMap.put("TIMES NEW ROMAN", Font.TIMES_NEW_ROMAN);
constantMap.put("COURIER", Font.COURIER);
constantMap.put("COURIER NEW", Font.COURIER_NEW);
TYPEFACE_TEXT_TO_CONSTANT = Collections.unmodifiableMap(constantMap);
}
/** not instantiable */
private FontKit() {
}
/**
* Creates an AWT font object from a Echo font object. This is
* useful for drawing on AWT images.
*
* @param echoFont - the nextapp.echo2.app.Font object to convert
* @param awtDefaultFont - a default AWT font to use if the conversion cant be done.
* @return - a new java.awt.Font object base don echoFont.
*/
public static java.awt.Font makeAwtFont(Font echoFont, java.awt.Font awtDefaultFont) {
if (echoFont == null)
return awtDefaultFont;
else {
int size = echoFont.getSize() == null ? 12 : echoFont.getSize().getValue();
int style = 0;
if (echoFont.isBold())
style |= java.awt.Font.BOLD;
if (echoFont.isItalic())
style |= java.awt.Font.ITALIC;
if (echoFont.isUnderline());
if (style == 0)
style = java.awt.Font.PLAIN;
return new java.awt.Font(echoFont.getTypeface().getName(), style, size);
}
}
/**
* Makes a W3C CSS font string in the special format
*
* fontstyle, fontsize, fontnames
*
* @param font - the font to represent as a W3C CSS string
* @return the W3C CSS string represenation
*/
public static String makeCSSFont(Font font) {
StringBuffer sb = new StringBuffer();
if (font.isBold())
sb.append("bold ");
if (font.isItalic())
sb.append("italic ");
if (font.isUnderline())
sb.append("underline ");
sb.append(font.getSize());
sb.append(" ");
int i = 0;
Font.Typeface face = font.getTypeface();
while (face != null) {
if (i > 0)
sb.append(", ");
sb.append("'");
sb.append(face.getName());
sb.append("'");
face = face.getAlternate();
i++;
}
return sb.toString();
}
/**
* Adds bold to a Component's font.
*
* If the component has no font, a findFont
is performed and
* the result is set in as the font.
*
* @see FontKit#addBold(Font)
*
* @param c - the component in question
*/
public static void addBold(Component c) {
Font font = findFont(c);
font = addBold(font);
c.setFont(font);
}
/**
* Adds italic to a Component's font.
*
* If the component has no font, a findFont
is performed and
* the result is set in as the font.
*
* @see FontKit#addItalic(Font)
*
* @param c - the component in question
*/
public static void addItalic(Component c) {
Font font = findFont(c);
c.setFont(addItalic(font));
}
/**
* Adds underline to a Component's font.
*
* If the component has no font, a findFont
is performed and
* the result is set in as the font.
*
* @see FontKit#addUnderline(Font)
*
* @param c - the component in question
*/
public static void addUnderline(Component c) {
Font font = findFont(c);
c.setFont(addUnderline(font));
}
/**
* Adds the specified style
to a Component's font.
*
* If the component has no font, a findFont
is performed and
* the result is set in as the font.
*
* @see FontKit#addStyle(Font, int)
*
* @param c - the component in question
*/
public static void addStyle(Component c, int style) {
Font font = findFont(c);
c.setFont(addStyle(font,style));
}
/**
* Sets a component's font smaller or large by the specified
* delta amount.
*
* If the component has no font, a findFont
is performed and
* the result is set in as the font.
*
* @see FontKit#addSize(Font, int)
*
* @param c - the component in question
* @param sizeDelta - a positive or negative delta amount
*/
public static void addSize(Component c, int sizeDelta) {
Font font = findFont(c);
c.setFont(addSize(font,sizeDelta));
}
/**
* Sets Component's font to bold.
*
* If the component has no font, a findFont
is performed and
* the result is set in as the font.
*
* @see FontKit#setStyle(Font, int)
*
* @param c - the component in question
*/
public static void setBold(Component c) {
Font font = findFont(c);
c.setFont(setStyle(font, Font.BOLD));
}
/**
* Sets Component's font to italic.
*
* If the component has no font, a findFont
is performed and
* the result is set in as the font.
*
* @see FontKit#setStyle(Font, int)
*
* @param c - the component in question
*/
public static void setItalic(Component c) {
Font font = findFont(c);
c.setFont(setStyle(font, Font.ITALIC));
}
/**
* Sets Component's font to underline.
*
* If the component has no font, a findFont
is performed and
* the result is set in as the font.
*
* @see FontKit#setStyle(Font, int)
*
* @param c - the component in question
*/
public static void setUnderline(Component c) {
Font font = findFont(c);
c.setFont(setStyle(font, Font.UNDERLINE));
}
/**
* Sets the size of a Component's font.
*
* If the component has no font, a findFont
is performed and
* the result is set in as the font.
*
* @see FontKit#setSize(Font, int)
*
* @param c - the component in question
* @param size - the new font size
*/
public static void setSize(Component c, int size) {
Font font = findFont(c);
c.setFont(addSize(font,size));
}
/**
* Searchs the heirarchy tree of the component and finds the first
* non null Font object. It will return Font(Font.SANS_SERIF,Font.PLAIN,new Extent(10,Extent.PT)
* if no ancestor components have a Font set.
*
*/
public static Font findFont(Component comp) {
while (comp != null) {
Font fnt = comp.getFont();
if (fnt != null)
return fnt;
comp = comp.getParent();
}
return new Font(Font.SANS_SERIF,Font.PLAIN,new Extent(10,Extent.PT));
}
/**
* Returns the Font value of the given Font string representation.
*
* The allowable forms are :
* - fontName,fontStyle,fontSize
* - font( fontName, fontStyle, fontSize)
*
* where
*
* fontName - eg. Verdana or 'Times New Roman'.
* If multiple font names are specified or there is
* white space in the font name, then it must be enclosed
* in single quotes and commas eg. 'Verdana, Times New Roman, Tahoma'.
*
* fontStyle - PLAIN|BOLD|ITALIC|UNDERLINE|OVERLINE|LINETHROUGH or nothing! (this is case insenstive)
*
* fontSize - an integer size value or an Extent value. (In the case where
* only an integer is specified, the default Extent units
* are points (pt).
*
* Examples :
*
* Verdana,,9 - is a legal font string
* Verdana,PLAIN,9 - is a legal font string
* Verdana,plain,9 - is a legal font string
* Verdana,plain,9em - is a legal font string
* Verdana,plain,9pt - is a legal font string
* Verdana,bold|italic,9 - is a legal font string
* 'Times New Roman',plain,9 - is a legal font string
* 'Verdana, Times New Roman',plain,9 - is a legal font string
*
* font(Verdana,,9) - is a legal font string
* font(Verdana,PLAIN,9pt) - is a legal font string
* font(Verdana,plain,9) - is a legal font string
* font(Verdana,bold|italic,9em) - is a legal font string
* font('Verdana, Times New Roman',plain,9) - is a legal font string
*
* Verdana, 3, 9 - is an ILLEGAL font string
* Verdana, Times New Roman,PLAIN,9 - is an ILLEGAL font string
* font(Verdana, 3, 9) - is an ILLEGAL font string
* font(Verdana, Times New Roman,PLAIN,9) - is an ILLEGAL font string
*
*
* The results of this operations is cached in a global static
* cache, so that Font objects can be re-used. This is okay
* since Font objects are immutable once created.
*
* @param fontString - the font string in question
* @return a new Font object
* @throws IllegalArgumentException - if the fontString is in an invalid format
*/
public static Font makeFont(String fontString) {
if (fontString == null)
throw new IllegalArgumentException("The fontString must be non null");
Font font = _getCachedFont(fontString);
if (font != null)
return font;
if (! isFont(fontString))
throw new IllegalArgumentException("The font string is invalid : " + fontString);
int index = 0;
String tokens[] = TokenizerKit.tokenize(fontString, "(,)|");
if (fontString.indexOf("font(") == 0)
index = 1;
// font names
String fontName = tokens[index].trim();
Font.Typeface fontNameVal = makeTypeface(fontName);
// font styles
int fontStyleVal = 0;
String fontStyleString = tokens[++index];
for (int i = index; i < tokens.length; i++) {
fontStyleString = tokens[i].trim();
if (_isInteger(fontStyleString) || ExtentKit.isExtent(fontStyleString))
break;
String arr[] = TokenizerKit.tokenize(fontStyleString,"|");
for (int j = 0; j < arr.length; j++) {
fontStyleString = arr[j].trim();
int k = _getFontStyle(fontStyleString);
if (k >= 0) {
fontStyleVal |= k;
}
}
}
// font size
String fontSize = fontStyleString;
Font newFont;
if (ExtentKit.isExtent(fontSize)) {
newFont = new Font(fontNameVal, fontStyleVal, ExtentKit.makeExtent(fontSize));
} else {
int fontSizeVal = Integer.parseInt(fontSize);
newFont = new Font(fontNameVal, fontStyleVal, new Extent(fontSizeVal,Extent.PT));
}
return _putCachedFont(fontString, newFont);
}
/**
* Shortcut synonym for makeFont(fontString);
*
* @see FontKit#makeFont(String)
*/
public static Font font(String fontString) {
return makeFont(fontString);
}
/**
* Returns true if the fontString
is a valid
* representation of a Font value.
*
* The allowable forms are :
* - fontName,fontStyle,fontSize
* - font( fontName, fontStyle, fontSize)
*
* where
*
* fontName - eg. Verdana or 'Times New Roman'.
* If multiple font names are specified or there is
* white space in the font name, then it must be enclosed
* in single quotes and commas eg. 'Verdana, Times New Roman, Tahoma'.
*
* fontStyle - PLAIN|BOLD|ITALIC|UNDERLINE|OVERLINE|LINETHROUGH or nothing! (this is case insenstive)
*
* fontSize - an integer size value or an Extent value. (In the case where
* only an integer is specified, the default Extent units
* are points (pt).
*
* Examples :
*
* Verdana,,9 - is a legal font string
* Verdana,PLAIN,9 - is a legal font string
* Verdana,plain,9 - is a legal font string
* Verdana,bold|italic,9 - is a legal font string
* 'Times New Roman',plain,9 - is a legal font string
* 'Verdana, Times New Roman',plain,9 - is a legal font string
*
* font(Verdana,,9) - is a legal font string
* font(Verdana,PLAIN,9) - is a legal font string
* font(Verdana,plain,9) - is a legal font string
* font(Verdana,bold|italic,9) - is a legal font string
* font('Verdana, Times New Roman',plain,9) - is a legal font string
*
* Verdana, 3, 9 - is an ILLEGAL font string
* Verdana, Times New Roman,PLAIN,9 - is an ILLEGAL font string
* font(Verdana, 3, 9) - is an ILLEGAL font string
* font(Verdana, Times New Roman,PLAIN,9) - is an ILLEGAL font string
*
*
* @param fontString - the font string representation to check
* @return true if the string is in the correct format
*/
public static boolean isFont(String fontString) {
if (fontString == null)
return false;
int index = 0;
String tokens[] = TokenizerKit.tokenize(fontString, "(,)|");
if (fontString.indexOf("font(") == 0)
index = 1;
if (tokens.length < 2)
return false;
// font name is ok. Technically anything is OK
// font styles
String font = tokens[++index];
for (;index < tokens.length; index++) {
font = tokens[index].trim();
if (_isInteger(font) || ExtentKit.isExtent(font))
break;
String arr[] = TokenizerKit.tokenize(font,"|");
for (int i = 0; i < arr.length; i++) {
font = arr[i].trim();
if (_getFontStyle(font) == -1) {
return false;
}
}
}
// font size
String fontSize = font;
if (! ExtentKit.isExtent(fontSize)) {
if (!_isInteger(fontSize))
return false;
}
if (index != tokens.length-1)
return false;
return true;
}
/**
* This will return a Font.Typeface object by first rtying to match
* the names against System provided ones and failing that it will
* create a font.Typeface for you.
*
* Multi named type faces must be encluded in spaces.
*
* Valid typeface name forms are :
*
* 'Font1, Font 2, Font2'
* 'Font1'
* Font1
*
*
* @param typeFaceNames - the name(s) of the intended typeface
*/
public static Font.Typeface makeTypeface(String typeFaceNames) {
if (typeFaceNames.charAt(0) == '\'' && typeFaceNames.charAt(typeFaceNames.length()-1) == '\'') {
typeFaceNames = typeFaceNames.substring(1,typeFaceNames.length()-1);
}
String[] tokens = TokenizerKit.tokenizeStrict(typeFaceNames,",");
if (tokens.length == 0)
return null;
Font.Typeface typeFace;
typeFace = getSystemTypeface(tokens[0].toUpperCase());
//
// if we have only one token and it matches a System
// Typeface, then we will go with it
if (tokens.length == 1 && typeFace != null) {
return typeFace;
} else {
if (_exactMatchingTypeFace(typeFace,tokens)) {
return typeFace;
} else {
typeFace = null;
for (int i = tokens.length-1; i >= 0; i--) {
typeFace = new Font.Typeface(tokens[i],typeFace);
}
return typeFace;
}
}
}
/*
* Returns true if the type face provided exactly matches the list of font names
*/
private static boolean _exactMatchingTypeFace(Font.Typeface typeFace, String[] fontNames) {
if (typeFace == null)
return false;
int i = 0;
while (typeFace != null) {
if (i >= fontNames.length)
return false;
if (! fontNames[i].equalsIgnoreCase(typeFace.getName())) {
return false;
}
typeFace = typeFace.getAlternate();
i++;
}
return true;
}
/*
* Gets a Font from the cache if its matches the font string
* It may be null if is not been cached. The strings are
* always stored in lower case for consistency
*/
private static Font _getCachedFont(String fontString) {
fontString = fontString.trim().toLowerCase();
Font font = (Font) fontMap.get(fontString);
return font;
}
/*
* Puts a Font into the shared cache and then returns it
*/
private static Font _putCachedFont(String fontString, Font font) {
fontString = fontString.trim().toLowerCase();
fontMap.put(fontString, font);
return font;
}
/*
* Well is it an integer?
*/
private static boolean _isInteger(String propertyValue) {
try {
Integer.parseInt(propertyValue.trim());
} catch (NumberFormatException nfe) {
return false;
}
return true;
}
/**
* Makes a string representation of a font in the format
*
* font(fontName, fontStyle, fontSize)
*
* where :
*
* fontName - is a font name such as 'Verdana' or 'Times New Roman'.
* fontStyle - PLAIN|BOLD|ITALIC|UNDERLINE
* fontSize - an integer size value or an Extent value. (In the case where
* only an integer is specified, the default Extent units
* are points (pt).
*
* @param font - the font to make into a String representation
* @return the string representation of the font
*/
public static String makeFontString(Font font) {
return _buildFontString(font.getTypeface(),_getStyle(font),font.getSize());
}
/**
* Returns one the standard Echo Font.Typefaces if the name string
* matches the first Typeface in the chain.
*
* @param name - the font typeface name to match.
* @return a TypeFace of null if a system one cannot be found
*/
public static Font.Typeface getSystemTypeface(String name) {
if (TYPEFACE_TEXT_TO_CONSTANT.containsKey(name.toUpperCase())) {
return (Font.Typeface) TYPEFACE_TEXT_TO_CONSTANT.get(name.toUpperCase());
} else {
return null;
}
}
/*
* Returns a font style integer value of -1 if it cant be found
*/
private static int _getFontStyle(String styleName) {
Integer styleInt = (Integer) FONTSTYLE_TEXT_TO_CONSTANT.get(styleName.toUpperCase());
if (styleInt == null)
return -1;
return styleInt.intValue();
}
/*
* Builds a Font string from the given font attributes, This
* can then be used for font caching etc.. as well as returned
* as a Font String representation
* */
private static String _buildFontString(Font.Typeface typeFace, int style, Extent sizeExtent) {
StringBuffer sb = new StringBuffer();
sb.append("font(");
int i = 0;
while (typeFace != null) {
if (i > 0)
sb.append(", ");
else
sb.append("'");
sb.append(typeFace.getName());
i++;
typeFace = typeFace.getAlternate();
if (typeFace == null)
sb.append("'");
}
boolean isplain = true;
boolean needsOr = false;
sb.append(",");
if ((style & Font.BOLD) == Font.BOLD) {
if (needsOr) sb.append("|");
sb.append("BOLD");
needsOr = true;
isplain = false;
}
if ((style & Font.ITALIC) == Font.ITALIC) {
if (needsOr) sb.append("|");
sb.append("ITALIC");
needsOr = true;
isplain = false;
}
if ((style & Font.UNDERLINE) == Font.UNDERLINE) {
if (needsOr) sb.append("|");
sb.append("UNDERLINE");
needsOr = true;
isplain = false;
}
if ((style & Font.OVERLINE) == Font.OVERLINE) {
if (needsOr) sb.append("|");
sb.append("OVERLINE");
needsOr = true;
isplain = false;
}
if ((style & Font.LINE_THROUGH) == Font.LINE_THROUGH) {
if (needsOr) sb.append("|");
sb.append("LINETHROUGH");
needsOr = true;
isplain = false;
}
if (isplain)
sb.append("PLAIN");
if (sizeExtent != null) {
sb.append(",");
sb.append(sizeExtent.toString());
}
sb.append(")");
return sb.toString();
}
/*
* Builds a Font object from a valid font string, which is
* then cached in our global Font cache.
*/
private static Font _buildFontObj(Font.Typeface typeface, int style, Extent sizeExtent) {
String fontString = _buildFontString(typeface,style,sizeExtent);
return makeFont(fontString);
}
/*
* Adds style bits to an exisitng font
*/
static private int _addFontStyle(Font font, int newStyle) {
int style = Font.PLAIN;
if (font.isBold())
style |= Font.BOLD;
if (font.isItalic())
style |= Font.ITALIC;
if (font.isUnderline())
style |= Font.UNDERLINE;
style |= newStyle;
return style;
}
/*
* Returns the style bits of the font.
*/
static private int _getStyle(Font font) {
return _addFontStyle(font,0);
}
/**
* Adds the Font.BOLD attribute to an exisitng Font object
* and returns a new Font.
*
* @param font - the font in question
* @return - a new Font object
*/
public static Font addBold(Font font) {
return addStyle(font,Font.BOLD);
}
/**
* Adds the Font.ITALIC attribute to an exisitng Font object
* and returns a new Font.
*
* @param font - the font in question
* @return - a new Font object
*/
public static Font addItalic(Font font) {
if (font == null)
return null;
return addStyle(font,Font.ITALIC);
}
/**
* Adds the Font.UNDERLINE attribute to an exisitng Font object
* and returns a new Font.
*
* @param font - the font in question
* @return - a new Font object
*/
public static Font addUnderline(Font font) {
return addStyle(font,Font.UNDERLINE);
}
/**
* Adds the specified style
to the Font
* and returns a new Font.
*
* @param font - the font in question
* @param style - the new font style to added to the font
* @return - a new Font object
*/
public static Font addStyle(Font font, int style) {
if (font == null)
return null;
return _buildFontObj(font.getTypeface(),_addFontStyle(font, style),font.getSize());
}
/**
* Makes a font smaller or larger by adding the specified amount to
* its current font size. The Font is made smaller if a negative
* value is used.
*
* @param font - the font in question
* @param sizeDelta - the size delta to add to the current size
* @return - a new Font object
*
*/
public static Font addSize(Font font, int sizeDelta) {
if (font == null)
return null;
Extent now = font.getSize();
if (now != null)
now = new Extent(now.getValue()+sizeDelta, now.getUnits());
return _buildFontObj(font.getTypeface(),_getStyle(font),now);
}
/**
* Returns a new Font object, based on font
, that
* has the specified size
.
*
* @param font - the font to use as a template
* @param size - the new size
* @return a new Font object with the new size
*/
public static Font setSize(Font font, int size) {
if (font == null)
return null;
Extent now = font.getSize();
if (now != null)
now = new Extent(size, now.getUnits());
return _buildFontObj(font.getTypeface(),_getStyle(font),now);
}
/**
* Returns a new Font object, based on font
, that
* has the specified style
.
*
* @param font - the font to use as a template
* @param style - the new style
* @return a new Font object with the new style
*/
public static Font setStyle(Font font, int style) {
if (font == null)
return null;
return _buildFontObj(font.getTypeface(),style,font.getSize());
}
/**
* Returns a new Font object, based on font
, that
* has the specified typeFace
.
*
* @param font - the font to use as a template
* @param typeFace - the new TypeFace to use
* @return a new Font object with the new font names
*/
public static Font setNames(Font font, Font.Typeface typeFace) {
if (font == null)
return null;
return _buildFontObj(typeFace,_getStyle(font),font.getSize());
}
/**
* Returns a new Font object, based on font
, that
* has the specified fontName
.
*
* If fontName
has whitespace in it, then
* you need to surround it in single quotes.
*
* @param font - the font to use as a template
* @param fontName - the new font name to use
* @return a new Font object with the new font name
*/
public static Font setName(Font font, String fontName) {
if (font == null)
return null;
Font.Typeface typeFace = makeTypeface(fontName);
return _buildFontObj(typeFace,_getStyle(font),font.getSize());
}
}