Source for gnu.java.awt.peer.gtk.GdkFontPeer

   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.java.awt.peer.ClasspathFontPeer;
  42: import gnu.java.awt.font.opentype.NameDecoder;
  43: 
  44: import java.awt.Font;
  45: import java.awt.FontMetrics;
  46: import java.awt.Toolkit;
  47: import java.awt.font.FontRenderContext;
  48: import java.awt.font.GlyphVector;
  49: import java.awt.font.GlyphMetrics;
  50: import java.awt.font.LineMetrics;
  51: import java.awt.geom.Rectangle2D;
  52: import java.text.CharacterIterator;
  53: import java.util.Locale;
  54: import java.util.Map;
  55: import java.util.ResourceBundle;
  56: import java.nio.ByteBuffer;
  57: import java.util.HashMap;
  58: 
  59: public class GdkFontPeer extends ClasspathFontPeer
  60: {
  61:   static native void initStaticState();
  62:   private final int native_state = GtkGenericPeer.getUniqueInteger ();
  63:   private static ResourceBundle bundle;
  64: 
  65:   /**
  66:    * Cache GlyphMetrics objects.
  67:    */
  68:   private HashMap metricsCache;
  69:   
  70:   static 
  71:   {
  72:     System.loadLibrary("gtkpeer");
  73: 
  74:     initStaticState ();
  75: 
  76:     try
  77:       {
  78:     bundle = ResourceBundle.getBundle ("gnu.java.awt.peer.gtk.font");
  79:       }
  80:     catch (Throwable ignored)
  81:       {
  82:     bundle = null;
  83:       }
  84:   }
  85: 
  86:   private ByteBuffer nameTable = null;
  87: 
  88:   private native void initState ();
  89:   private native void dispose ();
  90:   private native void setFont (String family, int style, int size);
  91: 
  92:   native void getFontMetrics(double [] metrics);
  93:   native void getTextMetrics(String str, double [] metrics);
  94: 
  95:   native void releasePeerGraphicsResource();
  96: 
  97: 
  98:   protected void finalize ()
  99:   {
 100:     releasePeerGraphicsResource();
 101:     dispose ();
 102:   }
 103: 
 104:   /* 
 105:    * Helpers for the 3-way overloading that this class seems to suffer
 106:    * from. Remove them if you feel like they're a performance bottleneck,
 107:    * for the time being I prefer my code not be written and debugged in
 108:    * triplicate.
 109:    */
 110: 
 111:   private String buildString(CharacterIterator iter)
 112:   {
 113:     StringBuffer sb = new StringBuffer();
 114:     for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) 
 115:       sb.append(c);
 116:     return sb.toString();
 117:   }
 118: 
 119:   private String buildString(CharacterIterator iter, int begin, int limit)
 120:   {
 121:     StringBuffer sb = new StringBuffer();
 122:     int i = 0;
 123:     for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next(), i++) 
 124:       {
 125:         if (begin <= i)
 126:           sb.append(c);
 127:         if (limit <= i)
 128:           break;
 129:       }
 130:     return sb.toString();
 131:   }
 132:   
 133:   private String buildString(char[] chars, int begin, int limit)
 134:   {
 135:     return new String(chars, begin, limit - begin);
 136:   }
 137: 
 138:   /* Public API */
 139: 
 140:   public GdkFontPeer (String name, int style)
 141:   {
 142:     // All fonts get a default size of 12 if size is not specified.
 143:     this(name, style, 12);
 144:   }
 145: 
 146:   public GdkFontPeer (String name, int style, int size)
 147:   {  
 148:     super(name, style, size);    
 149:     initState ();
 150:     setFont (this.familyName, this.style, (int)this.size);
 151:     metricsCache = new HashMap();
 152:   }
 153: 
 154:   public GdkFontPeer (String name, Map attributes)
 155:   {
 156:     super(name, attributes);
 157:     initState ();
 158:     setFont (this.familyName, this.style, (int)this.size);
 159:     metricsCache = new HashMap();
 160:   }
 161: 
 162:   /**
 163:    * Unneeded, but implemented anyway.
 164:    */  
 165:   public String getSubFamilyName(Font font, Locale locale)
 166:   {
 167:     String name;
 168:     
 169:     if (locale == null)
 170:       locale = Locale.getDefault();
 171:     
 172:     name = getName(NameDecoder.NAME_SUBFAMILY, locale);
 173:     if (name == null)
 174:       {
 175:     name = getName(NameDecoder.NAME_SUBFAMILY, Locale.ENGLISH);
 176:     if ("Regular".equals(name))
 177:       name = null;
 178:       }
 179: 
 180:     return name;
 181:   }
 182: 
 183:   /**
 184:    * Returns the bytes belonging to a TrueType/OpenType table,
 185:    * Parameters n,a,m,e identify the 4-byte ASCII tag of the table.
 186:    *
 187:    * Returns null if the font is not TT, the table is nonexistant, 
 188:    * or if some other unexpected error occured.
 189:    *
 190:    */
 191:   private native byte[] getTrueTypeTable(byte n, byte a, byte m, byte e);
 192: 
 193:   /**
 194:    * Returns the PostScript name of the font, defaults to the familyName if 
 195:    * a PS name could not be retrieved.
 196:    */
 197:   public String getPostScriptName(Font font)
 198:   {
 199:     String name = getName(NameDecoder.NAME_POSTSCRIPT, 
 200:               /* any language */ null);
 201:     if( name == null )
 202:       return this.familyName;
 203: 
 204:     return name;
 205:   }
 206: 
 207:   /**
 208:    * Extracts a String from the font&#x2019;s name table.
 209:    *
 210:    * @param name the numeric TrueType or OpenType name ID.
 211:    *
 212:    * @param locale the locale for which names shall be localized, or
 213:    * <code>null</code> if the locale does mot matter because the name
 214:    * is known to be language-independent (for example, because it is
 215:    * the PostScript name).
 216:    */
 217:   private String getName(int name, Locale locale)
 218:   {
 219:     if (nameTable == null)
 220:       {
 221:     byte[] data = getTrueTypeTable((byte)'n', (byte) 'a', 
 222:                        (byte) 'm', (byte) 'e');
 223:     if( data == null )
 224:       return null;
 225: 
 226:     nameTable = ByteBuffer.wrap( data );
 227:       }
 228: 
 229:     return NameDecoder.getName(nameTable, name, locale);
 230:   }
 231: 
 232:   public boolean canDisplay (Font font, char c)
 233:   {
 234:     // FIXME: inquire with pango
 235:     return true;
 236:   }
 237: 
 238:   public int canDisplayUpTo (Font font, CharacterIterator i, int start, int limit)
 239:   {
 240:     // FIXME: inquire with pango
 241:     return -1;
 242:   }
 243:   
 244:   public GlyphVector createGlyphVector (Font font, 
 245:                                         FontRenderContext ctx, 
 246:                                         CharacterIterator i)
 247:   {
 248:     return new FreetypeGlyphVector(font, buildString (i), ctx);
 249:   }
 250: 
 251:   public GlyphVector createGlyphVector (Font font, 
 252:                                         FontRenderContext ctx, 
 253:                                         int[] glyphCodes)
 254:   {
 255:     return new FreetypeGlyphVector(font, glyphCodes, ctx);
 256:   }
 257: 
 258:   public byte getBaselineFor (Font font, char c)
 259:   {
 260:     // FIXME: Actually check.
 261:     return Font.ROMAN_BASELINE;
 262:   }
 263: 
 264:   private static class GdkFontLineMetrics extends LineMetrics
 265:   {
 266:     private FontMetrics fm;
 267:     private int nchars; 
 268:     private float strikethroughOffset, strikethroughThickness,
 269:       underlineOffset, underlineThickness;
 270: 
 271:     public GdkFontLineMetrics (GdkFontPeer fp, FontMetrics m, int n)
 272:     {
 273:       fm = m;
 274:       nchars = n;
 275:       strikethroughOffset = 0f;
 276:       underlineOffset = 0f;
 277:       strikethroughThickness = ((float)fp.getSize(null)) / 12f;
 278:       underlineThickness = strikethroughThickness;
 279:     }
 280: 
 281:     public float getAscent()
 282:     {
 283:       return (float) fm.getAscent ();
 284:     }
 285:   
 286:     public int getBaselineIndex()
 287:     {      
 288:       // FIXME
 289:       return Font.ROMAN_BASELINE;
 290:     }
 291:     
 292:     public float[] getBaselineOffsets()
 293:     {
 294:       return new float[3];
 295:     }
 296:     
 297:     public float getDescent()
 298:     {
 299:       return (float) fm.getDescent ();
 300:     }
 301:     
 302:     public float getHeight()
 303:     {
 304:       return (float) fm.getHeight ();
 305:     }
 306:     
 307:     public float getLeading() { return 0.f; }    
 308:     public int getNumChars() { return nchars; }
 309:     public float getStrikethroughOffset() { return 0.f; }    
 310:     public float getStrikethroughThickness() { return 0.f; }  
 311:     public float getUnderlineOffset() { return 0.f; }
 312:     public float getUnderlineThickness() { return 0.f; }
 313: 
 314:   }
 315: 
 316:   public LineMetrics getLineMetrics (Font font, CharacterIterator ci, 
 317:                                      int begin, int limit, FontRenderContext rc)
 318:   {
 319:     return new GdkFontLineMetrics (this, getFontMetrics (font), limit - begin);
 320:   }
 321: 
 322:   public Rectangle2D getMaxCharBounds (Font font, FontRenderContext rc)
 323:   {
 324:     throw new UnsupportedOperationException ();
 325:   }
 326: 
 327:   public int getMissingGlyphCode (Font font)
 328:   {
 329:     throw new UnsupportedOperationException ();
 330:   }
 331: 
 332:   public String getGlyphName (Font font, int glyphIndex)
 333:   {
 334:     throw new UnsupportedOperationException ();
 335:   }
 336: 
 337:   public int getNumGlyphs (Font font)
 338:   {
 339:     byte[] data = getTrueTypeTable((byte)'m', (byte) 'a', 
 340:                    (byte)'x', (byte) 'p');
 341:     if( data == null )
 342:       return -1;
 343: 
 344:     ByteBuffer buf = ByteBuffer.wrap( data );       
 345:     return buf.getShort(4);
 346:   }
 347: 
 348:   public Rectangle2D getStringBounds (Font font, CharacterIterator ci, 
 349:                                       int begin, int limit, FontRenderContext frc)
 350:   {
 351:     GlyphVector gv = new FreetypeGlyphVector( font, 
 352:                           buildString(ci, begin, limit),
 353:                           frc);
 354:     return gv.getVisualBounds();
 355:   }
 356: 
 357:   public boolean hasUniformLineMetrics (Font font)
 358:   {
 359:     return true;
 360:   }
 361: 
 362:   public GlyphVector layoutGlyphVector (Font font, FontRenderContext frc, 
 363:                                         char[] chars, int start, int limit, 
 364:                                         int flags)
 365:   {
 366:     return new FreetypeGlyphVector( font, new String( chars, start, 
 367:                               limit - start),
 368:                     frc, flags);
 369:   }
 370: 
 371:   public LineMetrics getLineMetrics (Font font, String str, 
 372:                                      FontRenderContext frc)
 373:   {
 374:     return new GdkFontLineMetrics (this, getFontMetrics (font), str.length ());
 375:   }
 376: 
 377:   public FontMetrics getFontMetrics (Font font)
 378:   {
 379:     // Get the font metrics through GtkToolkit to take advantage of
 380:     // the metrics cache.
 381:     return Toolkit.getDefaultToolkit().getFontMetrics (font);
 382:   }
 383: 
 384:   /**
 385:    * Returns a cached GlyphMetrics object for a given glyphcode,
 386:    * or null if it doesn't exist in the cache.
 387:    */
 388:   GlyphMetrics getGlyphMetrics( int glyphCode )
 389:   {
 390:     return (GlyphMetrics)metricsCache.get( new Integer( glyphCode ) );
 391:   }
 392: 
 393:   /**
 394:    * Put a GlyphMetrics object in the cache.
 395:    */ 
 396:   void putGlyphMetrics( int glyphCode, Object metrics )
 397:   {
 398:     metricsCache.put( new Integer( glyphCode ), metrics );
 399:   }
 400: }