home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / awt / Font.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  56.3 KB  |  1,716 lines

  1. /*
  2.  * @(#)Font.java    1.72 98/03/18
  3.  *
  4.  * Copyright 1995-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.awt;
  16.  
  17. import java.awt.font.GlyphMetrics;
  18. import java.awt.font.GlyphSet;
  19. import java.awt.font.GlyphJustificationInfo;
  20. import java.awt.font.TextAttributeSet;
  21. //import java.awt.font.TextAttributes;
  22. import java.awt.geom.AffineTransform;
  23. import java.awt.geom.Point2D;
  24. import java.awt.geom.Rectangle2D;
  25. import java.awt.peer.FontPeer;
  26. import java.text.AttributeSet;
  27. import java.text.AttributedCharacterIterator;
  28. import java.text.CharacterIterator;
  29. import java.text.StringCharacterIterator;
  30. import java.util.Locale;
  31. import java.util.Hashtable;
  32. import sun.awt.font.FontObject;
  33. import sun.awt.font.Ligaturizer;
  34.  
  35. /**
  36.  * This class represents fonts.  The capabilities of this class have been
  37.  * extended over the java.awt.Font class in JDK 1.1 and earlier releases
  38.  * to provide developers the ability to utilize more sophisticated
  39.  * typographic features.
  40.  * <p>
  41.  * It is important to present the concepts behind using the words
  42.  * character and glyph separately. A <b>character</b> is a symbol that
  43.  * represents items like letters and numbers in a given writing system,
  44.  * for example <i>lowercase-g</i>. When a given character is drawn, a shape
  45.  * now represents this character. This shape is called a <b>glyph</b>.
  46.  * <p>
  47.  * Chararcter encoding is a conversion table that maps character codes
  48.  * to glyph codes in the font.  The character encoding used in the Java 2D
  49.  * API is Unicode.  For more information on Unicode you may visit the site
  50.  * <a href="http://www.unicode.org">http://www.unicode.org</a>.
  51.  * <p>
  52.  * Characters and glyphs do not have one-to-one correspondence. As an example
  53.  * <i>lowercase-a acute</i> can be represented by two glyphs: <i>lowercase-a</i>
  54.  * and <i>acute</i>.
  55.  * Another example is ligatures such as <i>ligature -fi</i> which is a
  56.  * single glyph representing two characters: <i>f</i> and <i>i</i>.
  57.  * <p>
  58.  * A font is a collection of glyphs. A font may have many faces, e.g.
  59.  * heavy, medium, oblique, gothic and regular. All of these faces have
  60.  * similar typographic design.
  61.  * <p>
  62.  * There are three different names that you can get from a Font object.
  63.  * The <i>logical font name</i>is the same as used by java.awt.Font in
  64.  * JDK 1.1 and earlier releases.  The java.awt.Toolkit.getFontList() method
  65.  * returns a short list of these logical names which are mapped onto specific
  66.  * fonts available on specific platforms.  The <i>font face name</i>,
  67.  * or just <i>font name</i> for short, is the name of a particular font
  68.  * face, like Helvetica Bold. The <i>family name</i> is the name
  69.  * of the font family that determines the typographic design across several
  70.  * faces, like Helvetica. The font face name
  71.  * is the one that should be used to specify fonts. This name signifies
  72.  * actual fonts in the host system, and does not identify font
  73.  * names with shape of font characters as the logical font name does.
  74.  * <p>
  75.  * The Font class represents an instance of a font face from a
  76.  * collection of  font faces that are present in the system resources
  77.  * of the host system.  As examples, Helvetica Bold and Courier Bold Italic
  78.  * are font faces.  There can be several Font objects associated with
  79.  * a font face, each differing in size, style, transform and font features.
  80.  * GraphicsEnvironment.getAllFonts() returns an array of all font faces
  81.  * available in the system. These font faces are returned as Font objects
  82.  * with a size of 1, identity transform and default font features. These
  83.  * base fonts can then be used to derive new Font objects with varying
  84.  * sizes, styles, transforms and font features via the deriveFont methods
  85.  * in this class.
  86.  * @see java.awt.Toolkit#getFontList
  87.  * @see GraphicsEnvironment#getFonts
  88.  * @see GraphicsEnvironment#getAllFonts
  89.  * @version 10 Feb 1997
  90.  */
  91. public class Font implements java.io.Serializable
  92. {
  93.    
  94.     static {
  95.     /* force the native libraries to be loaded */
  96.     Toolkit.loadLibraries();
  97.         initIDs();
  98.     }
  99.  
  100.     private TextAttributeSet fRequestedAttributes;
  101.     private transient GlyphMetrics[] fMetricsCache;
  102.     private transient FontObject fntObj;
  103.     private transient GlyphJustificationInfo[] fJustificationCache;
  104.  
  105.     private transient int fontObjectID;
  106.     private transient int numGlyphs = -1;
  107.     /*
  108.      * A default font for general-purpose rendering.
  109.      */
  110.     public static final Font DEFAULT = new Font(TextAttributeSet.EMPTY);
  111.  
  112.     /*
  113.      * Constants to be used for styles. Can be combined to mix
  114.      * styles.
  115.      */
  116.  
  117.     /**
  118.      * The plain style constant.
  119.      */
  120.     public static final int PLAIN    = 0;
  121.  
  122.     /**
  123.      * The bold style constant.  This can be combined with the other style
  124.      * constants (except PLAIN) for mixed styles.
  125.      */
  126.     public static final int BOLD    = 1;
  127.  
  128.     /**
  129.      * The italicized style constant.  This can be combined with the other
  130.      * style constants (except PLAIN) for mixed styles.
  131.      */
  132.     public static final int ITALIC    = 2;
  133.  
  134.  
  135.     public static final byte ROMAN_BASELINE = 0;
  136.     public static final byte CENTER_BASELINE = 1;
  137.     public static final byte HANGING_BASELINE = 2;
  138.     
  139.     /**
  140.      * Private data.
  141.      */
  142.     transient private long pData;
  143.  
  144.     /**
  145.      * The platform specific family name of this font.
  146.      */
  147.     transient private String family;
  148.  
  149.     /**
  150.      * The logical name of this font.
  151.      * @since JDK1.0
  152.      */
  153.     protected String name;
  154.     protected String fontName;
  155.  
  156.     /**
  157.      * The style of the font, as passed to the constructor.  This may be
  158.      * PLAIN, BOLD, ITALIC, or BOLD+ITALIC.
  159.      */
  160.     protected int style;
  161.  
  162.     /**
  163.      * The point size of this font, rounded to integer.
  164.      */
  165.     protected int size;
  166.  
  167.     /**
  168.      * The point size of this font in float.
  169.      */
  170.     private float pointSize;
  171.     private float ascent = -1;
  172.     private float descent = -1;
  173.     private float leading = -1;
  174.     private float maxAdvance = -1;
  175.  
  176.     /**
  177.      * Info about the font.
  178.      */
  179.     //private FontDescriptor  fontDescriptor;
  180.  
  181.     /**
  182.      * The platform specific font information.
  183.      */
  184.     transient FontPeer peer;
  185.  
  186.     /*
  187.      * JDK 1.1 serialVersionUID
  188.      */
  189.     private static final long serialVersionUID = -4206021311591459213L;
  190.  
  191.     /**
  192.      * Gets the peer of the font.
  193.      * @return  the peer of the font.
  194.      * @since JDK1.1
  195.      */
  196.     public FontPeer getPeer(){
  197.     return peer;
  198.     }
  199.  
  200.     /* package */
  201.     boolean usesVerticalMetrics(){
  202.         //REMIND jk. Need to use the AttributeSet to define orientation
  203.         return false;
  204.     }
  205.  
  206.     private void initializeFont(TextAttributeSet attributes){
  207.         try {
  208.             java.security.AccessController.beginPrivileged();
  209.             family = System.getProperty("awt.font." + name.toLowerCase(), name);
  210.     } finally {
  211.             java.security.AccessController.endPrivileged();
  212.     }
  213.         this.peer = Toolkit.getDefaultToolkit().getFontPeer(name, style);
  214.  
  215.         fontObjectID = -1;
  216.         /* need to do this more efficiently */
  217.         GraphicsEnvironment env =
  218.             GraphicsEnvironment.getLocalGraphicsEnvironment();
  219.         Font theFont = env.getFont(name);
  220.         name = theFont.getFontName();
  221.         if (theFont != null) {
  222.             if (attributes == null) {
  223.                 fRequestedAttributes =
  224.                     (TextAttributeSet) ffApply(name, style, size,
  225.                                                ffApply(new AffineTransform(),
  226.                                                        TextAttributeSet.EMPTY));
  227.             } else {
  228.                 fRequestedAttributes =
  229.                     (TextAttributeSet) ffApply(style, attributes);
  230.             }
  231.             fntObj = theFont.fntObj;
  232.             // REMIND: Goes away with the new architecture
  233.             name = fntObj.getFontFullName(0);
  234.             pointSize = size;
  235.         }
  236.     }
  237.  
  238.     /**
  239.      * Creates a new font from the specified name, style and point size.
  240.      * @param name the font name.  This can be a logical font name or a
  241.      * font face name.
  242.      * @param style the style constant for the font.
  243.      * @param size the point size of the font.
  244.      * @see java.awt.Toolkit#getFontList
  245.      * @see GraphicsEnvironment#getFonts
  246.      * @see GraphicsEnvironment#getAllFonts
  247.      */
  248.     public Font(String name, int style, int size) {
  249.     this.name = name;
  250.     this.style = style;
  251.     this.size = size;
  252.     initializeFont(null);
  253.     }
  254.  
  255.     /* REMIND jk . old architecture. get rid of this*/
  256.     public Font(FontObject fontObject){
  257.         fntObj = fontObject;
  258.         this.fontObjectID = -1;
  259.         this.name = fntObj.getFontFullName(0);
  260.         this.style = Font.PLAIN;
  261.         this.fontName = name;
  262.         this.size = 1;
  263.         fRequestedAttributes =
  264.             (TextAttributeSet) ffApply(name, style, size,
  265.                                        ffApply(new AffineTransform(),
  266.                                                TextAttributeSet.EMPTY));
  267.     }
  268.  
  269.     public int getFontID() {
  270.         if (fontObjectID == -1) {
  271.             fontObjectID = fntObj.findFontObj(name, style);
  272.         }
  273.         return fontObjectID;
  274.     }
  275.  
  276.     /**
  277.      *    Create a new font with the specified attributes
  278.      */
  279.     public Font(AttributeSet attributes){
  280.         if((attributes != null) &&
  281.            (!attributes.equals(TextAttributeSet.EMPTY)))
  282.         {
  283.             Object obj;
  284.             fRequestedAttributes = (TextAttributeSet) attributes;
  285.             if ((obj = attributes.get(TextAttributeSet.FAMILY)) != null) {
  286.                 this.name = (String)obj;
  287.             }
  288.             // set the style to PLAIN as default
  289.             this.style = PLAIN;
  290.             if ((obj = attributes.get(TextAttributeSet.WEIGHT)) != null){
  291.                 if(obj.equals(TextAttributeSet.WEIGHT_BOLD)) {
  292.                     this.style += BOLD;
  293.                 }
  294.             }
  295.  
  296.             if ((obj = attributes.get(TextAttributeSet.POSTURE)) != null){
  297.                 if(obj.equals(TextAttributeSet.POSTURE_ITALIC)) {
  298.                     this.style += ITALIC;
  299.                 }
  300.             }
  301.  
  302.             if ((obj = attributes.get(TextAttributeSet.SIZE)) != null){
  303.                 this.size = ((Float)obj).intValue();
  304.             }
  305.             initializeFont(fRequestedAttributes);
  306.         }
  307.     }
  308.  
  309.     private static Hashtable fontCache = new Hashtable();
  310.  
  311.      /**
  312.      * Returns a font appropriate to this attribute set.
  313.      */
  314.     public static Font getFont(AttributeSet attributes) {
  315.         Font font = (Font)attributes.get(TextAttributeSet.FONT);
  316.         if (font != null) {
  317.             return font;
  318.         }
  319.  
  320.         font = (Font)fontCache.get(attributes);
  321.         if (font != null) {
  322.             return font;
  323.         }
  324.  
  325.         font = new Font(attributes);
  326.         fontCache.put(attributes, font);
  327.  
  328.         return font;
  329.     }
  330.  
  331.     /**
  332.      * Returns the transform associated with this font.
  333.      */
  334.     public AffineTransform getTransform(){
  335.         return (AffineTransform)fRequestedAttributes.
  336.             get(TextAttributeSet.TRANSFORM);
  337.     }
  338.  
  339.     /**
  340.      * Returns the family name of the font (for example, Helvetica).
  341.      * Use getName to get the logical name of the font.
  342.      * Use getFontName to get the font face name of the font.
  343.      * @see #getName
  344.      * @see #getFontName
  345.      */
  346.  
  347.     public String getFamily() {
  348.         if (family == null) {
  349.             family = fntObj.getFamilyName(getFontID());
  350.         }
  351.     return family;
  352.     }
  353.  
  354.     /**
  355.      * Returns the family name of the font (for example, Helvetica), localized
  356.      * for the given Locale.
  357.      * Use getFontName to get the font face name of the font.
  358.      * @param l Locale for which to get the family name.
  359.      * @see #getFontName
  360.      * @see java.util.Locale
  361.      */
  362.     public String getFamily(Locale l) {
  363.         if (family == null) {
  364.             family = fntObj.getFamilyName(getFontID());
  365.         }
  366.     return family;
  367.     }
  368.  
  369.     /**
  370.      * Returns the postscript name of the font.
  371.      * Use getFamily to get the family name of the font.
  372.      * Use getFontName to get the font face name of the font.
  373.      */
  374.     public String getPSName() {
  375.     return fontName;
  376.     }
  377.  
  378.     /**
  379.      * Returns the logical name of the font.
  380.      * Use getFamily to get the family name of the font.
  381.      * Use getFontName to get the font face name of the font.
  382.      * @see #getFamily
  383.      * @see #getFontName
  384.      */
  385.     public String getName() {
  386.     return name;
  387.     }
  388.  
  389.     /**
  390.      * Returns the font face name of the font (for example, Helvetica Bold).
  391.      * Use getFamily to get the family name of the font.
  392.      * Use getName to get the logical name of the font.
  393.      * @see #getFamily
  394.      * @see #getName
  395.      */
  396.     public String getFontName() {
  397.         return fntObj.getFontFullName(getFontID());
  398.         //return fontName;
  399.     }
  400.  
  401.     /**
  402.      * Returns the font face name of the font (for example, Helvetica Fett)
  403.      * localized for the specified locale.
  404.      * Use getFamily to get the family name of the font.
  405.      * @param l Get the localized font face name for this locale.
  406.      * @see #getFamily
  407.      * @see java.util.Locale
  408.      */
  409.     public String getFontName(Locale l) {
  410.         return fntObj.getFontFullName(getFontID());
  411.     }
  412.  
  413.     /**
  414.      * Returns the style of the font.  This may be
  415.      * PLAIN, BOLD, ITALIC, or BOLD+ITALIC.
  416.      * @see #isPlain
  417.      * @see #isBold
  418.      * @see #isItalic
  419.      * @since JDK1.0
  420.      */
  421.     public int getStyle() {
  422.     return style;
  423.     }
  424.  
  425.     /**
  426.      * Returns the point size of the font, rounded to integer.
  427.      * Most users are familiar with the idea of using <i>point size</i> to
  428.      * specify the size of glyphs in a font. This point size defines a
  429.      * measurement between the baseline of one line to the baseline of the
  430.      * following line in a single spaced text document. The point size is
  431.      * based on <i>typographic points</i>, approximately 1/72 of an inch.
  432.      * <p>
  433.      * The Java2D API adopts the convention that one point is equivalent
  434.      * to one unit in user coordinates.  When using a normalized transform
  435.      * for converting user space coordinates to device space coordinates
  436.      * (see GraphicsConfiguration.getDefaultTransform() and
  437.      * GraphicsConfiguration.getNormalizingTransform()), 72 user space units
  438.      * equal 1 inch in device space.  In this case one point is 1/72 of an
  439.      * inch.
  440.      * @see #getSize2D
  441.      * @see GraphicsConfiguration#getDefaultTransform
  442.      * @see GraphicsConfiguration#getNormalizingTransform
  443.      */
  444.     public int getSize() {
  445.     return size;
  446.     }
  447.  
  448.     /**
  449.      * Returns the point size of the font in float.
  450.      * @see #getSize
  451.      */
  452.     public float getSize2D() {
  453.     return pointSize;
  454.     }
  455.  
  456.     /**
  457.      * Returns true if the font is plain.
  458.      * @see #getStyle
  459.      */
  460.     public boolean isPlain() {
  461.     return style == 0;
  462.     }
  463.  
  464.     /**
  465.      * Indicates whether the font's style is bold.
  466.      * @return    <code>true</code> if the font is bold;
  467.      *            <code>false</code> otherwise.
  468.      * @see       java.awt.Font#getStyle
  469.      * @since     JDK1.0
  470.      */
  471.     public boolean isBold() {
  472.     return (style & BOLD) != 0;
  473.     }
  474.  
  475.     /**
  476.      * Indicates whether the font's style is italic.
  477.      * @return    <code>true</code> if the font is italic;
  478.      *            <code>false</code> otherwise.
  479.      * @see       java.awt.Font#getStyle
  480.      * @since     JDK1.0
  481.      */
  482.     public boolean isItalic() {
  483.     return (style & ITALIC) != 0;
  484.     }
  485.  
  486.     /**
  487.      * Returns a font from the system properties list.
  488.      * @param nm the property name.
  489.      */
  490.     public static Font getFont(String nm) {
  491.     SecurityManager security = System.getSecurityManager();
  492.     if (security != null) {
  493.       security.checkPropertyAccess(nm);
  494.     }
  495.     return getFont(nm, null);
  496.     }
  497.  
  498.     /**
  499.      * Returns the specified font using the name passed in.
  500.      * @param str the name.
  501.      */
  502.     public static Font decode(String str) {
  503.     String fontName = str;
  504.     int fontSize = 12;
  505.     int fontStyle = Font.PLAIN;
  506.  
  507.     int i = str.indexOf('-');
  508.     if (i >= 0) {
  509.         fontName = str.substring(0, i);
  510.         str = str.substring(i+1);
  511.         if ((i = str.indexOf('-')) >= 0) {
  512.         if (str.startsWith("bold-")) {
  513.             fontStyle = Font.BOLD;
  514.         } else if (str.startsWith("italic-")) {
  515.             fontStyle = Font.ITALIC;
  516.         } else if (str.startsWith("bolditalic-")) {
  517.             fontStyle = Font.BOLD | Font.ITALIC;
  518.         }
  519.         str = str.substring(i + 1);
  520.         }
  521.         try {
  522.         fontSize = Integer.valueOf(str).intValue();
  523.         } catch (NumberFormatException e) {
  524.         }
  525.     }
  526.     return new Font(fontName, fontStyle, fontSize);
  527.     }
  528.  
  529.     /**
  530.      * Returns the specified font from the system properties list.
  531.      * @param nm the property name.
  532.      * @param font a default font to return if property 'nm' is not defined.
  533.      */
  534.     public static Font getFont(String nm, Font font) {
  535.     String text;
  536.     try {
  537.         java.security.AccessController.beginPrivileged();
  538.         text = System.getProperty(nm);
  539.     } finally {
  540.         java.security.AccessController.endPrivileged();
  541.     }
  542.         if (text == null)
  543.             // return font;
  544.             text = nm;
  545.  
  546.         // parse string for name, name-style, name-size, or name-style-size.
  547.         // style can be 'bold', 'italic', or 'bolditalic'
  548.  
  549.         String namestr = null;
  550.         String stylestr = null;
  551.         String sizestr = null;
  552.  
  553.         int h1 = text.indexOf('-');
  554.         if (h1 != -1) {
  555.             int h2 = text.indexOf('-', h1 + 1);
  556.             if (h2 != -1) {
  557.                 sizestr = text.substring(h2+1, text.length());
  558.             } else {
  559.                 h2 = text.length();
  560.             }
  561.             stylestr = text.substring(h1+1, h2);
  562.         } else {
  563.             h1 = text.length();
  564.         }
  565.         namestr = text.substring(0, h1);
  566.  
  567.         int style = Font.PLAIN;
  568.         if (stylestr != null) {
  569.             if (stylestr.equals("bold"))
  570.                 style = Font.BOLD;
  571.             else if (stylestr.equals("italic"))
  572.                 style = Font.ITALIC;
  573.             else if (stylestr.equals("bolditalic"))
  574.                 style = Font.BOLD + Font.ITALIC;
  575.             else if (sizestr == null)
  576.                 sizestr = stylestr;
  577.         }
  578.  
  579.         float size = 18.0f;
  580.         if (sizestr != null) {
  581.             try {
  582.                 size = Float.valueOf(sizestr).floatValue();
  583.             }
  584.             catch (NumberFormatException e) {
  585.                 System.out.println(e);
  586.             }
  587.         }
  588.  
  589.         return new Font(namestr, style, (int)size);
  590.     }
  591.  
  592.     /**
  593.      * Returns a hashcode for this font.
  594.      * @return     a hashcode value for this font.
  595.      * @since      JDK1.0
  596.      */
  597.     public int hashCode() {
  598.     return name.hashCode() ^ style ^ size;
  599.     }
  600.  
  601.     /**
  602.      * Compares this object to the specified object.
  603.      * @param obj the object to compare with.
  604.      * @return true if the objects are the same; false otherwise.
  605.      */
  606.     public boolean equals(Object obj) {
  607.     if (obj instanceof Font) {
  608.         Font font = (Font)obj;
  609.         return (size == font.size) && (style == font.style) && name.equals(font.name);
  610.     }
  611.     return false;
  612.     }
  613.  
  614.     /**
  615.      * Converts this object to a String representation.
  616.      * @return     a string representation of this object
  617.      * @since      JDK1.0
  618.      */
  619.     public String toString() {
  620.     String    strStyle;
  621.  
  622.     if (isBold()) {
  623.         strStyle = isItalic() ? "bolditalic" : "bold";
  624.     } else {
  625.         strStyle = isItalic() ? "italic" : "plain";
  626.     }
  627.  
  628.     return getClass().getName() + "[family=" + family + ",name=" + name + ",style=" +
  629.         strStyle + ",size=" + size + "]";
  630.     }
  631.  
  632.  
  633.     /* Serialization support.  A readObject method is neccessary because
  634.      * the constructor creates the fonts peer, and we can't serialize the
  635.      * peer.  Similarly the computed font "family" may be different
  636.      * at readObject time than at writeObject time.  An integer version is
  637.      * written so that future versions of this class will be able to recognize
  638.      * serialized output from this one.
  639.      */
  640.  
  641.     private int fontSerializedDataVersion = 1;
  642.  
  643.     private void writeObject(java.io.ObjectOutputStream s)
  644.       throws java.lang.ClassNotFoundException,
  645.          java.io.IOException
  646.     {
  647.       s.defaultWriteObject();
  648.     }
  649.  
  650.     private void readObject(java.io.ObjectInputStream s)
  651.       throws java.lang.ClassNotFoundException,
  652.          java.io.IOException
  653.     {
  654.       s.defaultReadObject();
  655.       // REMIND jk need to pass the TextAttributeSet read from stream
  656.       initializeFont(null);
  657.     }
  658.  
  659.     /**
  660.      * Returns number of glyphs in the font. Glyph codes for the font
  661.      * will range from 0 to getNumGlyphs() - 1.
  662.      */
  663.     public  int getNumGlyphs(){
  664.         if (numGlyphs == -1) {
  665.             numGlyphs = fntObj.getNumGlyphs();
  666.         }
  667.         return numGlyphs;
  668.     }
  669.  
  670.     public int getMissingGlyphCode(){
  671.         return fntObj.getMissingGlyphCode();
  672.     }
  673.  
  674.     public boolean isVerticalBaseline() {
  675.         return false;
  676.     }
  677.     /**
  678.      * Returns the metrics information for a glyph specified by a glyph code.
  679.      * @param glyphCode the glyph to get metrics for.
  680.      * @return a GlyphMetrics object with metric information for the glyph.
  681.      */
  682.     public GlyphMetrics getGlyphMetrics(int glyphCode){
  683.         // !!! unrotated text only
  684.         // !!! a total hack since we don't really have character bounding boxes.
  685.  
  686.         if (glyphCode >= getNumGlyphs()) {
  687.             glyphCode = getMissingGlyphCode();
  688.         }
  689.  
  690.         if (fMetricsCache == null) {
  691.             fMetricsCache = new GlyphMetrics[getNumGlyphs()];
  692.         }
  693.  
  694.         GlyphMetrics gm = fMetricsCache[glyphCode];
  695.         if (gm == null) {
  696.  
  697.             float lsb = 0;
  698.             float rsb = 0;
  699.  
  700.             float x = 0f;
  701.             float y = getHeight();
  702.             byte glyphType = GlyphMetrics.STANDARD;
  703.  
  704.             x = getAdvance(glyphCode);
  705.             glyphType = fntObj.getGlyphType(glyphCode);
  706.             
  707.             if (isVerticalBaseline()) {
  708.                 gm = new GlyphMetrics(y,
  709.                                       new Rectangle2D.Float(-x/2f, 0, x,
  710.                                                             getAscent() +
  711.                                                             getDescent()),
  712.                                       glyphType);
  713.             } else {
  714.  
  715.                 gm = new GlyphMetrics(x,
  716.                                       new Rectangle2D.Float(lsb,
  717.                                                             (-getAscent()),
  718.                                                             x - lsb - rsb,
  719.                                                             getAscent() +
  720.                                                             getDescent()),
  721.                                       glyphType);
  722.             }
  723.  
  724.             fMetricsCache[glyphCode] = gm;
  725.         }
  726.         return gm;
  727.     }
  728.  
  729.   /**
  730.      * Return the baseline appropriate for displaying this character.
  731.      * <p>
  732.      * Large fonts can support different writing systems, and each system can
  733.      * use a different baseline.
  734.      * The character argument determines the writing system to use. Clients
  735.      * should not assume all characters will use the same baseline.
  736.      *
  737.      * @param c a character used to identify the writing system
  738.      * @see #getBaselineOffsetsFor
  739.      * @see #ROMAN_BASELINE
  740.      * @see #CENTER_BASELINE
  741.      * @see #HANGING_BASELINE
  742.      */
  743.     public byte getBaselineFor(char c) {
  744.         return fntObj.getBaselineFor(c);
  745.     }
  746.  
  747.     /**
  748.      * Return a list of relative offsets for the different baselines.
  749.      * <p>
  750.      * Large fonts can support different writing systems, and each system can
  751.      * have its own set of preferred baseline offsets.  The character argument
  752.      * determines the writing system to use.
  753.      * <p>
  754.      * These all are relative to the most common baseline used with this font
  755.      * (the one from which ascent and descent are measured).  Negative values
  756.      * are up on horizontal lines, and right on vertical lines.
  757.      *
  758.      * @param c a character used to identify the writing system
  759.      * @see #getBaselineFor
  760.      * @see #ROMAN_BASELINE
  761.      * @see #CENTER_BASELINE
  762.      * @see #HANGING_BASELINE
  763.      */
  764.     public float[] getBaselineOffsetsFor(char c) {
  765.         float[] baselines = new float[3];
  766.  
  767.         baselines[ROMAN_BASELINE] = 0;
  768.         baselines[CENTER_BASELINE] = (getAscent() + getDescent()) / 2 -
  769.             getAscent();
  770.         baselines[HANGING_BASELINE] = getDescent() - getAscent();
  771.         return baselines;
  772.     }
  773.  
  774.     /**
  775.      * Return true if this font places all glyphs on a single baseline.
  776.      */
  777.     public boolean isUniformBaseline() {
  778.         return fntObj.isUniformBaseline();
  779.     }
  780.  
  781.     /**
  782.      *
  783.      * Return the index of the first character with a different baseline
  784.      * from the first character in the range, or end index if all characters
  785.      * in the range have the same baseline.
  786.      */
  787.     public int sameBaselineUpTo(java.text.CharacterIterator iter) {
  788.         return fntObj.sameBaselineUpTo(iter);
  789.     }
  790.  
  791.     /**
  792.      *
  793.      * Return the index of the first character with a different baseline from the
  794.      * character at start, or limit if all characters between start and limit have
  795.      * the same baseline.
  796.      */
  797.     public int sameBaselineUpTo(char[] text, int start, int limit) {
  798.         return fntObj.sameBaselineUpTo(text, start, limit);
  799.     }
  800.  
  801.     /**
  802.      * Return the underline offset from the roman baseline for this character.
  803.      */
  804.     public float getUnderlineOffsetFor(char c) {
  805.         return fntObj.getUnderlineOffsetFor(c);
  806.     }
  807.  
  808.     /**
  809.      * Return the underline thickness for this character.  Zero means the
  810.      * character is not underlined.
  811.      */
  812.     public float getUnderlineThicknessFor(char c) {
  813.         return fntObj.getUnderlineThicknessFor(c);
  814.     }
  815.  
  816.     /**
  817.      * Return the strikethrough offset from the roman baseline for this
  818.      * character.
  819.      */
  820.     public float getStrikethroughOffsetFor(char c) {
  821.         return fntObj.getStrikethroughOffsetFor(c, getFontID());
  822.     }
  823.  
  824.     /**
  825.      * Return the strikethrough thickness for this character.  Zero means the
  826.      * character is
  827.      * not underlined.
  828.      */
  829.     public float getStrikethroughThicknessFor(char c) {
  830.         return fntObj.getStrikethroughThicknessFor(c);
  831.     }
  832.  
  833.     /**
  834.      * Returns the outline description of a glyph specified by a glyph code.
  835.      * @param glyphCode the glyph for which to get the outline.
  836.      * @param x the <i>x</i> coordinate for the glyph's position.
  837.      * @param y the <i>y</i> coordinate for the glyph's position, 
  838.      *          indicating the font baseline for the glyph.
  839.      * @return a Shape object representing the outline of the glyph.
  840.      */
  841.     public Shape getGlyphOutline(int glyphCode, float x, float y){
  842.         int[] glyphCodes = new int[1];
  843.         glyphCodes[0] = glyphCode;
  844.  
  845.         Point2D origin = new Point2D.Float(x,y);
  846.  
  847.         return fntObj.createOutline(1,glyphCodes,
  848.                                     origin, null, null, null,
  849.                                     (float)size,false, getFontID());
  850.     }
  851.  
  852.     public Shape getOutline(GlyphSet glyphs, AffineTransform tx, float x,
  853.                             float y) {
  854.         int glyphCount = glyphs.getNumGlyphs();
  855.         int[] glyphCodes = glyphs.getGlyphCodes();
  856.         float[] xpos = ((StandardGlyphSet)glyphs).getXAdvances( true );
  857.         float[] ypos =  ((StandardGlyphSet)glyphs).getYAdvances( true );
  858.  
  859.         Point2D origin = new Point2D.Float(x,y);
  860.  
  861.         return fntObj.createOutline(glyphCount,glyphCodes,
  862.                                     origin,xpos,
  863.                                     ypos,tx,
  864.                                     (float)size,false,getFontID());
  865.     }
  866.  
  867.     public float[] getMetrics(GlyphSet glyphs, AffineTransform tx) {
  868.         int[] codes = glyphs.getGlyphCodes();
  869.         return fntObj.getMetrics(codes.length, codes, null, getSize2D(),
  870.                                  getFontID());
  871.     }
  872.  
  873.     public float getAdvance(char c) {
  874.         return fntObj.getAdvance(c, null, getSize2D(), getFontID());
  875.     }
  876.  
  877.     public float getAdvance(char[] text) {
  878.         return fntObj.getAdvance(text, null, getSize2D(), getFontID());
  879.     }
  880.  
  881.     float getAdvance(int c) {
  882.         return fntObj.getAdvance(c, null, getSize2D(), getFontID());
  883.     }
  884.  
  885.     /**
  886.      * Returns an array of font attributes available in this font.
  887.      * Attributes include things like ligatures and glyph substitution.
  888.      * @return a FontAttribute array.
  889.      */
  890.     public AttributeSet getAttributes(){
  891.         return fRequestedAttributes;
  892.     }
  893.  
  894.     /**
  895.      * Returns the names of all the attributes supported by this font.
  896.      * These attributes may be used to derive other fonts.
  897.      */
  898.     public String[] getAvailableAttributes(){
  899.         String attributes[] = {
  900.             TextAttributeSet.FAMILY,
  901.             TextAttributeSet.WEIGHT,
  902.             TextAttributeSet.POSTURE,
  903.             TextAttributeSet.SIZE
  904.         };
  905.  
  906.         return attributes;
  907.     }
  908.  
  909.     /**
  910.      * Return justification info for the glyph specified by glyphCode.
  911.      *
  912.      * If left is true, return info for the left side of the glyph, otherwise
  913.      * for the right.
  914.      * If grow is true, return info for growing a line, otherwise for
  915.      * shrinking it.
  916.      *
  917.      * !!! unify into one set of information per glyph?
  918.      * Then it's the accessing code's problem...
  919.      */
  920.     public GlyphJustificationInfo getGlyphJustificationInfo(int glyphCode) {
  921.         if (glyphCode >= getNumGlyphs()) {
  922.             glyphCode = getMissingGlyphCode();
  923.         }
  924.  
  925.         if (fJustificationCache == null) {
  926.             fJustificationCache = new GlyphJustificationInfo[getNumGlyphs()];
  927.         }
  928.  
  929.         GlyphJustificationInfo gji = fJustificationCache[glyphCode];
  930.         if (gji == null) {
  931.             float weight = getSize2D(); // adjust weight based on size
  932.             if (weight == 0) {
  933.                 // REMIND jk (df) token default size, should we do this?
  934.                 weight = 12;
  935.             }
  936.             boolean growAbsorb = false; // no absorbing characters for now.
  937.             boolean shrinkAbsorb = false;
  938.  
  939.             int growPriority = 4;
  940.             int shrinkPriority = 4;
  941.  
  942.             float growLimit = (float)(weight / 2.0); // left and right
  943.             float shrinkLimit = (float)(weight / 4.0); // left and right
  944.  
  945.             boolean space = glyphCode == ' ';
  946.             boolean combining = !space &&
  947.                 getGlyphMetrics(glyphCode).isCombining();
  948.  
  949.             if (space) {
  950.                 growPriority = 1;
  951.                 shrinkPriority = 2;
  952.                 // growLimit = weight;
  953.             } else if (combining) {
  954.                 growPriority = 4; // never adjusted
  955.                 shrinkPriority = 4;
  956.             } else {
  957.                 growPriority = 2;
  958.                 shrinkPriority = 1;
  959.             }
  960.  
  961.             // weight,
  962.             // growAbsorb, growPriority, growLeftLimit, growRightLimit
  963.             // shrinkAbsorb, shrinkPriority, shrinkLeftLimit, shrinkRightLimit
  964.             gji = new GlyphJustificationInfo(weight, growAbsorb,
  965.                                              growPriority, growLimit, growLimit,
  966.                                              shrinkAbsorb, shrinkPriority,
  967.                                              shrinkLimit, shrinkLimit);
  968.  
  969.             fJustificationCache[glyphCode] = gji;
  970.         }
  971.  
  972.         return gji;
  973.     }
  974.  
  975.     /**
  976.      * Return the offset of the glyphs rendered by this font from the natural
  977.      * baseline.
  978.      *
  979.      * Superscripted glyphs always have a negative offset.  On vertical lines,
  980.      * a positive offset will move glyphs to the right.  This value is
  981.      * determined by the superscript attribute.
  982.      *
  983.      * The default is zero.
  984.      */
  985.     private float getOffset() {
  986.         float[] ff = (float[])getRequestedAttributes().
  987.             get(TextAttributeSet.SUPERSUBSCRIPT);
  988.         if (ff != null) {
  989.             return getSize2D() * ff[1];
  990.         }
  991.         return 0;
  992.     }
  993.  
  994.     /**
  995.      * Return the actual font size used by this font.
  996.      *
  997.      * This size is a multiple of the base size, as determined by the
  998.      * superscript attribute.
  999.      */
  1000.     private float getActualSize() {
  1001.         float[] ff = (float[])getRequestedAttributes().
  1002.             get(TextAttributeSet.SUPERSUBSCRIPT);
  1003.         if (ff != null) {
  1004.             return getSize2D() * ff[0];
  1005.         }
  1006.  
  1007.         return getSize2D();
  1008.     }
  1009.  
  1010.     /**
  1011.      * Returns the attribute set used to create this font.
  1012.      * NOTE: This method may be moved into another class.
  1013.      */
  1014.     public AttributeSet getRequestedAttributes(){
  1015.         return fRequestedAttributes;
  1016.     }
  1017.  
  1018.     /**
  1019.      * Creates a new Font object by replicating the current Font object
  1020.      * with a new style, size and font attributes associated with it.
  1021.      * @param style the style for the new Font.
  1022.      * @param size the size in float for the new Font.
  1023.      * @param attributes an array of AttributeSets enabled for the new Font.
  1024.      * @return a new Font object.
  1025.      */
  1026.     public Font deriveFont(int style,float size){
  1027.         return new Font(ffApply(style, ffApply(size,
  1028.                                                getRequestedAttributes())));
  1029.     }
  1030.  
  1031.     /**
  1032.      * Creates a new Font object by replicating the current Font object
  1033.      * with a new style, transform and font attributes associated with it.
  1034.      * @param style the style for the new Font.
  1035.      * @param trans the AffineTransform associated with the new Font.
  1036.      * @param attributes an array of AttributeSets enabled for the new Font.
  1037.      * @return a new Font object.
  1038.      */
  1039.     public Font deriveFont(int style,AffineTransform trans){
  1040.         return new Font(ffApply(style, ffApply(trans,
  1041.                                                getRequestedAttributes())));
  1042.     }
  1043.  
  1044.     /**
  1045.      * Creates a new Font object by replicating the current Font object
  1046.      * with a new size associated with it.
  1047.      * @param size the size in float for the new Font.
  1048.      * @return a new Font object.
  1049.      */
  1050.     public Font deriveFont(float size){
  1051.         // REMIND jk. Need to create a new font object.
  1052.         this.size = (int)size;
  1053.         pointSize = size;
  1054.         return this;
  1055.     }
  1056.  
  1057.     /**
  1058.      * Creates a new Font object by replicating the current Font object
  1059.      * with a new transform associated with it.
  1060.      * @param trans the AffineTransform associated with the new Font.
  1061.      * @return a new Font object.
  1062.      */
  1063.     public Font deriveFont(AffineTransform trans){
  1064.         return new Font(ffApply(trans, getRequestedAttributes()));
  1065.     }
  1066.  
  1067.     /**
  1068.      * Creates a new Font object by replicating the current Font object
  1069.      * with a new style associated with it.
  1070.      * @param style the style for the new Font.
  1071.      * @return a new Font object.
  1072.      */
  1073.     public Font deriveFont(int style){
  1074.         return new Font(ffApply(style, getRequestedAttributes()));
  1075.     }
  1076.  
  1077.     /**
  1078.      * Creates a new Font object by replicating the current Font object
  1079.      * with a new set of font attributes associated with it.
  1080.      * @param attributes an array of AttributeSets enabled for the new Font.
  1081.      * @return a new Font object.
  1082.      */
  1083.     public Font deriveFont(TextAttributeSet attributes){
  1084.         TextAttributeSet newAttrs =
  1085.             new TextAttributeSet(getRequestedAttributes());
  1086.         newAttrs.add(attributes);
  1087.         return new Font(newAttrs);
  1088.     }
  1089.  
  1090.     /**
  1091.      * Create a glyph set for the text between start and limit.
  1092.      *
  1093.      * @see GlyphSet
  1094.      *
  1095.      * @param context the text for the entire line.  The whole
  1096.      * context is provided for fonts that need to do glyph shaping.
  1097.      * @param start the start of the subrange for which to create glyphs.
  1098.      * @param limit the limit of the subrange for which to create glyphs.
  1099.      * @param order a mapping from logical to visual positions for
  1100.      * bidirectional text.  For example, the value 5 at position 0 in the
  1101.      * array means that the character at logical position 0 (from the
  1102.      * start of the text in the iterator) displays at visual position 5
  1103.      * (assuming all characters are displayed).
  1104.      * <p>If order is null, the visual position is the logical position.
  1105.      * Position zero in this array is position startIndex in the context.
  1106.      * The array length must be at least the number of characters in the
  1107.      * iterator.
  1108.      * <p>Note the meaning of this array is the inverse of the mapping
  1109.      * passed to the GlyphSet constructor.  This is to faciliate indexing
  1110.      * into the array using start and limit.
  1111.      * @param levels an array indicating the bidirection level of each
  1112.      * character.  Even values are left to right, odd are right to left.
  1113.      * This will affect hit testing and cursor positioning.
  1114.      * If null, all characters are left to right.  Position zero in this
  1115.      * array is position beginIndex in the context, and the array length
  1116.      * is greater than or equal to endIndex - beginIndex.
  1117.      */
  1118.     public GlyphSet getGlyphSet(CharacterIterator context,
  1119.                                 int start, int limit,
  1120.                                 byte baseline,
  1121.                                 int[] order, byte[] levels) {
  1122.  
  1123.         /*
  1124.          * This implementation doesn't do shaping, so we don't need the
  1125.          * context.  Extract the remainder of the characters and call
  1126.          * through to the array-based api.
  1127.          */
  1128.         int offset = context.getBeginIndex();
  1129.         char[] chars = new char[limit - start];
  1130.         int n = 0;
  1131.         for (char c = context.setIndex(start);
  1132.              n < chars.length;
  1133.              c = context.next()) {
  1134.  
  1135.             chars[n++] = c;
  1136.         }
  1137.  
  1138.         int[] newOrder = GlyphSet.getNormalizedOrder(order, levels,
  1139.             start - offset, limit - offset);
  1140.  
  1141.         // it would be handy if System provided a utility to do this...
  1142.         byte[] newLevels = null;
  1143.     if (levels != null) {
  1144.         newLevels = new byte[limit - start];
  1145.         System.arraycopy(levels, start - offset, newLevels, 0,
  1146.                 newLevels.length);
  1147.     }
  1148.  
  1149.     return getGlyphSet(chars, 0, chars.length, baseline, newOrder,
  1150.             newLevels);
  1151.  
  1152.     }
  1153.  
  1154.     /*
  1155.      * For testing.  Set QUICK to true for no ligatures or other fancy
  1156.      * stuff in the glyphsets created by this font.
  1157.      */
  1158.     private static boolean QUICK = false;
  1159.  
  1160.     /**
  1161.      * Construct a GlyphSet for the text between start and limit.
  1162.      */
  1163.      public GlyphSet getGlyphSet(char[] context, int start, int limit,
  1164.     byte baseline, int[] order, byte[] levels) {
  1165.  
  1166.          /*
  1167.           * REMIND jeet-- the font has to implement all of this, since
  1168.           * only the font knows how to compute ligatures.
  1169.           */
  1170.          // return fntObj.getGlyphSet(context, start, limit, baseline,
  1171.          //     order, levels);
  1172.  
  1173.          // this implementation ignores context, so strip it off
  1174.  
  1175.          int count = limit - start;
  1176.  
  1177.          int[] newOrder = order;
  1178.          if (order != null) {
  1179.              newOrder = GlyphSet.getNormalizedOrder(order, levels,
  1180.                                                     start, limit);
  1181.              newOrder = GlyphSet.getInverseOrder(newOrder);
  1182.          }
  1183.  
  1184.          byte[] newLevels = levels;
  1185.          if (levels != null && count != context.length) {
  1186.              newLevels = new byte[count];
  1187.              System.arraycopy(levels, start, newLevels, 0, count);
  1188.          }
  1189.  
  1190.          //REMIND jk define QUICK based on the fast case conversion
  1191.          // differences needed between TrueType and Type1
  1192.          if (QUICK) {
  1193.              // glyph codes are just the unicode values,
  1194.              // advances are all the default advances
  1195.              int[] glyphs = new int[count];
  1196.              for (int i = start; i < limit; i++) {
  1197.                  glyphs[i-start] = context[i];
  1198.              }
  1199.  
  1200.              return new StandardGlyphSet(this, glyphs, baseline, 0,
  1201.                                          null, 0, null, newOrder, newLevels, count);
  1202.          } else if (!QUICK) {
  1203.              // glyph codes are just the unicode values,
  1204.              // advances are all the default advances
  1205.              int[] glyphs = new int[count];
  1206.              glyphs = fntObj.getGlyphIndicies(getFontID(),
  1207.                                               new String(context) );
  1208.  
  1209.              return new StandardGlyphSet(this, glyphs, baseline, 0,
  1210.                                          null, 0, null, newOrder, newLevels, count);
  1211.  
  1212.          } else {
  1213.              boolean vert = isVerticalBaseline();
  1214.  
  1215.              int[] glyphs = new int[count];
  1216.              byte[] newGlyphs = new byte[count];
  1217.              float[] advs = new float[count];
  1218.  
  1219.              /*
  1220.               * We require at least one glyph per character.
  1221.               * Ligatures will have a glyph code representing the ligature
  1222.               * followed by 'ignore' glyph codes as placeholders for the
  1223.               * other characters. Characters that are combined forms will
  1224.               * be treated similarly.  This assumes each combined form can
  1225.               * be represented by a single glyph code, so we don't have to
  1226.               * break them out into more glyphs then there were characters.
  1227.               */
  1228.  
  1229.              /*
  1230.               * mock up ligature support
  1231.               * NOTE!  This implementation of component glyphs does some
  1232.               * really inefficient object creation and array copying.  It
  1233.               * is for expository purposes only.  Don't implement this way
  1234.               * in production code.
  1235.               *
  1236.               * REMIND jeet if you use code like this, you need a new
  1237.               * constructor for the ligaturizer that takes a char array.
  1238.               */
  1239.  
  1240.              Ligaturizer ligaturizer = new Ligaturizer(context, start, limit);
  1241.  
  1242.              int i = 0;
  1243.              int g = ligaturizer.first();
  1244.              while (g != Ligaturizer.DONE) {
  1245.                  newGlyphs[i] = (byte)g;
  1246.                  advs[i] = getGlyphMetrics(g).getAdvance();
  1247.  
  1248.                  ++i;
  1249.  
  1250.                  /*
  1251.                   * {jbr} boy is this a hack.  Better to scan before we
  1252.                   * start this process and get array sizes right.  This
  1253.                   * is nothing more than cheap expediency
  1254.                   */
  1255.                  while (ligaturizer.hasComponentGlyphs()) {
  1256.                      newGlyphs = expandByteArray(newGlyphs, i-1);
  1257.                      newLevels = expandByteArray(newLevels, i-1);
  1258.                      advs = expandFloatArray(advs, i-1);
  1259.  
  1260.                      ++count;
  1261.  
  1262.                      /*
  1263.                       * if we're expanding a right-to-left glyph, and the
  1264.                       * visual order is null, we should create a visual
  1265.                       * order since the component glyphs must be visually
  1266.                       * ordered right-to-left.
  1267.                       */
  1268.                      if (newOrder == null && newLevels != null &&
  1269.                          (newLevels[i-1] & 0x1) != 0) {
  1270.  
  1271.                          newOrder = new int[newLevels.length-1];
  1272.                          for (int indx=0; indx < newOrder.length; indx++) {
  1273.                              newOrder[indx] = indx;
  1274.                          }
  1275.                      }
  1276.  
  1277.                      if (newOrder != null) {
  1278.                          int[] logToVis = GlyphSet.getInverseOrder(newOrder);
  1279.  
  1280.                          // Let's see just how inefficient we can be...
  1281.                          logToVis = expandIntArray(logToVis, i-1);
  1282.                          int currentVis = logToVis[i-1];
  1283.                          for (int k=0; k<count; k++) {
  1284.                              if (logToVis[k] > currentVis) {
  1285.                                  logToVis[k]++;
  1286.                              }
  1287.                          }
  1288.  
  1289.                          if (newLevels != null &&
  1290.                              (newLevels[i-1] & 0x1) != 0) {
  1291.                                 // ie if glyph is rtl
  1292.                              logToVis[i-1]++;
  1293.                          } else {
  1294.                              logToVis[i]++;
  1295.                          }
  1296.  
  1297.                          newOrder = StandardGlyphSet.getInverseOrder(logToVis);
  1298.                      }
  1299.  
  1300.                      newGlyphs[i] = (byte)ligaturizer.nextComponentGlyph();
  1301.                      advs[i] = getGlyphMetrics(g).getAdvance();
  1302.  
  1303.                      ++i;
  1304.                  }
  1305.  
  1306.                  g = ligaturizer.next();
  1307.              }
  1308.  
  1309.              /* REMIND jk (df). got to do this efficiently and correctly */
  1310.              /*
  1311.                 Note to Jeet. the FontObject has to implement this.
  1312.                 We're collecting glyphs from the ligature as we go, we
  1313.                 can't just change them here and assume all the advances
  1314.                 are ok.  Besides which, the ligaturizer is creating
  1315.                 ligatures, they are not characters, so you can't create
  1316.                 a string from them.
  1317.                 */
  1318.              glyphs = fntObj.getGlyphIndicies(getFontID(),
  1319.                                               new String(newGlyphs) );
  1320.  
  1321.  
  1322.              float[] xadv = null; // vert ? null : advs;
  1323.              float[] yadv = null; // vert ? advs : null;
  1324.  
  1325.              float offset = getOffset();
  1326.              float xa = vert ? offset : 0;
  1327.              float ya = vert ? 0 : offset;
  1328.  
  1329.              return new StandardGlyphSet(this, glyphs, baseline, xa,
  1330.                                          xadv, ya, yadv, newOrder, newLevels, glyphs.length);
  1331.          }
  1332.      }
  1333.  
  1334.     private boolean isLatin(String str) {
  1335.         //REMIND jk. Use a proper method for determination.
  1336.         return str.charAt(0) > 0x00ff;
  1337.     }
  1338.  
  1339.     /**
  1340.      * Converts all characters in the String Object to font glyph codes.
  1341.      * Glyph substitution may be performed.
  1342.      * @param str a String object.
  1343.      * @return a GlyphSet containing a collection of glyphs and glyph positions.
  1344.      */
  1345.     public GlyphSet getGlyphSet(String str){
  1346.         // REMIND jk. This shortcut should be applied in the graphics pipeline.
  1347.         if(true || isLatin(str)) {
  1348.             return getGlyphSet( new StringCharacterIterator(str),
  1349.                                 0,str.length(),
  1350.                                 Font.ROMAN_BASELINE, null, null);
  1351.         } else {
  1352.             int count = str.length();
  1353.             int[] glyphs = new int[count];
  1354.             float[] advs = new float[count];
  1355.             boolean vert = isVerticalBaseline();
  1356.             glyphs = fntObj.getGlyphIndicies(getFontID(), str);
  1357.             advs = fntObj.getMetrics(glyphs.length, glyphs, null, getSize(),
  1358.                                      getFontID());
  1359.  
  1360.             float[] xadv = vert ? null : advs;
  1361.             float[] yadv = vert ? advs : null;
  1362.  
  1363.             float offset = 0;//REMIND jk getOffset();
  1364.             float xa = vert ? offset : 0;
  1365.             float ya = vert ? 0 : offset;
  1366.  
  1367.             return new StandardGlyphSet(this , glyphs, Font.ROMAN_BASELINE, xa, xadv,
  1368.                                         ya, yadv, null, null, count);
  1369.         }
  1370.     }
  1371.  
  1372.     /**
  1373.      * Checks if this font has a glyph for the specified character.
  1374.      * @param c a unicode character code.
  1375.      * @return true if the font can display the character.
  1376.      */
  1377.     public boolean canDisplay(char c){
  1378.         return fntObj.canDisplay(c);
  1379.     }
  1380.  
  1381.     /**
  1382.      * Indicates whether a string is displayable by this Font.
  1383.      * For strings with Unicode encoding, it is important to know if a given
  1384.      * Font can display the string. This method returns an offset
  1385.      * into the String str which is the first character the Font
  1386.      * cannot display without using the missing glyph code. If the Font can
  1387.      * display all characters, -1 is returned.
  1388.      * @param str a String object.
  1389.      * @return an offset into the String object that can be displayed by this
  1390.      * font.
  1391.      */
  1392.     public int canDisplayUpTo(String str){
  1393.         return canDisplayUpTo(new StringCharacterIterator(str), 0,
  1394.             str.length());
  1395.     }
  1396.  
  1397.     /**
  1398.      * A convenience overload.
  1399.      */
  1400.     public int canDisplayUpTo(char[] text, int start, int limit) {
  1401.     while (start < limit && fntObj.canDisplay(text[start])) {
  1402.         ++start;
  1403.     }
  1404.  
  1405.     return start;
  1406.     }
  1407.  
  1408.     /**
  1409.      * Indicates whether a string is displayable by this Font.
  1410.      * For strings with Unicode encoding, it is important to know if a given
  1411.      * Font can display the string. This method returns an offset
  1412.      * into the String str which is the first character the Font
  1413.      * cannot display without using the missing glyph code . If the Font can
  1414.      * display all characters, -1 is returned.
  1415.      * @param text a CharacterIterator object.
  1416.      * @return an offset into the String object that can be displayed by this
  1417.      * font.
  1418.      */
  1419.     public int canDisplayUpTo(CharacterIterator iter, int start, int limit) {
  1420.         for (char c = iter.setIndex(start);
  1421.              iter.getIndex() < limit && fntObj.canDisplay(c);
  1422.              c = iter.next()) {
  1423.         }
  1424.  
  1425.     return iter.getIndex();
  1426.     }
  1427.  
  1428.  
  1429.     /**
  1430.      * Returns the offset from the roman baseline of the font to the specified
  1431.      * baseline.
  1432.      */
  1433.     public float getBaselineOffset(byte baselineType){
  1434.         if (baselineType < ROMAN_BASELINE || baselineType > HANGING_BASELINE){
  1435.             throw new IllegalArgumentException("bad baseline code");
  1436.         }
  1437.  
  1438.         float result = 0f;
  1439.  
  1440.         switch (baselineType) {
  1441.         case ROMAN_BASELINE:
  1442.             result = getAscent();
  1443.             break;
  1444.         case CENTER_BASELINE:
  1445.             result = (getAscent() + getDescent()) / 2;
  1446.             break;
  1447.         case HANGING_BASELINE:
  1448.             result = getDescent();
  1449.             break; // arbitrary REMIND df
  1450.         }
  1451.  
  1452.         return result;
  1453.     }
  1454.  
  1455.     /**
  1456.      * Returns the ascent of the font above the roman baseline.
  1457.      */
  1458.     public float getAscent() {
  1459.         if (ascent == -1) {
  1460.             ascent = fntObj.getAscent(pointSize, usesVerticalMetrics(),
  1461.                                       getFontID());
  1462.         }
  1463.         return ascent;
  1464.     }
  1465.  
  1466.     /**
  1467.      * Returns the descent of the font above the roman baseline.
  1468.      */
  1469.     public float getDescent() {
  1470.         if (descent == -1) {
  1471.             descent = fntObj.getDescent(pointSize, usesVerticalMetrics(),
  1472.                                         getFontID());
  1473.         }
  1474.         return descent;
  1475.     }
  1476.  
  1477.     /**
  1478.      * Returns the leading for this font;
  1479.      */
  1480.     public float getLeading(){
  1481.         if (leading == -1) {
  1482.             leading = fntObj.getLeading(pointSize, usesVerticalMetrics(),
  1483.                                         getFontID());
  1484.         }
  1485.         return leading;
  1486.     }
  1487.  
  1488.     private float getHeight() {
  1489.         return getAscent() + getDescent() + getLeading();
  1490.     }
  1491.  
  1492.     /**
  1493.      * Returns the maximum advance of any glyph in this font.
  1494.      */
  1495.     public float getMaxAdvance(){
  1496.         if (maxAdvance == -1) {
  1497.             maxAdvance = fntObj.getMaxAdvance(pointSize,
  1498.                                               usesVerticalMetrics(),
  1499.                                               getFontID());
  1500.         }
  1501.         return maxAdvance;
  1502.     }
  1503.  
  1504.     /**
  1505.      * Returns the maximum bounding box of this font.
  1506.      */
  1507.     public Rectangle2D getMaxBounds2D(){
  1508.         if (usesVerticalMetrics()){
  1509.             return new Rectangle2D.Float(getMaxAdvance() / 2f, 0,
  1510.                      getMaxAdvance(),
  1511.                                          getHeight());
  1512.         } else {
  1513.             return new Rectangle2D.Float(0, -getAscent(),
  1514.                      getMaxAdvance(),
  1515.                                          getHeight());
  1516.         }
  1517.     }
  1518.  
  1519.     /**
  1520.      * Returns the italic angle of this font.
  1521.      */
  1522.     public float getItalicAngle(){
  1523.         return fntObj.getItalicAngle();
  1524.     }
  1525.  
  1526.     // REMIND jk. need to get this from GraphicsEnvironment
  1527.     private static String[] getAvailableFamilyNames(){
  1528.         String[] names = {
  1529.             "Courier",
  1530.             "Helvetica"
  1531.         };
  1532.  
  1533.         return names;
  1534.     }
  1535.  
  1536.     /**
  1537.      * Resolve styles on the character at start into an instance of Font
  1538.      * that can best render the text between start and limit.
  1539.      * REMIND jk. Move it to graphics environment.
  1540.      */
  1541.     public static Font getBestFontFor(AttributedCharacterIterator text) {
  1542.     return getBestFontFor(text, text.getBeginIndex(), text.getEndIndex());
  1543.     }
  1544.  
  1545.     /**
  1546.      * Resolve styles on the character at start into an instance of Font
  1547.      * that can best render the text between start and limit.
  1548.      * REMIND jk. Move it to graphics environment.
  1549.      */
  1550.     public static Font getBestFontFor(AttributedCharacterIterator text,
  1551.                                       int start, int limit) {
  1552.  
  1553.         /*
  1554.          * choose the first font that can display the first character
  1555.      * first iterate through the styles in the range of text we were
  1556.          * passed.  If none of them work, iterate through font families
  1557.          * using the attributes on the first character.  If this also
  1558.          * fails, use the first font.
  1559.          */
  1560.         char c = text.setIndex(start);
  1561.     AttributeSet ff = text.getAttributes();
  1562.     Font font = getFont(ff);
  1563.  
  1564.         while (!font.canDisplay(c) && (text.getRunLimit() < limit)) {
  1565.         text.setIndex(text.getRunLimit());
  1566.         font = getFont(text.getAttributes());
  1567.     }
  1568.  
  1569.     if (!font.canDisplay(c)) {
  1570.         text.setIndex(start);
  1571.         String[] families = getAvailableFamilyNames(); // hack
  1572.         for (int i = 0; i < families.length; ++i) {
  1573.             TextAttributeSet newAttrs = new TextAttributeSet(ff);
  1574.             newAttrs.add(TextAttributeSet.FAMILY, families[i]);
  1575.             font = getFont(newAttrs);
  1576.             if (font.canDisplay(c)) {
  1577.                 break;
  1578.             }
  1579.         }
  1580.  
  1581.         if (!font.canDisplay(c)) {
  1582.             font = getFont(ff);
  1583.         }
  1584.     }
  1585.  
  1586.     return font;
  1587.     }
  1588.  
  1589.     /*
  1590.      * Utility to expand oldArray by 1, duplicating the value at breakPoint
  1591.      */
  1592.     private static int[] expandIntArray(int[] oldArray, int breakPoint) {
  1593.  
  1594.         if (oldArray == null) {
  1595.             return null;
  1596.         }
  1597.  
  1598.         int[] newArray = new int[oldArray.length+1];
  1599.  
  1600.         System.arraycopy(oldArray, 0, newArray, 0, breakPoint+1);
  1601.         System.arraycopy(oldArray, breakPoint, newArray, breakPoint+1,
  1602.                          (oldArray.length-breakPoint));
  1603.  
  1604.         return newArray;
  1605.     }
  1606.  
  1607.     /*
  1608.      * Utility to expand oldArray by 1, duplicating the value at breakPoint
  1609.      */
  1610.     private static char[] expandCharArray(char[] oldArray, int breakPoint) {
  1611.  
  1612.         if (oldArray == null) {
  1613.             return null;
  1614.         }
  1615.  
  1616.         char[] newArray = new char[oldArray.length+1];
  1617.  
  1618.         System.arraycopy(oldArray, 0, newArray, 0, breakPoint+1);
  1619.         System.arraycopy(oldArray, breakPoint, newArray, breakPoint+1,
  1620.                          (oldArray.length-breakPoint));
  1621.  
  1622.         return newArray;
  1623.     }
  1624.  
  1625.     /*
  1626.      * Utility to expand oldArray by 1, duplicating the value at breakPoint
  1627.      */
  1628.     private static byte[] expandByteArray(byte[] oldArray, int breakPoint) {
  1629.  
  1630.         if (oldArray == null) {
  1631.             return null;
  1632.         }
  1633.  
  1634.         byte[] newArray = new byte[oldArray.length+1];
  1635.  
  1636.         System.arraycopy(oldArray, 0, newArray, 0, breakPoint+1);
  1637.         System.arraycopy(oldArray, breakPoint, newArray, breakPoint+1,
  1638.                          (oldArray.length-breakPoint));
  1639.  
  1640.         return newArray;
  1641.     }
  1642.  
  1643.     private static float[] expandFloatArray(float[] oldArray,
  1644.                                                 int breakPoint)
  1645.     {
  1646.  
  1647.         if (oldArray == null) {
  1648.             return null;
  1649.         }
  1650.  
  1651.         float[] newArray = new float[oldArray.length+1];
  1652.  
  1653.         System.arraycopy(oldArray, 0, newArray, 0, breakPoint+1);
  1654.         System.arraycopy(oldArray, breakPoint, newArray, breakPoint+1,
  1655.                          (oldArray.length-breakPoint));
  1656.  
  1657.         return newArray;
  1658.     }
  1659.  
  1660.     private static AttributeSet ffApply(String name, AttributeSet attributes) {
  1661.         TextAttributeSet rval = new TextAttributeSet(attributes);
  1662.         rval.add(TextAttributeSet.FAMILY, name);
  1663.         return rval;
  1664.     }
  1665.  
  1666.     private static AttributeSet ffApply(AffineTransform trans,
  1667.                                         AttributeSet attributes) {
  1668.         TextAttributeSet rval = new TextAttributeSet(attributes);
  1669.         rval.add(TextAttributeSet.TRANSFORM, trans);
  1670.         return rval;
  1671.     }
  1672.  
  1673.     private static AttributeSet ffApply(int style, AttributeSet attributes) {
  1674.         TextAttributeSet rval = new TextAttributeSet(attributes);
  1675.  
  1676.         if ((style & BOLD) != 0) {
  1677.             rval.add(TextAttributeSet.WEIGHT, TextAttributeSet.WEIGHT_BOLD);
  1678.         } else {
  1679.             rval.remove(TextAttributeSet.WEIGHT);
  1680.         }
  1681.  
  1682.         if ((style & ITALIC) != 0) {
  1683.             rval.add(TextAttributeSet.POSTURE, TextAttributeSet.POSTURE_ITALIC);
  1684.         } else {
  1685.             rval.remove(TextAttributeSet.POSTURE);
  1686.         }
  1687.  
  1688.         return rval;
  1689.     }
  1690.  
  1691.     private static AttributeSet ffApply(float size, AttributeSet attributes) {
  1692.         TextAttributeSet rval = new TextAttributeSet(attributes);
  1693.         rval.add(TextAttributeSet.SIZE, new Float(size));
  1694.         return rval;
  1695.     }
  1696.  
  1697.     private static AttributeSet ffApply(String name, int style,
  1698.                                       float size, AttributeSet attributes)
  1699.     {
  1700.         return ffApply(name, ffApply(style, ffApply(size, attributes)));
  1701.     }
  1702.  
  1703.     /*
  1704.      * Initialize JNI field and method IDs
  1705.      */
  1706.     private static native void initIDs();
  1707.     private native void pDispose();
  1708.  
  1709.     protected void finalize() throws Throwable {
  1710.         pDispose();
  1711.         super.finalize();
  1712.     }
  1713.  
  1714. }
  1715.  
  1716.