OSDN Git Service

2006-08-14 Mark Wielaard <mark@klomp.org>
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / awt / peer / x / XFontPeer.java
diff --git a/libjava/classpath/gnu/java/awt/peer/x/XFontPeer.java b/libjava/classpath/gnu/java/awt/peer/x/XFontPeer.java
new file mode 100644 (file)
index 0000000..fd293d8
--- /dev/null
@@ -0,0 +1,766 @@
+/* XFontPeer.java -- The font peer for X
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.awt.peer.x;
+
+import java.awt.AWTError;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.font.LineMetrics;
+import java.awt.font.TextAttribute;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.CharacterIterator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+import gnu.java.awt.peer.ClasspathFontPeer;
+import gnu.x11.Display;
+import gnu.x11.Fontable;
+
+/**
+ * The bridge from AWT to X fonts.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class XFontPeer
+  extends ClasspathFontPeer
+{
+
+  /**
+   * The font mapping as specified in the file fonts.properties.
+   */
+  private static Properties fontProperties;
+  static
+  {
+    fontProperties = new Properties();
+    InputStream in = XFontPeer.class.getResourceAsStream("fonts.properties");
+    try
+      {
+        fontProperties.load(in);
+      }
+    catch (IOException e)
+      {
+        e.printStackTrace();
+      }
+  }
+
+  /**
+   * The FontMetrics implementation for XFontPeer.
+   */
+  private class XFontMetrics
+    extends FontMetrics
+  {
+    /**
+     * The ascent of the font.
+     */ 
+    int ascent;
+
+    /**
+     * The descent of the font.
+     */ 
+    int descent;
+
+    /**
+     * The maximum of the character advances.
+     */
+    private int maxAdvance;
+
+    /**
+     * The internal leading.
+     */
+    int leading;
+
+    /**
+     * Cached string metrics. This caches string metrics locally so that the
+     * server doesn't have to be asked each time.
+     */
+    private HashMap metricsCache;
+
+    /**
+     * The widths of the characters indexed by the characters themselves.
+     */
+    private int[] charWidths;
+
+    /**
+     * Creates a new XFontMetrics for the specified font.
+     *
+     * @param font the font
+     */
+    protected XFontMetrics(Font font)
+    {
+      super(font);
+      metricsCache = new HashMap();
+      Fontable.FontReply info = getXFont().info();
+      ascent = info.font_ascent();
+      descent = info.font_descent();
+      maxAdvance = info.max_bounds().character_width();
+      leading = 0; // TODO: Not provided by X. Possible not needed.
+
+      if (info.min_byte1() == 0 && info.max_byte1() == 0)
+        readCharWidthsLinear(info);
+      else
+        readCharWidthsNonLinear(info);
+    }
+
+    /**
+     * Reads the character widths when specified in a linear fashion. That is
+     * when the min-byte1 and max-byte2 fields are both zero in the X protocol.
+     *
+     * @param info the font info reply
+     */
+    private void readCharWidthsLinear(Fontable.FontReply info)
+    {
+      int startIndex = info.min_char_or_byte2();
+      int endIndex = info.max_char_or_byte2();
+      charWidths = new int[endIndex + 1];
+      // All the characters before startIndex are zero width.
+      for (int i = 0; i < startIndex; i++)
+        {
+          charWidths[i] = 0;
+        }
+      // All the other character info is fetched from the font info.
+      int index = startIndex;
+      Iterator charInfos = info.char_infos().iterator();
+      while (charInfos.hasNext())
+        {
+          Fontable.FontReply.CharInfo charInfo =
+            (Fontable.FontReply.CharInfo) charInfos.next();
+          charWidths[index] = charInfo.character_width();
+          index++;
+        }
+    }
+
+    private void readCharWidthsNonLinear(Fontable.FontReply info)
+    {
+      // TODO: Implement.
+      throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    /**
+     * Returns the ascent of the font.
+     *
+     * @return the ascent of the font
+     */
+    public int getAscent()
+    {
+      return ascent;
+    }
+
+    /**
+     * Returns the descent of the font.
+     *
+     * @return the descent of the font
+     */
+    public int getDescent()
+    {
+      return descent;
+    }
+
+    /**
+     * Returns the overall height of the font. This is the distance from
+     * baseline to baseline (usually ascent + descent + leading).
+     *
+     * @return the overall height of the font
+     */
+    public int getHeight()
+    {
+      return ascent + descent;
+    }
+
+    /**
+     * Returns the leading of the font.
+     *
+     * @return the leading of the font
+     */
+    public int getLeading()
+    {
+      return leading;
+    }
+
+    /**
+     * Returns the maximum advance for this font.
+     *
+     * @return the maximum advance for this font
+     */
+    public int getMaxAdvance()
+    {
+      return maxAdvance;
+    }
+
+    /**
+     * Determines the width of the specified character <code>c</code>.
+     *
+     * @param c the character
+     *
+     * @return the width of the character
+     */
+    public int charWidth(char c)
+    {
+      int width;
+      if (c > charWidths.length)
+        width = charWidths['?'];
+      else
+        width = charWidths[c];
+      return width;
+    }
+
+    /**
+     * Determines the overall width of the specified string.
+     *
+     * @param c the char buffer holding the string
+     * @param offset the starting offset of the string in the buffer
+     * @param length the number of characters in the string buffer 
+     *
+     * @return the overall width of the specified string
+     */
+    public int charsWidth(char[] c, int offset, int length)
+    {
+      int width = 0;
+      if (c.length > 0 && length > 0)
+        {
+          String s = new String(c, offset, length);
+          width = stringWidth(s);
+        }
+      return width;
+    }
+
+    /**
+     * Determines the overall width of the specified string.
+     *
+     * @param s the string
+     *
+     * @return the overall width of the specified string
+     */
+    public int stringWidth(String s)
+    {
+      int width = 0;
+      if (s.length() > 0)
+        {
+          if (metricsCache.containsKey(s))
+            {
+              width = ((Integer) metricsCache.get(s)).intValue();
+            }
+          else
+            {
+              Fontable.TextExtentReply extents = getXFont().text_extent(s);
+              /*
+               System.err.println("string: '" + s + "' : ");
+               System.err.println("ascent: " + extents.getAscent());
+               System.err.println("descent: " + extents.getDescent());
+               System.err.println("overall ascent: " + extents.getOverallAscent());
+               System.err.println("overall descent: " + extents.getOverallDescent());
+               System.err.println("overall width: " + extents.getOverallWidth());
+               System.err.println("overall left: " + extents.getOverallLeft());
+               System.err.println("overall right: " + extents.getOverallRight());
+               */
+              width = extents.overall_width(); // + extents.overall_left();
+              //System.err.println("String: " + s + ", width: " + width);
+              metricsCache.put(s, new Integer(width));
+            }
+        }
+      //System.err.print("stringWidth: '" + s + "': ");
+      //System.err.println(width);
+      return width;
+    }
+  }
+
+  /**
+   * The LineMetrics implementation for the XFontPeer.
+   */
+  private class XLineMetrics
+    extends LineMetrics
+  {
+
+    /**
+     * Returns the ascent of the font.
+     *
+     * @return the ascent of the font
+     */
+    public float getAscent()
+    {
+      return fontMetrics.ascent;
+    }
+
+    public int getBaselineIndex()
+    {
+      // FIXME: Implement this.
+      throw new UnsupportedOperationException();
+    }
+
+    public float[] getBaselineOffsets()
+    {
+      // FIXME: Implement this.
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the descent of the font.
+     *
+     * @return the descent of the font
+     */
+    public float getDescent()
+    {
+      return fontMetrics.descent;
+    }
+
+    /**
+     * Returns the overall height of the font. This is the distance from
+     * baseline to baseline (usually ascent + descent + leading).
+     *
+     * @return the overall height of the font
+     */
+    public float getHeight()
+    {
+      return fontMetrics.ascent + fontMetrics.descent;
+    }
+
+    /**
+     * Returns the leading of the font.
+     *
+     * @return the leading of the font
+     */
+    public float getLeading()
+    {
+      return fontMetrics.leading;
+    }
+
+    public int getNumChars()
+    {
+      // FIXME: Implement this.
+      throw new UnsupportedOperationException();
+    }
+
+    public float getStrikethroughOffset()
+    {
+      return 0.F; // TODO: Provided by X??
+    }
+
+    public float getStrikethroughThickness()
+    {
+      return 1.F; // TODO: Provided by X??
+    }
+
+    public float getUnderlineOffset()
+    {
+      return 0.F; // TODO: Provided by X??
+    }
+
+    public float getUnderlineThickness()
+    {
+      return 1.F; // TODO: Provided by X??
+    }
+      
+  }
+
+  /**
+   * The X font.
+   */
+  private gnu.x11.Font xfont;
+
+  private String name;
+
+  private int style;
+
+  private int size;
+
+  /**
+   * The font metrics for this font.
+   */
+  XFontMetrics fontMetrics; 
+
+  /**
+   * Creates a new XFontPeer for the specified font name, style and size.
+   *
+   * @param name the font name
+   * @param style the font style (bold / italic / normal)
+   * @param size the size of the font
+   */
+  public XFontPeer(String name, int style, int size)
+  {
+    super(name, style, size);
+    this.name = name;
+    this.style = style;
+    this.size = size;
+  }
+
+  /**
+   * Creates a new XFontPeer for the specified font name and style
+   * attributes.
+   *
+   * @param name the font name
+   * @param atts the font attributes
+   */
+  public XFontPeer(String name, Map atts)
+  {
+    super(name, atts);
+    String family = name;
+    if (family == null || family.equals(""))
+      family = (String) atts.get(TextAttribute.FAMILY);
+    if (family == null)
+      family = "SansSerif";
+
+    int size = 12;
+    Float sizeFl = (Float) atts.get(TextAttribute.SIZE);
+    if (sizeFl != null)
+      size = sizeFl.intValue();
+
+    int style = 0;
+    // Detect italic attribute.
+    Float posture = (Float) atts.get(TextAttribute.POSTURE);
+    if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR))
+      style |= Font.ITALIC;
+
+    // Detect bold attribute.
+    Float weight = (Float) atts.get(TextAttribute.WEIGHT);
+    if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0)
+      style |= Font.BOLD;
+
+    this.name = name;
+    this.style = style;
+    this.size = size;
+  }
+
+  /**
+   * Initializes the font peer with the specified attributes. This method is
+   * called from both constructors.
+   *
+   * @param name the font name
+   * @param style the font style
+   * @param size the font size
+   */
+  private void init(String name, int style, int size)
+  {
+    GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
+    GraphicsDevice dev = env.getDefaultScreenDevice();
+    if (dev instanceof XGraphicsDevice)
+      {
+        Display display = ((XGraphicsDevice) dev).getDisplay();
+        String fontDescr = encodeFont(name, style, size);
+        if (XToolkit.DEBUG)
+          System.err.println("XLFD font description: " + fontDescr);
+        xfont = new gnu.x11.Font(display, fontDescr);
+      }
+    else
+      {
+        throw new AWTError("Local GraphicsEnvironment is not XWindowGraphicsEnvironment");
+      }
+  }
+
+  public boolean canDisplay(Font font, char c)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public int canDisplayUpTo(Font font, CharacterIterator i, int start, int limit)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public String getSubFamilyName(Font font, Locale locale)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public String getPostScriptName(Font font)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public int getNumGlyphs(Font font)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public int getMissingGlyphCode(Font font)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public byte getBaselineFor(Font font, char c)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public String getGlyphName(Font font, int glyphIndex)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public GlyphVector createGlyphVector(Font font, FontRenderContext frc,
+                                       CharacterIterator ci)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public GlyphVector createGlyphVector(Font font, FontRenderContext ctx,
+                                       int[] glyphCodes)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public GlyphVector layoutGlyphVector(Font font, FontRenderContext frc,
+                                       char[] chars, int start, int limit,
+                                       int flags)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  /**
+   * Returns the font metrics for the specified font.
+   *
+   * @param font the font for which to fetch the font metrics
+   *
+   * @return the font metrics for the specified font
+   */
+  public FontMetrics getFontMetrics(Font font)
+  {
+    if (font.getPeer() != this)
+      throw new AWTError("The specified font has a different peer than this");
+
+    if (fontMetrics == null)
+      fontMetrics = new XFontMetrics(font);
+    return fontMetrics;
+  }
+
+  /**
+   * Frees the font in the X server.
+   */
+  protected void finalize()
+  {
+    if (xfont != null)
+      xfont.close();
+  }
+
+  public boolean hasUniformLineMetrics(Font font)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  /**
+   * Returns the line metrics for this font and the specified string and
+   * font render context.
+   */
+  public LineMetrics getLineMetrics(Font font, CharacterIterator ci, int begin,
+                                    int limit, FontRenderContext rc)
+  {
+    return new XLineMetrics();
+  }
+
+  public Rectangle2D getMaxCharBounds(Font font, FontRenderContext rc)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  public Rectangle2D getStringBounds(Font font, CharacterIterator ci,
+                                     int begin, int limit, FontRenderContext frc)
+  {
+    // TODO: Implement this.
+    throw new UnsupportedOperationException("Not yet implemented.");
+  }
+
+  /**
+   * Encodes a font name + style + size specification into a X logical font
+   * description (XLFD) as described here:
+   *
+   * http://www.meretrx.com/e93/docs/xlfd.html
+   *
+   * This is implemented to look up the font description in the
+   * fonts.properties of this package.
+   *
+   * @param name the font name
+   * @param atts the text attributes
+   *
+   * @return the encoded font description
+   */
+  static String encodeFont(String name, Map atts)
+  {
+    String family = name;
+    if (family == null || family.equals(""))
+      family = (String) atts.get(TextAttribute.FAMILY);
+    if (family == null)
+      family = "SansSerif";
+
+    int size = 12;
+    Float sizeFl = (Float) atts.get(TextAttribute.SIZE);
+    if (sizeFl != null)
+      size = sizeFl.intValue();
+
+    int style = 0;
+    // Detect italic attribute.
+    Float posture = (Float) atts.get(TextAttribute.POSTURE);
+    if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR))
+      style |= Font.ITALIC;
+
+    // Detect bold attribute.
+    Float weight = (Float) atts.get(TextAttribute.WEIGHT);
+    if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0)
+      style |= Font.BOLD;
+
+    return encodeFont(name, style, size);
+  }
+
+  /**
+   * Encodes a font name + style + size specification into a X logical font
+   * description (XLFD) as described here:
+   *
+   * http://www.meretrx.com/e93/docs/xlfd.html
+   *
+   * This is implemented to look up the font description in the
+   * fonts.properties of this package.
+   *
+   * @param name the font name
+   * @param style the font style
+   * @param size the font size
+   *
+   * @return the encoded font description
+   */
+  static String encodeFont(String name, int style, int size)
+  {
+    StringBuilder key = new StringBuilder();
+    key.append(validName(name));
+    key.append('.');
+    switch (style)
+    {
+      case Font.BOLD:
+        key.append("bold");
+        break;
+      case Font.ITALIC:
+        key.append("italic");
+        break;
+      case (Font.BOLD | Font.ITALIC):
+        key.append("bolditalic");
+        break;
+      case Font.PLAIN:
+      default:
+        key.append("plain");
+      
+    }
+
+    String protoType = fontProperties.getProperty(key.toString());
+    int s = validSize(size);
+    return protoType.replaceFirst("%d", String.valueOf(s * 10));
+  }
+
+  /**
+   * Checks the specified font name for a valid font name. If the font name
+   * is not known, then this returns 'sansserif' as fallback.
+   *
+   * @param name the font name to check
+   *
+   * @return a valid font name
+   */
+  static String validName(String name)
+  {
+    String retVal;
+    if (name.equalsIgnoreCase("sansserif")
+        || name.equalsIgnoreCase("serif")
+        || name.equalsIgnoreCase("monospaced")
+        || name.equalsIgnoreCase("dialog")
+        || name.equalsIgnoreCase("dialoginput"))
+      {
+        retVal = name.toLowerCase();
+      }
+    else
+      {
+        retVal = "sansserif";
+      }
+    return retVal;
+  }
+
+  /**
+   * Translates an arbitrary point size to a size that is typically available
+   * on an X server. These are the sizes 8, 10, 12, 14, 18 and 24.
+   *
+   * @param size the queried size
+   * @return the real available size
+   */
+  private static final int validSize(int size)
+  {
+    int val;
+    if (size <= 9)
+      val = 8;
+    else if (size <= 11)
+      val = 10;
+    else if (size <= 13)
+      val = 12;
+    else if (size <= 17)
+      val = 14;
+    else if (size <= 23)
+      val = 18;
+    else
+      val = 24;
+    return val;
+  }
+
+  /**
+   * Returns the X Font reference. This lazily loads the font when first
+   * requested.
+   *
+   * @return the X Font reference
+   */
+  gnu.x11.Font getXFont()
+  {
+    if (xfont == null)
+      {
+        init(name, style, size);
+      }
+    return xfont;
+  }
+}