1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.awt;
19
20import com.android.internal.awt.AndroidGraphics2D;
21
22import java.awt.font.FontRenderContext;
23import java.awt.font.GlyphVector;
24import java.awt.font.LineMetrics;
25import java.awt.font.TextAttribute;
26import java.awt.font.TransformAttribute;
27import java.awt.geom.AffineTransform;
28import java.awt.geom.Rectangle2D;
29import java.io.File;
30import java.io.FileInputStream;
31import java.io.BufferedInputStream;
32import java.io.FileOutputStream;
33import java.io.IOException;
34import java.io.InputStream;
35import java.io.Serializable;
36import java.text.CharacterIterator;
37import java.text.AttributedCharacterIterator.Attribute;
38import java.util.Hashtable;
39import java.util.Locale;
40import java.util.Map;
41import java.util.StringTokenizer;
42
43import org.apache.harmony.awt.gl.font.AndroidGlyphVector;
44import org.apache.harmony.awt.gl.font.CommonGlyphVector;
45import org.apache.harmony.awt.gl.font.FontPeerImpl;
46import org.apache.harmony.awt.gl.font.FontMetricsImpl;
47import org.apache.harmony.awt.gl.font.LineMetricsImpl;
48import org.apache.harmony.awt.internal.nls.Messages;
49import org.apache.harmony.luni.util.NotImplementedException;
50import org.apache.harmony.misc.HashCode;
51
52/**
53 * The Font class represents fonts for rendering text. This class allow to map
54 * characters to glyphs.
55 * <p>
56 * A glyph is a shape used to render a character or a sequence of characters.
57 * For example one character of Latin writing system represented by one glyph,
58 * but in complex writing system such as South and South-East Asian there is
59 * more complicated correspondence between characters and glyphs.
60 * <p>
61 * The Font object is identified by two types of names. The logical font name is
62 * the name that is used to construct the font. The font name is the name of a
63 * particular font face (for example, Arial Bold). The family name is the font's
64 * family name that specifies the typographic design across several faces (for
65 * example, Arial). In all the Font is identified by three attributes: the
66 * family name, the style (such as bold or italic), and the size.
67 *
68 * @since Android 1.0
69 */
70public class Font implements Serializable {
71
72    /**
73     * The Constant serialVersionUID.
74     */
75    private static final long serialVersionUID = -4206021311591459213L;
76
77    // Identity Transform attribute
78    /**
79     * The Constant IDENTITY_TRANSFORM.
80     */
81    private static final TransformAttribute IDENTITY_TRANSFORM = new TransformAttribute(
82            new AffineTransform());
83
84    /**
85     * The Constant PLAIN indicates font's plain style.
86     */
87    public static final int PLAIN = 0;
88
89    /**
90     * The Constant BOLD indicates font's bold style.
91     */
92    public static final int BOLD = 1;
93
94    /**
95     * The Constant ITALIC indicates font's italic style.
96     */
97    public static final int ITALIC = 2;
98
99    /**
100     * The Constant ROMAN_BASELINE indicated roman baseline.
101     */
102    public static final int ROMAN_BASELINE = 0;
103
104    /**
105     * The Constant CENTER_BASELINE indicates center baseline.
106     */
107    public static final int CENTER_BASELINE = 1;
108
109    /**
110     * The Constant HANGING_BASELINE indicates hanging baseline.
111     */
112    public static final int HANGING_BASELINE = 2;
113
114    /**
115     * The Constant TRUETYPE_FONT indicates a font resource of type TRUETYPE.
116     */
117    public static final int TRUETYPE_FONT = 0;
118
119    /**
120     * The Constant TYPE1_FONT indicates a font resource of type TYPE1.
121     */
122    public static final int TYPE1_FONT = 1;
123
124    /**
125     * The Constant LAYOUT_LEFT_TO_RIGHT indicates that text is left to right.
126     */
127    public static final int LAYOUT_LEFT_TO_RIGHT = 0;
128
129    /**
130     * The Constant LAYOUT_RIGHT_TO_LEFT indicates that text is right to left.
131     */
132    public static final int LAYOUT_RIGHT_TO_LEFT = 1;
133
134    /**
135     * The Constant LAYOUT_NO_START_CONTEXT indicates that the text in the char
136     * array before the indicated start should not be examined.
137     */
138    public static final int LAYOUT_NO_START_CONTEXT = 2;
139
140    /**
141     * The Constant LAYOUT_NO_LIMIT_CONTEXT indicates that text in the char
142     * array after the indicated limit should not be examined.
143     */
144    public static final int LAYOUT_NO_LIMIT_CONTEXT = 4;
145
146    /**
147     * The Constant DEFAULT_FONT.
148     */
149    static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 12); //$NON-NLS-1$
150
151    /**
152     * The name of the Font.
153     */
154    protected String name;
155
156    /**
157     * The style of the Font.
158     */
159    protected int style;
160
161    /**
162     * The size of the Font.
163     */
164    protected int size;
165
166    /**
167     * The point size of the Font.
168     */
169    protected float pointSize;
170
171    // Flag if the Font object transformed
172    /**
173     * The transformed.
174     */
175    private boolean transformed;
176
177    // Set of font attributes
178    /**
179     * The requested attributes.
180     */
181    private Hashtable<Attribute, Object> fRequestedAttributes;
182
183    // font peer object corresponding to this Font
184    /**
185     * The font peer.
186     */
187    private transient FontPeerImpl fontPeer;
188
189    // number of glyphs in this Font
190    /**
191     * The num glyphs.
192     */
193    private transient int numGlyphs = -1;
194
195    // code for missing glyph for this Font
196    /**
197     * The missing glyph code.
198     */
199    private transient int missingGlyphCode = -1;
200
201    /**
202     * Writes object to ObjectOutputStream.
203     *
204     * @param out
205     *            ObjectOutputStream.
206     * @throws IOException
207     *             Signals that an I/O exception has occurred.
208     */
209    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
210        out.defaultWriteObject();
211    }
212
213    /**
214     * Reads object from ObjectInputStream object and set native platform
215     * dependent fields to default values.
216     *
217     * @param in
218     *            ObjectInputStream object.
219     * @throws IOException
220     *             Signals that an I/O exception has occurred.
221     * @throws ClassNotFoundException
222     *             the class not found exception.
223     */
224    private void readObject(java.io.ObjectInputStream in) throws IOException,
225            ClassNotFoundException {
226        in.defaultReadObject();
227
228        numGlyphs = -1;
229        missingGlyphCode = -1;
230
231    }
232
233    /**
234     * Instantiates a new Font with the specified attributes. The Font will be
235     * created with default attributes if the attribute's parameter is null.
236     *
237     * @param attributes
238     *            the attributes to be assigned to the new Font, or null.
239     */
240    public Font(Map<? extends Attribute, ?> attributes) {
241        Object currAttr;
242
243        // Default values are taken from the documentation of the Font class.
244        // See Font constructor, decode and getFont sections.
245
246        this.name = "default"; //$NON-NLS-1$
247        this.size = 12;
248        this.pointSize = 12;
249        this.style = Font.PLAIN;
250
251        if (attributes != null) {
252
253            fRequestedAttributes = new Hashtable<Attribute, Object>(attributes);
254
255            currAttr = attributes.get(TextAttribute.SIZE);
256            if (currAttr != null) {
257                this.pointSize = ((Float)currAttr).floatValue();
258                this.size = (int)Math.ceil(this.pointSize);
259            }
260
261            currAttr = attributes.get(TextAttribute.POSTURE);
262            if (currAttr != null && currAttr.equals(TextAttribute.POSTURE_OBLIQUE)) {
263                this.style |= Font.ITALIC;
264            }
265
266            currAttr = attributes.get(TextAttribute.WEIGHT);
267            if ((currAttr != null)
268                    && (((Float)currAttr).floatValue() >= (TextAttribute.WEIGHT_BOLD).floatValue())) {
269                this.style |= Font.BOLD;
270            }
271
272            currAttr = attributes.get(TextAttribute.FAMILY);
273            if (currAttr != null) {
274                this.name = (String)currAttr;
275            }
276
277            currAttr = attributes.get(TextAttribute.TRANSFORM);
278            if (currAttr != null) {
279                if (currAttr instanceof TransformAttribute) {
280                    this.transformed = !((TransformAttribute)currAttr).getTransform().isIdentity();
281                } else if (currAttr instanceof AffineTransform) {
282                    this.transformed = !((AffineTransform)currAttr).isIdentity();
283                }
284            }
285
286        } else {
287            fRequestedAttributes = new Hashtable<Attribute, Object>(5);
288            fRequestedAttributes.put(TextAttribute.TRANSFORM, IDENTITY_TRANSFORM);
289
290            this.transformed = false;
291
292            fRequestedAttributes.put(TextAttribute.FAMILY, name);
293
294            fRequestedAttributes.put(TextAttribute.SIZE, new Float(this.size));
295
296            if ((this.style & Font.BOLD) != 0) {
297                fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
298            } else {
299                fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR);
300            }
301            if ((this.style & Font.ITALIC) != 0) {
302                fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
303            } else {
304                fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR);
305            }
306
307        }
308    }
309
310    /**
311     * Instantiates a new Font with the specified name, style and size.
312     *
313     * @param name
314     *            the name of font.
315     * @param style
316     *            the style of font.
317     * @param size
318     *            the size of font.
319     */
320    public Font(String name, int style, int size) {
321
322        this.name = (name != null) ? name : "Default"; //$NON-NLS-1$
323        this.size = (size >= 0) ? size : 0;
324        this.style = (style & ~0x03) == 0 ? style : Font.PLAIN;
325        this.pointSize = this.size;
326
327        fRequestedAttributes = new Hashtable<Attribute, Object>(5);
328
329        fRequestedAttributes.put(TextAttribute.TRANSFORM, IDENTITY_TRANSFORM);
330
331        this.transformed = false;
332
333        fRequestedAttributes.put(TextAttribute.FAMILY, this.name);
334        fRequestedAttributes.put(TextAttribute.SIZE, new Float(this.size));
335
336        if ((this.style & Font.BOLD) != 0) {
337            fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
338        } else {
339            fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR);
340        }
341        if ((this.style & Font.ITALIC) != 0) {
342            fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
343        } else {
344            fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR);
345        }
346    }
347
348    /**
349     * Returns true if this Font has a glyph for the specified character.
350     *
351     * @param c
352     *            the character.
353     * @return true if this Font has a glyph for the specified character, false
354     *         otherwise.
355     */
356    public boolean canDisplay(char c) {
357        FontPeerImpl peer = (FontPeerImpl)this.getPeer();
358        return peer.canDisplay(c);
359    }
360
361    /**
362     * Returns true if the Font can display the characters of the the specified
363     * text from the specified start position to the specified limit position.
364     *
365     * @param text
366     *            the text.
367     * @param start
368     *            the start offset (in the character array).
369     * @param limit
370     *            the limit offset (in the character array).
371     * @return the a character's position in the text that this Font can not
372     *         display, or -1 if this Font can display all characters in this
373     *         text.
374     */
375    public int canDisplayUpTo(char[] text, int start, int limit) {
376        int st = start;
377        int result;
378        while ((st < limit) && canDisplay(text[st])) {
379            st++;
380        }
381
382        if (st == limit) {
383            result = -1;
384        } else {
385            result = st;
386        }
387
388        return result;
389    }
390
391    /**
392     * Returns true if the Font can display the characters of the the specified
393     * CharacterIterator from the specified start position and the specified
394     * limit position.
395     *
396     * @param iter
397     *            the CharacterIterator.
398     * @param start
399     *            the start offset.
400     * @param limit
401     *            the limit offset.
402     * @return the a character's position in the CharacterIterator that this
403     *         Font can not display, or -1 if this Font can display all
404     *         characters in this text.
405     */
406    public int canDisplayUpTo(CharacterIterator iter, int start, int limit) {
407        int st = start;
408        char c = iter.setIndex(start);
409        int result;
410
411        while ((st < limit) && (canDisplay(c))) {
412            st++;
413            c = iter.next();
414        }
415        if (st == limit) {
416            result = -1;
417        } else {
418            result = st;
419        }
420
421        return result;
422    }
423
424    /**
425     * Returns true if this Font can display a specified String.
426     *
427     * @param str
428     *            the String.
429     * @return the a character's position in the String that this Font can not
430     *         display, or -1 if this Font can display all characters in this
431     *         text.
432     */
433    public int canDisplayUpTo(String str) {
434        char[] chars = str.toCharArray();
435        return canDisplayUpTo(chars, 0, chars.length);
436    }
437
438    /**
439     * Creates a GlyphVector of associating characters to glyphs based on the
440     * Unicode map of this Font.
441     *
442     * @param frc
443     *            the FontRenderContext.
444     * @param chars
445     *            the characters array.
446     * @return the GlyphVector of associating characters to glyphs based on the
447     *         Unicode map of this Font.
448     */
449    public GlyphVector createGlyphVector(FontRenderContext frc, char[] chars) {
450        return new AndroidGlyphVector(chars, frc, this, 0);
451    }
452
453    /**
454     * Creates a GlyphVector of associating characters contained in the
455     * specified CharacterIterator to glyphs based on the Unicode map of this
456     * Font.
457     *
458     * @param frc
459     *            the FontRenderContext.
460     * @param iter
461     *            the CharacterIterator.
462     * @return the GlyphVector of associating characters contained in the
463     *         specified CharacterIterator to glyphs based on the Unicode map of
464     *         this Font.
465     */
466    public GlyphVector createGlyphVector(FontRenderContext frc, CharacterIterator iter) {
467        throw new RuntimeException("Not implemented!"); //$NON-NLS-1$
468    }
469
470    /**
471     * Creates a GlyphVector of associating characters to glyphs based on the
472     * Unicode map of this Font.
473     *
474     * @param frc
475     *            the FontRenderContext.
476     * @param glyphCodes
477     *            the specified integer array of glyph codes.
478     * @return the GlyphVector of associating characters to glyphs based on the
479     *         Unicode map of this Font.
480     * @throws NotImplementedException
481     *             if this method is not implemented by a subclass.
482     */
483    public GlyphVector createGlyphVector(FontRenderContext frc, int[] glyphCodes)
484            throws org.apache.harmony.luni.util.NotImplementedException {
485        throw new RuntimeException("Not implemented!"); //$NON-NLS-1$
486    }
487
488    /**
489     * Creates a GlyphVector of associating characters to glyphs based on the
490     * Unicode map of this Font.
491     *
492     * @param frc
493     *            the FontRenderContext.
494     * @param str
495     *            the specified String.
496     * @return the GlyphVector of associating characters to glyphs based on the
497     *         Unicode map of this Font.
498     */
499    public GlyphVector createGlyphVector(FontRenderContext frc, String str) {
500        return new AndroidGlyphVector(str.toCharArray(), frc, this, 0);
501
502    }
503
504    /**
505     * Returns the font style constant value corresponding to one of the font
506     * style names ("BOLD", "ITALIC", "BOLDITALIC"). This method returns
507     * Font.PLAIN if the argument is not one of the predefined style names.
508     *
509     * @param fontStyleName
510     *            font style name.
511     * @return font style constant value corresponding to the font style name
512     *         specified.
513     */
514    private static int getFontStyle(String fontStyleName) {
515        int result = Font.PLAIN;
516
517        if (fontStyleName.toUpperCase().equals("BOLDITALIC")) { //$NON-NLS-1$
518            result = Font.BOLD | Font.ITALIC;
519        } else if (fontStyleName.toUpperCase().equals("BOLD")) { //$NON-NLS-1$
520            result = Font.BOLD;
521        } else if (fontStyleName.toUpperCase().equals("ITALIC")) { //$NON-NLS-1$
522            result = Font.ITALIC;
523        }
524
525        return result;
526    }
527
528    /**
529     * Decodes the specified string which described the Font. The string should
530     * have the following format: fontname-style-pointsize. The style can be
531     * PLAIN, BOLD, BOLDITALIC, or ITALIC.
532     *
533     * @param str
534     *            the string which describes the font.
535     * @return the Font from the specified string.
536     */
537    public static Font decode(String str) {
538        // XXX: Documentation doesn't describe all cases, e.g. fonts face names
539        // with
540        // symbols that are suggested as delimiters in the documentation.
541        // In this decode implementation only ***-***-*** format is used with
542        // '-'
543        // as the delimiter to avoid unexpected parse results of font face names
544        // with spaces.
545
546        if (str == null) {
547            return DEFAULT_FONT;
548        }
549
550        StringTokenizer strTokens;
551        String delim = "-"; //$NON-NLS-1$
552        String substr;
553
554        int fontSize = DEFAULT_FONT.size;
555        int fontStyle = DEFAULT_FONT.style;
556        String fontName = DEFAULT_FONT.name;
557
558        strTokens = new StringTokenizer(str.trim(), delim);
559
560        // Font Name
561        if (strTokens.hasMoreTokens()) {
562            fontName = strTokens.nextToken(); // first token is the font name
563        }
564
565        // Font Style or Size (if the style is undefined)
566        if (strTokens.hasMoreTokens()) {
567            substr = strTokens.nextToken();
568
569            try {
570                // if second token is the font size
571                fontSize = Integer.parseInt(substr);
572            } catch (NumberFormatException e) {
573                // then second token is the font style
574                fontStyle = getFontStyle(substr);
575            }
576
577        }
578
579        // Font Size
580        if (strTokens.hasMoreTokens()) {
581            try {
582                fontSize = Integer.parseInt(strTokens.nextToken());
583            } catch (NumberFormatException e) {
584            }
585        }
586
587        return new Font(fontName, fontStyle, fontSize);
588    }
589
590    /**
591     * Performs the specified affine transform to the Font and returns a new
592     * Font.
593     *
594     * @param trans
595     *            the AffineTransform.
596     * @return the Font object.
597     * @throws IllegalArgumentException
598     *             if affine transform parameter is null.
599     */
600    @SuppressWarnings("unchecked")
601    public Font deriveFont(AffineTransform trans) {
602
603        if (trans == null) {
604            // awt.94=transform can not be null
605            throw new IllegalArgumentException(Messages.getString("awt.94")); //$NON-NLS-1$
606        }
607
608        Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes
609                .clone();
610
611        derivefRequestedAttributes.put(TextAttribute.TRANSFORM, new TransformAttribute(trans));
612
613        return new Font(derivefRequestedAttributes);
614
615    }
616
617    /**
618     * Returns a new Font that is a copy of the current Font modified so that
619     * the size is the specified size.
620     *
621     * @param size
622     *            the size of font.
623     * @return the Font object.
624     */
625    @SuppressWarnings("unchecked")
626    public Font deriveFont(float size) {
627        Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes
628                .clone();
629        derivefRequestedAttributes.put(TextAttribute.SIZE, new Float(size));
630        return new Font(derivefRequestedAttributes);
631    }
632
633    /**
634     * Returns a new Font that is a copy of the current Font modified so that
635     * the style is the specified style.
636     *
637     * @param style
638     *            the style of font.
639     * @return the Font object.
640     */
641    @SuppressWarnings("unchecked")
642    public Font deriveFont(int style) {
643        Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes
644                .clone();
645
646        if ((style & Font.BOLD) != 0) {
647            derivefRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
648        } else if (derivefRequestedAttributes.get(TextAttribute.WEIGHT) != null) {
649            derivefRequestedAttributes.remove(TextAttribute.WEIGHT);
650        }
651
652        if ((style & Font.ITALIC) != 0) {
653            derivefRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
654        } else if (derivefRequestedAttributes.get(TextAttribute.POSTURE) != null) {
655            derivefRequestedAttributes.remove(TextAttribute.POSTURE);
656        }
657
658        return new Font(derivefRequestedAttributes);
659    }
660
661    /**
662     * Returns a new Font that is a copy of the current Font modified to match
663     * the specified style and with the specified affine transform applied to
664     * its glyphs.
665     *
666     * @param style
667     *            the style of font.
668     * @param trans
669     *            the AffineTransform.
670     * @return the Font object.
671     */
672    @SuppressWarnings("unchecked")
673    public Font deriveFont(int style, AffineTransform trans) {
674
675        if (trans == null) {
676            // awt.94=transform can not be null
677            throw new IllegalArgumentException(Messages.getString("awt.94")); //$NON-NLS-1$
678        }
679        Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes
680                .clone();
681
682        if ((style & BOLD) != 0) {
683            derivefRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
684        } else if (derivefRequestedAttributes.get(TextAttribute.WEIGHT) != null) {
685            derivefRequestedAttributes.remove(TextAttribute.WEIGHT);
686        }
687
688        if ((style & ITALIC) != 0) {
689            derivefRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
690        } else if (derivefRequestedAttributes.get(TextAttribute.POSTURE) != null) {
691            derivefRequestedAttributes.remove(TextAttribute.POSTURE);
692        }
693        derivefRequestedAttributes.put(TextAttribute.TRANSFORM, new TransformAttribute(trans));
694
695        return new Font(derivefRequestedAttributes);
696    }
697
698    /**
699     * Returns a new Font that is a copy of the current Font modified so that
700     * the size and style are the specified size and style.
701     *
702     * @param style
703     *            the style of font.
704     * @param size
705     *            the size of font.
706     * @return the Font object.
707     */
708    @SuppressWarnings("unchecked")
709    public Font deriveFont(int style, float size) {
710        Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes
711                .clone();
712
713        if ((style & BOLD) != 0) {
714            derivefRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
715        } else if (derivefRequestedAttributes.get(TextAttribute.WEIGHT) != null) {
716            derivefRequestedAttributes.remove(TextAttribute.WEIGHT);
717        }
718
719        if ((style & ITALIC) != 0) {
720            derivefRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
721        } else if (derivefRequestedAttributes.get(TextAttribute.POSTURE) != null) {
722            derivefRequestedAttributes.remove(TextAttribute.POSTURE);
723        }
724
725        derivefRequestedAttributes.put(TextAttribute.SIZE, new Float(size));
726        return new Font(derivefRequestedAttributes);
727
728    }
729
730    /**
731     * Returns a new Font object with a new set of font attributes.
732     *
733     * @param attributes
734     *            the map of attributes.
735     * @return the Font.
736     */
737    @SuppressWarnings("unchecked")
738    public Font deriveFont(Map<? extends Attribute, ?> attributes) {
739        Attribute[] avalAttributes = this.getAvailableAttributes();
740
741        Hashtable<Attribute, Object> derivefRequestedAttributes = (Hashtable<Attribute, Object>)fRequestedAttributes
742                .clone();
743        Object currAttribute;
744        for (Attribute element : avalAttributes) {
745            currAttribute = attributes.get(element);
746            if (currAttribute != null) {
747                derivefRequestedAttributes.put(element, currAttribute);
748            }
749        }
750        return new Font(derivefRequestedAttributes);
751    }
752
753    /**
754     * Compares the specified Object with the current Font.
755     *
756     * @param obj
757     *            the Object to be compared.
758     * @return true, if the specified Object is an instance of Font with the
759     *         same family, size, and style as this Font, false otherwise.
760     */
761    @Override
762    public boolean equals(Object obj) {
763        if (obj == this) {
764            return true;
765        }
766
767        if (obj != null) {
768            try {
769                Font font = (Font)obj;
770
771                return ((this.style == font.style) && (this.size == font.size)
772                        && this.name.equals(font.name) && (this.pointSize == font.pointSize) && (this
773                        .getTransform()).equals(font.getTransform()));
774            } catch (ClassCastException e) {
775            }
776        }
777
778        return false;
779    }
780
781    /**
782     * Gets the map of font's attributes.
783     *
784     * @return the map of font's attributes.
785     */
786    @SuppressWarnings("unchecked")
787    public Map<TextAttribute, ?> getAttributes() {
788        return (Map<TextAttribute, ?>)fRequestedAttributes.clone();
789    }
790
791    /**
792     * Gets the keys of all available attributes.
793     *
794     * @return the keys array of all available attributes.
795     */
796    public Attribute[] getAvailableAttributes() {
797        Attribute[] attrs = {
798                TextAttribute.FAMILY, TextAttribute.POSTURE, TextAttribute.SIZE,
799                TextAttribute.TRANSFORM, TextAttribute.WEIGHT, TextAttribute.SUPERSCRIPT,
800                TextAttribute.WIDTH
801        };
802        return attrs;
803    }
804
805    /**
806     * Gets the baseline for this character.
807     *
808     * @param c
809     *            the character.
810     * @return the baseline for this character.
811     */
812    public byte getBaselineFor(char c) {
813        // TODO: implement using TT BASE table data
814        return 0;
815    }
816
817    /**
818     * Gets the family name of the Font.
819     *
820     * @return the family name of the Font.
821     */
822    public String getFamily() {
823        if (fRequestedAttributes != null) {
824            fRequestedAttributes.get(TextAttribute.FAMILY);
825        }
826        return null;
827    }
828
829    /**
830     * Returns the family name of this Font associated with the specified
831     * locale.
832     *
833     * @param l
834     *            the locale.
835     * @return the family name of this Font associated with the specified
836     *         locale.
837     */
838    public String getFamily(Locale l) {
839        if (l == null) {
840            // awt.01='{0}' parameter is null
841            throw new NullPointerException(Messages.getString("awt.01", "Locale")); //$NON-NLS-1$ //$NON-NLS-2$
842        }
843        return getFamily();
844    }
845
846    /**
847     * Gets a Font with the specified attribute set.
848     *
849     * @param attributes
850     *            the attributes to be assigned to the new Font.
851     * @return the Font.
852     */
853    public static Font getFont(Map<? extends Attribute, ?> attributes) {
854        Font fnt = (Font)attributes.get(TextAttribute.FONT);
855        if (fnt != null) {
856            return fnt;
857        }
858        return new Font(attributes);
859    }
860
861    /**
862     * Gets a Font object from the system properties list with the specified
863     * name or returns the specified Font if there is no such property.
864     *
865     * @param sp
866     *            the specified property name.
867     * @param f
868     *            the Font.
869     * @return the Font object from the system properties list with the
870     *         specified name or the specified Font if there is no such
871     *         property.
872     */
873    public static Font getFont(String sp, Font f) {
874        String pr = System.getProperty(sp);
875        if (pr == null) {
876            return f;
877        }
878        return decode(pr);
879    }
880
881    /**
882     * Gets a Font object from the system properties list with the specified
883     * name.
884     *
885     * @param sp
886     *            the system property name.
887     * @return the Font, or null if there is no such property with the specified
888     *         name.
889     */
890    public static Font getFont(String sp) {
891        return getFont(sp, null);
892    }
893
894    /**
895     * Gets the font name.
896     *
897     * @return the font name.
898     */
899    public String getFontName() {
900        if (fRequestedAttributes != null) {
901            fRequestedAttributes.get(TextAttribute.FAMILY);
902        }
903        return null;
904    }
905
906    /**
907     * Returns the font name associated with the specified locale.
908     *
909     * @param l
910     *            the locale.
911     * @return the font name associated with the specified locale.
912     */
913    public String getFontName(Locale l) {
914        return getFamily();
915    }
916
917    /**
918     * Returns a LineMetrics object created with the specified parameters.
919     *
920     * @param chars
921     *            the chars array.
922     * @param start
923     *            the start offset.
924     * @param end
925     *            the end offset.
926     * @param frc
927     *            the FontRenderContext.
928     * @return the LineMetrics for the specified parameters.
929     */
930    public LineMetrics getLineMetrics(char[] chars, int start, int end, FontRenderContext frc) {
931        if (frc == null) {
932            // awt.00=FontRenderContext is null
933            throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$
934        }
935
936        // FontMetrics fm = AndroidGraphics2D.getInstance().getFontMetrics();
937        FontMetrics fm = new FontMetricsImpl(this);
938        float[] fmet = {
939                fm.getAscent(), fm.getDescent(), fm.getLeading()
940        };
941        return new LineMetricsImpl(chars.length, fmet, null);
942    }
943
944    /**
945     * Returns a LineMetrics object created with the specified parameters.
946     *
947     * @param iter
948     *            the CharacterIterator.
949     * @param start
950     *            the start offset.
951     * @param end
952     *            the end offset.
953     * @param frc
954     *            the FontRenderContext.
955     * @return the LineMetrics for the specified parameters.
956     */
957    public LineMetrics getLineMetrics(CharacterIterator iter, int start, int end,
958            FontRenderContext frc) {
959
960        if (frc == null) {
961            // awt.00=FontRenderContext is null
962            throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$
963        }
964
965        String resultString;
966        int iterCount;
967
968        iterCount = end - start;
969        if (iterCount < 0) {
970            resultString = ""; //$NON-NLS-1$
971        } else {
972            char[] chars = new char[iterCount];
973            int i = 0;
974            for (char c = iter.setIndex(start); c != CharacterIterator.DONE && (i < iterCount); c = iter
975                    .next()) {
976                chars[i] = c;
977                i++;
978            }
979            resultString = new String(chars);
980        }
981        return this.getLineMetrics(resultString, frc);
982    }
983
984    /**
985     * Returns a LineMetrics object created with the specified parameters.
986     *
987     * @param str
988     *            the String.
989     * @param frc
990     *            the FontRenderContext.
991     * @return the LineMetrics for the specified parameters.
992     */
993    public LineMetrics getLineMetrics(String str, FontRenderContext frc) {
994        // FontMetrics fm = AndroidGraphics2D.getInstance().getFontMetrics();
995        FontMetrics fm = new FontMetricsImpl(this);
996        float[] fmet = {
997                fm.getAscent(), fm.getDescent(), fm.getLeading()
998        };
999        // Log.i("FONT FMET", fmet.toString());
1000        return new LineMetricsImpl(str.length(), fmet, null);
1001
1002    }
1003
1004    /**
1005     * Returns a LineMetrics object created with the specified parameters.
1006     *
1007     * @param str
1008     *            the String.
1009     * @param start
1010     *            the start offset.
1011     * @param end
1012     *            the end offset.
1013     * @param frc
1014     *            the FontRenderContext.
1015     * @return the LineMetrics for the specified parameters.
1016     */
1017    public LineMetrics getLineMetrics(String str, int start, int end, FontRenderContext frc) {
1018        return this.getLineMetrics(str.substring(start, end), frc);
1019    }
1020
1021    /**
1022     * Gets the logical bounds of the specified String in the specified
1023     * FontRenderContext. The logical bounds contains the origin, ascent,
1024     * advance, and height.
1025     *
1026     * @param ci
1027     *            the specified CharacterIterator.
1028     * @param start
1029     *            the start offset.
1030     * @param end
1031     *            the end offset.
1032     * @param frc
1033     *            the FontRenderContext.
1034     * @return a Rectangle2D object.
1035     */
1036    public Rectangle2D getStringBounds(CharacterIterator ci, int start, int end,
1037            FontRenderContext frc) {
1038        int first = ci.getBeginIndex();
1039        int finish = ci.getEndIndex();
1040        char[] chars;
1041
1042        if (start < first) {
1043            // awt.95=Wrong start index: {0}
1044            throw new IndexOutOfBoundsException(Messages.getString("awt.95", start)); //$NON-NLS-1$
1045        }
1046        if (end > finish) {
1047            // awt.96=Wrong finish index: {0}
1048            throw new IndexOutOfBoundsException(Messages.getString("awt.96", end)); //$NON-NLS-1$
1049        }
1050        if (start > end) {
1051            // awt.97=Wrong range length: {0}
1052            throw new IndexOutOfBoundsException(Messages.getString("awt.97", //$NON-NLS-1$
1053                    (end - start)));
1054        }
1055
1056        if (frc == null) {
1057            throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$
1058        }
1059
1060        chars = new char[end - start];
1061
1062        ci.setIndex(start);
1063        for (int i = 0; i < chars.length; i++) {
1064            chars[i] = ci.current();
1065            ci.next();
1066        }
1067
1068        return this.getStringBounds(chars, 0, chars.length, frc);
1069
1070    }
1071
1072    /**
1073     * Gets the logical bounds of the specified String in the specified
1074     * FontRenderContext. The logical bounds contains the origin, ascent,
1075     * advance, and height.
1076     *
1077     * @param str
1078     *            the specified String.
1079     * @param frc
1080     *            the FontRenderContext.
1081     * @return a Rectangle2D object.
1082     */
1083    public Rectangle2D getStringBounds(String str, FontRenderContext frc) {
1084        char[] chars = str.toCharArray();
1085        return this.getStringBounds(chars, 0, chars.length, frc);
1086
1087    }
1088
1089    /**
1090     * Gets the logical bounds of the specified String in the specified
1091     * FontRenderContext. The logical bounds contains the origin, ascent,
1092     * advance, and height.
1093     *
1094     * @param str
1095     *            the specified String.
1096     * @param start
1097     *            the start offset.
1098     * @param end
1099     *            the end offset.
1100     * @param frc
1101     *            the FontRenderContext.
1102     * @return a Rectangle2D object.
1103     */
1104    public Rectangle2D getStringBounds(String str, int start, int end, FontRenderContext frc) {
1105
1106        return this.getStringBounds((str.substring(start, end)), frc);
1107    }
1108
1109    /**
1110     * Gets the logical bounds of the specified String in the specified
1111     * FontRenderContext. The logical bounds contains the origin, ascent,
1112     * advance, and height.
1113     *
1114     * @param chars
1115     *            the specified character array.
1116     * @param start
1117     *            the start offset.
1118     * @param end
1119     *            the end offset.
1120     * @param frc
1121     *            the FontRenderContext.
1122     * @return a Rectangle2D object.
1123     */
1124    public Rectangle2D getStringBounds(char[] chars, int start, int end, FontRenderContext frc) {
1125        if (start < 0) {
1126            // awt.95=Wrong start index: {0}
1127            throw new IndexOutOfBoundsException(Messages.getString("awt.95", start)); //$NON-NLS-1$
1128        }
1129        if (end > chars.length) {
1130            // awt.96=Wrong finish index: {0}
1131            throw new IndexOutOfBoundsException(Messages.getString("awt.96", end)); //$NON-NLS-1$
1132        }
1133        if (start > end) {
1134            // awt.97=Wrong range length: {0}
1135            throw new IndexOutOfBoundsException(Messages.getString("awt.97", //$NON-NLS-1$
1136                    (end - start)));
1137        }
1138
1139        if (frc == null) {
1140            throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$
1141        }
1142
1143        FontPeerImpl peer = (FontPeerImpl)this.getPeer();
1144
1145        final int TRANSFORM_MASK = AffineTransform.TYPE_GENERAL_ROTATION
1146                | AffineTransform.TYPE_GENERAL_TRANSFORM;
1147        Rectangle2D bounds;
1148
1149        AffineTransform transform = getTransform();
1150
1151        // XXX: for transforms where an angle between basis vectors is not 90
1152        // degrees Rectanlge2D class doesn't fit as Logical bounds.
1153        if ((transform.getType() & TRANSFORM_MASK) == 0) {
1154            int width = 0;
1155            for (int i = start; i < end; i++) {
1156                width += peer.charWidth(chars[i]);
1157            }
1158            // LineMetrics nlm = peer.getLineMetrics();
1159
1160            LineMetrics nlm = getLineMetrics(chars, start, end, frc);
1161
1162            bounds = transform.createTransformedShape(
1163                    new Rectangle2D.Float(0, -nlm.getAscent(), width, nlm.getHeight()))
1164                    .getBounds2D();
1165        } else {
1166            int len = end - start;
1167            char[] subChars = new char[len];
1168            System.arraycopy(chars, start, subChars, 0, len);
1169            bounds = createGlyphVector(frc, subChars).getLogicalBounds();
1170        }
1171        return bounds;
1172    }
1173
1174    /**
1175     * Gets the character's maximum bounds as defined in the specified
1176     * FontRenderContext.
1177     *
1178     * @param frc
1179     *            the FontRenderContext.
1180     * @return the character's maximum bounds.
1181     */
1182    public Rectangle2D getMaxCharBounds(FontRenderContext frc) {
1183        if (frc == null) {
1184            // awt.00=FontRenderContext is null
1185            throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$
1186        }
1187
1188        FontPeerImpl peer = (FontPeerImpl)this.getPeer();
1189
1190        Rectangle2D bounds = peer.getMaxCharBounds(frc);
1191        AffineTransform transform = getTransform();
1192        // !! Documentation doesn't describe meaning of max char bounds
1193        // for the fonts that have rotate transforms. For all transforms
1194        // returned bounds are the bounds of transformed maxCharBounds
1195        // Rectangle2D that corresponds to the font with identity transform.
1196        // TODO: resolve this issue to return correct bounds
1197        bounds = transform.createTransformedShape(bounds).getBounds2D();
1198
1199        return bounds;
1200    }
1201
1202    /**
1203     * Returns a new GlyphVector object performing full layout of the text.
1204     *
1205     * @param frc
1206     *            the FontRenderContext.
1207     * @param chars
1208     *            the character array to be layout.
1209     * @param start
1210     *            the start offset of the text to use for the GlyphVector.
1211     * @param count
1212     *            the count of characters to use for the GlyphVector.
1213     * @param flags
1214     *            the flag indicating text direction: LAYOUT_RIGHT_TO_LEFT,
1215     *            LAYOUT_LEFT_TO_RIGHT.
1216     * @return the GlyphVector.
1217     */
1218    public GlyphVector layoutGlyphVector(FontRenderContext frc, char[] chars, int start, int count,
1219            int flags) {
1220        // TODO: implement method for bidirectional text.
1221        // At the moment only LTR and RTL texts supported.
1222        if (start < 0) {
1223            // awt.95=Wrong start index: {0}
1224            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.95", //$NON-NLS-1$
1225                    start));
1226        }
1227
1228        if (count < 0) {
1229            // awt.98=Wrong count value, can not be negative: {0}
1230            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.98", //$NON-NLS-1$
1231                    count));
1232        }
1233
1234        if (start + count > chars.length) {
1235            // awt.99=Wrong [start + count] is out of range: {0}
1236            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.99", //$NON-NLS-1$
1237                    (start + count)));
1238        }
1239
1240        char[] out = new char[count];
1241        System.arraycopy(chars, start, out, 0, count);
1242
1243        return new CommonGlyphVector(out, frc, this, flags);
1244    }
1245
1246    /**
1247     * Returns the String representation of this Font.
1248     *
1249     * @return the String representation of this Font.
1250     */
1251    @Override
1252    public String toString() {
1253        String stl = "plain"; //$NON-NLS-1$
1254        String result;
1255
1256        if (this.isBold() && this.isItalic()) {
1257            stl = "bolditalic"; //$NON-NLS-1$
1258        }
1259        if (this.isBold() && !this.isItalic()) {
1260            stl = "bold"; //$NON-NLS-1$
1261        }
1262
1263        if (!this.isBold() && this.isItalic()) {
1264            stl = "italic"; //$NON-NLS-1$
1265        }
1266
1267        result = this.getClass().getName() + "[family=" + this.getFamily() + //$NON-NLS-1$
1268                ",name=" + this.name + //$NON-NLS-1$
1269                ",style=" + stl + //$NON-NLS-1$
1270                ",size=" + this.size + "]"; //$NON-NLS-1$ //$NON-NLS-2$
1271        return result;
1272    }
1273
1274    /**
1275     * Gets the postscript name of this Font.
1276     *
1277     * @return the postscript name of this Font.
1278     */
1279    public String getPSName() {
1280        FontPeerImpl peer = (FontPeerImpl)this.getPeer();
1281        return peer.getPSName();
1282    }
1283
1284    /**
1285     * Gets the logical name of this Font.
1286     *
1287     * @return the logical name of this Font.
1288     */
1289    public String getName() {
1290        return (this.name);
1291    }
1292
1293    /**
1294     * Gets the peer of this Font.
1295     *
1296     * @return the peer of this Font.
1297     * @deprecated Font rendering is platform independent now.
1298     */
1299    @Deprecated
1300    public java.awt.peer.FontPeer getPeer() {
1301        if (fontPeer == null) {
1302            fontPeer = (FontPeerImpl)Toolkit.getDefaultToolkit().getGraphicsFactory().getFontPeer(
1303                    this);
1304        }
1305        return fontPeer;
1306
1307    }
1308
1309    /**
1310     * Gets the transform acting on this Font (from the Font's attributes).
1311     *
1312     * @return the transformation of this Font.
1313     */
1314    public AffineTransform getTransform() {
1315        Object transform = fRequestedAttributes.get(TextAttribute.TRANSFORM);
1316
1317        if (transform != null) {
1318            if (transform instanceof TransformAttribute) {
1319                return ((TransformAttribute)transform).getTransform();
1320            }
1321            if (transform instanceof AffineTransform) {
1322                return new AffineTransform((AffineTransform)transform);
1323            }
1324        } else {
1325            transform = new AffineTransform();
1326        }
1327        return (AffineTransform)transform;
1328
1329    }
1330
1331    /**
1332     * Checks if this font is transformed or not.
1333     *
1334     * @return true, if this font is transformed, false otherwise.
1335     */
1336    public boolean isTransformed() {
1337        return this.transformed;
1338    }
1339
1340    /**
1341     * Checks if this font has plain style or not.
1342     *
1343     * @return true, if this font has plain style, false otherwise.
1344     */
1345    public boolean isPlain() {
1346        return (this.style == PLAIN);
1347    }
1348
1349    /**
1350     * Checks if this font has italic style or not.
1351     *
1352     * @return true, if this font has italic style, false otherwise.
1353     */
1354    public boolean isItalic() {
1355        return (this.style & ITALIC) != 0;
1356    }
1357
1358    /**
1359     * Checks if this font has bold style or not.
1360     *
1361     * @return true, if this font has bold style, false otherwise.
1362     */
1363    public boolean isBold() {
1364        return (this.style & BOLD) != 0;
1365    }
1366
1367    /**
1368     * Returns true if this Font has uniform line metrics.
1369     *
1370     * @return true if this Font has uniform line metrics, false otherwise.
1371     */
1372    public boolean hasUniformLineMetrics() {
1373        FontPeerImpl peer = (FontPeerImpl)this.getPeer();
1374        return peer.hasUniformLineMetrics();
1375    }
1376
1377    /**
1378     * Returns hash code of this Font object.
1379     *
1380     * @return the hash code of this Font object.
1381     */
1382    @Override
1383    public int hashCode() {
1384        HashCode hash = new HashCode();
1385
1386        hash.append(this.name);
1387        hash.append(this.style);
1388        hash.append(this.size);
1389
1390        return hash.hashCode();
1391    }
1392
1393    /**
1394     * Gets the style of this Font.
1395     *
1396     * @return the style of this Font.
1397     */
1398    public int getStyle() {
1399        return this.style;
1400    }
1401
1402    /**
1403     * Gets the size of this Font.
1404     *
1405     * @return the size of this Font.
1406     */
1407    public int getSize() {
1408        return this.size;
1409    }
1410
1411    /**
1412     * Gets the number of glyphs for this Font.
1413     *
1414     * @return the number of glyphs for this Font.
1415     */
1416    public int getNumGlyphs() {
1417        if (numGlyphs == -1) {
1418            FontPeerImpl peer = (FontPeerImpl)this.getPeer();
1419            this.numGlyphs = peer.getNumGlyphs();
1420        }
1421        return this.numGlyphs;
1422    }
1423
1424    /**
1425     * Gets the glyphCode which is used as default glyph when this Font does not
1426     * have a glyph for a specified Unicode.
1427     *
1428     * @return the missing glyph code.
1429     */
1430    public int getMissingGlyphCode() {
1431        if (missingGlyphCode == -1) {
1432            FontPeerImpl peer = (FontPeerImpl)this.getPeer();
1433            this.missingGlyphCode = peer.getMissingGlyphCode();
1434        }
1435        return this.missingGlyphCode;
1436    }
1437
1438    /**
1439     * Gets the float value of font's size.
1440     *
1441     * @return the float value of font's size.
1442     */
1443    public float getSize2D() {
1444        return this.pointSize;
1445    }
1446
1447    /**
1448     * Gets the italic angle of this Font.
1449     *
1450     * @return the italic angle of this Font.
1451     */
1452    public float getItalicAngle() {
1453        FontPeerImpl peer = (FontPeerImpl)this.getPeer();
1454        return peer.getItalicAngle();
1455    }
1456
1457    /**
1458     * Creates the font with the specified font format and font file.
1459     *
1460     * @param fontFormat
1461     *            the font format.
1462     * @param fontFile
1463     *            the file object represented the input data for the font.
1464     * @return the Font.
1465     * @throws FontFormatException
1466     *             is thrown if fontFile does not contain the required font
1467     *             tables for the specified format.
1468     * @throws IOException
1469     *             signals that an I/O exception has occurred.
1470     */
1471    public static Font createFont(int fontFormat, File fontFile) throws FontFormatException,
1472            IOException {
1473        // ???AWT not supported
1474        InputStream is = new FileInputStream(fontFile);
1475        try {
1476            return createFont(fontFormat, is);
1477        } finally {
1478            is.close();
1479        }
1480    }
1481
1482    /**
1483     * Creates the font with the specified font format and input stream.
1484     *
1485     * @param fontFormat
1486     *            the font format.
1487     * @param fontStream
1488     *            the input stream represented input data for the font.
1489     * @return the Font.
1490     * @throws FontFormatException
1491     *             is thrown if fontFile does not contain the required font
1492     *             tables for the specified format.
1493     * @throws IOException
1494     *             signals that an I/O exception has occurred.
1495     */
1496    public static Font createFont(int fontFormat, InputStream fontStream)
1497            throws FontFormatException, IOException {
1498
1499        // ???AWT not supported
1500
1501        BufferedInputStream buffStream;
1502        int bRead = 0;
1503        int size = 8192;
1504        // memory page size, for the faster reading
1505        byte buf[] = new byte[size];
1506
1507        if (fontFormat != TRUETYPE_FONT) { // awt.9A=Unsupported font format
1508            throw new IllegalArgumentException(Messages.getString("awt.9A")); //$NON-NLS-1$
1509        }
1510
1511        /* Get font file in system-specific directory */
1512
1513        File fontFile = Toolkit.getDefaultToolkit().getGraphicsFactory().getFontManager()
1514                .getTempFontFile();
1515
1516        // BEGIN android-modified
1517        buffStream = new BufferedInputStream(fontStream, 8192);
1518        // END android-modified
1519        FileOutputStream fOutStream = new FileOutputStream(fontFile);
1520
1521        bRead = buffStream.read(buf, 0, size);
1522
1523        while (bRead != -1) {
1524            fOutStream.write(buf, 0, bRead);
1525            bRead = buffStream.read(buf, 0, size);
1526        }
1527
1528        buffStream.close();
1529        fOutStream.close();
1530
1531        Font font = null;
1532
1533        font = Toolkit.getDefaultToolkit().getGraphicsFactory().embedFont(
1534                fontFile.getAbsolutePath());
1535        if (font == null) { // awt.9B=Can't create font - bad font data
1536            throw new FontFormatException(Messages.getString("awt.9B")); //$NON-NLS-1$
1537        }
1538        return font;
1539    }
1540
1541}
1542