OSDN Git Service

Normalise whitespace in GNU Classpath.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / awt / peer / gtk / GdkFontPeer.java
1 /* GdkFontPeer.java -- Implements FontPeer with GTK+
2    Copyright (C) 1999, 2004, 2005, 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.gtk;
40
41 import gnu.classpath.Configuration;
42 import gnu.classpath.Pointer;
43
44 import gnu.java.awt.ClasspathToolkit;
45 import gnu.java.awt.peer.ClasspathFontPeer;
46 import gnu.java.awt.font.opentype.NameDecoder;
47
48 import gnu.java.lang.CPStringBuilder;
49
50 import java.awt.Font;
51 import java.awt.FontMetrics;
52 import java.awt.Toolkit;
53 import java.awt.font.FontRenderContext;
54 import java.awt.font.GlyphVector;
55 import java.awt.font.GlyphMetrics;
56 import java.awt.font.LineMetrics;
57 import java.awt.font.TextLayout;
58 import java.awt.geom.Rectangle2D;
59 import java.text.CharacterIterator;
60 import java.util.Locale;
61 import java.util.Map;
62 import java.nio.ByteBuffer;
63 import java.util.HashMap;
64
65 public class GdkFontPeer extends ClasspathFontPeer
66 {
67   static final FontRenderContext DEFAULT_CTX =
68     new FontRenderContext(null, false, false);
69
70   /**
71    * Caches TextLayout instances for use in charsWidth() and drawString().
72    * The size of the cache has been chosen so that relativly large GUIs with
73    * text documents are still efficient.
74    */
75   HashMap<String,TextLayout> textLayoutCache = new GtkToolkit.LRUCache<String,TextLayout>(500);
76
77   private class GdkFontMetrics extends FontMetrics
78   {
79
80     public GdkFontMetrics (Font font)
81     {
82       super(initFont(font));
83     }
84
85     public int stringWidth (String str)
86     {
87       TextLayout tl = textLayoutCache.get(str);
88       if (tl == null)
89         {
90           tl = new TextLayout(str, font, DEFAULT_CTX);
91           textLayoutCache.put(str, tl);
92         }
93       return (int) tl.getAdvance();
94     }
95
96     public int charWidth (char ch)
97     {
98       return stringWidth (new String (new char[] { ch }));
99     }
100
101     public int charsWidth (char data[], int off, int len)
102     {
103       return stringWidth (new String (data, off, len));
104     }
105
106     public int getHeight()
107     {
108       return (int) height;
109     }
110
111     public int getLeading ()
112     {
113       return (int) (height - (ascent + descent));
114     }
115
116     public int getAscent ()
117     {
118       return (int) ascent;
119     }
120
121     public int getMaxAscent ()
122     {
123       return (int) ascent;
124     }
125
126     public int getDescent ()
127     {
128       return (int) descent;
129     }
130
131     public int getMaxDescent ()
132     {
133       return (int) maxDescent;
134     }
135
136     public int getMaxAdvance ()
137     {
138       return (int) maxAdvance;
139     }
140   }
141
142   static native void initStaticState();
143   private final int native_state = GtkGenericPeer.getUniqueInteger ();
144
145   /**
146    * Cache GlyphMetrics objects.
147    */
148   private HashMap<Integer,GlyphMetrics> metricsCache;
149
150   private static final int FONT_METRICS_ASCENT = 0;
151   private static final int FONT_METRICS_MAX_ASCENT = 1;
152   private static final int FONT_METRICS_DESCENT = 2;
153   private static final int FONT_METRICS_MAX_DESCENT = 3;
154   private static final int FONT_METRICS_MAX_ADVANCE = 4;
155   private static final int FONT_METRICS_HEIGHT = 5;
156   private static final int FONT_METRICS_UNDERLINE_OFFSET = 6;
157   private static final int FONT_METRICS_UNDERLINE_THICKNESS = 7;
158
159   float ascent;
160   float descent;
161   float maxAscent;
162   float maxDescent;
163   float maxAdvance;
164   float height;
165   float underlineOffset;
166   float underlineThickness;
167
168   GdkFontMetrics metrics;
169
170   static
171   {
172     if (true) // GCJ LOCAL
173       {
174         System.loadLibrary("gtkpeer");
175       }
176
177     initStaticState ();
178
179   }
180
181   private ByteBuffer nameTable = null;
182
183   /**
184    * The pointer to the native font data.
185    *
186    * This field is manipulated by native code. Don't change or remove
187    * without adjusting the native code.
188    */
189   private Pointer nativeFont;
190
191   private native void initState ();
192   private native void dispose ();
193   private native void setFont (String family, int style, int size);
194
195   native synchronized void getFontMetrics(double [] metrics);
196   native synchronized void getTextMetrics(String str, double [] metrics);
197
198   native void releasePeerGraphicsResource();
199
200
201   protected void finalize ()
202   {
203     releasePeerGraphicsResource();
204     dispose ();
205   }
206
207   /*
208    * Helpers for the 3-way overloading that this class seems to suffer
209    * from. Remove them if you feel like they're a performance bottleneck,
210    * for the time being I prefer my code not be written and debugged in
211    * triplicate.
212    */
213
214   private String buildString(CharacterIterator iter)
215   {
216     CPStringBuilder sb = new CPStringBuilder();
217     for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next())
218       sb.append(c);
219     return sb.toString();
220   }
221
222   private String buildString(CharacterIterator iter, int begin, int limit)
223   {
224     CPStringBuilder sb = new CPStringBuilder();
225     int i = 0;
226     for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next(), i++)
227       {
228         if (begin <= i)
229           sb.append(c);
230         if (limit <= i)
231           break;
232       }
233     return sb.toString();
234   }
235
236   private String buildString(char[] chars, int begin, int limit)
237   {
238     return new String(chars, begin, limit - begin);
239   }
240
241   /* Public API */
242
243   public GdkFontPeer (String name, int style)
244   {
245     // All fonts get a default size of 12 if size is not specified.
246     this(name, style, 12);
247   }
248
249   public GdkFontPeer (String name, int style, int size)
250   {
251     super(name, style, size);
252     initState ();
253     setFont (this.familyName, this.style, (int)this.size);
254     metricsCache = new HashMap<Integer,GlyphMetrics>();
255     setupMetrics();
256   }
257
258   public GdkFontPeer (String name, Map attributes)
259   {
260     super(name, attributes);
261     initState ();
262     setFont (this.familyName, this.style, (int)this.size);
263     metricsCache = new HashMap<Integer,GlyphMetrics>();
264     setupMetrics();
265   }
266
267
268   /**
269    * Makes sure to return a Font based on the given Font that has as
270    * peer a GdkFontPeer. Used in the initializer.
271    */
272   static Font initFont(Font font)
273   {
274     if (font == null)
275       return new Font("Dialog", Font.PLAIN, 12);
276     else if (font.getPeer() instanceof GdkFontPeer)
277       return font;
278     else
279       {
280         ClasspathToolkit toolkit;
281         toolkit = (ClasspathToolkit) Toolkit.getDefaultToolkit();
282         return toolkit.getFont(font.getName(), font.getAttributes());
283       }
284   }
285
286   private void setupMetrics()
287   {
288     double [] hires = new double[8];
289     getFontMetrics(hires);
290     ascent = (float) hires[FONT_METRICS_ASCENT];
291     maxAscent = (float) hires[FONT_METRICS_MAX_ASCENT];
292     descent = (float) hires[FONT_METRICS_DESCENT];
293     maxDescent = (float) hires[FONT_METRICS_MAX_DESCENT];
294     maxAdvance = (float) hires[FONT_METRICS_MAX_ADVANCE];
295     height = (float) hires[FONT_METRICS_HEIGHT];
296     underlineOffset = (float) hires[FONT_METRICS_UNDERLINE_OFFSET];
297     underlineThickness = (float) hires[FONT_METRICS_UNDERLINE_THICKNESS];
298   }
299
300   /**
301    * Unneeded, but implemented anyway.
302    */
303   public String getSubFamilyName(Font font, Locale locale)
304   {
305     String name;
306
307     if (locale == null)
308       locale = Locale.getDefault();
309
310     name = getName(NameDecoder.NAME_SUBFAMILY, locale);
311     if (name == null)
312       {
313         name = getName(NameDecoder.NAME_SUBFAMILY, Locale.ENGLISH);
314         if ("Regular".equals(name))
315           name = null;
316       }
317
318     return name;
319   }
320
321   /**
322    * Returns the bytes belonging to a TrueType/OpenType table,
323    * Parameters n,a,m,e identify the 4-byte ASCII tag of the table.
324    *
325    * Returns null if the font is not TT, the table is nonexistant,
326    * or if some other unexpected error occured.
327    *
328    */
329   private native byte[] getTrueTypeTable(byte n, byte a, byte m, byte e);
330
331   /**
332    * Returns the PostScript name of the font, defaults to the familyName if
333    * a PS name could not be retrieved.
334    */
335   public String getPostScriptName(Font font)
336   {
337     String name = getName(NameDecoder.NAME_POSTSCRIPT,
338                           /* any language */ null);
339     if( name == null )
340       return this.familyName;
341
342     return name;
343   }
344
345   /**
346    * Extracts a String from the font&#x2019;s name table.
347    *
348    * @param name the numeric TrueType or OpenType name ID.
349    *
350    * @param locale the locale for which names shall be localized, or
351    * <code>null</code> if the locale does mot matter because the name
352    * is known to be language-independent (for example, because it is
353    * the PostScript name).
354    */
355   private String getName(int name, Locale locale)
356   {
357     if (nameTable == null)
358       {
359         byte[] data = getTrueTypeTable((byte)'n', (byte) 'a',
360                                        (byte) 'm', (byte) 'e');
361         if( data == null )
362           return null;
363
364         nameTable = ByteBuffer.wrap( data );
365       }
366
367     return NameDecoder.getName(nameTable, name, locale);
368   }
369
370   public boolean canDisplay (Font font, int c)
371   {
372     // FIXME: inquire with pango
373     return true;
374   }
375
376   public int canDisplayUpTo (Font font, CharacterIterator i, int start, int limit)
377   {
378     // FIXME: inquire with pango
379     return -1;
380   }
381
382   public GlyphVector createGlyphVector (Font font,
383                                         FontRenderContext ctx,
384                                         CharacterIterator i)
385   {
386     return new FreetypeGlyphVector(font, buildString (i), ctx);
387   }
388
389   public GlyphVector createGlyphVector (Font font,
390                                         FontRenderContext ctx,
391                                         int[] glyphCodes)
392   {
393     return new FreetypeGlyphVector(font, glyphCodes, ctx);
394   }
395
396   public byte getBaselineFor (Font font, char c)
397   {
398     // FIXME: Actually check.
399     return Font.ROMAN_BASELINE;
400   }
401
402   private class GdkFontLineMetrics extends LineMetrics
403   {
404     private int nchars;
405     public GdkFontLineMetrics (GdkFontPeer fp, int n)
406     {
407       nchars = n;
408     }
409
410     public float getAscent()
411     {
412       return ascent;
413     }
414
415     public int getBaselineIndex()
416     {
417       // FIXME
418       return Font.ROMAN_BASELINE;
419     }
420
421     public float[] getBaselineOffsets()
422     {
423       return new float[3];
424     }
425
426     public float getDescent()
427     {
428       return descent;
429     }
430
431     public float getHeight()
432     {
433       return height;
434     }
435
436     public float getLeading()
437     {
438       return height - (ascent + descent);
439     }
440
441     public int getNumChars()
442     {
443       return nchars;
444     }
445
446     public float getStrikethroughOffset()
447     {
448       // FreeType doesn't seem to provide a value here.
449       return ascent / 2;
450     }
451
452     public float getStrikethroughThickness()
453     {
454       // FreeType doesn't seem to provide a value here.
455       return 1.f;
456     }
457
458     public float getUnderlineOffset()
459     {
460       return underlineOffset;
461     }
462
463     public float getUnderlineThickness()
464     {
465       return underlineThickness;
466     }
467
468   }
469
470   public LineMetrics getLineMetrics (Font font, CharacterIterator ci,
471                                      int begin, int limit, FontRenderContext rc)
472   {
473     return new GdkFontLineMetrics (this, limit - begin);
474   }
475
476   public Rectangle2D getMaxCharBounds (Font font, FontRenderContext rc)
477   {
478     throw new UnsupportedOperationException ();
479   }
480
481   public int getMissingGlyphCode (Font font)
482   {
483     throw new UnsupportedOperationException ();
484   }
485
486   public String getGlyphName (Font font, int glyphIndex)
487   {
488     throw new UnsupportedOperationException ();
489   }
490
491   public int getNumGlyphs (Font font)
492   {
493     byte[] data = getTrueTypeTable((byte)'m', (byte) 'a',
494                                    (byte)'x', (byte) 'p');
495     if( data == null )
496       return -1;
497
498     ByteBuffer buf = ByteBuffer.wrap( data );
499     return buf.getShort(4);
500   }
501
502   public boolean hasUniformLineMetrics (Font font)
503   {
504     return true;
505   }
506
507   public GlyphVector layoutGlyphVector (Font font, FontRenderContext frc,
508                                         char[] chars, int start, int limit,
509                                         int flags)
510   {
511     return new FreetypeGlyphVector(font, chars, start, limit - start,
512                                    frc, flags);
513   }
514
515   public LineMetrics getLineMetrics (Font font, String str,
516                                      FontRenderContext frc)
517   {
518     return new GdkFontLineMetrics (this, str.length ());
519   }
520
521   public FontMetrics getFontMetrics (Font font)
522   {
523     if (metrics == null)
524       metrics = new GdkFontMetrics(font);
525     return metrics;
526   }
527
528   /**
529    * Returns a cached GlyphMetrics object for a given glyphcode,
530    * or null if it doesn't exist in the cache.
531    */
532   GlyphMetrics getGlyphMetrics( int glyphCode )
533   {
534     return metricsCache.get(new Integer(glyphCode));
535   }
536
537   /**
538    * Put a GlyphMetrics object in the cache.
539    */
540   void putGlyphMetrics( int glyphCode, GlyphMetrics metrics )
541   {
542     metricsCache.put( new Integer( glyphCode ), metrics );
543   }
544
545 }