001    package echopoint.util;
002    /* 
003     * This file is part of the Echo Point Project.  This project is a collection
004     * of Components that have extended the Echo Web Application Framework.
005     *
006     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
007     *
008     * The contents of this file are subject to the Mozilla Public License Version
009     * 1.1 (the "License"); you may not use this file except in compliance with
010     * the License. You may obtain a copy of the License at
011     * http://www.mozilla.org/MPL/
012     *
013     * Software distributed under the License is distributed on an "AS IS" basis,
014     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
015     * for the specific language governing rights and limitations under the
016     * License.
017     *
018     * Alternatively, the contents of this file may be used under the terms of
019     * either the GNU General Public License Version 2 or later (the "GPL"), or
020     * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
021     * in which case the provisions of the GPL or the LGPL are applicable instead
022     * of those above. If you wish to allow use of your version of this file only
023     * under the terms of either the GPL or the LGPL, and not to allow others to
024     * use your version of this file under the terms of the MPL, indicate your
025     * decision by deleting the provisions above and replace them with the notice
026     * and other provisions required by the GPL or the LGPL. If you do not delete
027     * the provisions above, a recipient may use your version of this file under
028     * the terms of any one of the MPL, the GPL or the LGPL.
029     */
030    
031    import java.util.Collections;
032    import java.util.HashMap;
033    import java.util.Map;
034    
035    import nextapp.echo.app.Component;
036    import nextapp.echo.app.Extent;
037    import nextapp.echo.app.Font;
038    
039    import echopoint.util.collections.ConcurrentReaderHashMap;
040    
041    /**
042     * A utility to class to help with Font manipulation
043     *
044     * @author Brad Baker 
045     */
046    public class FontKit {
047    
048            /* our static cache of Font objects by Font String representation */ 
049            private static Map fontMap = new ConcurrentReaderHashMap();
050            
051       private static final Map FONTSTYLE_TEXT_TO_CONSTANT;
052        static {
053            Map constantMap = new HashMap();
054            constantMap.put("PLAIN", new Integer(Font.PLAIN));
055            constantMap.put("FONT.PLAIN", new Integer(Font.PLAIN));
056            constantMap.put("BOLD", new Integer(Font.BOLD));
057            constantMap.put("FONT.BOLD", new Integer(Font.BOLD));
058            constantMap.put("ITALIC", new Integer(Font.ITALIC));
059            constantMap.put("FONT.ITALIC", new Integer(Font.ITALIC));
060            constantMap.put("LINETHROUGH", new Integer(Font.LINE_THROUGH));
061            constantMap.put("FONT.LINETHROUGH", new Integer(Font.LINE_THROUGH));
062            constantMap.put("OVERLINE", new Integer(Font.OVERLINE));
063            constantMap.put("FONT.OVERLINE", new Integer(Font.OVERLINE));
064            constantMap.put("UNDERLINE", new Integer(Font.UNDERLINE));
065            constantMap.put("FONT.UNDERLINE", new Integer(Font.UNDERLINE));
066            FONTSTYLE_TEXT_TO_CONSTANT = Collections.unmodifiableMap(constantMap);
067        }
068        
069    
070        private static final Map TYPEFACE_TEXT_TO_CONSTANT;
071        static {
072            Map constantMap = new HashMap();
073            constantMap.put("HELVETICA", Font.HELVETICA);
074            constantMap.put("ARIAL", Font.ARIAL);
075            constantMap.put("VERDANA", Font.VERDANA);
076            constantMap.put("TIMES", Font.TIMES);
077            constantMap.put("TIMES ROMAN", Font.TIMES_ROMAN);
078            constantMap.put("TIMES NEW ROMAN", Font.TIMES_NEW_ROMAN);
079            constantMap.put("COURIER", Font.COURIER);
080            constantMap.put("COURIER NEW", Font.COURIER_NEW);
081            TYPEFACE_TEXT_TO_CONSTANT = Collections.unmodifiableMap(constantMap);
082        }
083    
084        
085            /** not instantiable */
086            private FontKit() {
087            }
088            
089            /**
090             * Creates an AWT font object from a Echo font object.  This is
091             * useful for drawing on AWT images.
092             * 
093             * @param echoFont - the nextapp.echo2.app.Font object to convert
094             * @param awtDefaultFont - a default AWT font to use if the conversion cant be done.
095             * @return - a new java.awt.Font object base don echoFont.  
096             */
097            public static java.awt.Font makeAwtFont(Font echoFont, java.awt.Font awtDefaultFont) {
098                    if (echoFont == null)
099                            return awtDefaultFont;
100                    else {
101                            int size = echoFont.getSize() == null ? 12 : echoFont.getSize().getValue();
102                            int style = 0;
103                            if (echoFont.isBold())
104                                    style |= java.awt.Font.BOLD;
105                            if (echoFont.isItalic())
106                                    style |= java.awt.Font.ITALIC;
107                            if (echoFont.isUnderline());
108                            if (style == 0)
109                                    style = java.awt.Font.PLAIN;
110    
111                            return new java.awt.Font(echoFont.getTypeface().getName(), style, size);
112                    }
113            }
114            
115            /**
116             * Makes a W3C CSS font string in the special format
117             * <p>
118             * <i>fontstyle</i>, <i>fontsize</i>, <i>fontnames</i>
119             * <p>
120             * @param font - the font to represent as a W3C CSS string
121             * @return the W3C CSS string represenation
122             */
123            public static String makeCSSFont(Font font) {
124                    StringBuffer sb = new StringBuffer();
125                    if (font.isBold())
126                            sb.append("bold ");
127                    if (font.isItalic())
128                            sb.append("italic ");
129                    if (font.isUnderline())
130                            sb.append("underline ");
131    
132                    sb.append(font.getSize());
133                    sb.append(" ");
134                    
135                    int i = 0;
136                    Font.Typeface face = font.getTypeface();
137                    while (face != null) {
138                            if (i > 0)
139                                    sb.append(", ");
140                            sb.append("'");
141                            sb.append(face.getName());      
142                            sb.append("'");
143                            face = face.getAlternate();
144                            i++;
145                    }
146                    return sb.toString();   
147            }
148            
149            /**
150             * Adds bold to a Component's font.
151             * <p>
152             * If the component has no font, a <code>findFont</code> is performed and
153             * the result is set in as the font.
154             * <p>
155             * @see FontKit#addBold(Font)
156             * 
157             * @param c - the component in question
158             */
159            public static void addBold(Component c) {
160                    Font font = findFont(c);
161                    font = addBold(font);
162                    c.setFont(font);
163            }
164    
165            /**
166             * Adds italic to a Component's font.
167             * <p>
168             * If the component has no font, a <code>findFont</code> is performed and
169             * the result is set in as the font.
170             * <p>
171             * @see FontKit#addItalic(Font)
172             * 
173             * @param c - the component in question
174             */
175            public static void addItalic(Component c) {
176                    Font font = findFont(c);
177                    c.setFont(addItalic(font));     
178            }
179    
180            /**
181             * Adds underline to a Component's font.
182             * <p>
183             * If the component has no font, a <code>findFont</code> is performed and
184             * the result is set in as the font.
185             * <p>
186             * @see FontKit#addUnderline(Font)
187             * 
188             * @param c - the component in question
189             */
190            public static void addUnderline(Component c) {
191                    Font font = findFont(c);
192                    c.setFont(addUnderline(font));  
193            }
194    
195            /**
196             * Adds the specified <code>style</code> to a Component's font.
197             * <p>
198             * If the component has no font, a <code>findFont</code> is performed and
199             * the result is set in as the font.
200             * <p>
201             * @see FontKit#addStyle(Font, int)
202             * 
203             * @param c - the component in question
204             */
205            public static void addStyle(Component c, int style) {
206                    Font font = findFont(c);
207                    c.setFont(addStyle(font,style));        
208            }
209    
210            /**
211             * Sets a component's font smaller or large by the specified 
212             * delta amount.
213             * <p>
214             * If the component has no font, a <code>findFont</code> is performed and
215             * the result is set in as the font.
216             * <p>
217             * @see FontKit#addSize(Font, int)
218             * 
219             * @param c - the component in question
220             * @param sizeDelta - a positive or negative delta amount
221             */
222            public static void addSize(Component c, int sizeDelta) {
223                    Font font = findFont(c);
224                    c.setFont(addSize(font,sizeDelta));
225            }
226    
227            /**
228             * Sets Component's font to bold.
229             * <p>
230             * If the component has no font, a <code>findFont</code> is performed and
231             * the result is set in as the font.
232             * <p>
233             * @see FontKit#setStyle(Font, int)
234             * 
235             * @param c - the component in question
236             */
237            public static void setBold(Component c) {
238                    Font font = findFont(c);
239                    c.setFont(setStyle(font, Font.BOLD));   
240            }
241    
242            /**
243             * Sets Component's font to italic.
244             * <p>
245             * If the component has no font, a <code>findFont</code> is performed and
246             * the result is set in as the font.
247             * <p>
248             * @see FontKit#setStyle(Font, int)
249             * 
250             * @param c - the component in question
251             */
252            public static void setItalic(Component c) {
253                    Font font = findFont(c);
254                    c.setFont(setStyle(font, Font.ITALIC)); 
255            }
256    
257            /**
258             * Sets Component's font to underline.
259             * <p>
260             * If the component has no font, a <code>findFont</code> is performed and
261             * the result is set in as the font.
262             * <p>
263             * @see FontKit#setStyle(Font, int)
264             * 
265             * @param c - the component in question
266             */
267            public static void setUnderline(Component c) {
268                    Font font = findFont(c);
269                    c.setFont(setStyle(font, Font.UNDERLINE));      
270            }
271            
272    
273            /**
274             * Sets the size of a Component's font.
275             * <p>
276             * If the component has no font, a <code>findFont</code> is performed and
277             * the result is set in as the font.
278             * <p>
279             * @see FontKit#setSize(Font, int)
280             * 
281             * @param c - the component in question
282             * @param size - the new font size
283             */
284            public static void setSize(Component c, int size) {
285                    Font font = findFont(c);
286                    c.setFont(addSize(font,size));
287            }
288    
289            
290    
291            /**
292             * Searchs the heirarchy tree of the component and finds the first
293             * non null Font object.  It will return <code>Font(Font.SANS_SERIF,Font.PLAIN,new Extent(10,Extent.PT)</code> 
294             * if no ancestor components have a Font set.
295             * 
296             */
297            public static Font findFont(Component comp) {
298                    while (comp != null) {
299                            Font fnt = comp.getFont();
300                            if (fnt != null)
301                                    return fnt;
302                            comp = comp.getParent();
303                    }
304                    return new Font(Font.SANS_SERIF,Font.PLAIN,new Extent(10,Extent.PT));
305            }
306    
307            /**
308             * Returns the Font value of the given Font string representation.  
309             * <p>
310             * The allowable forms are : <p>
311             *       - fontName,fontStyle,fontSize<br>
312             *       - font( fontName, fontStyle, fontSize)<br>
313             * <p>
314             * where 
315             * <p>
316             * fontName  -  eg. Verdana or 'Times New Roman'.
317             *                              If multiple font names are specified or there is
318             *                              white space in the font name, then it must be enclosed
319             *                              in single quotes and commas eg. 'Verdana, Times New Roman, Tahoma'.
320             * <br>  
321             * fontStyle - PLAIN|BOLD|ITALIC|UNDERLINE|OVERLINE|LINETHROUGH or nothing! (this is case insenstive)
322             * <br>
323             * fontSize - an integer size value or an Extent value.  (In the case where
324             *                              only an integer is specified, the default Extent units 
325             *                              are points (pt).
326             * <p>
327             * Examples :<br>
328             * <blockquote><pre>
329             *      Verdana,,9                                                                      - is a legal font string
330             *      Verdana,PLAIN,9                                                         - is a legal font string
331             *      Verdana,plain,9                                                         - is a legal font string
332             *      Verdana,plain,9em                                                       - is a legal font string
333             *      Verdana,plain,9pt                                                       - is a legal font string
334             *      Verdana,bold|italic,9                                           - is a legal font string
335             *      'Times New Roman',plain,9                                       - is a legal font string
336             *      'Verdana, Times New Roman',plain,9                      - is a legal font string
337             * 
338             *      font(Verdana,,9)                                                        - is a legal font string
339             *      font(Verdana,PLAIN,9pt)                                         - is a legal font string
340             *      font(Verdana,plain,9)                                           - is a legal font string
341             *      font(Verdana,bold|italic,9em)                           - is a legal font string
342             *      font('Verdana, Times New Roman',plain,9)        - is a legal font string
343             * 
344             *      Verdana, 3, 9                                                           - is an ILLEGAL font string
345             *      Verdana, Times New Roman,PLAIN,9                        - is an ILLEGAL font string
346             *      font(Verdana, 3, 9)                                                     - is an ILLEGAL font string
347             *      font(Verdana, Times New Roman,PLAIN,9)          - is an ILLEGAL font string
348             * </pre></blockquote>
349             * <p>
350             * The results of this operations is cached in a global static
351             * cache, so that Font objects can be re-used.  This is okay 
352             * since Font objects are immutable once created. 
353             * 
354             * @param fontString - the font string in question
355             * @return a new Font object     
356             * @throws IllegalArgumentException - if the fontString is in an invalid format
357             */     
358            public static Font makeFont(String fontString) {
359                    if (fontString == null)
360                            throw new IllegalArgumentException("The fontString must be non null");
361                                            
362                    Font font = _getCachedFont(fontString);
363                    if (font != null)
364                            return font;
365                    
366                    if (! isFont(fontString))
367                            throw new IllegalArgumentException("The font string is invalid : " + fontString);
368                            
369                    int index = 0;
370                    String tokens[] = TokenizerKit.tokenize(fontString, "(,)|");
371                    if (fontString.indexOf("font(") == 0)
372                            index = 1;
373                    
374                    // font names
375                    String fontName = tokens[index].trim();
376                    Font.Typeface fontNameVal = makeTypeface(fontName);
377    
378                    // font styles
379                    int fontStyleVal = 0;
380                    String fontStyleString = tokens[++index];
381                    for (int i = index; i < tokens.length; i++) {
382                            fontStyleString = tokens[i].trim();
383                            if (_isInteger(fontStyleString) || ExtentKit.isExtent(fontStyleString))
384                                    break;
385                            String arr[] = TokenizerKit.tokenize(fontStyleString,"|");
386                            for (int j = 0; j < arr.length; j++) {
387                                    fontStyleString = arr[j].trim();
388                                    int k = _getFontStyle(fontStyleString);
389                                    if (k >= 0) {
390                                            fontStyleVal |= k;
391                                    }
392                            }
393                    }
394    
395                    // font size
396                    String fontSize = fontStyleString;
397                    Font newFont;
398                    if (ExtentKit.isExtent(fontSize)) {
399                            newFont = new Font(fontNameVal, fontStyleVal, ExtentKit.makeExtent(fontSize));
400                    } else {
401                            int fontSizeVal = Integer.parseInt(fontSize);
402                            newFont = new Font(fontNameVal, fontStyleVal, new Extent(fontSizeVal,Extent.PT));
403                    }
404                    return _putCachedFont(fontString, newFont);
405            }
406            
407            /**
408             * Shortcut synonym for makeFont(fontString);
409             * <p>
410             * @see FontKit#makeFont(String)
411             */
412            public static Font font(String fontString) {
413                    return makeFont(fontString);
414            }
415            
416            /**
417             * Returns true if the <code>fontString</code> is a valid 
418             * representation of a Font value.
419             * <p>
420             * The allowable forms are : <p>
421             *       - fontName,fontStyle,fontSize<br>
422             *       - font( fontName, fontStyle, fontSize)<br>
423             * <p>
424             * where 
425             * <p>
426             * fontName  -  eg. Verdana or 'Times New Roman'.
427             *                              If multiple font names are specified or there is
428             *                              white space in the font name, then it must be enclosed
429             *                              in single quotes and commas eg. 'Verdana, Times New Roman, Tahoma'.
430             * <br>  
431             * fontStyle - PLAIN|BOLD|ITALIC|UNDERLINE|OVERLINE|LINETHROUGH or nothing! (this is case insenstive)
432             * <br>
433             * fontSize - an integer size value or an Extent value.  (In the case where
434             *                              only an integer is specified, the default Extent units 
435             *                              are points (pt).
436             * <p>
437             * Examples :<br>
438             * <blockquote><pre>
439             *      Verdana,,9                                                                      - is a legal font string
440             *      Verdana,PLAIN,9                                                         - is a legal font string
441             *      Verdana,plain,9                                                         - is a legal font string
442             *      Verdana,bold|italic,9                                           - is a legal font string
443             *      'Times New Roman',plain,9                                       - is a legal font string
444             *      'Verdana, Times New Roman',plain,9                      - is a legal font string
445             * 
446             *      font(Verdana,,9)                                                        - is a legal font string
447             *      font(Verdana,PLAIN,9)                                           - is a legal font string
448             *      font(Verdana,plain,9)                                           - is a legal font string
449             *      font(Verdana,bold|italic,9)                                     - is a legal font string
450             *      font('Verdana, Times New Roman',plain,9)        - is a legal font string
451             * 
452             *      Verdana, 3, 9                                                           - is an ILLEGAL font string
453             *      Verdana, Times New Roman,PLAIN,9                        - is an ILLEGAL font string
454             *      font(Verdana, 3, 9)                                                     - is an ILLEGAL font string
455             *      font(Verdana, Times New Roman,PLAIN,9)          - is an ILLEGAL font string
456             * </pre></blockquote>
457             *  
458             * @param fontString - the font string representation to check
459             * @return true if the string is in the correct format
460             */
461            public static boolean isFont(String fontString) {
462                    if (fontString == null)
463                            return false;
464                    
465                    int index = 0;
466                    String tokens[] = TokenizerKit.tokenize(fontString, "(,)|");
467                    if (fontString.indexOf("font(") == 0)
468                            index = 1;
469    
470                    if (tokens.length < 2)
471                            return false;
472    
473                    // font name is ok.  Technically anything is OK
474                    
475                    // font styles
476                    String font = tokens[++index];
477                    for (;index < tokens.length; index++) {
478                            font = tokens[index].trim();
479                            if (_isInteger(font) || ExtentKit.isExtent(font))
480                                    break;
481                            String arr[] = TokenizerKit.tokenize(font,"|");
482                            for (int i = 0; i < arr.length; i++) {
483                                    font = arr[i].trim();
484                                    if (_getFontStyle(font) == -1) {
485                                            return false;
486                                    }
487                            }
488                    }
489                    // font size
490                    String fontSize = font;
491                    if (! ExtentKit.isExtent(fontSize)) {
492                            if (!_isInteger(fontSize))
493                                    return false;
494                    }
495    
496                    if (index != tokens.length-1)
497                            return false;
498                            
499                    return true;
500            }
501            
502            /**
503             * This will return a Font.Typeface object by first rtying to match
504             * the names against System provided ones and failing that it will
505             * create a font.Typeface for you. 
506             * <p>
507             * Multi named type faces must be encluded in spaces.
508             * <p>  
509             * Valid typeface name forms are :
510             * <pre>
511             * 'Font1, Font 2, Font2'
512             * 'Font1'
513             * Font1
514             * <pre>
515             * 
516             * @param typeFaceNames - the name(s) of the intended typeface
517             */
518            public static Font.Typeface makeTypeface(String typeFaceNames) {
519                    if (typeFaceNames.charAt(0) == '\'' && typeFaceNames.charAt(typeFaceNames.length()-1) == '\'') {
520                            typeFaceNames = typeFaceNames.substring(1,typeFaceNames.length()-1);
521                    }
522                    String[] tokens = TokenizerKit.tokenizeStrict(typeFaceNames,",");
523                    if (tokens.length == 0)
524                            return null;
525                    Font.Typeface typeFace;
526                    typeFace = getSystemTypeface(tokens[0].toUpperCase());
527                    //
528                    // if we have only one token and it matches a System
529                    // Typeface, then we will go with it
530                    if (tokens.length == 1 && typeFace != null) {
531                            return typeFace;
532                    } else {
533                            if (_exactMatchingTypeFace(typeFace,tokens)) {
534                                    return typeFace;
535                            } else {
536                                    typeFace = null;
537                                    for (int i = tokens.length-1; i >= 0; i--) {
538                                            typeFace = new Font.Typeface(tokens[i],typeFace);
539                                    }
540                                    return typeFace;
541                            }
542                    }
543            }
544    
545            /*
546             * Returns true if the type face provided exactly matches the list of font names
547             */
548            private static boolean _exactMatchingTypeFace(Font.Typeface typeFace, String[] fontNames) {
549                    if (typeFace == null)
550                            return false;
551                    int i = 0;
552                    while (typeFace != null) {
553                            if (i >= fontNames.length)
554                                    return false;
555                            if (! fontNames[i].equalsIgnoreCase(typeFace.getName())) {
556                                    return false;
557                            }
558                            typeFace = typeFace.getAlternate();
559                            i++;
560                    }
561                    return true;
562            }
563    
564            /*
565             * Gets a Font from the cache if its matches the font string
566             * It may be null if is not been cached.  The strings are
567             * always stored in lower case for consistency
568             */
569            private static Font _getCachedFont(String fontString) {
570                    fontString = fontString.trim().toLowerCase();
571                    Font font = (Font) fontMap.get(fontString);
572                    return font;
573            }
574            
575            /*
576             * Puts a Font into the shared cache and then returns it
577             */
578            private static Font _putCachedFont(String fontString, Font font) {
579                    fontString = fontString.trim().toLowerCase();
580                    fontMap.put(fontString, font);
581                    return font;
582            }
583             
584            
585            /*
586             * Well is it an integer?
587             */
588            private static boolean _isInteger(String propertyValue) {
589                    try {
590                            Integer.parseInt(propertyValue.trim());
591                    } catch (NumberFormatException nfe) {
592                            return false;
593                    }
594                    return true;
595            }
596    
597            /**
598             * Makes a string representation of a font in the format
599             * <p>
600             *       font(fontName, fontStyle, fontSize)
601             * <p>
602             * where :
603             * <p>
604             * fontName  - is a font name such as 'Verdana' or 'Times New Roman'.  
605             * fontStyle - PLAIN|BOLD|ITALIC|UNDERLINE
606             * fontSize - an integer size value or an Extent value.  (In the case where
607             *                              only an integer is specified, the default Extent units 
608             *                              are points (pt).
609             * 
610             * @param font - the font to make into a String representation
611             * @return the string representation of the font
612             */
613            public static String makeFontString(Font font) {
614                    return _buildFontString(font.getTypeface(),_getStyle(font),font.getSize());
615            }
616    
617            /**
618             * Returns one the standard Echo Font.Typefaces if the name string
619             * matches the first Typeface in the chain.
620             * 
621             * @param name - the font typeface name to match.
622             * @return a TypeFace of null if a system one cannot be found
623             */
624        public static Font.Typeface getSystemTypeface(String name) {
625            if (TYPEFACE_TEXT_TO_CONSTANT.containsKey(name.toUpperCase())) {
626                return (Font.Typeface) TYPEFACE_TEXT_TO_CONSTANT.get(name.toUpperCase());
627            } else {
628                return null;
629            }
630        }
631            
632        
633        /*
634         * Returns a font style integer value of -1 if it cant be found
635         */
636        private static int _getFontStyle(String styleName) {
637            Integer styleInt = (Integer) FONTSTYLE_TEXT_TO_CONSTANT.get(styleName.toUpperCase());
638            if (styleInt == null)
639                    return -1;
640            return styleInt.intValue();
641        }
642            
643            /* 
644             * Builds a Font string from the given font attributes,  This
645             * can then be used for font caching etc.. as well as returned
646             * as a Font String representation  
647             * */
648            private static String _buildFontString(Font.Typeface typeFace, int style, Extent sizeExtent) {
649                    StringBuffer sb = new StringBuffer();
650                    sb.append("font(");
651                    
652                    int i = 0;
653                    while (typeFace != null) {
654                            if (i > 0)
655                                    sb.append(", ");
656                            else    
657                                    sb.append("'");
658                                    
659                            sb.append(typeFace.getName());  
660                            i++;
661                            typeFace = typeFace.getAlternate();
662                            if (typeFace == null)
663                                    sb.append("'");
664                    }
665    
666                    boolean isplain = true;
667                    boolean needsOr = false;
668                    sb.append(",");
669                    if ((style & Font.BOLD) == Font.BOLD) {
670                            if (needsOr) sb.append("|");
671                            sb.append("BOLD");
672                            needsOr = true;
673                            isplain = false;
674                    }
675                    if ((style & Font.ITALIC) == Font.ITALIC) {
676                            if (needsOr) sb.append("|");
677                            sb.append("ITALIC");
678                            needsOr = true;
679                            isplain = false;
680                    }
681                    if ((style & Font.UNDERLINE) == Font.UNDERLINE) {
682                            if (needsOr) sb.append("|");
683                            sb.append("UNDERLINE");
684                            needsOr = true;
685                            isplain = false;
686                    }
687                    if ((style & Font.OVERLINE) == Font.OVERLINE) {
688                            if (needsOr) sb.append("|");
689                            sb.append("OVERLINE");
690                            needsOr = true;
691                            isplain = false;
692                    }
693                    if ((style & Font.LINE_THROUGH) == Font.LINE_THROUGH) {
694                            if (needsOr) sb.append("|");
695                            sb.append("LINETHROUGH");
696                            needsOr = true;
697                            isplain = false;
698                    }
699                    if (isplain)
700                            sb.append("PLAIN");
701    
702                    if (sizeExtent != null) {
703                            sb.append(",");
704                            sb.append(sizeExtent.toString());
705                    }
706                    sb.append(")");
707                    return sb.toString();   
708            }       
709    
710            /*
711             * Builds a Font object from a valid font string, which is
712             * then cached in our global Font cache.
713             */
714            private static Font _buildFontObj(Font.Typeface typeface, int style, Extent sizeExtent) {
715                    String fontString = _buildFontString(typeface,style,sizeExtent);
716                    return makeFont(fontString);
717            }
718    
719    
720            /* 
721             * Adds style bits to an exisitng font  
722             */
723            static private int _addFontStyle(Font font, int newStyle) {
724                    int style = Font.PLAIN;
725                    if (font.isBold())
726                            style |= Font.BOLD;
727                    if (font.isItalic())
728                            style |= Font.ITALIC;
729                    if (font.isUnderline())
730                            style |= Font.UNDERLINE;
731    
732                    style |= newStyle;
733                    return style;
734            }
735    
736            /*
737             * Returns the style bits of the font.
738             */
739            static private int _getStyle(Font font) {
740                    return _addFontStyle(font,0);
741            }
742    
743            /**
744             * Adds the Font.BOLD attribute to an exisitng Font object
745             * and returns a new Font.
746             * <p>
747             * @param font - the font in question
748             * @return  - a new Font object
749             */
750            public static Font addBold(Font font) {
751                    return addStyle(font,Font.BOLD);
752            }
753    
754            /**
755             * Adds the Font.ITALIC attribute to an exisitng Font object
756             * and returns a new Font.
757             * <p>
758             * @param font - the font in question
759             * @return  - a new Font object
760             */
761            public static Font addItalic(Font font) {
762                    if (font == null)
763                            return null;
764                    return addStyle(font,Font.ITALIC);
765            }
766    
767            /**
768             * Adds the Font.UNDERLINE attribute to an exisitng Font object
769             * and returns a new Font.
770             * <p>
771             * @param font - the font in question
772             * @return  - a new Font object
773             */
774            public static Font addUnderline(Font font) {
775                    return addStyle(font,Font.UNDERLINE);
776            }
777    
778            /**
779             * Adds the specified <code>style</code> to the Font
780             * and returns a new Font.
781             * <p>
782             * @param font - the font in question
783             * @param style - the new font style to added to the font
784             * @return  - a new Font object
785             */
786            public static Font addStyle(Font font, int style) {
787                    if (font == null)
788                            return null;
789                    return _buildFontObj(font.getTypeface(),_addFontStyle(font, style),font.getSize());
790            }
791    
792            /**
793             * Makes a font smaller or larger by adding the specified amount to
794             * its current font size. The Font is made smaller if a negative
795             * value is used.
796             * 
797             * @param font - the font in question
798             * @param sizeDelta - the size delta to add to the current size
799             * @return  - a new Font object
800             *
801             */
802            public static Font addSize(Font font, int sizeDelta) {
803                    if (font == null)
804                            return null;
805                    Extent now = font.getSize();
806                    if (now != null)
807                            now = new Extent(now.getValue()+sizeDelta, now.getUnits());
808                    return _buildFontObj(font.getTypeface(),_getStyle(font),now);
809            }
810    
811            /**
812             * Returns a new Font object, based on <code>font</code>, that
813             * has the specified <code>size</code>.
814             *   
815             * @param font - the font to use as a template
816             * @param size - the new size
817             * @return a new Font object with the new size
818             */
819            public static Font setSize(Font font, int size) {
820                    if (font == null)
821                            return null;
822                    Extent now = font.getSize();
823                    if (now != null)
824                            now = new Extent(size, now.getUnits());
825                    return _buildFontObj(font.getTypeface(),_getStyle(font),now);
826            }
827    
828            /**
829             * Returns a new Font object, based on <code>font</code>, that
830             * has the specified <code>style</code>.
831             *   
832             * @param font - the font to use as a template
833             * @param style - the new style
834             * @return a new Font object with the new style
835             */
836            public static Font setStyle(Font font, int style) {
837                    if (font == null)
838                            return null;
839                    return _buildFontObj(font.getTypeface(),style,font.getSize());
840            }
841            
842            /**
843             * Returns a new Font object, based on <code>font</code>, that
844             * has the specified <code>typeFace</code>.
845             *   
846             * @param font - the font to use as a template
847             * @param typeFace - the new TypeFace to use
848             * @return a new Font object with the new font names
849             */
850            public static Font setNames(Font font, Font.Typeface typeFace) {
851                    if (font == null)
852                            return null;
853                    return _buildFontObj(typeFace,_getStyle(font),font.getSize());
854            }
855    
856            /**
857             * Returns a new Font object, based on <code>font</code>, that
858             * has the specified <code>fontName</code>.
859             * <p>
860             * If <code>fontName</code> has whitespace in it, then
861             * you need to surround it in single quotes.
862             *   
863             * @param font - the font to use as a template
864             * @param fontName - the new font name to use
865             * @return a new Font object with the new font name
866             */
867            public static Font setName(Font font, String fontName) {
868                    if (font  == null)
869                            return null;
870                    Font.Typeface typeFace = makeTypeface(fontName);
871                    return _buildFontObj(typeFace,_getStyle(font),font.getSize());
872            }
873    }