/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.awt; import com.android.internal.awt.AndroidGraphics2D; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.font.LineMetrics; import java.awt.font.TextAttribute; import java.awt.font.TransformAttribute; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.io.File; import java.io.FileInputStream; import java.io.BufferedInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.text.CharacterIterator; import java.text.AttributedCharacterIterator.Attribute; import java.util.Hashtable; import java.util.Locale; import java.util.Map; import java.util.StringTokenizer; import org.apache.harmony.awt.gl.font.AndroidGlyphVector; import org.apache.harmony.awt.gl.font.CommonGlyphVector; import org.apache.harmony.awt.gl.font.FontPeerImpl; import org.apache.harmony.awt.gl.font.FontMetricsImpl; import org.apache.harmony.awt.gl.font.LineMetricsImpl; import org.apache.harmony.awt.internal.nls.Messages; import org.apache.harmony.luni.util.NotImplementedException; import org.apache.harmony.misc.HashCode; /** * The Font class represents fonts for rendering text. This class allow to map * characters to glyphs. * <p> * A glyph is a shape used to render a character or a sequence of characters. * For example one character of Latin writing system represented by one glyph, * but in complex writing system such as South and South-East Asian there is * more complicated correspondence between characters and glyphs. * <p> * The Font object is identified by two types of names. The logical font name is * the name that is used to construct the font. The font name is the name of a * particular font face (for example, Arial Bold). The family name is the font's * family name that specifies the typographic design across several faces (for * example, Arial). In all the Font is identified by three attributes: the * family name, the style (such as bold or italic), and the size. * * @since Android 1.0 */ public class Font implements Serializable { /** * The Constant serialVersionUID. */ private static final long serialVersionUID = -4206021311591459213L; // Identity Transform attribute /** * The Constant IDENTITY_TRANSFORM. */ private static final TransformAttribute IDENTITY_TRANSFORM = new TransformAttribute( new AffineTransform()); /** * The Constant PLAIN indicates font's plain style. */ public static final int PLAIN = 0; /** * The Constant BOLD indicates font's bold style. */ public static final int BOLD = 1; /** * The Constant ITALIC indicates font's italic style. */ public static final int ITALIC = 2; /** * The Constant ROMAN_BASELINE indicated roman baseline. */ public static final int ROMAN_BASELINE = 0; /** * The Constant CENTER_BASELINE indicates center baseline. */ public static final int CENTER_BASELINE = 1; /** * The Constant HANGING_BASELINE indicates hanging baseline. */ public static final int HANGING_BASELINE = 2; /** * The Constant TRUETYPE_FONT indicates a font resource of type TRUETYPE. */ public static final int TRUETYPE_FONT = 0; /** * The Constant TYPE1_FONT indicates a font resource of type TYPE1. */ public static final int TYPE1_FONT = 1; /** * The Constant LAYOUT_LEFT_TO_RIGHT indicates that text is left to right. */ public static final int LAYOUT_LEFT_TO_RIGHT = 0; /** * The Constant LAYOUT_RIGHT_TO_LEFT indicates that text is right to left. */ public static final int LAYOUT_RIGHT_TO_LEFT = 1; /** * The Constant LAYOUT_NO_START_CONTEXT indicates that the text in the char * array before the indicated start should not be examined. */ public static final int LAYOUT_NO_START_CONTEXT = 2; /** * The Constant LAYOUT_NO_LIMIT_CONTEXT indicates that text in the char * array after the indicated limit should not be examined. */ public static final int LAYOUT_NO_LIMIT_CONTEXT = 4; /** * The Constant DEFAULT_FONT. */ static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 12); //$NON-NLS-1$ /** * The name of the Font. */ protected String name; /** * The style of the Font. */ protected int style; /** * The size of the Font. */ protected int size; /** * The point size of the Font. */ protected float pointSize; // Flag if the Font object transformed /** * The transformed. */ private boolean transformed; // Set of font attributes /** * The requested attributes. */ private Hashtable<Attribute, Object> fRequestedAttributes; // font peer object corresponding to this Font /** * The font peer. */ private transient FontPeerImpl fontPeer; // number of glyphs in this Font /** * The num glyphs. */ private transient int numGlyphs = -1; // code for missing glyph for this Font /** * The missing glyph code. */ private transient int missingGlyphCode = -1; /** * Writes object to ObjectOutputStream. * * @param out * ObjectOutputStream. * @throws IOException * Signals that an I/O exception has occurred. */ private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.defaultWriteObject(); } /** * Reads object from ObjectInputStream object and set native platform * dependent fields to default values. * * @param in * ObjectInputStream object. * @throws IOException * Signals that an I/O exception has occurred. * @throws ClassNotFoundException * the class not found exception. */ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); numGlyphs = -1; missingGlyphCode = -1; } /** * Instantiates a new Font with the specified attributes. The Font will be * created with default attributes if the attribute's parameter is null. * * @param attributes * the attributes to be assigned to the new Font, or null. */ public Font(Map<? extends Attribute, ?> attributes) { Object currAttr; // Default values are taken from the documentation of the Font class. // See Font constructor, decode and getFont sections. this.name = "default"; //$NON-NLS-1$ this.size = 12; this.pointSize = 12; this.style = Font.PLAIN; if (attributes != null) { fRequestedAttributes = new Hashtable<Attribute, Object>(attributes); currAttr = attributes.get(TextAttribute.SIZE); if (currAttr != null) { this.pointSize = ((Float)currAttr).floatValue(); this.size = (int)Math.ceil(this.pointSize); } currAttr = attributes.get(TextAttribute.POSTURE); if (currAttr != null && currAttr.equals(TextAttribute.POSTURE_OBLIQUE)) { this.style |= Font.ITALIC; } currAttr = attributes.get(TextAttribute.WEIGHT); if ((currAttr != null) && (((Float)currAttr).floatValue() >= (TextAttribute.WEIGHT_BOLD).floatValue())) { this.style |= Font.BOLD; } currAttr = attributes.get(TextAttribute.FAMILY); if (currAttr != null) { this.name = (String)currAttr; } currAttr = attributes.get(TextAttribute.TRANSFORM); if (currAttr != null) { if (currAttr instanceof TransformAttribute) { this.transformed = !((TransformAttribute)currAttr).getTransform().isIdentity(); } else if (currAttr instanceof AffineTransform) { this.transformed = !((AffineTransform)currAttr).isIdentity(); } } } else { fRequestedAttributes = new Hashtable<Attribute, Object>(5); fRequestedAttributes.put(TextAttribute.TRANSFORM, IDENTITY_TRANSFORM); this.transformed = false; fRequestedAttributes.put(TextAttribute.FAMILY, name); fRequestedAttributes.put(TextAttribute.SIZE, new Float(this.size)); if ((this.style & Font.BOLD) != 0) { fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); } else { fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR); } if ((this.style & Font.ITALIC) != 0) { fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); } else { fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR); } } } /** * Instantiates a new Font with the specified name, style and size. * * @param name * the name of font. * @param style * the style of font. * @param size * the size of font. */ public Font(String name, int style, int size) { this.name = (name != null) ? name : "Default"; //$NON-NLS-1$ this.size = (size >= 0) ? size : 0; this.style = (style & ~0x03) == 0 ? style : Font.PLAIN; this.pointSize = this.size; fRequestedAttributes = new Hashtable<Attribute, Object>(5); fRequestedAttributes.put(TextAttribute.TRANSFORM, IDENTITY_TRANSFORM); this.transformed = false; fRequestedAttributes.put(TextAttribute.FAMILY, this.name); fRequestedAttributes.put(TextAttribute.SIZE, new Float(this.size)); if ((this.style & Font.BOLD) != 0) { fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); } else { fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR); } if ((this.style & Font.ITALIC) != 0) { fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); } else { fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR); } } /** * Returns true if this Font has a glyph for the specified character. * * @param c * the character. * @return true if this Font has a glyph for the specified character, false * otherwise. */ public boolean canDisplay(char c) { FontPeerImpl peer = (FontPeerImpl)this.getPeer(); return peer.canDisplay(c); } /** * Returns true if the Font can display the characters of the the specified * text from the specified start position to the specified limit position. * * @param text * the text. * @param start * the start offset (in the character array). * @param limit * the limit offset (in the character array). * @return the a character's position in the text that this Font can not * display, or -1 if this Font can display all characters in this * text. */ public int canDisplayUpTo(char[] text, int start, int limit) { int st = start; int result; while ((st < limit) && canDisplay(text[st])) { st++; } if (st == limit) { result = -1; } else { result = st; } return result; } /** * Returns true if the Font can display the characters of the the specified * CharacterIterator from the specified start position and the specified * limit position. * * @param iter * the CharacterIterator. * @param start * the start offset. * @param limit * the limit offset. * @return the a character's position in the CharacterIterator that this * Font can not display, or -1 if this Font can display all * characters in this text. */ public int canDisplayUpTo(CharacterIterator iter, int start, int limit) { int st = start; char c = iter.setIndex(start); int result; while ((st < limit) && (canDisplay(c))) { st++; c = iter.next(); } if (st == limit) { result = -1; } else { result = st; } return result; } /** * Returns true if this Font can display a specified String. * * @param str * the String. * @return the a character's position in the String that this Font can not * display, or -1 if this Font can display all characters in this * text. */ public int canDisplayUpTo(String str) { char[] chars = str.toCharArray(); return canDisplayUpTo(chars, 0, chars.length); } /** * Creates a GlyphVector of associating characters to glyphs based on the * Unicode map of this Font. * * @param frc * the FontRenderContext. * @param chars * the characters array. * @return the GlyphVector of associating characters to glyphs based on the * Unicode map of this Font. */ public GlyphVector createGlyphVector(FontRenderContext frc, char[] chars) { return new AndroidGlyphVector(chars, frc, this, 0); } /** * Creates a GlyphVector of associating characters contained in the * specified CharacterIterator to glyphs based on the Unicode map of this * Font. * * @param frc * the FontRenderContext. * @param iter * the CharacterIterator. * @return the GlyphVector of associating characters contained in the * specified CharacterIterator to glyphs based on the Unicode map of * this Font. */ public GlyphVector createGlyphVector(FontRenderContext frc, CharacterIterator iter) { throw new RuntimeException("Not implemented!"); //$NON-NLS-1$ } /** * Creates a GlyphVector of associating characters to glyphs based on the * Unicode map of this Font. * * @param frc * the FontRenderContext. * @param glyphCodes * the specified integer array of glyph codes. * @return the GlyphVector of associating characters to glyphs based on the * Unicode map of this Font. * @throws NotImplementedException * if this method is not implemented by a subclass. */ public GlyphVector createGlyphVector(FontRenderContext frc, int[] glyphCodes) throws org.apache.harmony.luni.util.NotImplementedException { throw new RuntimeException("Not implemented!"); //$NON-NLS-1$ } /** * Creates a GlyphVector of associating characters to glyphs based on the * Unicode map of this Font. * * @param frc * the FontRenderContext. * @param str * the specified String. * @return the GlyphVector of associating characters to glyphs based on the * Unicode map of this Font. */ public GlyphVector createGlyphVector(FontRenderContext frc, String str) { return new AndroidGlyphVector(str.toCharArray(), frc, this, 0); } /** * Returns the font style constant value corresponding to one of the font * style names ("BOLD", "ITALIC", "BOLDITALIC"). This method returns * Font.PLAIN if the argument is not one of the predefined style names. * * @param fontStyleName * font style name. * @return font style constant value corresponding to the font style name * specified. */ private static int getFontStyle(String fontStyleName) { int result = Font.PLAIN; if (fontStyleName.toUpperCase().equals("BOLDITALIC")) { //$NON-NLS-1$ result = Font.BOLD | Font.ITALIC; } else if (fontStyleName.toUpperCase().equals("BOLD")) { //$NON-NLS-1$ result = Font.BOLD; } else if (fontStyleName.toUpperCase().equals("ITALIC")) { //$NON-NLS-1$ result = Font.ITALIC; } return result; } /** * Decodes the specified string which described the Font. The string should * have the following format: fontname-style-pointsize. The style can be * PLAIN, BOLD, BOLDITALIC, or ITALIC. * * @param str * the string which describes the font. * @return the Font from the specified string. */ public static Font decode(String str) { // XXX: Documentation doesn't describe all cases, e.g. fonts face names // with // symbols that are suggested as delimiters in the documentation. // In this decode implementation only ***-***-*** format is used with // '-' // as the delimiter to avoid unexpected parse results of font face names // with spaces. if (str == null) { return DEFAULT_FONT; } StringTokenizer strTokens; String delim = "-"; //$NON-NLS-1$ String substr; int fontSize = DEFAULT_FONT.size; int fontStyle = DEFAULT_FONT.style; String fontName = DEFAULT_FONT.name; strTokens = new StringTokenizer(str.trim(), delim); // Font Name if (strTokens.hasMoreTokens()) { fontName = strTokens.nextToken(); // first token is the font name } // Font Style or Size (if the style is undefined) if (strTokens.hasMoreTokens()) { substr = strTokens.nextToken(); try { // if second token is the font size fontSize = Integer.parseInt(substr); } catch (NumberFormatException e) { // then second token is the font style fontStyle = getFontStyle(substr); } } // Font Size if (strTokens.hasMoreTokens()) { try { fontSize = Integer.parseInt(strTokens.nextToken()); } catch (NumberFormatException e) { } } return new Font(fontName, fontStyle, fontSize); } /** * Performs the specified affine transform to the Font and returns a new * Font. * * @param trans * the AffineTransform. * @return the Font object. * @throws IllegalArgumentException * if affine transform parameter is null. */ @SuppressWarnings("unchecked") public Font deriveFont(AffineTransform trans) { if (trans == null) { // awt.94=transform can not be null throw new IllegalArgumentException(Messages.getString("awt.94")); //$NON-NLS-1$ } Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes .clone(); derivefRequestedAttributes.put(TextAttribute.TRANSFORM, new TransformAttribute(trans)); return new Font(derivefRequestedAttributes); } /** * Returns a new Font that is a copy of the current Font modified so that * the size is the specified size. * * @param size * the size of font. * @return the Font object. */ @SuppressWarnings("unchecked") public Font deriveFont(float size) { Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes .clone(); derivefRequestedAttributes.put(TextAttribute.SIZE, new Float(size)); return new Font(derivefRequestedAttributes); } /** * Returns a new Font that is a copy of the current Font modified so that * the style is the specified style. * * @param style * the style of font. * @return the Font object. */ @SuppressWarnings("unchecked") public Font deriveFont(int style) { Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes .clone(); if ((style & Font.BOLD) != 0) { derivefRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); } else if (derivefRequestedAttributes.get(TextAttribute.WEIGHT) != null) { derivefRequestedAttributes.remove(TextAttribute.WEIGHT); } if ((style & Font.ITALIC) != 0) { derivefRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); } else if (derivefRequestedAttributes.get(TextAttribute.POSTURE) != null) { derivefRequestedAttributes.remove(TextAttribute.POSTURE); } return new Font(derivefRequestedAttributes); } /** * Returns a new Font that is a copy of the current Font modified to match * the specified style and with the specified affine transform applied to * its glyphs. * * @param style * the style of font. * @param trans * the AffineTransform. * @return the Font object. */ @SuppressWarnings("unchecked") public Font deriveFont(int style, AffineTransform trans) { if (trans == null) { // awt.94=transform can not be null throw new IllegalArgumentException(Messages.getString("awt.94")); //$NON-NLS-1$ } Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes .clone(); if ((style & BOLD) != 0) { derivefRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); } else if (derivefRequestedAttributes.get(TextAttribute.WEIGHT) != null) { derivefRequestedAttributes.remove(TextAttribute.WEIGHT); } if ((style & ITALIC) != 0) { derivefRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); } else if (derivefRequestedAttributes.get(TextAttribute.POSTURE) != null) { derivefRequestedAttributes.remove(TextAttribute.POSTURE); } derivefRequestedAttributes.put(TextAttribute.TRANSFORM, new TransformAttribute(trans)); return new Font(derivefRequestedAttributes); } /** * Returns a new Font that is a copy of the current Font modified so that * the size and style are the specified size and style. * * @param style * the style of font. * @param size * the size of font. * @return the Font object. */ @SuppressWarnings("unchecked") public Font deriveFont(int style, float size) { Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes .clone(); if ((style & BOLD) != 0) { derivefRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); } else if (derivefRequestedAttributes.get(TextAttribute.WEIGHT) != null) { derivefRequestedAttributes.remove(TextAttribute.WEIGHT); } if ((style & ITALIC) != 0) { derivefRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); } else if (derivefRequestedAttributes.get(TextAttribute.POSTURE) != null) { derivefRequestedAttributes.remove(TextAttribute.POSTURE); } derivefRequestedAttributes.put(TextAttribute.SIZE, new Float(size)); return new Font(derivefRequestedAttributes); } /** * Returns a new Font object with a new set of font attributes. * * @param attributes * the map of attributes. * @return the Font. */ @SuppressWarnings("unchecked") public Font deriveFont(Map<? extends Attribute, ?> attributes) { Attribute[] avalAttributes = this.getAvailableAttributes(); Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes .clone(); Object currAttribute; for (Attribute element : avalAttributes) { currAttribute = attributes.get(element); if (currAttribute != null) { derivefRequestedAttributes.put(element, currAttribute); } } return new Font(derivefRequestedAttributes); } /** * Compares the specified Object with the current Font. * * @param obj * the Object to be compared. * @return true, if the specified Object is an instance of Font with the * same family, size, and style as this Font, false otherwise. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj != null) { try { Font font = (Font)obj; return ((this.style == font.style) && (this.size == font.size) && this.name.equals(font.name) && (this.pointSize == font.pointSize) && (this .getTransform()).equals(font.getTransform())); } catch (ClassCastException e) { } } return false; } /** * Gets the map of font's attributes. * * @return the map of font's attributes. */ @SuppressWarnings("unchecked") public Map<TextAttribute, ?> getAttributes() { return (Map<TextAttribute, ?>)fRequestedAttributes.clone(); } /** * Gets the keys of all available attributes. * * @return the keys array of all available attributes. */ public Attribute[] getAvailableAttributes() { Attribute[] attrs = { TextAttribute.FAMILY, TextAttribute.POSTURE, TextAttribute.SIZE, TextAttribute.TRANSFORM, TextAttribute.WEIGHT, TextAttribute.SUPERSCRIPT, TextAttribute.WIDTH }; return attrs; } /** * Gets the baseline for this character. * * @param c * the character. * @return the baseline for this character. */ public byte getBaselineFor(char c) { // TODO: implement using TT BASE table data return 0; } /** * Gets the family name of the Font. * * @return the family name of the Font. */ public String getFamily() { if (fRequestedAttributes != null) { fRequestedAttributes.get(TextAttribute.FAMILY); } return null; } /** * Returns the family name of this Font associated with the specified * locale. * * @param l * the locale. * @return the family name of this Font associated with the specified * locale. */ public String getFamily(Locale l) { if (l == null) { // awt.01='{0}' parameter is null throw new NullPointerException(Messages.getString("awt.01", "Locale")); //$NON-NLS-1$ //$NON-NLS-2$ } return getFamily(); } /** * Gets a Font with the specified attribute set. * * @param attributes * the attributes to be assigned to the new Font. * @return the Font. */ public static Font getFont(Map<? extends Attribute, ?> attributes) { Font fnt = (Font)attributes.get(TextAttribute.FONT); if (fnt != null) { return fnt; } return new Font(attributes); } /** * Gets a Font object from the system properties list with the specified * name or returns the specified Font if there is no such property. * * @param sp * the specified property name. * @param f * the Font. * @return the Font object from the system properties list with the * specified name or the specified Font if there is no such * property. */ public static Font getFont(String sp, Font f) { String pr = System.getProperty(sp); if (pr == null) { return f; } return decode(pr); } /** * Gets a Font object from the system properties list with the specified * name. * * @param sp * the system property name. * @return the Font, or null if there is no such property with the specified * name. */ public static Font getFont(String sp) { return getFont(sp, null); } /** * Gets the font name. * * @return the font name. */ public String getFontName() { if (fRequestedAttributes != null) { fRequestedAttributes.get(TextAttribute.FAMILY); } return null; } /** * Returns the font name associated with the specified locale. * * @param l * the locale. * @return the font name associated with the specified locale. */ public String getFontName(Locale l) { return getFamily(); } /** * Returns a LineMetrics object created with the specified parameters. * * @param chars * the chars array. * @param start * the start offset. * @param end * the end offset. * @param frc * the FontRenderContext. * @return the LineMetrics for the specified parameters. */ public LineMetrics getLineMetrics(char[] chars, int start, int end, FontRenderContext frc) { if (frc == null) { // awt.00=FontRenderContext is null throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$ } // FontMetrics fm = AndroidGraphics2D.getInstance().getFontMetrics(); FontMetrics fm = new FontMetricsImpl(this); float[] fmet = { fm.getAscent(), fm.getDescent(), fm.getLeading() }; return new LineMetricsImpl(chars.length, fmet, null); } /** * Returns a LineMetrics object created with the specified parameters. * * @param iter * the CharacterIterator. * @param start * the start offset. * @param end * the end offset. * @param frc * the FontRenderContext. * @return the LineMetrics for the specified parameters. */ public LineMetrics getLineMetrics(CharacterIterator iter, int start, int end, FontRenderContext frc) { if (frc == null) { // awt.00=FontRenderContext is null throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$ } String resultString; int iterCount; iterCount = end - start; if (iterCount < 0) { resultString = ""; //$NON-NLS-1$ } else { char[] chars = new char[iterCount]; int i = 0; for (char c = iter.setIndex(start); c != CharacterIterator.DONE && (i < iterCount); c = iter .next()) { chars[i] = c; i++; } resultString = new String(chars); } return this.getLineMetrics(resultString, frc); } /** * Returns a LineMetrics object created with the specified parameters. * * @param str * the String. * @param frc * the FontRenderContext. * @return the LineMetrics for the specified parameters. */ public LineMetrics getLineMetrics(String str, FontRenderContext frc) { // FontMetrics fm = AndroidGraphics2D.getInstance().getFontMetrics(); FontMetrics fm = new FontMetricsImpl(this); float[] fmet = { fm.getAscent(), fm.getDescent(), fm.getLeading() }; // Log.i("FONT FMET", fmet.toString()); return new LineMetricsImpl(str.length(), fmet, null); } /** * Returns a LineMetrics object created with the specified parameters. * * @param str * the String. * @param start * the start offset. * @param end * the end offset. * @param frc * the FontRenderContext. * @return the LineMetrics for the specified parameters. */ public LineMetrics getLineMetrics(String str, int start, int end, FontRenderContext frc) { return this.getLineMetrics(str.substring(start, end), frc); } /** * Gets the logical bounds of the specified String in the specified * FontRenderContext. The logical bounds contains the origin, ascent, * advance, and height. * * @param ci * the specified CharacterIterator. * @param start * the start offset. * @param end * the end offset. * @param frc * the FontRenderContext. * @return a Rectangle2D object. */ public Rectangle2D getStringBounds(CharacterIterator ci, int start, int end, FontRenderContext frc) { int first = ci.getBeginIndex(); int finish = ci.getEndIndex(); char[] chars; if (start < first) { // awt.95=Wrong start index: {0} throw new IndexOutOfBoundsException(Messages.getString("awt.95", start)); //$NON-NLS-1$ } if (end > finish) { // awt.96=Wrong finish index: {0} throw new IndexOutOfBoundsException(Messages.getString("awt.96", end)); //$NON-NLS-1$ } if (start > end) { // awt.97=Wrong range length: {0} throw new IndexOutOfBoundsException(Messages.getString("awt.97", //$NON-NLS-1$ (end - start))); } if (frc == null) { throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$ } chars = new char[end - start]; ci.setIndex(start); for (int i = 0; i < chars.length; i++) { chars[i] = ci.current(); ci.next(); } return this.getStringBounds(chars, 0, chars.length, frc); } /** * Gets the logical bounds of the specified String in the specified * FontRenderContext. The logical bounds contains the origin, ascent, * advance, and height. * * @param str * the specified String. * @param frc * the FontRenderContext. * @return a Rectangle2D object. */ public Rectangle2D getStringBounds(String str, FontRenderContext frc) { char[] chars = str.toCharArray(); return this.getStringBounds(chars, 0, chars.length, frc); } /** * Gets the logical bounds of the specified String in the specified * FontRenderContext. The logical bounds contains the origin, ascent, * advance, and height. * * @param str * the specified String. * @param start * the start offset. * @param end * the end offset. * @param frc * the FontRenderContext. * @return a Rectangle2D object. */ public Rectangle2D getStringBounds(String str, int start, int end, FontRenderContext frc) { return this.getStringBounds((str.substring(start, end)), frc); } /** * Gets the logical bounds of the specified String in the specified * FontRenderContext. The logical bounds contains the origin, ascent, * advance, and height. * * @param chars * the specified character array. * @param start * the start offset. * @param end * the end offset. * @param frc * the FontRenderContext. * @return a Rectangle2D object. */ public Rectangle2D getStringBounds(char[] chars, int start, int end, FontRenderContext frc) { if (start < 0) { // awt.95=Wrong start index: {0} throw new IndexOutOfBoundsException(Messages.getString("awt.95", start)); //$NON-NLS-1$ } if (end > chars.length) { // awt.96=Wrong finish index: {0} throw new IndexOutOfBoundsException(Messages.getString("awt.96", end)); //$NON-NLS-1$ } if (start > end) { // awt.97=Wrong range length: {0} throw new IndexOutOfBoundsException(Messages.getString("awt.97", //$NON-NLS-1$ (end - start))); } if (frc == null) { throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$ } FontPeerImpl peer = (FontPeerImpl)this.getPeer(); final int TRANSFORM_MASK = AffineTransform.TYPE_GENERAL_ROTATION | AffineTransform.TYPE_GENERAL_TRANSFORM; Rectangle2D bounds; AffineTransform transform = getTransform(); // XXX: for transforms where an angle between basis vectors is not 90 // degrees Rectanlge2D class doesn't fit as Logical bounds. if ((transform.getType() & TRANSFORM_MASK) == 0) { int width = 0; for (int i = start; i < end; i++) { width += peer.charWidth(chars[i]); } // LineMetrics nlm = peer.getLineMetrics(); LineMetrics nlm = getLineMetrics(chars, start, end, frc); bounds = transform.createTransformedShape( new Rectangle2D.Float(0, -nlm.getAscent(), width, nlm.getHeight())) .getBounds2D(); } else { int len = end - start; char[] subChars = new char[len]; System.arraycopy(chars, start, subChars, 0, len); bounds = createGlyphVector(frc, subChars).getLogicalBounds(); } return bounds; } /** * Gets the character's maximum bounds as defined in the specified * FontRenderContext. * * @param frc * the FontRenderContext. * @return the character's maximum bounds. */ public Rectangle2D getMaxCharBounds(FontRenderContext frc) { if (frc == null) { // awt.00=FontRenderContext is null throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$ } FontPeerImpl peer = (FontPeerImpl)this.getPeer(); Rectangle2D bounds = peer.getMaxCharBounds(frc); AffineTransform transform = getTransform(); // !! Documentation doesn't describe meaning of max char bounds // for the fonts that have rotate transforms. For all transforms // returned bounds are the bounds of transformed maxCharBounds // Rectangle2D that corresponds to the font with identity transform. // TODO: resolve this issue to return correct bounds bounds = transform.createTransformedShape(bounds).getBounds2D(); return bounds; } /** * Returns a new GlyphVector object performing full layout of the text. * * @param frc * the FontRenderContext. * @param chars * the character array to be layout. * @param start * the start offset of the text to use for the GlyphVector. * @param count * the count of characters to use for the GlyphVector. * @param flags * the flag indicating text direction: LAYOUT_RIGHT_TO_LEFT, * LAYOUT_LEFT_TO_RIGHT. * @return the GlyphVector. */ public GlyphVector layoutGlyphVector(FontRenderContext frc, char[] chars, int start, int count, int flags) { // TODO: implement method for bidirectional text. // At the moment only LTR and RTL texts supported. if (start < 0) { // awt.95=Wrong start index: {0} throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.95", //$NON-NLS-1$ start)); } if (count < 0) { // awt.98=Wrong count value, can not be negative: {0} throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.98", //$NON-NLS-1$ count)); } if (start + count > chars.length) { // awt.99=Wrong [start + count] is out of range: {0} throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.99", //$NON-NLS-1$ (start + count))); } char[] out = new char[count]; System.arraycopy(chars, start, out, 0, count); return new CommonGlyphVector(out, frc, this, flags); } /** * Returns the String representation of this Font. * * @return the String representation of this Font. */ @Override public String toString() { String stl = "plain"; //$NON-NLS-1$ String result; if (this.isBold() && this.isItalic()) { stl = "bolditalic"; //$NON-NLS-1$ } if (this.isBold() && !this.isItalic()) { stl = "bold"; //$NON-NLS-1$ } if (!this.isBold() && this.isItalic()) { stl = "italic"; //$NON-NLS-1$ } result = this.getClass().getName() + "[family=" + this.getFamily() + //$NON-NLS-1$ ",name=" + this.name + //$NON-NLS-1$ ",style=" + stl + //$NON-NLS-1$ ",size=" + this.size + "]"; //$NON-NLS-1$ //$NON-NLS-2$ return result; } /** * Gets the postscript name of this Font. * * @return the postscript name of this Font. */ public String getPSName() { FontPeerImpl peer = (FontPeerImpl)this.getPeer(); return peer.getPSName(); } /** * Gets the logical name of this Font. * * @return the logical name of this Font. */ public String getName() { return (this.name); } /** * Gets the peer of this Font. * * @return the peer of this Font. * @deprecated Font rendering is platform independent now. */ @Deprecated public java.awt.peer.FontPeer getPeer() { if (fontPeer == null) { fontPeer = (FontPeerImpl)Toolkit.getDefaultToolkit().getGraphicsFactory().getFontPeer( this); } return fontPeer; } /** * Gets the transform acting on this Font (from the Font's attributes). * * @return the transformation of this Font. */ public AffineTransform getTransform() { Object transform = fRequestedAttributes.get(TextAttribute.TRANSFORM); if (transform != null) { if (transform instanceof TransformAttribute) { return ((TransformAttribute)transform).getTransform(); } if (transform instanceof AffineTransform) { return new AffineTransform((AffineTransform)transform); } } else { transform = new AffineTransform(); } return (AffineTransform)transform; } /** * Checks if this font is transformed or not. * * @return true, if this font is transformed, false otherwise. */ public boolean isTransformed() { return this.transformed; } /** * Checks if this font has plain style or not. * * @return true, if this font has plain style, false otherwise. */ public boolean isPlain() { return (this.style == PLAIN); } /** * Checks if this font has italic style or not. * * @return true, if this font has italic style, false otherwise. */ public boolean isItalic() { return (this.style & ITALIC) != 0; } /** * Checks if this font has bold style or not. * * @return true, if this font has bold style, false otherwise. */ public boolean isBold() { return (this.style & BOLD) != 0; } /** * Returns true if this Font has uniform line metrics. * * @return true if this Font has uniform line metrics, false otherwise. */ public boolean hasUniformLineMetrics() { FontPeerImpl peer = (FontPeerImpl)this.getPeer(); return peer.hasUniformLineMetrics(); } /** * Returns hash code of this Font object. * * @return the hash code of this Font object. */ @Override public int hashCode() { HashCode hash = new HashCode(); hash.append(this.name); hash.append(this.style); hash.append(this.size); return hash.hashCode(); } /** * Gets the style of this Font. * * @return the style of this Font. */ public int getStyle() { return this.style; } /** * Gets the size of this Font. * * @return the size of this Font. */ public int getSize() { return this.size; } /** * Gets the number of glyphs for this Font. * * @return the number of glyphs for this Font. */ public int getNumGlyphs() { if (numGlyphs == -1) { FontPeerImpl peer = (FontPeerImpl)this.getPeer(); this.numGlyphs = peer.getNumGlyphs(); } return this.numGlyphs; } /** * Gets the glyphCode which is used as default glyph when this Font does not * have a glyph for a specified Unicode. * * @return the missing glyph code. */ public int getMissingGlyphCode() { if (missingGlyphCode == -1) { FontPeerImpl peer = (FontPeerImpl)this.getPeer(); this.missingGlyphCode = peer.getMissingGlyphCode(); } return this.missingGlyphCode; } /** * Gets the float value of font's size. * * @return the float value of font's size. */ public float getSize2D() { return this.pointSize; } /** * Gets the italic angle of this Font. * * @return the italic angle of this Font. */ public float getItalicAngle() { FontPeerImpl peer = (FontPeerImpl)this.getPeer(); return peer.getItalicAngle(); } /** * Creates the font with the specified font format and font file. * * @param fontFormat * the font format. * @param fontFile * the file object represented the input data for the font. * @return the Font. * @throws FontFormatException * is thrown if fontFile does not contain the required font * tables for the specified format. * @throws IOException * signals that an I/O exception has occurred. */ public static Font createFont(int fontFormat, File fontFile) throws FontFormatException, IOException { // ???AWT not supported InputStream is = new FileInputStream(fontFile); try { return createFont(fontFormat, is); } finally { is.close(); } } /** * Creates the font with the specified font format and input stream. * * @param fontFormat * the font format. * @param fontStream * the input stream represented input data for the font. * @return the Font. * @throws FontFormatException * is thrown if fontFile does not contain the required font * tables for the specified format. * @throws IOException * signals that an I/O exception has occurred. */ public static Font createFont(int fontFormat, InputStream fontStream) throws FontFormatException, IOException { // ???AWT not supported BufferedInputStream buffStream; int bRead = 0; int size = 8192; // memory page size, for the faster reading byte buf[] = new byte[size]; if (fontFormat != TRUETYPE_FONT) { // awt.9A=Unsupported font format throw new IllegalArgumentException(Messages.getString("awt.9A")); //$NON-NLS-1$ } /* Get font file in system-specific directory */ File fontFile = Toolkit.getDefaultToolkit().getGraphicsFactory().getFontManager() .getTempFontFile(); // BEGIN android-modified buffStream = new BufferedInputStream(fontStream, 8192); // END android-modified FileOutputStream fOutStream = new FileOutputStream(fontFile); bRead = buffStream.read(buf, 0, size); while (bRead != -1) { fOutStream.write(buf, 0, bRead); bRead = buffStream.read(buf, 0, size); } buffStream.close(); fOutStream.close(); Font font = null; font = Toolkit.getDefaultToolkit().getGraphicsFactory().embedFont( fontFile.getAbsolutePath()); if (font == null) { // awt.9B=Can't create font - bad font data throw new FontFormatException(Messages.getString("awt.9B")); //$NON-NLS-1$ } return font; } }