Source for gnu.java.awt.peer.x.XFontPeer

   1: /* XFontPeer.java -- The font peer for X
   2:    Copyright (C) 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.awt.peer.x;
  40: 
  41: import java.awt.AWTError;
  42: import java.awt.Font;
  43: import java.awt.FontMetrics;
  44: import java.awt.GraphicsDevice;
  45: import java.awt.GraphicsEnvironment;
  46: import java.awt.font.FontRenderContext;
  47: import java.awt.font.GlyphVector;
  48: import java.awt.font.LineMetrics;
  49: import java.awt.font.TextAttribute;
  50: import java.awt.geom.Rectangle2D;
  51: import java.io.IOException;
  52: import java.io.InputStream;
  53: import java.text.CharacterIterator;
  54: import java.util.HashMap;
  55: import java.util.Iterator;
  56: import java.util.Locale;
  57: import java.util.Map;
  58: import java.util.Properties;
  59: 
  60: import gnu.java.awt.peer.ClasspathFontPeer;
  61: import gnu.x11.Display;
  62: import gnu.x11.Fontable;
  63: 
  64: /**
  65:  * The bridge from AWT to X fonts.
  66:  *
  67:  * @author Roman Kennke (kennke@aicas.com)
  68:  */
  69: public class XFontPeer
  70:   extends ClasspathFontPeer
  71: {
  72: 
  73:   /**
  74:    * The font mapping as specified in the file fonts.properties.
  75:    */
  76:   private static Properties fontProperties;
  77:   static
  78:   {
  79:     fontProperties = new Properties();
  80:     InputStream in = XFontPeer.class.getResourceAsStream("fonts.properties");
  81:     try
  82:       {
  83:         fontProperties.load(in);
  84:       }
  85:     catch (IOException e)
  86:       {
  87:         e.printStackTrace();
  88:       }
  89:   }
  90: 
  91:   /**
  92:    * The FontMetrics implementation for XFontPeer.
  93:    */
  94:   private class XFontMetrics
  95:     extends FontMetrics
  96:   {
  97:     /**
  98:      * The ascent of the font.
  99:      */ 
 100:     int ascent;
 101: 
 102:     /**
 103:      * The descent of the font.
 104:      */ 
 105:     int descent;
 106: 
 107:     /**
 108:      * The maximum of the character advances.
 109:      */
 110:     private int maxAdvance;
 111: 
 112:     /**
 113:      * The internal leading.
 114:      */
 115:     int leading;
 116: 
 117:     /**
 118:      * Cached string metrics. This caches string metrics locally so that the
 119:      * server doesn't have to be asked each time.
 120:      */
 121:     private HashMap metricsCache;
 122: 
 123:     /**
 124:      * The widths of the characters indexed by the characters themselves.
 125:      */
 126:     private int[] charWidths;
 127: 
 128:     /**
 129:      * Creates a new XFontMetrics for the specified font.
 130:      *
 131:      * @param font the font
 132:      */
 133:     protected XFontMetrics(Font font)
 134:     {
 135:       super(font);
 136:       metricsCache = new HashMap();
 137:       Fontable.FontReply info = getXFont().info();
 138:       ascent = info.font_ascent();
 139:       descent = info.font_descent();
 140:       maxAdvance = info.max_bounds().character_width();
 141:       leading = 0; // TODO: Not provided by X. Possible not needed.
 142: 
 143:       if (info.min_byte1() == 0 && info.max_byte1() == 0)
 144:         readCharWidthsLinear(info);
 145:       else
 146:         readCharWidthsNonLinear(info);
 147:     }
 148: 
 149:     /**
 150:      * Reads the character widths when specified in a linear fashion. That is
 151:      * when the min-byte1 and max-byte2 fields are both zero in the X protocol.
 152:      *
 153:      * @param info the font info reply
 154:      */
 155:     private void readCharWidthsLinear(Fontable.FontReply info)
 156:     {
 157:       int startIndex = info.min_char_or_byte2();
 158:       int endIndex = info.max_char_or_byte2();
 159:       charWidths = new int[endIndex + 1];
 160:       // All the characters before startIndex are zero width.
 161:       for (int i = 0; i < startIndex; i++)
 162:         {
 163:           charWidths[i] = 0;
 164:         }
 165:       // All the other character info is fetched from the font info.
 166:       int index = startIndex;
 167:       Iterator charInfos = info.char_infos().iterator();
 168:       while (charInfos.hasNext())
 169:         {
 170:           Fontable.FontReply.CharInfo charInfo =
 171:             (Fontable.FontReply.CharInfo) charInfos.next();
 172:           charWidths[index] = charInfo.character_width();
 173:           index++;
 174:         }
 175:     }
 176: 
 177:     private void readCharWidthsNonLinear(Fontable.FontReply info)
 178:     {
 179:       // TODO: Implement.
 180:       throw new UnsupportedOperationException("Not yet implemented");
 181:     }
 182: 
 183:     /**
 184:      * Returns the ascent of the font.
 185:      *
 186:      * @return the ascent of the font
 187:      */
 188:     public int getAscent()
 189:     {
 190:       return ascent;
 191:     }
 192: 
 193:     /**
 194:      * Returns the descent of the font.
 195:      *
 196:      * @return the descent of the font
 197:      */
 198:     public int getDescent()
 199:     {
 200:       return descent;
 201:     }
 202: 
 203:     /**
 204:      * Returns the overall height of the font. This is the distance from
 205:      * baseline to baseline (usually ascent + descent + leading).
 206:      *
 207:      * @return the overall height of the font
 208:      */
 209:     public int getHeight()
 210:     {
 211:       return ascent + descent;
 212:     }
 213: 
 214:     /**
 215:      * Returns the leading of the font.
 216:      *
 217:      * @return the leading of the font
 218:      */
 219:     public int getLeading()
 220:     {
 221:       return leading;
 222:     }
 223: 
 224:     /**
 225:      * Returns the maximum advance for this font.
 226:      *
 227:      * @return the maximum advance for this font
 228:      */
 229:     public int getMaxAdvance()
 230:     {
 231:       return maxAdvance;
 232:     }
 233: 
 234:     /**
 235:      * Determines the width of the specified character <code>c</code>.
 236:      *
 237:      * @param c the character
 238:      *
 239:      * @return the width of the character
 240:      */
 241:     public int charWidth(char c)
 242:     {
 243:       int width;
 244:       if (c > charWidths.length)
 245:         width = charWidths['?'];
 246:       else
 247:         width = charWidths[c];
 248:       return width;
 249:     }
 250: 
 251:     /**
 252:      * Determines the overall width of the specified string.
 253:      *
 254:      * @param c the char buffer holding the string
 255:      * @param offset the starting offset of the string in the buffer
 256:      * @param length the number of characters in the string buffer 
 257:      *
 258:      * @return the overall width of the specified string
 259:      */
 260:     public int charsWidth(char[] c, int offset, int length)
 261:     {
 262:       int width = 0;
 263:       if (c.length > 0 && length > 0)
 264:         {
 265:           String s = new String(c, offset, length);
 266:           width = stringWidth(s);
 267:         }
 268:       return width;
 269:     }
 270: 
 271:     /**
 272:      * Determines the overall width of the specified string.
 273:      *
 274:      * @param s the string
 275:      *
 276:      * @return the overall width of the specified string
 277:      */
 278:     public int stringWidth(String s)
 279:     {
 280:       int width = 0;
 281:       if (s.length() > 0)
 282:         {
 283:           if (metricsCache.containsKey(s))
 284:             {
 285:               width = ((Integer) metricsCache.get(s)).intValue();
 286:             }
 287:           else
 288:             {
 289:               Fontable.TextExtentReply extents = getXFont().text_extent(s);
 290:               /*
 291:                System.err.println("string: '" + s + "' : ");
 292:                System.err.println("ascent: " + extents.getAscent());
 293:                System.err.println("descent: " + extents.getDescent());
 294:                System.err.println("overall ascent: " + extents.getOverallAscent());
 295:                System.err.println("overall descent: " + extents.getOverallDescent());
 296:                System.err.println("overall width: " + extents.getOverallWidth());
 297:                System.err.println("overall left: " + extents.getOverallLeft());
 298:                System.err.println("overall right: " + extents.getOverallRight());
 299:                */
 300:               width = extents.overall_width(); // + extents.overall_left();
 301:               //System.err.println("String: " + s + ", width: " + width);
 302:               metricsCache.put(s, new Integer(width));
 303:             }
 304:         }
 305:       //System.err.print("stringWidth: '" + s + "': ");
 306:       //System.err.println(width);
 307:       return width;
 308:     }
 309:   }
 310: 
 311:   /**
 312:    * The LineMetrics implementation for the XFontPeer.
 313:    */
 314:   private class XLineMetrics
 315:     extends LineMetrics
 316:   {
 317: 
 318:     /**
 319:      * Returns the ascent of the font.
 320:      *
 321:      * @return the ascent of the font
 322:      */
 323:     public float getAscent()
 324:     {
 325:       return fontMetrics.ascent;
 326:     }
 327: 
 328:     public int getBaselineIndex()
 329:     {
 330:       // FIXME: Implement this.
 331:       throw new UnsupportedOperationException();
 332:     }
 333: 
 334:     public float[] getBaselineOffsets()
 335:     {
 336:       // FIXME: Implement this.
 337:       throw new UnsupportedOperationException();
 338:     }
 339: 
 340:     /**
 341:      * Returns the descent of the font.
 342:      *
 343:      * @return the descent of the font
 344:      */
 345:     public float getDescent()
 346:     {
 347:       return fontMetrics.descent;
 348:     }
 349: 
 350:     /**
 351:      * Returns the overall height of the font. This is the distance from
 352:      * baseline to baseline (usually ascent + descent + leading).
 353:      *
 354:      * @return the overall height of the font
 355:      */
 356:     public float getHeight()
 357:     {
 358:       return fontMetrics.ascent + fontMetrics.descent;
 359:     }
 360: 
 361:     /**
 362:      * Returns the leading of the font.
 363:      *
 364:      * @return the leading of the font
 365:      */
 366:     public float getLeading()
 367:     {
 368:       return fontMetrics.leading;
 369:     }
 370: 
 371:     public int getNumChars()
 372:     {
 373:       // FIXME: Implement this.
 374:       throw new UnsupportedOperationException();
 375:     }
 376: 
 377:     public float getStrikethroughOffset()
 378:     {
 379:       return 0.F; // TODO: Provided by X??
 380:     }
 381: 
 382:     public float getStrikethroughThickness()
 383:     {
 384:       return 1.F; // TODO: Provided by X??
 385:     }
 386: 
 387:     public float getUnderlineOffset()
 388:     {
 389:       return 0.F; // TODO: Provided by X??
 390:     }
 391: 
 392:     public float getUnderlineThickness()
 393:     {
 394:       return 1.F; // TODO: Provided by X??
 395:     }
 396:       
 397:   }
 398: 
 399:   /**
 400:    * The X font.
 401:    */
 402:   private gnu.x11.Font xfont;
 403: 
 404:   private String name;
 405: 
 406:   private int style;
 407: 
 408:   private int size;
 409: 
 410:   /**
 411:    * The font metrics for this font.
 412:    */
 413:   XFontMetrics fontMetrics; 
 414: 
 415:   /**
 416:    * Creates a new XFontPeer for the specified font name, style and size.
 417:    *
 418:    * @param name the font name
 419:    * @param style the font style (bold / italic / normal)
 420:    * @param size the size of the font
 421:    */
 422:   public XFontPeer(String name, int style, int size)
 423:   {
 424:     super(name, style, size);
 425:     this.name = name;
 426:     this.style = style;
 427:     this.size = size;
 428:   }
 429: 
 430:   /**
 431:    * Creates a new XFontPeer for the specified font name and style
 432:    * attributes.
 433:    *
 434:    * @param name the font name
 435:    * @param atts the font attributes
 436:    */
 437:   public XFontPeer(String name, Map atts)
 438:   {
 439:     super(name, atts);
 440:     String family = name;
 441:     if (family == null || family.equals(""))
 442:       family = (String) atts.get(TextAttribute.FAMILY);
 443:     if (family == null)
 444:       family = "SansSerif";
 445: 
 446:     int size = 12;
 447:     Float sizeFl = (Float) atts.get(TextAttribute.SIZE);
 448:     if (sizeFl != null)
 449:       size = sizeFl.intValue();
 450: 
 451:     int style = 0;
 452:     // Detect italic attribute.
 453:     Float posture = (Float) atts.get(TextAttribute.POSTURE);
 454:     if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR))
 455:       style |= Font.ITALIC;
 456: 
 457:     // Detect bold attribute.
 458:     Float weight = (Float) atts.get(TextAttribute.WEIGHT);
 459:     if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0)
 460:       style |= Font.BOLD;
 461: 
 462:     this.name = name;
 463:     this.style = style;
 464:     this.size = size;
 465:   }
 466: 
 467:   /**
 468:    * Initializes the font peer with the specified attributes. This method is
 469:    * called from both constructors.
 470:    *
 471:    * @param name the font name
 472:    * @param style the font style
 473:    * @param size the font size
 474:    */
 475:   private void init(String name, int style, int size)
 476:   {
 477:     GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
 478:     GraphicsDevice dev = env.getDefaultScreenDevice();
 479:     if (dev instanceof XGraphicsDevice)
 480:       {
 481:         Display display = ((XGraphicsDevice) dev).getDisplay();
 482:         String fontDescr = encodeFont(name, style, size);
 483:         if (XToolkit.DEBUG)
 484:           System.err.println("XLFD font description: " + fontDescr);
 485:         xfont = new gnu.x11.Font(display, fontDescr);
 486:       }
 487:     else
 488:       {
 489:         throw new AWTError("Local GraphicsEnvironment is not XWindowGraphicsEnvironment");
 490:       }
 491:   }
 492: 
 493:   public boolean canDisplay(Font font, char c)
 494:   {
 495:     // TODO: Implement this.
 496:     throw new UnsupportedOperationException("Not yet implemented.");
 497:   }
 498: 
 499:   public int canDisplayUpTo(Font font, CharacterIterator i, int start, int limit)
 500:   {
 501:     // TODO: Implement this.
 502:     throw new UnsupportedOperationException("Not yet implemented.");
 503:   }
 504: 
 505:   public String getSubFamilyName(Font font, Locale locale)
 506:   {
 507:     // TODO: Implement this.
 508:     throw new UnsupportedOperationException("Not yet implemented.");
 509:   }
 510: 
 511:   public String getPostScriptName(Font font)
 512:   {
 513:     // TODO: Implement this.
 514:     throw new UnsupportedOperationException("Not yet implemented.");
 515:   }
 516: 
 517:   public int getNumGlyphs(Font font)
 518:   {
 519:     // TODO: Implement this.
 520:     throw new UnsupportedOperationException("Not yet implemented.");
 521:   }
 522: 
 523:   public int getMissingGlyphCode(Font font)
 524:   {
 525:     // TODO: Implement this.
 526:     throw new UnsupportedOperationException("Not yet implemented.");
 527:   }
 528: 
 529:   public byte getBaselineFor(Font font, char c)
 530:   {
 531:     // TODO: Implement this.
 532:     throw new UnsupportedOperationException("Not yet implemented.");
 533:   }
 534: 
 535:   public String getGlyphName(Font font, int glyphIndex)
 536:   {
 537:     // TODO: Implement this.
 538:     throw new UnsupportedOperationException("Not yet implemented.");
 539:   }
 540: 
 541:   public GlyphVector createGlyphVector(Font font, FontRenderContext frc,
 542:                                        CharacterIterator ci)
 543:   {
 544:     // TODO: Implement this.
 545:     throw new UnsupportedOperationException("Not yet implemented.");
 546:   }
 547: 
 548:   public GlyphVector createGlyphVector(Font font, FontRenderContext ctx,
 549:                                        int[] glyphCodes)
 550:   {
 551:     // TODO: Implement this.
 552:     throw new UnsupportedOperationException("Not yet implemented.");
 553:   }
 554: 
 555:   public GlyphVector layoutGlyphVector(Font font, FontRenderContext frc,
 556:                                        char[] chars, int start, int limit,
 557:                                        int flags)
 558:   {
 559:     // TODO: Implement this.
 560:     throw new UnsupportedOperationException("Not yet implemented.");
 561:   }
 562: 
 563:   /**
 564:    * Returns the font metrics for the specified font.
 565:    *
 566:    * @param font the font for which to fetch the font metrics
 567:    *
 568:    * @return the font metrics for the specified font
 569:    */
 570:   public FontMetrics getFontMetrics(Font font)
 571:   {
 572:     if (font.getPeer() != this)
 573:       throw new AWTError("The specified font has a different peer than this");
 574: 
 575:     if (fontMetrics == null)
 576:       fontMetrics = new XFontMetrics(font);
 577:     return fontMetrics;
 578:   }
 579: 
 580:   /**
 581:    * Frees the font in the X server.
 582:    */
 583:   protected void finalize()
 584:   {
 585:     if (xfont != null)
 586:       xfont.close();
 587:   }
 588: 
 589:   public boolean hasUniformLineMetrics(Font font)
 590:   {
 591:     // TODO: Implement this.
 592:     throw new UnsupportedOperationException("Not yet implemented.");
 593:   }
 594: 
 595:   /**
 596:    * Returns the line metrics for this font and the specified string and
 597:    * font render context.
 598:    */
 599:   public LineMetrics getLineMetrics(Font font, CharacterIterator ci, int begin,
 600:                                     int limit, FontRenderContext rc)
 601:   {
 602:     return new XLineMetrics();
 603:   }
 604: 
 605:   public Rectangle2D getMaxCharBounds(Font font, FontRenderContext rc)
 606:   {
 607:     // TODO: Implement this.
 608:     throw new UnsupportedOperationException("Not yet implemented.");
 609:   }
 610: 
 611:   public Rectangle2D getStringBounds(Font font, CharacterIterator ci,
 612:                                      int begin, int limit, FontRenderContext frc)
 613:   {
 614:     // TODO: Implement this.
 615:     throw new UnsupportedOperationException("Not yet implemented.");
 616:   }
 617: 
 618:   /**
 619:    * Encodes a font name + style + size specification into a X logical font
 620:    * description (XLFD) as described here:
 621:    *
 622:    * http://www.meretrx.com/e93/docs/xlfd.html
 623:    *
 624:    * This is implemented to look up the font description in the
 625:    * fonts.properties of this package.
 626:    *
 627:    * @param name the font name
 628:    * @param atts the text attributes
 629:    *
 630:    * @return the encoded font description
 631:    */
 632:   static String encodeFont(String name, Map atts)
 633:   {
 634:     String family = name;
 635:     if (family == null || family.equals(""))
 636:       family = (String) atts.get(TextAttribute.FAMILY);
 637:     if (family == null)
 638:       family = "SansSerif";
 639: 
 640:     int size = 12;
 641:     Float sizeFl = (Float) atts.get(TextAttribute.SIZE);
 642:     if (sizeFl != null)
 643:       size = sizeFl.intValue();
 644: 
 645:     int style = 0;
 646:     // Detect italic attribute.
 647:     Float posture = (Float) atts.get(TextAttribute.POSTURE);
 648:     if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR))
 649:       style |= Font.ITALIC;
 650: 
 651:     // Detect bold attribute.
 652:     Float weight = (Float) atts.get(TextAttribute.WEIGHT);
 653:     if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0)
 654:       style |= Font.BOLD;
 655: 
 656:     return encodeFont(name, style, size);
 657:   }
 658: 
 659:   /**
 660:    * Encodes a font name + style + size specification into a X logical font
 661:    * description (XLFD) as described here:
 662:    *
 663:    * http://www.meretrx.com/e93/docs/xlfd.html
 664:    *
 665:    * This is implemented to look up the font description in the
 666:    * fonts.properties of this package.
 667:    *
 668:    * @param name the font name
 669:    * @param style the font style
 670:    * @param size the font size
 671:    *
 672:    * @return the encoded font description
 673:    */
 674:   static String encodeFont(String name, int style, int size)
 675:   {
 676:     StringBuilder key = new StringBuilder();
 677:     key.append(validName(name));
 678:     key.append('.');
 679:     switch (style)
 680:     {
 681:       case Font.BOLD:
 682:         key.append("bold");
 683:         break;
 684:       case Font.ITALIC:
 685:         key.append("italic");
 686:         break;
 687:       case (Font.BOLD | Font.ITALIC):
 688:         key.append("bolditalic");
 689:         break;
 690:       case Font.PLAIN:
 691:       default:
 692:         key.append("plain");
 693:       
 694:     }
 695: 
 696:     String protoType = fontProperties.getProperty(key.toString());
 697:     int s = validSize(size);
 698:     return protoType.replaceFirst("%d", String.valueOf(s * 10));
 699:   }
 700: 
 701:   /**
 702:    * Checks the specified font name for a valid font name. If the font name
 703:    * is not known, then this returns 'sansserif' as fallback.
 704:    *
 705:    * @param name the font name to check
 706:    *
 707:    * @return a valid font name
 708:    */
 709:   static String validName(String name)
 710:   {
 711:     String retVal;
 712:     if (name.equalsIgnoreCase("sansserif")
 713:         || name.equalsIgnoreCase("serif")
 714:         || name.equalsIgnoreCase("monospaced")
 715:         || name.equalsIgnoreCase("dialog")
 716:         || name.equalsIgnoreCase("dialoginput"))
 717:       {
 718:         retVal = name.toLowerCase();
 719:       }
 720:     else
 721:       {
 722:         retVal = "sansserif";
 723:       }
 724:     return retVal;
 725:   }
 726: 
 727:   /**
 728:    * Translates an arbitrary point size to a size that is typically available
 729:    * on an X server. These are the sizes 8, 10, 12, 14, 18 and 24.
 730:    *
 731:    * @param size the queried size
 732:    * @return the real available size
 733:    */
 734:   private static final int validSize(int size)
 735:   {
 736:     int val;
 737:     if (size <= 9)
 738:       val = 8;
 739:     else if (size <= 11)
 740:       val = 10;
 741:     else if (size <= 13)
 742:       val = 12;
 743:     else if (size <= 17)
 744:       val = 14;
 745:     else if (size <= 23)
 746:       val = 18;
 747:     else
 748:       val = 24;
 749:     return val;
 750:   }
 751: 
 752:   /**
 753:    * Returns the X Font reference. This lazily loads the font when first
 754:    * requested.
 755:    *
 756:    * @return the X Font reference
 757:    */
 758:   gnu.x11.Font getXFont()
 759:   {
 760:     if (xfont == null)
 761:       {
 762:         init(name, style, size);
 763:       }
 764:     return xfont;
 765:   }
 766: }