/* * Font.java * Transform * * Copyright (c) 2009-2010 Flagstone Software Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Flagstone Software Ltd. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package com.flagstone.transform.util.font; import java.util.ArrayList; import java.util.List; import com.flagstone.transform.coder.Coder; import com.flagstone.transform.datatype.Bounds; import com.flagstone.transform.exception.IllegalArgumentRangeException; import com.flagstone.transform.font.CharacterFormat; import com.flagstone.transform.font.DefineFont2; import com.flagstone.transform.font.Kerning; import com.flagstone.transform.shape.Shape; /** * <p> * Font is used to add embedded fonts to a movie. * </p> * * <p> * Flash supports two types of font definition: embedded fonts where the Flash * file contains the glyphs that are drawn to represents the text characters and * device fonts where the font is provided by the Flash Player showing the * movie. Embedded fonts are preferred since the movie will always look the same * regardless of where it is played - if a Flash Player does not contain a * device font it will substitute it with another. * </p> * * <p> * Device fonts can be added to a movie by simply creating a DefineFont or * DefineFont2 object which contain the name of the font. An embedded font must * contain all the information to draw and layout the glyphs representing the * text to be displayed. The Font class hides all this detail and makes it easy * to add embedded fonts to a movie. * <p> * * <p> * The Font class can be used to create embedded fonts in three ways: * </p> * * <ol> * <li>Using TrueType or OpenType font definition stored in a file.</li> * <li>Using an existing font definition from a flash file.</li> * <li>Using a given Java AWT font as a template.</li> * </ol> * * <P> * For OpenType or TrueType fonts, files with the extensions ".otf" or ".ttf" * may be used. Files containing collections of fonts ".otc" are not currently * supported. * </p> * * <p> * Using an existing Flash font definition is the most interesting. Fonts can * initially be created using AWT Font objects or TrueType files and all the * visible characters included. If the generated Flash definition is saved to a * file it can easily and quickly be loaded. Indeed the overhead of parsing an * AWT or TrueType font is significant (sometimes several seconds) so creating * libraries of "pre-parsed" flash fonts is the preferred way of use fonts. * </p> */ public final class Font { /** The face describing the font. */ private FontFace face; /** The encoding used for character codes. */ private CharacterFormat encoding; /** The height of the font above the baseline. */ private int ascent; /** The height of the font below the baseline. */ private int descent; /** The spacing between lines. */ private int leading; /** Table mapping character codes to glyphs. */ private transient int[] charToGlyph; /** Table mapping glyphs to character codes. */ private transient int[] glyphToChar; /** Table of glyphs. */ private transient Glyph[] glyphTable; /** The current glyph. */ private transient int glyphIndex; /** The number of glyphs in the font. */ private transient int glyphCount; /** The index of the glyph used to represent undisplayable characters. */ private transient int missingGlyph; /** The highest character code. */ private transient char highestChar; /** List of kernings for selected pairs of characters. */ private final transient List<Kerning> kernings = new ArrayList<Kerning>(); /** * Get the FontFace that contains the font name and style. * @return the FontFace. */ public FontFace getFace() { return face; } /** * Set the FontFace that contains the font name and style. * @param fontFace the FontFace. */ public void setFace(final FontFace fontFace) { face = fontFace; } /** * Get the encoding scheme used for the character codes, either UCS2, * ANSI or SJIS. * * @return the encoding used for the character codes. */ public CharacterFormat getEncoding() { return encoding; } /** * Set the encoding scheme used for the character codes, either UCS2, * ANSI or SJIS. * * @param enc the encoding used for the character codes. */ public void setEncoding(final CharacterFormat enc) { encoding = enc; } /** * Get the ascent for the font in twips. * * @return the ascent for the font. */ public int getAscent() { return ascent; } /** * Sets the ascent for the font in twips. * * @param dist * the ascent for the font in the range -32768..32767. */ public void setAscent(final int dist) { if ((dist < Coder.SHORT_MIN) || (dist > Coder.SHORT_MAX)) { throw new IllegalArgumentRangeException( Coder.SHORT_MIN, Coder.SHORT_MAX, dist); } ascent = dist; } /** * Get the descent for the font in twips. * * @return the descent for the font. */ public int getDescent() { return descent; } /** * Sets the descent for the font in twips. * * @param dist * the descent for the font in the range -32768..32767. */ public void setDescent(final int dist) { if ((dist < Coder.SHORT_MIN) || (dist > Coder.SHORT_MAX)) { throw new IllegalArgumentRangeException( Coder.SHORT_MIN, Coder.SHORT_MAX, dist); } descent = dist; } /** * Returns the leading for the font in twips. * * @return the leading for the font. */ public int getLeading() { return leading; } /** * Sets the leading for the font in twips. * * @param dist * the descent for the font in the range -32768..32767. */ public void setLeading(final int dist) { if ((dist < Coder.SHORT_MIN) || (dist > Coder.SHORT_MAX)) { throw new IllegalArgumentRangeException( Coder.SHORT_MIN, Coder.SHORT_MAX, dist); } leading = dist; } /** * Get the number of glyphs defined in the font. * * @return the number of glyphs. */ public int getNumberOfGlyphs() { return glyphCount; } /** * Set the number of glyphs defined in the font. * * @param count the number of glyphs. */ public void setNumberOfGlyphs(final int count) { // glyphTable = new Glyph[Coder.USHORT_MAX + 1]; // glyphToChar = new int[Coder.USHORT_MAX + 1]; glyphTable = new Glyph[count]; glyphToChar = new int[count]; glyphIndex = 0; } /** * Get the highest character code defined in the font. * * @return the character with the highest character code. */ public char getHighestChar() { return highestChar; } /** * Set the highest character code defined in the font. * * @param highest the highest character code. */ public void setHighestChar(final char highest) { highestChar = highest; //charToGlyph = new int[Coder.USHORT_MAX + 1]; charToGlyph = new int[highest + 1]; } /** * Get the index of the glyph used to represent characters that are not * supported int the font. * @return the index of the glyph for unsupported characters. */ public int getMissingGlyph() { return missingGlyph; } /** * Select the glyph used to represent characters that are not * supported int the font. * @param index the index of the glyph that will be used to display * unsupported characters. */ public void setMissingGlyph(final int index) { missingGlyph = index; } /** * Get the glyph from the specified position in the table. * @param index the index of the glyph. * @return the corresponding glyph. */ public Glyph getGlyph(final int index) { return glyphTable[index]; } /** * Add a character and the corresponding glyph that is displayed to the * font. * @param code the character * @param glyph the glyph displayed for the character. */ public void addGlyph(final char code, final Glyph glyph) { glyphTable[glyphIndex] = glyph; glyphToChar[glyphIndex] = code; charToGlyph[code] = glyphIndex; glyphIndex++; } /** * Add a character to the font where the missing glyph will be displayed. * @param code the character where there is no corresponding glyph. */ public void addMissingGlyph(final char code) { charToGlyph[code] = missingGlyph; } /** * Create and return a DefineFont2 object that contains information to * display a set of characters. * * @param identifier * the unique identifier that will be used to reference the font * definition in the flash file. * * @param characters * the set of characters that the font must contain glyphs and * layout information for, * * @return a font definition that contains information for all the glyphs in * the set of characters. */ public DefineFont2 defineFont(final int identifier, final List<Character> characters) { DefineFont2 fontDefinition = null; final int count = characters.size(); final ArrayList<Shape> glyphsArray = new ArrayList<Shape>(count); final ArrayList<Integer> codesArray = new ArrayList<Integer>(count); final ArrayList<Integer> advancesArray = new ArrayList<Integer>(count); final ArrayList<Bounds> boundsArray = new ArrayList<Bounds>(count); for (final Character character : characters) { final Glyph glyph = glyphTable[charToGlyph[character]]; glyphsArray.add(glyph.getShape()); codesArray.add((int) character); advancesArray.add(glyph.getAdvance()); if (glyph.getBounds() != null) { boundsArray.add(glyph.getBounds()); } } fontDefinition = new DefineFont2(identifier, face.getName()); fontDefinition.setEncoding(encoding); fontDefinition.setItalic(face.isItalic()); fontDefinition.setBold(face.isBold()); fontDefinition.setAscent(ascent); fontDefinition.setDescent(descent); fontDefinition.setLeading(leading); fontDefinition.setShapes(glyphsArray); fontDefinition.setCodes(codesArray); fontDefinition.setAdvances(advancesArray); fontDefinition.setBounds(boundsArray); fontDefinition.setKernings(kernings); return fontDefinition; } /** * Tests whether the font can display all the characters in a string. * * @param aString * the string to be displayed. * * @return the index of the character that cannot be displayed or -1 if all * characters have corresponding glyphs. */ public int canDisplay(final String aString) { int firstMissingChar = -1; for (int i = 0; i < aString.length(); i++) { if (!canDisplay(aString.charAt(i))) { firstMissingChar = i; break; } } return firstMissingChar; } /** * Tests whether a character can be displayed by the font or whether the * "missing" character glyph (usually an empty box) will be displayed * instead. * * @param character * the character to be displayed. * * @return true if the font contains a glyph for character or false if there * is no corresponding glyph and the missing character glyph will be * displayed. */ public boolean canDisplay(final char character) { boolean canDisplay; if ((character < charToGlyph.length) && ((character == ' ') || (charToGlyph[character] != 0))) { canDisplay = true; } else { canDisplay = false; } return canDisplay; } /** * Returns the glyph for the specified character. * * @param character * the character. * * @return the Glyph object which contains the layout information. */ public int glyphForCharacter(final char character) { return charToGlyph[character]; } /** * Returns the characters code for the glyph at a specified index in the * font. This method is used for extracting the strings displayed by static * text (DefineText, DefineText2) fields. * * @param index * the index of the font. * * @return the character code for the glyph. */ public char characterForGlyph(final int index) { return (char) glyphToChar[index]; } /** * Returns the default advance for the font as defined in the EM Square - * conceptually a font with a point size of 1024. The number returned needs * to be scaled to the correct size in order to calculate the advance to the * next character. * * @param character * the character code. * @return the advance in twips to the next character. */ public int advanceForCharacter(final char character) { return glyphTable[charToGlyph[character]].getAdvance(); } }