OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / awt / peer / x / XFontPeer.java
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   /**
612    * Encodes a font name + style + size specification into a X logical font
613    * description (XLFD) as described here:
614    *
615    * http://www.meretrx.com/e93/docs/xlfd.html
616    *
617    * This is implemented to look up the font description in the
618    * fonts.properties of this package.
619    *
620    * @param name the font name
621    * @param atts the text attributes
622    *
623    * @return the encoded font description
624    */
625   static String encodeFont(String name, Map atts)
626   {
627     String family = name;
628     if (family == null || family.equals(""))
629       family = (String) atts.get(TextAttribute.FAMILY);
630     if (family == null)
631       family = "SansSerif";
632
633     int size = 12;
634     Float sizeFl = (Float) atts.get(TextAttribute.SIZE);
635     if (sizeFl != null)
636       size = sizeFl.intValue();
637
638     int style = 0;
639     // Detect italic attribute.
640     Float posture = (Float) atts.get(TextAttribute.POSTURE);
641     if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR))
642       style |= Font.ITALIC;
643
644     // Detect bold attribute.
645     Float weight = (Float) atts.get(TextAttribute.WEIGHT);
646     if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0)
647       style |= Font.BOLD;
648
649     return encodeFont(name, style, size);
650   }
651
652   /**
653    * Encodes a font name + style + size specification into a X logical font
654    * description (XLFD) as described here:
655    *
656    * http://www.meretrx.com/e93/docs/xlfd.html
657    *
658    * This is implemented to look up the font description in the
659    * fonts.properties of this package.
660    *
661    * @param name the font name
662    * @param style the font style
663    * @param size the font size
664    *
665    * @return the encoded font description
666    */
667   static String encodeFont(String name, int style, int size)
668   {
669     StringBuilder key = new StringBuilder();
670     key.append(validName(name));
671     key.append('.');
672     switch (style)
673     {
674       case Font.BOLD:
675         key.append("bold");
676         break;
677       case Font.ITALIC:
678         key.append("italic");
679         break;
680       case (Font.BOLD | Font.ITALIC):
681         key.append("bolditalic");
682         break;
683       case Font.PLAIN:
684       default:
685         key.append("plain");
686       
687     }
688
689     String protoType = fontProperties.getProperty(key.toString());
690     int s = validSize(size);
691     return protoType.replaceFirst("%d", String.valueOf(s * 10));
692   }
693
694   /**
695    * Checks the specified font name for a valid font name. If the font name
696    * is not known, then this returns 'sansserif' as fallback.
697    *
698    * @param name the font name to check
699    *
700    * @return a valid font name
701    */
702   static String validName(String name)
703   {
704     String retVal;
705     if (name.equalsIgnoreCase("sansserif")
706         || name.equalsIgnoreCase("serif")
707         || name.equalsIgnoreCase("monospaced")
708         || name.equalsIgnoreCase("dialog")
709         || name.equalsIgnoreCase("dialoginput"))
710       {
711         retVal = name.toLowerCase();
712       }
713     else
714       {
715         retVal = "sansserif";
716       }
717     return retVal;
718   }
719
720   /**
721    * Translates an arbitrary point size to a size that is typically available
722    * on an X server. These are the sizes 8, 10, 12, 14, 18 and 24.
723    *
724    * @param size the queried size
725    * @return the real available size
726    */
727   private static final int validSize(int size)
728   {
729     int val;
730     if (size <= 9)
731       val = 8;
732     else if (size <= 11)
733       val = 10;
734     else if (size <= 13)
735       val = 12;
736     else if (size <= 17)
737       val = 14;
738     else if (size <= 23)
739       val = 18;
740     else
741       val = 24;
742     return val;
743   }
744
745   /**
746    * Returns the X Font reference. This lazily loads the font when first
747    * requested.
748    *
749    * @return the X Font reference
750    */
751   gnu.x11.Font getXFont()
752   {
753     if (xfont == null)
754       {
755         init(name, style, size);
756       }
757     return xfont;
758   }
759 }