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:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
UTF-8
Wrap
Java Source
|
1998-03-20
|
56.3 KB
|
1,716 lines
/*
* @(#)Font.java 1.72 98/03/18
*
* Copyright 1995-1998 by Sun Microsystems, Inc.,
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of Sun Microsystems, Inc. ("Confidential Information"). You
* shall not disclose such Confidential Information and shall use
* it only in accordance with the terms of the license agreement
* you entered into with Sun.
*/
package java.awt;
import java.awt.font.GlyphMetrics;
import java.awt.font.GlyphSet;
import java.awt.font.GlyphJustificationInfo;
import java.awt.font.TextAttributeSet;
//import java.awt.font.TextAttributes;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.peer.FontPeer;
import java.text.AttributeSet;
import java.text.AttributedCharacterIterator;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Locale;
import java.util.Hashtable;
import sun.awt.font.FontObject;
import sun.awt.font.Ligaturizer;
/**
* This class represents fonts. The capabilities of this class have been
* extended over the java.awt.Font class in JDK 1.1 and earlier releases
* to provide developers the ability to utilize more sophisticated
* typographic features.
* <p>
* It is important to present the concepts behind using the words
* character and glyph separately. A <b>character</b> is a symbol that
* represents items like letters and numbers in a given writing system,
* for example <i>lowercase-g</i>. When a given character is drawn, a shape
* now represents this character. This shape is called a <b>glyph</b>.
* <p>
* Chararcter encoding is a conversion table that maps character codes
* to glyph codes in the font. The character encoding used in the Java 2D
* API is Unicode. For more information on Unicode you may visit the site
* <a href="http://www.unicode.org">http://www.unicode.org</a>.
* <p>
* Characters and glyphs do not have one-to-one correspondence. As an example
* <i>lowercase-a acute</i> can be represented by two glyphs: <i>lowercase-a</i>
* and <i>acute</i>.
* Another example is ligatures such as <i>ligature -fi</i> which is a
* single glyph representing two characters: <i>f</i> and <i>i</i>.
* <p>
* A font is a collection of glyphs. A font may have many faces, e.g.
* heavy, medium, oblique, gothic and regular. All of these faces have
* similar typographic design.
* <p>
* There are three different names that you can get from a Font object.
* The <i>logical font name</i>is the same as used by java.awt.Font in
* JDK 1.1 and earlier releases. The java.awt.Toolkit.getFontList() method
* returns a short list of these logical names which are mapped onto specific
* fonts available on specific platforms. The <i>font face name</i>,
* or just <i>font name</i> for short, is the name of a particular font
* face, like Helvetica Bold. The <i>family name</i> is the name
* of the font family that determines the typographic design across several
* faces, like Helvetica. The font face name
* is the one that should be used to specify fonts. This name signifies
* actual fonts in the host system, and does not identify font
* names with shape of font characters as the logical font name does.
* <p>
* The Font class represents an instance of a font face from a
* collection of font faces that are present in the system resources
* of the host system. As examples, Helvetica Bold and Courier Bold Italic
* are font faces. There can be several Font objects associated with
* a font face, each differing in size, style, transform and font features.
* GraphicsEnvironment.getAllFonts() returns an array of all font faces
* available in the system. These font faces are returned as Font objects
* with a size of 1, identity transform and default font features. These
* base fonts can then be used to derive new Font objects with varying
* sizes, styles, transforms and font features via the deriveFont methods
* in this class.
* @see java.awt.Toolkit#getFontList
* @see GraphicsEnvironment#getFonts
* @see GraphicsEnvironment#getAllFonts
* @version 10 Feb 1997
*/
public class Font implements java.io.Serializable
{
static {
/* force the native libraries to be loaded */
Toolkit.loadLibraries();
initIDs();
}
private TextAttributeSet fRequestedAttributes;
private transient GlyphMetrics[] fMetricsCache;
private transient FontObject fntObj;
private transient GlyphJustificationInfo[] fJustificationCache;
private transient int fontObjectID;
private transient int numGlyphs = -1;
/*
* A default font for general-purpose rendering.
*/
public static final Font DEFAULT = new Font(TextAttributeSet.EMPTY);
/*
* Constants to be used for styles. Can be combined to mix
* styles.
*/
/**
* The plain style constant.
*/
public static final int PLAIN = 0;
/**
* The bold style constant. This can be combined with the other style
* constants (except PLAIN) for mixed styles.
*/
public static final int BOLD = 1;
/**
* The italicized style constant. This can be combined with the other
* style constants (except PLAIN) for mixed styles.
*/
public static final int ITALIC = 2;
public static final byte ROMAN_BASELINE = 0;
public static final byte CENTER_BASELINE = 1;
public static final byte HANGING_BASELINE = 2;
/**
* Private data.
*/
transient private long pData;
/**
* The platform specific family name of this font.
*/
transient private String family;
/**
* The logical name of this font.
* @since JDK1.0
*/
protected String name;
protected String fontName;
/**
* The style of the font, as passed to the constructor. This may be
* PLAIN, BOLD, ITALIC, or BOLD+ITALIC.
*/
protected int style;
/**
* The point size of this font, rounded to integer.
*/
protected int size;
/**
* The point size of this font in float.
*/
private float pointSize;
private float ascent = -1;
private float descent = -1;
private float leading = -1;
private float maxAdvance = -1;
/**
* Info about the font.
*/
//private FontDescriptor fontDescriptor;
/**
* The platform specific font information.
*/
transient FontPeer peer;
/*
* JDK 1.1 serialVersionUID
*/
private static final long serialVersionUID = -4206021311591459213L;
/**
* Gets the peer of the font.
* @return the peer of the font.
* @since JDK1.1
*/
public FontPeer getPeer(){
return peer;
}
/* package */
boolean usesVerticalMetrics(){
//REMIND jk. Need to use the AttributeSet to define orientation
return false;
}
private void initializeFont(TextAttributeSet attributes){
try {
java.security.AccessController.beginPrivileged();
family = System.getProperty("awt.font." + name.toLowerCase(), name);
} finally {
java.security.AccessController.endPrivileged();
}
this.peer = Toolkit.getDefaultToolkit().getFontPeer(name, style);
fontObjectID = -1;
/* need to do this more efficiently */
GraphicsEnvironment env =
GraphicsEnvironment.getLocalGraphicsEnvironment();
Font theFont = env.getFont(name);
name = theFont.getFontName();
if (theFont != null) {
if (attributes == null) {
fRequestedAttributes =
(TextAttributeSet) ffApply(name, style, size,
ffApply(new AffineTransform(),
TextAttributeSet.EMPTY));
} else {
fRequestedAttributes =
(TextAttributeSet) ffApply(style, attributes);
}
fntObj = theFont.fntObj;
// REMIND: Goes away with the new architecture
name = fntObj.getFontFullName(0);
pointSize = size;
}
}
/**
* Creates a new font from the specified name, style and point size.
* @param name the font name. This can be a logical font name or a
* font face name.
* @param style the style constant for the font.
* @param size the point size of the font.
* @see java.awt.Toolkit#getFontList
* @see GraphicsEnvironment#getFonts
* @see GraphicsEnvironment#getAllFonts
*/
public Font(String name, int style, int size) {
this.name = name;
this.style = style;
this.size = size;
initializeFont(null);
}
/* REMIND jk . old architecture. get rid of this*/
public Font(FontObject fontObject){
fntObj = fontObject;
this.fontObjectID = -1;
this.name = fntObj.getFontFullName(0);
this.style = Font.PLAIN;
this.fontName = name;
this.size = 1;
fRequestedAttributes =
(TextAttributeSet) ffApply(name, style, size,
ffApply(new AffineTransform(),
TextAttributeSet.EMPTY));
}
public int getFontID() {
if (fontObjectID == -1) {
fontObjectID = fntObj.findFontObj(name, style);
}
return fontObjectID;
}
/**
* Create a new font with the specified attributes
*/
public Font(AttributeSet attributes){
if((attributes != null) &&
(!attributes.equals(TextAttributeSet.EMPTY)))
{
Object obj;
fRequestedAttributes = (TextAttributeSet) attributes;
if ((obj = attributes.get(TextAttributeSet.FAMILY)) != null) {
this.name = (String)obj;
}
// set the style to PLAIN as default
this.style = PLAIN;
if ((obj = attributes.get(TextAttributeSet.WEIGHT)) != null){
if(obj.equals(TextAttributeSet.WEIGHT_BOLD)) {
this.style += BOLD;
}
}
if ((obj = attributes.get(TextAttributeSet.POSTURE)) != null){
if(obj.equals(TextAttributeSet.POSTURE_ITALIC)) {
this.style += ITALIC;
}
}
if ((obj = attributes.get(TextAttributeSet.SIZE)) != null){
this.size = ((Float)obj).intValue();
}
initializeFont(fRequestedAttributes);
}
}
private static Hashtable fontCache = new Hashtable();
/**
* Returns a font appropriate to this attribute set.
*/
public static Font getFont(AttributeSet attributes) {
Font font = (Font)attributes.get(TextAttributeSet.FONT);
if (font != null) {
return font;
}
font = (Font)fontCache.get(attributes);
if (font != null) {
return font;
}
font = new Font(attributes);
fontCache.put(attributes, font);
return font;
}
/**
* Returns the transform associated with this font.
*/
public AffineTransform getTransform(){
return (AffineTransform)fRequestedAttributes.
get(TextAttributeSet.TRANSFORM);
}
/**
* Returns the family name of the font (for example, Helvetica).
* Use getName to get the logical name of the font.
* Use getFontName to get the font face name of the font.
* @see #getName
* @see #getFontName
*/
public String getFamily() {
if (family == null) {
family = fntObj.getFamilyName(getFontID());
}
return family;
}
/**
* Returns the family name of the font (for example, Helvetica), localized
* for the given Locale.
* Use getFontName to get the font face name of the font.
* @param l Locale for which to get the family name.
* @see #getFontName
* @see java.util.Locale
*/
public String getFamily(Locale l) {
if (family == null) {
family = fntObj.getFamilyName(getFontID());
}
return family;
}
/**
* Returns the postscript name of the font.
* Use getFamily to get the family name of the font.
* Use getFontName to get the font face name of the font.
*/
public String getPSName() {
return fontName;
}
/**
* Returns the logical name of the font.
* Use getFamily to get the family name of the font.
* Use getFontName to get the font face name of the font.
* @see #getFamily
* @see #getFontName
*/
public String getName() {
return name;
}
/**
* Returns the font face name of the font (for example, Helvetica Bold).
* Use getFamily to get the family name of the font.
* Use getName to get the logical name of the font.
* @see #getFamily
* @see #getName
*/
public String getFontName() {
return fntObj.getFontFullName(getFontID());
//return fontName;
}
/**
* Returns the font face name of the font (for example, Helvetica Fett)
* localized for the specified locale.
* Use getFamily to get the family name of the font.
* @param l Get the localized font face name for this locale.
* @see #getFamily
* @see java.util.Locale
*/
public String getFontName(Locale l) {
return fntObj.getFontFullName(getFontID());
}
/**
* Returns the style of the font. This may be
* PLAIN, BOLD, ITALIC, or BOLD+ITALIC.
* @see #isPlain
* @see #isBold
* @see #isItalic
* @since JDK1.0
*/
public int getStyle() {
return style;
}
/**
* Returns the point size of the font, rounded to integer.
* Most users are familiar with the idea of using <i>point size</i> to
* specify the size of glyphs in a font. This point size defines a
* measurement between the baseline of one line to the baseline of the
* following line in a single spaced text document. The point size is
* based on <i>typographic points</i>, approximately 1/72 of an inch.
* <p>
* The Java2D API adopts the convention that one point is equivalent
* to one unit in user coordinates. When using a normalized transform
* for converting user space coordinates to device space coordinates
* (see GraphicsConfiguration.getDefaultTransform() and
* GraphicsConfiguration.getNormalizingTransform()), 72 user space units
* equal 1 inch in device space. In this case one point is 1/72 of an
* inch.
* @see #getSize2D
* @see GraphicsConfiguration#getDefaultTransform
* @see GraphicsConfiguration#getNormalizingTransform
*/
public int getSize() {
return size;
}
/**
* Returns the point size of the font in float.
* @see #getSize
*/
public float getSize2D() {
return pointSize;
}
/**
* Returns true if the font is plain.
* @see #getStyle
*/
public boolean isPlain() {
return style == 0;
}
/**
* Indicates whether the font's style is bold.
* @return <code>true</code> if the font is bold;
* <code>false</code> otherwise.
* @see java.awt.Font#getStyle
* @since JDK1.0
*/
public boolean isBold() {
return (style & BOLD) != 0;
}
/**
* Indicates whether the font's style is italic.
* @return <code>true</code> if the font is italic;
* <code>false</code> otherwise.
* @see java.awt.Font#getStyle
* @since JDK1.0
*/
public boolean isItalic() {
return (style & ITALIC) != 0;
}
/**
* Returns a font from the system properties list.
* @param nm the property name.
*/
public static Font getFont(String nm) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPropertyAccess(nm);
}
return getFont(nm, null);
}
/**
* Returns the specified font using the name passed in.
* @param str the name.
*/
public static Font decode(String str) {
String fontName = str;
int fontSize = 12;
int fontStyle = Font.PLAIN;
int i = str.indexOf('-');
if (i >= 0) {
fontName = str.substring(0, i);
str = str.substring(i+1);
if ((i = str.indexOf('-')) >= 0) {
if (str.startsWith("bold-")) {
fontStyle = Font.BOLD;
} else if (str.startsWith("italic-")) {
fontStyle = Font.ITALIC;
} else if (str.startsWith("bolditalic-")) {
fontStyle = Font.BOLD | Font.ITALIC;
}
str = str.substring(i + 1);
}
try {
fontSize = Integer.valueOf(str).intValue();
} catch (NumberFormatException e) {
}
}
return new Font(fontName, fontStyle, fontSize);
}
/**
* Returns the specified font from the system properties list.
* @param nm the property name.
* @param font a default font to return if property 'nm' is not defined.
*/
public static Font getFont(String nm, Font font) {
String text;
try {
java.security.AccessController.beginPrivileged();
text = System.getProperty(nm);
} finally {
java.security.AccessController.endPrivileged();
}
if (text == null)
// return font;
text = nm;
// parse string for name, name-style, name-size, or name-style-size.
// style can be 'bold', 'italic', or 'bolditalic'
String namestr = null;
String stylestr = null;
String sizestr = null;
int h1 = text.indexOf('-');
if (h1 != -1) {
int h2 = text.indexOf('-', h1 + 1);
if (h2 != -1) {
sizestr = text.substring(h2+1, text.length());
} else {
h2 = text.length();
}
stylestr = text.substring(h1+1, h2);
} else {
h1 = text.length();
}
namestr = text.substring(0, h1);
int style = Font.PLAIN;
if (stylestr != null) {
if (stylestr.equals("bold"))
style = Font.BOLD;
else if (stylestr.equals("italic"))
style = Font.ITALIC;
else if (stylestr.equals("bolditalic"))
style = Font.BOLD + Font.ITALIC;
else if (sizestr == null)
sizestr = stylestr;
}
float size = 18.0f;
if (sizestr != null) {
try {
size = Float.valueOf(sizestr).floatValue();
}
catch (NumberFormatException e) {
System.out.println(e);
}
}
return new Font(namestr, style, (int)size);
}
/**
* Returns a hashcode for this font.
* @return a hashcode value for this font.
* @since JDK1.0
*/
public int hashCode() {
return name.hashCode() ^ style ^ size;
}
/**
* Compares this object to the specified object.
* @param obj the object to compare with.
* @return true if the objects are the same; false otherwise.
*/
public boolean equals(Object obj) {
if (obj instanceof Font) {
Font font = (Font)obj;
return (size == font.size) && (style == font.style) && name.equals(font.name);
}
return false;
}
/**
* Converts this object to a String representation.
* @return a string representation of this object
* @since JDK1.0
*/
public String toString() {
String strStyle;
if (isBold()) {
strStyle = isItalic() ? "bolditalic" : "bold";
} else {
strStyle = isItalic() ? "italic" : "plain";
}
return getClass().getName() + "[family=" + family + ",name=" + name + ",style=" +
strStyle + ",size=" + size + "]";
}
/* Serialization support. A readObject method is neccessary because
* the constructor creates the fonts peer, and we can't serialize the
* peer. Similarly the computed font "family" may be different
* at readObject time than at writeObject time. An integer version is
* written so that future versions of this class will be able to recognize
* serialized output from this one.
*/
private int fontSerializedDataVersion = 1;
private void writeObject(java.io.ObjectOutputStream s)
throws java.lang.ClassNotFoundException,
java.io.IOException
{
s.defaultWriteObject();
}
private void readObject(java.io.ObjectInputStream s)
throws java.lang.ClassNotFoundException,
java.io.IOException
{
s.defaultReadObject();
// REMIND jk need to pass the TextAttributeSet read from stream
initializeFont(null);
}
/**
* Returns number of glyphs in the font. Glyph codes for the font
* will range from 0 to getNumGlyphs() - 1.
*/
public int getNumGlyphs(){
if (numGlyphs == -1) {
numGlyphs = fntObj.getNumGlyphs();
}
return numGlyphs;
}
public int getMissingGlyphCode(){
return fntObj.getMissingGlyphCode();
}
public boolean isVerticalBaseline() {
return false;
}
/**
* Returns the metrics information for a glyph specified by a glyph code.
* @param glyphCode the glyph to get metrics for.
* @return a GlyphMetrics object with metric information for the glyph.
*/
public GlyphMetrics getGlyphMetrics(int glyphCode){
// !!! unrotated text only
// !!! a total hack since we don't really have character bounding boxes.
if (glyphCode >= getNumGlyphs()) {
glyphCode = getMissingGlyphCode();
}
if (fMetricsCache == null) {
fMetricsCache = new GlyphMetrics[getNumGlyphs()];
}
GlyphMetrics gm = fMetricsCache[glyphCode];
if (gm == null) {
float lsb = 0;
float rsb = 0;
float x = 0f;
float y = getHeight();
byte glyphType = GlyphMetrics.STANDARD;
x = getAdvance(glyphCode);
glyphType = fntObj.getGlyphType(glyphCode);
if (isVerticalBaseline()) {
gm = new GlyphMetrics(y,
new Rectangle2D.Float(-x/2f, 0, x,
getAscent() +
getDescent()),
glyphType);
} else {
gm = new GlyphMetrics(x,
new Rectangle2D.Float(lsb,
(-getAscent()),
x - lsb - rsb,
getAscent() +
getDescent()),
glyphType);
}
fMetricsCache[glyphCode] = gm;
}
return gm;
}
/**
* Return the baseline appropriate for displaying this character.
* <p>
* Large fonts can support different writing systems, and each system can
* use a different baseline.
* The character argument determines the writing system to use. Clients
* should not assume all characters will use the same baseline.
*
* @param c a character used to identify the writing system
* @see #getBaselineOffsetsFor
* @see #ROMAN_BASELINE
* @see #CENTER_BASELINE
* @see #HANGING_BASELINE
*/
public byte getBaselineFor(char c) {
return fntObj.getBaselineFor(c);
}
/**
* Return a list of relative offsets for the different baselines.
* <p>
* Large fonts can support different writing systems, and each system can
* have its own set of preferred baseline offsets. The character argument
* determines the writing system to use.
* <p>
* These all are relative to the most common baseline used with this font
* (the one from which ascent and descent are measured). Negative values
* are up on horizontal lines, and right on vertical lines.
*
* @param c a character used to identify the writing system
* @see #getBaselineFor
* @see #ROMAN_BASELINE
* @see #CENTER_BASELINE
* @see #HANGING_BASELINE
*/
public float[] getBaselineOffsetsFor(char c) {
float[] baselines = new float[3];
baselines[ROMAN_BASELINE] = 0;
baselines[CENTER_BASELINE] = (getAscent() + getDescent()) / 2 -
getAscent();
baselines[HANGING_BASELINE] = getDescent() - getAscent();
return baselines;
}
/**
* Return true if this font places all glyphs on a single baseline.
*/
public boolean isUniformBaseline() {
return fntObj.isUniformBaseline();
}
/**
*
* Return the index of the first character with a different baseline
* from the first character in the range, or end index if all characters
* in the range have the same baseline.
*/
public int sameBaselineUpTo(java.text.CharacterIterator iter) {
return fntObj.sameBaselineUpTo(iter);
}
/**
*
* Return the index of the first character with a different baseline from the
* character at start, or limit if all characters between start and limit have
* the same baseline.
*/
public int sameBaselineUpTo(char[] text, int start, int limit) {
return fntObj.sameBaselineUpTo(text, start, limit);
}
/**
* Return the underline offset from the roman baseline for this character.
*/
public float getUnderlineOffsetFor(char c) {
return fntObj.getUnderlineOffsetFor(c);
}
/**
* Return the underline thickness for this character. Zero means the
* character is not underlined.
*/
public float getUnderlineThicknessFor(char c) {
return fntObj.getUnderlineThicknessFor(c);
}
/**
* Return the strikethrough offset from the roman baseline for this
* character.
*/
public float getStrikethroughOffsetFor(char c) {
return fntObj.getStrikethroughOffsetFor(c, getFontID());
}
/**
* Return the strikethrough thickness for this character. Zero means the
* character is
* not underlined.
*/
public float getStrikethroughThicknessFor(char c) {
return fntObj.getStrikethroughThicknessFor(c);
}
/**
* Returns the outline description of a glyph specified by a glyph code.
* @param glyphCode the glyph for which to get the outline.
* @param x the <i>x</i> coordinate for the glyph's position.
* @param y the <i>y</i> coordinate for the glyph's position,
* indicating the font baseline for the glyph.
* @return a Shape object representing the outline of the glyph.
*/
public Shape getGlyphOutline(int glyphCode, float x, float y){
int[] glyphCodes = new int[1];
glyphCodes[0] = glyphCode;
Point2D origin = new Point2D.Float(x,y);
return fntObj.createOutline(1,glyphCodes,
origin, null, null, null,
(float)size,false, getFontID());
}
public Shape getOutline(GlyphSet glyphs, AffineTransform tx, float x,
float y) {
int glyphCount = glyphs.getNumGlyphs();
int[] glyphCodes = glyphs.getGlyphCodes();
float[] xpos = ((StandardGlyphSet)glyphs).getXAdvances( true );
float[] ypos = ((StandardGlyphSet)glyphs).getYAdvances( true );
Point2D origin = new Point2D.Float(x,y);
return fntObj.createOutline(glyphCount,glyphCodes,
origin,xpos,
ypos,tx,
(float)size,false,getFontID());
}
public float[] getMetrics(GlyphSet glyphs, AffineTransform tx) {
int[] codes = glyphs.getGlyphCodes();
return fntObj.getMetrics(codes.length, codes, null, getSize2D(),
getFontID());
}
public float getAdvance(char c) {
return fntObj.getAdvance(c, null, getSize2D(), getFontID());
}
public float getAdvance(char[] text) {
return fntObj.getAdvance(text, null, getSize2D(), getFontID());
}
float getAdvance(int c) {
return fntObj.getAdvance(c, null, getSize2D(), getFontID());
}
/**
* Returns an array of font attributes available in this font.
* Attributes include things like ligatures and glyph substitution.
* @return a FontAttribute array.
*/
public AttributeSet getAttributes(){
return fRequestedAttributes;
}
/**
* Returns the names of all the attributes supported by this font.
* These attributes may be used to derive other fonts.
*/
public String[] getAvailableAttributes(){
String attributes[] = {
TextAttributeSet.FAMILY,
TextAttributeSet.WEIGHT,
TextAttributeSet.POSTURE,
TextAttributeSet.SIZE
};
return attributes;
}
/**
* Return justification info for the glyph specified by glyphCode.
*
* If left is true, return info for the left side of the glyph, otherwise
* for the right.
* If grow is true, return info for growing a line, otherwise for
* shrinking it.
*
* !!! unify into one set of information per glyph?
* Then it's the accessing code's problem...
*/
public GlyphJustificationInfo getGlyphJustificationInfo(int glyphCode) {
if (glyphCode >= getNumGlyphs()) {
glyphCode = getMissingGlyphCode();
}
if (fJustificationCache == null) {
fJustificationCache = new GlyphJustificationInfo[getNumGlyphs()];
}
GlyphJustificationInfo gji = fJustificationCache[glyphCode];
if (gji == null) {
float weight = getSize2D(); // adjust weight based on size
if (weight == 0) {
// REMIND jk (df) token default size, should we do this?
weight = 12;
}
boolean growAbsorb = false; // no absorbing characters for now.
boolean shrinkAbsorb = false;
int growPriority = 4;
int shrinkPriority = 4;
float growLimit = (float)(weight / 2.0); // left and right
float shrinkLimit = (float)(weight / 4.0); // left and right
boolean space = glyphCode == ' ';
boolean combining = !space &&
getGlyphMetrics(glyphCode).isCombining();
if (space) {
growPriority = 1;
shrinkPriority = 2;
// growLimit = weight;
} else if (combining) {
growPriority = 4; // never adjusted
shrinkPriority = 4;
} else {
growPriority = 2;
shrinkPriority = 1;
}
// weight,
// growAbsorb, growPriority, growLeftLimit, growRightLimit
// shrinkAbsorb, shrinkPriority, shrinkLeftLimit, shrinkRightLimit
gji = new GlyphJustificationInfo(weight, growAbsorb,
growPriority, growLimit, growLimit,
shrinkAbsorb, shrinkPriority,
shrinkLimit, shrinkLimit);
fJustificationCache[glyphCode] = gji;
}
return gji;
}
/**
* Return the offset of the glyphs rendered by this font from the natural
* baseline.
*
* Superscripted glyphs always have a negative offset. On vertical lines,
* a positive offset will move glyphs to the right. This value is
* determined by the superscript attribute.
*
* The default is zero.
*/
private float getOffset() {
float[] ff = (float[])getRequestedAttributes().
get(TextAttributeSet.SUPERSUBSCRIPT);
if (ff != null) {
return getSize2D() * ff[1];
}
return 0;
}
/**
* Return the actual font size used by this font.
*
* This size is a multiple of the base size, as determined by the
* superscript attribute.
*/
private float getActualSize() {
float[] ff = (float[])getRequestedAttributes().
get(TextAttributeSet.SUPERSUBSCRIPT);
if (ff != null) {
return getSize2D() * ff[0];
}
return getSize2D();
}
/**
* Returns the attribute set used to create this font.
* NOTE: This method may be moved into another class.
*/
public AttributeSet getRequestedAttributes(){
return fRequestedAttributes;
}
/**
* Creates a new Font object by replicating the current Font object
* with a new style, size and font attributes associated with it.
* @param style the style for the new Font.
* @param size the size in float for the new Font.
* @param attributes an array of AttributeSets enabled for the new Font.
* @return a new Font object.
*/
public Font deriveFont(int style,float size){
return new Font(ffApply(style, ffApply(size,
getRequestedAttributes())));
}
/**
* Creates a new Font object by replicating the current Font object
* with a new style, transform and font attributes associated with it.
* @param style the style for the new Font.
* @param trans the AffineTransform associated with the new Font.
* @param attributes an array of AttributeSets enabled for the new Font.
* @return a new Font object.
*/
public Font deriveFont(int style,AffineTransform trans){
return new Font(ffApply(style, ffApply(trans,
getRequestedAttributes())));
}
/**
* Creates a new Font object by replicating the current Font object
* with a new size associated with it.
* @param size the size in float for the new Font.
* @return a new Font object.
*/
public Font deriveFont(float size){
// REMIND jk. Need to create a new font object.
this.size = (int)size;
pointSize = size;
return this;
}
/**
* Creates a new Font object by replicating the current Font object
* with a new transform associated with it.
* @param trans the AffineTransform associated with the new Font.
* @return a new Font object.
*/
public Font deriveFont(AffineTransform trans){
return new Font(ffApply(trans, getRequestedAttributes()));
}
/**
* Creates a new Font object by replicating the current Font object
* with a new style associated with it.
* @param style the style for the new Font.
* @return a new Font object.
*/
public Font deriveFont(int style){
return new Font(ffApply(style, getRequestedAttributes()));
}
/**
* Creates a new Font object by replicating the current Font object
* with a new set of font attributes associated with it.
* @param attributes an array of AttributeSets enabled for the new Font.
* @return a new Font object.
*/
public Font deriveFont(TextAttributeSet attributes){
TextAttributeSet newAttrs =
new TextAttributeSet(getRequestedAttributes());
newAttrs.add(attributes);
return new Font(newAttrs);
}
/**
* Create a glyph set for the text between start and limit.
*
* @see GlyphSet
*
* @param context the text for the entire line. The whole
* context is provided for fonts that need to do glyph shaping.
* @param start the start of the subrange for which to create glyphs.
* @param limit the limit of the subrange for which to create glyphs.
* @param order a mapping from logical to visual positions for
* bidirectional text. For example, the value 5 at position 0 in the
* array means that the character at logical position 0 (from the
* start of the text in the iterator) displays at visual position 5
* (assuming all characters are displayed).
* <p>If order is null, the visual position is the logical position.
* Position zero in this array is position startIndex in the context.
* The array length must be at least the number of characters in the
* iterator.
* <p>Note the meaning of this array is the inverse of the mapping
* passed to the GlyphSet constructor. This is to faciliate indexing
* into the array using start and limit.
* @param levels an array indicating the bidirection level of each
* character. Even values are left to right, odd are right to left.
* This will affect hit testing and cursor positioning.
* If null, all characters are left to right. Position zero in this
* array is position beginIndex in the context, and the array length
* is greater than or equal to endIndex - beginIndex.
*/
public GlyphSet getGlyphSet(CharacterIterator context,
int start, int limit,
byte baseline,
int[] order, byte[] levels) {
/*
* This implementation doesn't do shaping, so we don't need the
* context. Extract the remainder of the characters and call
* through to the array-based api.
*/
int offset = context.getBeginIndex();
char[] chars = new char[limit - start];
int n = 0;
for (char c = context.setIndex(start);
n < chars.length;
c = context.next()) {
chars[n++] = c;
}
int[] newOrder = GlyphSet.getNormalizedOrder(order, levels,
start - offset, limit - offset);
// it would be handy if System provided a utility to do this...
byte[] newLevels = null;
if (levels != null) {
newLevels = new byte[limit - start];
System.arraycopy(levels, start - offset, newLevels, 0,
newLevels.length);
}
return getGlyphSet(chars, 0, chars.length, baseline, newOrder,
newLevels);
}
/*
* For testing. Set QUICK to true for no ligatures or other fancy
* stuff in the glyphsets created by this font.
*/
private static boolean QUICK = false;
/**
* Construct a GlyphSet for the text between start and limit.
*/
public GlyphSet getGlyphSet(char[] context, int start, int limit,
byte baseline, int[] order, byte[] levels) {
/*
* REMIND jeet-- the font has to implement all of this, since
* only the font knows how to compute ligatures.
*/
// return fntObj.getGlyphSet(context, start, limit, baseline,
// order, levels);
// this implementation ignores context, so strip it off
int count = limit - start;
int[] newOrder = order;
if (order != null) {
newOrder = GlyphSet.getNormalizedOrder(order, levels,
start, limit);
newOrder = GlyphSet.getInverseOrder(newOrder);
}
byte[] newLevels = levels;
if (levels != null && count != context.length) {
newLevels = new byte[count];
System.arraycopy(levels, start, newLevels, 0, count);
}
//REMIND jk define QUICK based on the fast case conversion
// differences needed between TrueType and Type1
if (QUICK) {
// glyph codes are just the unicode values,
// advances are all the default advances
int[] glyphs = new int[count];
for (int i = start; i < limit; i++) {
glyphs[i-start] = context[i];
}
return new StandardGlyphSet(this, glyphs, baseline, 0,
null, 0, null, newOrder, newLevels, count);
} else if (!QUICK) {
// glyph codes are just the unicode values,
// advances are all the default advances
int[] glyphs = new int[count];
glyphs = fntObj.getGlyphIndicies(getFontID(),
new String(context) );
return new StandardGlyphSet(this, glyphs, baseline, 0,
null, 0, null, newOrder, newLevels, count);
} else {
boolean vert = isVerticalBaseline();
int[] glyphs = new int[count];
byte[] newGlyphs = new byte[count];
float[] advs = new float[count];
/*
* We require at least one glyph per character.
* Ligatures will have a glyph code representing the ligature
* followed by 'ignore' glyph codes as placeholders for the
* other characters. Characters that are combined forms will
* be treated similarly. This assumes each combined form can
* be represented by a single glyph code, so we don't have to
* break them out into more glyphs then there were characters.
*/
/*
* mock up ligature support
* NOTE! This implementation of component glyphs does some
* really inefficient object creation and array copying. It
* is for expository purposes only. Don't implement this way
* in production code.
*
* REMIND jeet if you use code like this, you need a new
* constructor for the ligaturizer that takes a char array.
*/
Ligaturizer ligaturizer = new Ligaturizer(context, start, limit);
int i = 0;
int g = ligaturizer.first();
while (g != Ligaturizer.DONE) {
newGlyphs[i] = (byte)g;
advs[i] = getGlyphMetrics(g).getAdvance();
++i;
/*
* {jbr} boy is this a hack. Better to scan before we
* start this process and get array sizes right. This
* is nothing more than cheap expediency
*/
while (ligaturizer.hasComponentGlyphs()) {
newGlyphs = expandByteArray(newGlyphs, i-1);
newLevels = expandByteArray(newLevels, i-1);
advs = expandFloatArray(advs, i-1);
++count;
/*
* if we're expanding a right-to-left glyph, and the
* visual order is null, we should create a visual
* order since the component glyphs must be visually
* ordered right-to-left.
*/
if (newOrder == null && newLevels != null &&
(newLevels[i-1] & 0x1) != 0) {
newOrder = new int[newLevels.length-1];
for (int indx=0; indx < newOrder.length; indx++) {
newOrder[indx] = indx;
}
}
if (newOrder != null) {
int[] logToVis = GlyphSet.getInverseOrder(newOrder);
// Let's see just how inefficient we can be...
logToVis = expandIntArray(logToVis, i-1);
int currentVis = logToVis[i-1];
for (int k=0; k<count; k++) {
if (logToVis[k] > currentVis) {
logToVis[k]++;
}
}
if (newLevels != null &&
(newLevels[i-1] & 0x1) != 0) {
// ie if glyph is rtl
logToVis[i-1]++;
} else {
logToVis[i]++;
}
newOrder = StandardGlyphSet.getInverseOrder(logToVis);
}
newGlyphs[i] = (byte)ligaturizer.nextComponentGlyph();
advs[i] = getGlyphMetrics(g).getAdvance();
++i;
}
g = ligaturizer.next();
}
/* REMIND jk (df). got to do this efficiently and correctly */
/*
Note to Jeet. the FontObject has to implement this.
We're collecting glyphs from the ligature as we go, we
can't just change them here and assume all the advances
are ok. Besides which, the ligaturizer is creating
ligatures, they are not characters, so you can't create
a string from them.
*/
glyphs = fntObj.getGlyphIndicies(getFontID(),
new String(newGlyphs) );
float[] xadv = null; // vert ? null : advs;
float[] yadv = null; // vert ? advs : null;
float offset = getOffset();
float xa = vert ? offset : 0;
float ya = vert ? 0 : offset;
return new StandardGlyphSet(this, glyphs, baseline, xa,
xadv, ya, yadv, newOrder, newLevels, glyphs.length);
}
}
private boolean isLatin(String str) {
//REMIND jk. Use a proper method for determination.
return str.charAt(0) > 0x00ff;
}
/**
* Converts all characters in the String Object to font glyph codes.
* Glyph substitution may be performed.
* @param str a String object.
* @return a GlyphSet containing a collection of glyphs and glyph positions.
*/
public GlyphSet getGlyphSet(String str){
// REMIND jk. This shortcut should be applied in the graphics pipeline.
if(true || isLatin(str)) {
return getGlyphSet( new StringCharacterIterator(str),
0,str.length(),
Font.ROMAN_BASELINE, null, null);
} else {
int count = str.length();
int[] glyphs = new int[count];
float[] advs = new float[count];
boolean vert = isVerticalBaseline();
glyphs = fntObj.getGlyphIndicies(getFontID(), str);
advs = fntObj.getMetrics(glyphs.length, glyphs, null, getSize(),
getFontID());
float[] xadv = vert ? null : advs;
float[] yadv = vert ? advs : null;
float offset = 0;//REMIND jk getOffset();
float xa = vert ? offset : 0;
float ya = vert ? 0 : offset;
return new StandardGlyphSet(this , glyphs, Font.ROMAN_BASELINE, xa, xadv,
ya, yadv, null, null, count);
}
}
/**
* Checks if this font has a glyph for the specified character.
* @param c a unicode character code.
* @return true if the font can display the character.
*/
public boolean canDisplay(char c){
return fntObj.canDisplay(c);
}
/**
* Indicates whether a string is displayable by this Font.
* For strings with Unicode encoding, it is important to know if a given
* Font can display the string. This method returns an offset
* into the String str which is the first character the Font
* cannot display without using the missing glyph code. If the Font can
* display all characters, -1 is returned.
* @param str a String object.
* @return an offset into the String object that can be displayed by this
* font.
*/
public int canDisplayUpTo(String str){
return canDisplayUpTo(new StringCharacterIterator(str), 0,
str.length());
}
/**
* A convenience overload.
*/
public int canDisplayUpTo(char[] text, int start, int limit) {
while (start < limit && fntObj.canDisplay(text[start])) {
++start;
}
return start;
}
/**
* Indicates whether a string is displayable by this Font.
* For strings with Unicode encoding, it is important to know if a given
* Font can display the string. This method returns an offset
* into the String str which is the first character the Font
* cannot display without using the missing glyph code . If the Font can
* display all characters, -1 is returned.
* @param text a CharacterIterator object.
* @return an offset into the String object that can be displayed by this
* font.
*/
public int canDisplayUpTo(CharacterIterator iter, int start, int limit) {
for (char c = iter.setIndex(start);
iter.getIndex() < limit && fntObj.canDisplay(c);
c = iter.next()) {
}
return iter.getIndex();
}
/**
* Returns the offset from the roman baseline of the font to the specified
* baseline.
*/
public float getBaselineOffset(byte baselineType){
if (baselineType < ROMAN_BASELINE || baselineType > HANGING_BASELINE){
throw new IllegalArgumentException("bad baseline code");
}
float result = 0f;
switch (baselineType) {
case ROMAN_BASELINE:
result = getAscent();
break;
case CENTER_BASELINE:
result = (getAscent() + getDescent()) / 2;
break;
case HANGING_BASELINE:
result = getDescent();
break; // arbitrary REMIND df
}
return result;
}
/**
* Returns the ascent of the font above the roman baseline.
*/
public float getAscent() {
if (ascent == -1) {
ascent = fntObj.getAscent(pointSize, usesVerticalMetrics(),
getFontID());
}
return ascent;
}
/**
* Returns the descent of the font above the roman baseline.
*/
public float getDescent() {
if (descent == -1) {
descent = fntObj.getDescent(pointSize, usesVerticalMetrics(),
getFontID());
}
return descent;
}
/**
* Returns the leading for this font;
*/
public float getLeading(){
if (leading == -1) {
leading = fntObj.getLeading(pointSize, usesVerticalMetrics(),
getFontID());
}
return leading;
}
private float getHeight() {
return getAscent() + getDescent() + getLeading();
}
/**
* Returns the maximum advance of any glyph in this font.
*/
public float getMaxAdvance(){
if (maxAdvance == -1) {
maxAdvance = fntObj.getMaxAdvance(pointSize,
usesVerticalMetrics(),
getFontID());
}
return maxAdvance;
}
/**
* Returns the maximum bounding box of this font.
*/
public Rectangle2D getMaxBounds2D(){
if (usesVerticalMetrics()){
return new Rectangle2D.Float(getMaxAdvance() / 2f, 0,
getMaxAdvance(),
getHeight());
} else {
return new Rectangle2D.Float(0, -getAscent(),
getMaxAdvance(),
getHeight());
}
}
/**
* Returns the italic angle of this font.
*/
public float getItalicAngle(){
return fntObj.getItalicAngle();
}
// REMIND jk. need to get this from GraphicsEnvironment
private static String[] getAvailableFamilyNames(){
String[] names = {
"Courier",
"Helvetica"
};
return names;
}
/**
* Resolve styles on the character at start into an instance of Font
* that can best render the text between start and limit.
* REMIND jk. Move it to graphics environment.
*/
public static Font getBestFontFor(AttributedCharacterIterator text) {
return getBestFontFor(text, text.getBeginIndex(), text.getEndIndex());
}
/**
* Resolve styles on the character at start into an instance of Font
* that can best render the text between start and limit.
* REMIND jk. Move it to graphics environment.
*/
public static Font getBestFontFor(AttributedCharacterIterator text,
int start, int limit) {
/*
* choose the first font that can display the first character
* first iterate through the styles in the range of text we were
* passed. If none of them work, iterate through font families
* using the attributes on the first character. If this also
* fails, use the first font.
*/
char c = text.setIndex(start);
AttributeSet ff = text.getAttributes();
Font font = getFont(ff);
while (!font.canDisplay(c) && (text.getRunLimit() < limit)) {
text.setIndex(text.getRunLimit());
font = getFont(text.getAttributes());
}
if (!font.canDisplay(c)) {
text.setIndex(start);
String[] families = getAvailableFamilyNames(); // hack
for (int i = 0; i < families.length; ++i) {
TextAttributeSet newAttrs = new TextAttributeSet(ff);
newAttrs.add(TextAttributeSet.FAMILY, families[i]);
font = getFont(newAttrs);
if (font.canDisplay(c)) {
break;
}
}
if (!font.canDisplay(c)) {
font = getFont(ff);
}
}
return font;
}
/*
* Utility to expand oldArray by 1, duplicating the value at breakPoint
*/
private static int[] expandIntArray(int[] oldArray, int breakPoint) {
if (oldArray == null) {
return null;
}
int[] newArray = new int[oldArray.length+1];
System.arraycopy(oldArray, 0, newArray, 0, breakPoint+1);
System.arraycopy(oldArray, breakPoint, newArray, breakPoint+1,
(oldArray.length-breakPoint));
return newArray;
}
/*
* Utility to expand oldArray by 1, duplicating the value at breakPoint
*/
private static char[] expandCharArray(char[] oldArray, int breakPoint) {
if (oldArray == null) {
return null;
}
char[] newArray = new char[oldArray.length+1];
System.arraycopy(oldArray, 0, newArray, 0, breakPoint+1);
System.arraycopy(oldArray, breakPoint, newArray, breakPoint+1,
(oldArray.length-breakPoint));
return newArray;
}
/*
* Utility to expand oldArray by 1, duplicating the value at breakPoint
*/
private static byte[] expandByteArray(byte[] oldArray, int breakPoint) {
if (oldArray == null) {
return null;
}
byte[] newArray = new byte[oldArray.length+1];
System.arraycopy(oldArray, 0, newArray, 0, breakPoint+1);
System.arraycopy(oldArray, breakPoint, newArray, breakPoint+1,
(oldArray.length-breakPoint));
return newArray;
}
private static float[] expandFloatArray(float[] oldArray,
int breakPoint)
{
if (oldArray == null) {
return null;
}
float[] newArray = new float[oldArray.length+1];
System.arraycopy(oldArray, 0, newArray, 0, breakPoint+1);
System.arraycopy(oldArray, breakPoint, newArray, breakPoint+1,
(oldArray.length-breakPoint));
return newArray;
}
private static AttributeSet ffApply(String name, AttributeSet attributes) {
TextAttributeSet rval = new TextAttributeSet(attributes);
rval.add(TextAttributeSet.FAMILY, name);
return rval;
}
private static AttributeSet ffApply(AffineTransform trans,
AttributeSet attributes) {
TextAttributeSet rval = new TextAttributeSet(attributes);
rval.add(TextAttributeSet.TRANSFORM, trans);
return rval;
}
private static AttributeSet ffApply(int style, AttributeSet attributes) {
TextAttributeSet rval = new TextAttributeSet(attributes);
if ((style & BOLD) != 0) {
rval.add(TextAttributeSet.WEIGHT, TextAttributeSet.WEIGHT_BOLD);
} else {
rval.remove(TextAttributeSet.WEIGHT);
}
if ((style & ITALIC) != 0) {
rval.add(TextAttributeSet.POSTURE, TextAttributeSet.POSTURE_ITALIC);
} else {
rval.remove(TextAttributeSet.POSTURE);
}
return rval;
}
private static AttributeSet ffApply(float size, AttributeSet attributes) {
TextAttributeSet rval = new TextAttributeSet(attributes);
rval.add(TextAttributeSet.SIZE, new Float(size));
return rval;
}
private static AttributeSet ffApply(String name, int style,
float size, AttributeSet attributes)
{
return ffApply(name, ffApply(style, ffApply(size, attributes)));
}
/*
* Initialize JNI field and method IDs
*/
private static native void initIDs();
private native void pDispose();
protected void finalize() throws Throwable {
pDispose();
super.finalize();
}
}