Source for gnu.java.awt.font.OpenTypeFontPeer

   1: /* XFontPeer2.java -- A Java based TTF 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: package gnu.java.awt.font;
  39: 
  40: 
  41: import gnu.java.awt.peer.ClasspathFontPeer;
  42: import gnu.java.lang.CPStringBuilder;
  43: 
  44: import java.awt.Font;
  45: import java.awt.FontMetrics;
  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.AffineTransform;
  51: import java.awt.geom.Point2D;
  52: import java.awt.geom.Rectangle2D;
  53: import java.io.File;
  54: import java.io.FileInputStream;
  55: import java.io.IOException;
  56: import java.io.InputStream;
  57: import java.nio.ByteBuffer;
  58: import java.nio.channels.FileChannel;
  59: import java.text.CharacterIterator;
  60: import java.text.StringCharacterIterator;
  61: import java.util.HashMap;
  62: import java.util.HashSet;
  63: import java.util.Locale;
  64: import java.util.Map;
  65: import java.util.Properties;
  66: import java.util.Set;
  67: 
  68: public class OpenTypeFontPeer
  69:   extends ClasspathFontPeer
  70: {
  71: 
  72:   /**
  73:    * The font mapping as specified in the file fonts.properties.
  74:    */
  75:   private static Properties fontProperties;
  76: 
  77:   /**
  78:    * The available font family names.
  79:    */
  80:   private static Set<String> availableFontNames;
  81: 
  82:   /**
  83:    * Font spec to file mapping.
  84:    */
  85:   private static Map<String,Map<String,String>> fontToFileMap;
  86: 
  87:   static
  88:   {
  89:     fontProperties = new Properties();
  90:     InputStream in = OpenTypeFontPeer.class.getResourceAsStream("fonts.properties");
  91:     try
  92:       {
  93:         fontProperties.load(in);
  94:       }
  95:     catch (IOException e)
  96:       {
  97:         e.printStackTrace();
  98:       }
  99:   }
 100: 
 101:   private class XLineMetrics
 102:     extends LineMetrics
 103:   {
 104: 
 105:     private Font font;
 106:     private GlyphVector glyphVector;
 107: //    private CharacterIterator characterIterator;
 108: //    private int begin;
 109: //    private int limit;
 110:     private FontRenderContext fontRenderContext;
 111:     XLineMetrics(Font f, CharacterIterator ci, int b, int l,
 112:                  FontRenderContext rc)
 113:     {
 114:       font = f;
 115: //      characterIterator = ci;
 116: //      begin = b;
 117: //      limit = l;
 118:       fontRenderContext = rc;
 119:       glyphVector = fontDelegate.createGlyphVector(font, fontRenderContext,
 120:                                                    ci);
 121:     }
 122: 
 123:     public float getAscent()
 124:     {
 125:       return fontDelegate.getAscent(font.getSize(), fontRenderContext.getTransform(),
 126:                              fontRenderContext.isAntiAliased(),
 127:                              fontRenderContext.usesFractionalMetrics(), true);
 128:     }
 129: 
 130:     public int getBaselineIndex()
 131:     {
 132:       // FIXME: Implement this.
 133:       throw new UnsupportedOperationException("Not yet implemented");
 134:     }
 135: 
 136:     public float[] getBaselineOffsets()
 137:     {
 138:       // FIXME: Implement this.
 139:       throw new UnsupportedOperationException("Not yet implemented");
 140:     }
 141: 
 142:     public float getDescent()
 143:     {
 144:       return (int) fontDelegate.getDescent(font.getSize(), IDENDITY, false,
 145:                                            false, false);
 146:     }
 147: 
 148:     public float getHeight()
 149:     {
 150:       return (float) glyphVector.getLogicalBounds().getHeight();
 151:     }
 152: 
 153:     public float getLeading()
 154:     {
 155:       return getHeight() - getAscent() - getDescent();
 156:     }
 157: 
 158:     public int getNumChars()
 159:     {
 160:       // FIXME: Implement this.
 161:       throw new UnsupportedOperationException("Not yet implemented");
 162:     }
 163: 
 164:     public float getStrikethroughOffset()
 165:     {
 166:       return 0.F;
 167:     }
 168: 
 169:     public float getStrikethroughThickness()
 170:     {
 171:       return 0.F;
 172:     }
 173: 
 174:     public float getUnderlineOffset()
 175:     {
 176:       return 0.F;
 177:     }
 178: 
 179:     public float getUnderlineThickness()
 180:     {
 181:       return 0.F;
 182:     }
 183: 
 184:   }
 185: 
 186:   private class XFontMetrics
 187:     extends FontMetrics
 188:   {
 189:     /**
 190:      * A cached point instance, to be used in #charWidth().
 191:      */
 192:     private Point2D cachedPoint = new Point2D.Double();
 193: 
 194:     XFontMetrics(Font f)
 195:     {
 196:       super(f);
 197:     }
 198: 
 199:     public int getAscent()
 200:     {
 201:       return (int) fontDelegate.getAscent(getFont().getSize(), IDENDITY,
 202:                                           false, false, false);
 203:     }
 204: 
 205:     public int getDescent()
 206:     {
 207:       return (int) fontDelegate.getDescent(getFont().getSize(), IDENDITY,
 208:                                            false, false, false);
 209:     }
 210: 
 211:     public int getHeight()
 212:     {
 213:       GlyphVector gv = fontDelegate.createGlyphVector(getFont(),
 214:                     new FontRenderContext(IDENDITY, false, false),
 215:                     new StringCharacterIterator("m"));
 216:       Rectangle2D b = gv.getVisualBounds();
 217:       return (int) b.getHeight();
 218:     }
 219: 
 220:     public int charWidth(char c)
 221:     {
 222:       int code = fontDelegate.getGlyphIndex(c);
 223:       Point2D advance = cachedPoint;
 224:       fontDelegate.getAdvance(code, font.getSize2D(), IDENDITY,
 225:                               false, false, true, advance);
 226:       return (int) advance.getX();
 227:     }
 228: 
 229:     public int charsWidth(char[] chars, int offs, int len)
 230:     {
 231:       return stringWidth(new String(chars, offs, len));
 232:     }
 233: 
 234:     public int stringWidth(String s)
 235:     {
 236:       GlyphVector gv = fontDelegate.createGlyphVector(getFont(),
 237:                     new FontRenderContext(IDENDITY, false, false),
 238:                     new StringCharacterIterator(s));
 239:       Rectangle2D b = gv.getVisualBounds();
 240:       return (int) b.getWidth();
 241:     }
 242:   }
 243: 
 244:   /**
 245:    * The indendity transform, to be used in several methods.
 246:    */
 247:   private static final AffineTransform IDENDITY = new AffineTransform();
 248: 
 249:   private FontDelegate fontDelegate;
 250: 
 251:   public OpenTypeFontPeer(String name, int style, int size)
 252:   {
 253:     super(name, style, size);
 254:     try
 255:       {
 256:         String fontSpec = encodeFont(name, style);
 257:         String filename = mapFontToFilename(fontSpec);
 258:         File fontfile = new File(filename);
 259:         FileInputStream in = new FileInputStream(fontfile);
 260:         FileChannel ch = in.getChannel();
 261:         ByteBuffer buffer = ch.map(FileChannel.MapMode.READ_ONLY, 0,
 262:                                    fontfile.length());
 263:         fontDelegate = FontFactory.createFonts(buffer)[0];
 264:       }
 265:     catch (Exception ex)
 266:       {
 267:         ex.printStackTrace();
 268:       }
 269:   }
 270: 
 271:   public OpenTypeFontPeer(String name, Map atts)
 272:   {
 273:     super(name, atts);
 274:     try
 275:       {
 276:         String fontSpec = encodeFont(name, atts);
 277:         String filename = mapFontToFilename(fontSpec);
 278:         File fontfile = new File(filename);
 279:         FileInputStream in = new FileInputStream(fontfile);
 280:         FileChannel ch = in.getChannel();
 281:         ByteBuffer buffer = ch.map(FileChannel.MapMode.READ_ONLY, 0,
 282:                                    fontfile.length());
 283:         fontDelegate = FontFactory.createFonts(buffer)[0];
 284:       }
 285:     catch (Exception ex)
 286:       {
 287:         ex.printStackTrace();
 288:       }
 289:   }
 290: 
 291:   public boolean canDisplay(Font font, int c)
 292:   {
 293:     // FIXME: Implement this.
 294:     throw new UnsupportedOperationException("Not yet implemented");
 295:   }
 296: 
 297:   public int canDisplayUpTo(Font font, CharacterIterator i, int start, int limit)
 298:   {
 299:     // FIXME: Implement this.
 300:     throw new UnsupportedOperationException("Not yet implemented");
 301:   }
 302: 
 303:   public String getSubFamilyName(Font font, Locale locale)
 304:   {
 305:     // FIXME: Implement this.
 306:     throw new UnsupportedOperationException("Not yet implemented");
 307:   }
 308: 
 309:   public String getPostScriptName(Font font)
 310:   {
 311:     // FIXME: Implement this.
 312:     throw new UnsupportedOperationException("Not yet implemented");
 313:   }
 314: 
 315:   public int getNumGlyphs(Font font)
 316:   {
 317:     // FIXME: Implement this.
 318:     throw new UnsupportedOperationException("Not yet implemented");
 319:   }
 320: 
 321:   public int getMissingGlyphCode(Font font)
 322:   {
 323:     // FIXME: Implement this.
 324:     throw new UnsupportedOperationException("Not yet implemented");
 325:   }
 326: 
 327:   public byte getBaselineFor(Font font, char c)
 328:   {
 329:     // FIXME: Implement this.
 330:     throw new UnsupportedOperationException("Not yet implemented");
 331:   }
 332: 
 333:   public String getGlyphName(Font font, int glyphIndex)
 334:   {
 335:     // FIXME: Implement this.
 336:     throw new UnsupportedOperationException("Not yet implemented");
 337:   }
 338: 
 339:   public GlyphVector createGlyphVector(Font font, FontRenderContext frc, CharacterIterator ci)
 340:   {
 341:     return fontDelegate.createGlyphVector(font, frc, ci);
 342:   }
 343: 
 344:   public GlyphVector createGlyphVector(Font font, FontRenderContext ctx, int[] glyphCodes)
 345:   {
 346:     // FIXME: Implement this.
 347:     throw new UnsupportedOperationException("Not yet implemented");
 348:   }
 349: 
 350:   public GlyphVector layoutGlyphVector(Font font, FontRenderContext frc, char[] chars, int start, int limit, int flags)
 351:   {
 352:     StringCharacterIterator i = new StringCharacterIterator(new String(chars), start, limit, 0);
 353:     return fontDelegate.createGlyphVector(font, frc, i);
 354:   }
 355: 
 356:   public FontMetrics getFontMetrics(Font font)
 357:   {
 358:     return new XFontMetrics(font);
 359:   }
 360: 
 361:   public boolean hasUniformLineMetrics(Font font)
 362:   {
 363:     // FIXME: Implement this.
 364:     throw new UnsupportedOperationException("Not yet implemented");
 365:   }
 366: 
 367:   public LineMetrics getLineMetrics(Font font, CharacterIterator ci, int begin, int limit, FontRenderContext rc)
 368:   {
 369:     return new XLineMetrics(font, ci, begin, limit, rc);
 370:   }
 371: 
 372:   public Rectangle2D getMaxCharBounds(Font font, FontRenderContext rc)
 373:   {
 374:     // FIXME: Implement this.
 375:     throw new UnsupportedOperationException("Not yet implemented");
 376:   }
 377: 
 378:   /**
 379:    * Encodes a font name + style + size specification into a X logical font
 380:    * description (XLFD) as described here:
 381:    *
 382:    * http://www.meretrx.com/e93/docs/xlfd.html
 383:    *
 384:    * This is implemented to look up the font description in the
 385:    * fonts.properties of this package.
 386:    *
 387:    * @param name the font name
 388:    * @param atts the text attributes
 389:    *
 390:    * @return the encoded font description
 391:    */
 392:   public static String encodeFont(String name, Map atts)
 393:   {
 394:     String family = name;
 395:     if (family == null || family.equals(""))
 396:       family = (String) atts.get(TextAttribute.FAMILY);
 397:     if (family == null)
 398:       family = "SansSerif";
 399: 
 400:     int style = 0;
 401:     // Detect italic attribute.
 402:     Float posture = (Float) atts.get(TextAttribute.POSTURE);
 403:     if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR))
 404:       style |= Font.ITALIC;
 405: 
 406:     // Detect bold attribute.
 407:     Float weight = (Float) atts.get(TextAttribute.WEIGHT);
 408:     if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0)
 409:       style |= Font.BOLD;
 410: 
 411:     return encodeFont(name, style);
 412:   }
 413: 
 414:   /**
 415:    * Encodes a font name + style into a combined string.
 416:    *
 417:    * This is implemented to look up the font description in the
 418:    * fonts.properties of this package.
 419:    *
 420:    * @param name the font name
 421:    * @param style the font style
 422:    *
 423:    * @return the encoded font description
 424:    */
 425:   static String encodeFont(String name, int style)
 426:   {
 427:     CPStringBuilder key = new CPStringBuilder();
 428:     key.append(validName(name));
 429:     key.append('/');
 430:     switch (style)
 431:     {
 432:       case Font.BOLD:
 433:         key.append("b");
 434:         break;
 435:       case Font.ITALIC:
 436:         key.append("i");
 437:         break;
 438:       case (Font.BOLD | Font.ITALIC):
 439:         key.append("bi");
 440:         break;
 441:       case Font.PLAIN:
 442:       default:
 443:         key.append("p");
 444: 
 445:     }
 446: 
 447:     return key.toString();
 448:   }
 449: 
 450:   /**
 451:    * Checks the specified font name for a valid font name. If the font name
 452:    * is not known, then this returns 'sansserif' as fallback.
 453:    *
 454:    * @param name the font name to check
 455:    *
 456:    * @return a valid font name
 457:    */
 458:   static String validName(String name)
 459:   {
 460:     String retVal;
 461:     Set<String> fontNames = getFontNames();
 462:     if (fontNames.contains(name))
 463:       {
 464:         retVal = name;
 465:       }
 466:     else
 467:       {
 468:         retVal = "SansSerif";
 469:       }
 470:     return retVal;
 471:   }
 472: 
 473:   public static String[] getAvailableFontFamilyNames(Locale l)
 474:   {
 475:     Set<String> fontNames = getFontNames();
 476:     int numNames = fontNames.size();
 477:     String[] ret = fontNames.toArray(new String[numNames]);
 478:     return ret;
 479:   }
 480: 
 481:   private static synchronized Set<String> getFontNames()
 482:   {
 483:     if (availableFontNames == null)
 484:       {
 485:         HashSet<String> familyNames = new HashSet<String>();
 486:         for (Object o : fontProperties.keySet())
 487:           {
 488:             if (o instanceof String)
 489:               {
 490:                 String key = (String) o;
 491:                 int slashIndex = key.indexOf('/');
 492:                 String name = key.substring(0, slashIndex);
 493:                 familyNames.add(name);
 494:               }
 495:           }
 496:         availableFontNames = familyNames;
 497:       }
 498:     return availableFontNames;
 499:   }
 500: 
 501:   /**
 502:    * Takes a font spec as returned by {@link #encodeFont(String, int)},
 503:    * and returns the corresponding font file, or <code>null</code> if no such
 504:    * font mapping exists.
 505:    *
 506:    * @param fontSpec font name and style as returned by
 507:    *        {@link #encodeFont(String, int)}
 508:    *
 509:    * @return filename of the corresponding font file
 510:    */
 511:   private synchronized String mapFontToFilename(String fontSpec)
 512:   {
 513:     if (fontToFileMap == null)
 514:       {
 515:         fontToFileMap = new HashMap<String,Map<String,String>>();
 516: 
 517:         // Initialize font spec to file mapping according to the
 518:         // font.properties.
 519:         for (Object o : fontProperties.keySet())
 520:           {
 521:             if (o instanceof String)
 522:               {
 523:                 String key = (String) o;
 524:                 int slashIndex = key.indexOf('/');
 525:                 String name = key.substring(0, slashIndex);
 526:                 String spec = key.substring(slashIndex + 1);
 527:                 // Handle aliases in the 2nd pass below.
 528:                 if (! spec.equals("a"))
 529:                   {
 530:                     Map<String,String> specToFileMap = fontToFileMap.get(name);
 531:                     if (specToFileMap == null)
 532:                       {
 533:                         specToFileMap = new HashMap<String,String>();
 534:                         fontToFileMap.put(name, specToFileMap);
 535:                       }
 536:                     specToFileMap.put(spec, fontProperties.getProperty(key));
 537:                   }
 538:               }
 539:           }
 540:         // 2nd pass for handling aliases.
 541:         for (Object o : fontProperties.keySet())
 542:           {
 543:             if (o instanceof String)
 544:               {
 545:                 String key = (String) o;
 546:                 int slashIndex = key.indexOf('/');
 547:                 String name = key.substring(0, slashIndex);
 548:                 String spec = key.substring(slashIndex + 1);
 549:                 // Handle aliases in the 2nd pass below.
 550:                 if (spec.equals("a"))
 551:                   {
 552:                     String alias = fontProperties.getProperty(key);
 553:                     Map<String,String> specToFileMap = fontToFileMap.get(alias);
 554:                     fontToFileMap.put(name, specToFileMap);
 555:                   }
 556:               }
 557:           }
 558:       }
 559:     // Look up font file.
 560:     int slashIndex = fontSpec.indexOf('/');
 561:     String name = fontSpec.substring(0, slashIndex);
 562:     String spec = fontSpec.substring(slashIndex + 1);
 563:     return fontToFileMap.get(name).get(spec);
 564:   }
 565: }