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 gnu.java.lang.CPStringBuilder;
  42: 
  43: import java.awt.AWTError;
  44: import java.awt.Font;
  45: import java.awt.FontMetrics;
  46: import java.awt.GraphicsDevice;
  47: import java.awt.GraphicsEnvironment;
  48: import java.awt.font.FontRenderContext;
  49: import java.awt.font.GlyphVector;
  50: import java.awt.font.LineMetrics;
  51: import java.awt.font.TextAttribute;
  52: import java.awt.geom.Rectangle2D;
  53: import java.io.IOException;
  54: import java.io.InputStream;
  55: import java.text.CharacterIterator;
  56: import java.util.HashMap;
  57: import java.util.Iterator;
  58: import java.util.Locale;
  59: import java.util.Map;
  60: import java.util.Properties;
  61: 
  62: import gnu.java.awt.peer.ClasspathFontPeer;
  63: import gnu.x11.Display;
  64: import gnu.x11.Fontable;
  65: 
  66: /**
  67:  * The bridge from AWT to X fonts.
  68:  *
  69:  * @author Roman Kennke (kennke@aicas.com)
  70:  */
  71: public class XFontPeer
  72:   extends ClasspathFontPeer
  73: {
  74: 
  75:   /**
  76:    * The font mapping as specified in the file fonts.properties.
  77:    */
  78:   private static Properties fontProperties;
  79:   static
  80:   {
  81:     fontProperties = new Properties();
  82:     InputStream in = XFontPeer.class.getResourceAsStream("xfonts.properties");
  83:     try
  84:       {
  85:         fontProperties.load(in);
  86:       }
  87:     catch (IOException e)
  88:       {
  89:         e.printStackTrace();
  90:       }
  91:   }
  92: 
  93:   /**
  94:    * The FontMetrics implementation for XFontPeer.
  95:    */
  96:   private class XFontMetrics
  97:     extends FontMetrics
  98:   {
  99:     /**
 100:      * The ascent of the font.
 101:      */
 102:     int ascent;
 103: 
 104:     /**
 105:      * The descent of the font.
 106:      */
 107:     int descent;
 108: 
 109:     /**
 110:      * The maximum of the character advances.
 111:      */
 112:     private int maxAdvance;
 113: 
 114:     /**
 115:      * The internal leading.
 116:      */
 117:     int leading;
 118: 
 119:     /**
 120:      * Cached string metrics. This caches string metrics locally so that the
 121:      * server doesn't have to be asked each time.
 122:      */
 123:     private HashMap metricsCache;
 124: 
 125:     /**
 126:      * The widths of the characters indexed by the characters themselves.
 127:      */
 128:     private int[] charWidths;
 129: 
 130:     /**
 131:      * Creates a new XFontMetrics for the specified font.
 132:      *
 133:      * @param font the font
 134:      */
 135:     protected XFontMetrics(Font font)
 136:     {
 137:       super(font);
 138:       metricsCache = new HashMap();
 139:       Fontable.FontInfo info = getXFont().info();
 140:       ascent = info.font_ascent();
 141:       descent = info.font_descent();
 142:       maxAdvance = info.max_bounds().character_width();
 143:       leading = 0; // TODO: Not provided by X. Possible not needed.
 144: 
 145:       if (info.min_byte1() == 0 && info.max_byte1() == 0)
 146:         readCharWidthsLinear(info);
 147:       else
 148:         readCharWidthsNonLinear(info);
 149:     }
 150: 
 151:     /**
 152:      * Reads the character widths when specified in a linear fashion. That is
 153:      * when the min-byte1 and max-byte2 fields are both zero in the X protocol.
 154:      *
 155:      * @param info the font info reply
 156:      */
 157:     private void readCharWidthsLinear(Fontable.FontInfo info)
 158:     {
 159:       int startIndex = info.min_char_or_byte2();
 160:       int endIndex = info.max_char_or_byte2();
 161:       charWidths = new int[endIndex + 1];
 162:       // All the characters before startIndex are zero width.
 163:       for (int i = 0; i < startIndex; i++)
 164:         {
 165:           charWidths[i] = 0;
 166:         }
 167:       // All the other character info is fetched from the font info.
 168:       int index = startIndex;
 169:       Fontable.FontInfo.CharInfo[] charInfos = info.char_infos();
 170:       for (Fontable.FontInfo.CharInfo charInfo : charInfos)
 171:         {
 172:           charWidths[index] = charInfo.character_width();
 173:           index++;
 174:         }
 175:     }
 176: 
 177:     private void readCharWidthsNonLinear(Fontable.FontInfo 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.TextExtentInfo 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:     if (name == null)
 478:       {
 479:         name = "SansSerif";
 480:       }
 481:     GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
 482:     GraphicsDevice dev = env.getDefaultScreenDevice();
 483:     if (dev instanceof XGraphicsDevice)
 484:       {
 485:         Display display = ((XGraphicsDevice) dev).getDisplay();
 486:         String fontDescr = encodeFont(name, style, size);
 487:         if (XToolkit.DEBUG)
 488:           System.err.println("XLFD font description: " + fontDescr);
 489:         xfont = new gnu.x11.Font(display, fontDescr);
 490:       }
 491:     else
 492:       {
 493:         throw new AWTError("Local GraphicsEnvironment is not XWindowGraphicsEnvironment");
 494:       }
 495:   }
 496: 
 497:   public boolean canDisplay(Font font, int c)
 498:   {
 499:     // TODO: Implement this.
 500:     throw new UnsupportedOperationException("Not yet implemented.");
 501:   }
 502: 
 503:   public int canDisplayUpTo(Font font, CharacterIterator i, int start, int limit)
 504:   {
 505:     // TODO: Implement this.
 506:     throw new UnsupportedOperationException("Not yet implemented.");
 507:   }
 508: 
 509:   public String getSubFamilyName(Font font, Locale locale)
 510:   {
 511:     // TODO: Implement this.
 512:     throw new UnsupportedOperationException("Not yet implemented.");
 513:   }
 514: 
 515:   public String getPostScriptName(Font font)
 516:   {
 517:     // TODO: Implement this.
 518:     throw new UnsupportedOperationException("Not yet implemented.");
 519:   }
 520: 
 521:   public int getNumGlyphs(Font font)
 522:   {
 523:     // TODO: Implement this.
 524:     throw new UnsupportedOperationException("Not yet implemented.");
 525:   }
 526: 
 527:   public int getMissingGlyphCode(Font font)
 528:   {
 529:     // TODO: Implement this.
 530:     throw new UnsupportedOperationException("Not yet implemented.");
 531:   }
 532: 
 533:   public byte getBaselineFor(Font font, char c)
 534:   {
 535:     // TODO: Implement this.
 536:     throw new UnsupportedOperationException("Not yet implemented.");
 537:   }
 538: 
 539:   public String getGlyphName(Font font, int glyphIndex)
 540:   {
 541:     // TODO: Implement this.
 542:     throw new UnsupportedOperationException("Not yet implemented.");
 543:   }
 544: 
 545:   public GlyphVector createGlyphVector(Font font, FontRenderContext frc,
 546:                                        CharacterIterator ci)
 547:   {
 548:     // TODO: Implement this.
 549:     throw new UnsupportedOperationException("Not yet implemented.");
 550:   }
 551: 
 552:   public GlyphVector createGlyphVector(Font font, FontRenderContext ctx,
 553:                                        int[] glyphCodes)
 554:   {
 555:     // TODO: Implement this.
 556:     throw new UnsupportedOperationException("Not yet implemented.");
 557:   }
 558: 
 559:   public GlyphVector layoutGlyphVector(Font font, FontRenderContext frc,
 560:                                        char[] chars, int start, int limit,
 561:                                        int flags)
 562:   {
 563:     // TODO: Implement this.
 564:     throw new UnsupportedOperationException("Not yet implemented.");
 565:   }
 566: 
 567:   /**
 568:    * Returns the font metrics for the specified font.
 569:    *
 570:    * @param font the font for which to fetch the font metrics
 571:    *
 572:    * @return the font metrics for the specified font
 573:    */
 574:   public FontMetrics getFontMetrics(Font font)
 575:   {
 576:     if (font.getPeer() != this)
 577:       throw new AWTError("The specified font has a different peer than this");
 578: 
 579:     if (fontMetrics == null)
 580:       fontMetrics = new XFontMetrics(font);
 581:     return fontMetrics;
 582:   }
 583: 
 584:   /**
 585:    * Frees the font in the X server.
 586:    */
 587:   protected void finalize()
 588:   {
 589:     if (xfont != null)
 590:       xfont.close();
 591:   }
 592: 
 593:   public boolean hasUniformLineMetrics(Font font)
 594:   {
 595:     // TODO: Implement this.
 596:     throw new UnsupportedOperationException("Not yet implemented.");
 597:   }
 598: 
 599:   /**
 600:    * Returns the line metrics for this font and the specified string and
 601:    * font render context.
 602:    */
 603:   public LineMetrics getLineMetrics(Font font, CharacterIterator ci, int begin,
 604:                                     int limit, FontRenderContext rc)
 605:   {
 606:     return new XLineMetrics();
 607:   }
 608: 
 609:   public Rectangle2D getMaxCharBounds(Font font, FontRenderContext rc)
 610:   {
 611:     // TODO: Implement this.
 612:     throw new UnsupportedOperationException("Not yet implemented.");
 613:   }
 614: 
 615:   public Rectangle2D getStringBounds(Font font, CharacterIterator ci,
 616:                                      int begin, int limit, FontRenderContext frc)
 617:   {
 618:     // TODO: Implement this.
 619:     throw new UnsupportedOperationException("Not yet implemented.");
 620:   }
 621: 
 622:   /**
 623:    * Encodes a font name + style + size specification into a X logical font
 624:    * description (XLFD) as described here:
 625:    *
 626:    * http://www.meretrx.com/e93/docs/xlfd.html
 627:    *
 628:    * This is implemented to look up the font description in the
 629:    * fonts.properties of this package.
 630:    *
 631:    * @param name the font name
 632:    * @param atts the text attributes
 633:    *
 634:    * @return the encoded font description
 635:    */
 636:   static String encodeFont(String name, Map atts)
 637:   {
 638:     String family = name;
 639:     if (family == null || family.equals(""))
 640:       family = (String) atts.get(TextAttribute.FAMILY);
 641:     if (family == null)
 642:       family = "SansSerif";
 643: 
 644:     int size = 12;
 645:     Float sizeFl = (Float) atts.get(TextAttribute.SIZE);
 646:     if (sizeFl != null)
 647:       size = sizeFl.intValue();
 648: 
 649:     int style = 0;
 650:     // Detect italic attribute.
 651:     Float posture = (Float) atts.get(TextAttribute.POSTURE);
 652:     if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR))
 653:       style |= Font.ITALIC;
 654: 
 655:     // Detect bold attribute.
 656:     Float weight = (Float) atts.get(TextAttribute.WEIGHT);
 657:     if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0)
 658:       style |= Font.BOLD;
 659: 
 660:     return encodeFont(family, style, size);
 661:   }
 662: 
 663:   /**
 664:    * Encodes a font name + style + size specification into a X logical font
 665:    * description (XLFD) as described here:
 666:    *
 667:    * http://www.meretrx.com/e93/docs/xlfd.html
 668:    *
 669:    * This is implemented to look up the font description in the
 670:    * fonts.properties of this package.
 671:    *
 672:    * @param name the font name
 673:    * @param style the font style
 674:    * @param size the font size
 675:    *
 676:    * @return the encoded font description
 677:    */
 678:   static String encodeFont(String name, int style, int size)
 679:   {
 680:     CPStringBuilder key = new CPStringBuilder();
 681:     key.append(validName(name));
 682:     key.append('.');
 683:     switch (style)
 684:     {
 685:       case Font.BOLD:
 686:         key.append("bold");
 687:         break;
 688:       case Font.ITALIC:
 689:         key.append("italic");
 690:         break;
 691:       case (Font.BOLD | Font.ITALIC):
 692:         key.append("bolditalic");
 693:         break;
 694:       case Font.PLAIN:
 695:       default:
 696:         key.append("plain");
 697: 
 698:     }
 699: 
 700:     String protoType = fontProperties.getProperty(key.toString());
 701:     int s = validSize(size);
 702:     return protoType.replaceFirst("%d", String.valueOf(s));
 703:   }
 704: 
 705:   /**
 706:    * Checks the specified font name for a valid font name. If the font name
 707:    * is not known, then this returns 'sansserif' as fallback.
 708:    *
 709:    * @param name the font name to check
 710:    *
 711:    * @return a valid font name
 712:    */
 713:   static String validName(String name)
 714:   {
 715:     String retVal;
 716:     if (name.equalsIgnoreCase("sansserif")
 717:         || name.equalsIgnoreCase("serif")
 718:         || name.equalsIgnoreCase("monospaced")
 719:         || name.equalsIgnoreCase("dialog")
 720:         || name.equalsIgnoreCase("dialoginput"))
 721:       {
 722:         retVal = name.toLowerCase();
 723:       }
 724:     else
 725:       {
 726:         retVal = "sansserif";
 727:       }
 728:     return retVal;
 729:   }
 730: 
 731:   /**
 732:    * Translates an arbitrary point size to a size that is typically available
 733:    * on an X server. These are the sizes 8, 10, 12, 14, 18 and 24.
 734:    *
 735:    * @param size the queried size
 736:    * @return the real available size
 737:    */
 738:   private static final int validSize(int size)
 739:   {
 740:     int val;
 741:     if (size <= 9)
 742:       val = 8;
 743:     else if (size <= 11)
 744:       val = 10;
 745:     else if (size <= 13)
 746:       val = 12;
 747:     else if (size <= 17)
 748:       val = 14;
 749:     else if (size <= 23)
 750:       val = 18;
 751:     else
 752:       val = 24;
 753:     return val;
 754:   }
 755: 
 756:   /**
 757:    * Returns the X Font reference. This lazily loads the font when first
 758:    * requested.
 759:    *
 760:    * @return the X Font reference
 761:    */
 762:   gnu.x11.Font getXFont()
 763:   {
 764:     if (xfont == null)
 765:       {
 766:         init(name, style, size);
 767:       }
 768:     return xfont;
 769:   }
 770: }