package pt.tumba.parser.swf;
import com.anotherbigidea.flash.SWFConstants;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* A Font Symbol. The Font references a FontDefinition object from which it
* takes the glyph definitions it needs.
*
*@author unknown
*@created 15 de Setembro de 2002
*/
public class Font extends Symbol {
/**
* Description of the Class
*
*@author unknown
*@created 15 de Setembro de 2002
*/
public class NoGlyphException extends Exception {
/**
* Description of the Field
*/
public int code;
/**
* Constructor for the NoGlyphException object
*
*@param code Description of the Parameter
*/
public NoGlyphException(int code) {
super("The font does not have a glyph definition for code " + code);
this.code = code;
}
}
/**
* A set of contiguous characters in one font.
*
*@author unknown
*@created 15 de Setembro de 2002
*/
public class Chars {
/**
* Description of the Field
*/
protected String chars;
/**
* Description of the Field
*/
protected double size;
/**
* Description of the Field
*/
protected int[] indices;
/**
* Description of the Field
*/
protected int[] advances;
/**
* Description of the Field
*/
protected double totalAdvance;
//total advance
/**
* Description of the Field
*/
protected double ascent;
/**
* Description of the Field
*/
protected double descent;
/**
* Description of the Field
*/
protected double leftMargin;
/**
* Description of the Field
*/
protected double rightMargin;
/**
* Description of the Method
*
*@return Description of the Return Value
*/
public String toString() {
return chars;
}
/**
* Gets the font attribute of the Chars object
*
*@return The font value
*/
public Font getFont() {
return Font.this;
}
/**
* Gets the size attribute of the Chars object
*
*@return The size value
*/
public double getSize() {
return size;
}
/**
* Gets the totalAdvance attribute of the Chars object
*
*@return The totalAdvance value
*/
public double getTotalAdvance() {
return totalAdvance;
}
/**
* Gets the ascent attribute of the Chars object
*
*@return The ascent value
*/
public double getAscent() {
return ascent;
}
/**
* Gets the descent attribute of the Chars object
*
*@return The descent value
*/
public double getDescent() {
return descent;
}
/**
* The left margin is the difference between the origin of the first
* glyph and the left edge of its geometry
*
*@return The leftMargin value
*/
public double getLeftMargin() {
return leftMargin;
}
/**
* The right margin is the different between the total advance and the
* right edge of the geometry of the last glyph
*
*@return The rightMargin value
*/
public double getRightMargin() {
return rightMargin;
}
/**
*@param chars the characters to display (displayable
* chars only - i.e. no newlines, tabs etc..)
*@param size point-size - only relevant if font is
* not null
*@exception NoGlyphException Description of the Exception
*/
protected Chars(String chars, double size) throws NoGlyphException {
this.chars = chars;
this.size = size;
init();
}
/**
* Description of the Method
*
*@exception NoGlyphException Description of the Exception
*/
protected final void init() throws NoGlyphException {
//--Figure out the indices and advances
char[] codes = chars.toCharArray();
indices = new int[codes.length];
advances = new int[codes.length];
double maxAscent = 0.0;
double maxDescent = 0.0;
double scale = size * SWFConstants.TWIPS / 1024.0;
for (int i = 0; i < codes.length; i++) {
int code = (int) codes[i];
int[] index = new int[1];
FontDefinition.Glyph glyph = getGlyph(code, index);
indices[i] = index[0];
if (glyph != null) {
Shape shape = glyph.getShape();
double[] outline = shape.getBoundingRectangle();
double x1 = outline[0] * scale;
double y1 = outline[1] * scale;
double x2 = outline[2] * scale;
double y2 = outline[3] * scale;
if (maxAscent < -y1) {
maxAscent = -y1;
}
if (maxDescent < y2) {
maxDescent = y2;
}
double advance = glyph.getAdvance() * scale;
if (advance == 0) {
advance = x2 - x1;
}
//Kerning adjustment
if (i < codes.length - 1) {
advance += (fontDef.getKerningOffset(code, (int) codes[i + 1]) * scale);
}
totalAdvance += advance;
advances[i] = (int) (advance * SWFConstants.TWIPS);
if (i == 0) {
leftMargin = -y1;
}
if (i == codes.length - 1) {
rightMargin = x2 - advance;
}
}
}
ascent = fontDef.getAscent() * scale;
if (ascent == 0.0) {
ascent = maxAscent;
}
descent = fontDef.getDescent() * scale;
if (descent == 0.0) {
descent = maxDescent;
}
}
}
/**
* Description of the Field
*/
protected Object font1Key = new Object();
//used in movie defined symbols lookup
/**
* Description of the Field
*/
protected Object font2Key = new Object();
//used in movie defined symbols lookup
/**
* Description of the Field
*/
protected FontDefinition fontDef;
/**
* Description of the Field
*/
protected Map glyphs = new HashMap();
//glyphs used by this font
/**
* Description of the Field
*/
protected Map indices = new HashMap();
//glyph indices
/**
* Description of the Field
*/
protected List glyphList = new ArrayList();
/**
* Gets the definition attribute of the Font object
*
*@return The definition value
*/
public FontDefinition getDefinition() {
return fontDef;
}
/**
* Constructor for the Font object
*
*@param fontDef Description of the Parameter
*/
public Font(FontDefinition fontDef) {
this.fontDef = fontDef;
}
/**
* Gets the glyphList attribute of the Font object
*
*@return The glyphList value
*/
public List getGlyphList() {
return glyphList;
}
/**
* Load the glyphs for the characters in the given string from the
* FontDefinition into this font.
*
*@param chars Description of the Parameter
*@exception NoGlyphException
*/
public void loadGlyphs(String chars) throws NoGlyphException {
char[] chs = chars.toCharArray();
for (int i = 0; i < chs.length; i++) {
getGlyph(chs[i], null);
}
}
/**
* Load all glyphs from the font definition
*/
public void loadAllGlyphs() {
List list = fontDef.getGlyphList();
for (Iterator it = list.iterator(); it.hasNext(); ) {
FontDefinition.Glyph g = (FontDefinition.Glyph) it.next();
addGlyph(g);
}
}
/**
* Get the Chars instance for the given string at the given font size
*
*@param chars Description of the Parameter
*@param fontSize Description of the Parameter
*@return Description of the Return Value
*@exception NoGlyphException Description of the Exception
*/
public Chars chars(String chars, double fontSize)
throws NoGlyphException {
return new Chars(chars, fontSize);
}
/**
* Gets the glyph attribute of the Font object
*
*@param code Description of the Parameter
*@param index Description of the Parameter
*@return The glyph value
*@exception NoGlyphException Description of the Exception
*/
protected FontDefinition.Glyph getGlyph(int code, int[] index2)
throws NoGlyphException {
int index[] = index2;
Integer codeI = new Integer(code);
FontDefinition.Glyph g = (FontDefinition.Glyph) glyphs.get(codeI);
if (g != null) {
if (index != null) {
Integer idx = (Integer) indices.get(codeI);
index[0] = idx.intValue();
}
return g;
}
g = fontDef.getGlyph(code);
if (g == null) {
throw new NoGlyphException(code);
}
int idx = addGlyph(g);
if (index != null) {
index[0] = idx;
}
return g;
}
/**
* Add a glyph and return the index
*
*@param glyph The feature to be added to the Glyph attribute
*@return Description of the Return Value
*/
public int addGlyph(FontDefinition.Glyph glyph) {
int idx = glyphs.size();
if (glyph.getCode() > 0) {
Integer codeI = new Integer(glyph.getCode());
indices.put(codeI, new Integer(idx));
glyphs.put(codeI, glyph);
}
glyphList.add(glyph);
return idx;
}
/**
* Set the code for the glyph at the given index
*
*@param index The new code value
*@param code The new code value
*/
public void setCode(int index, int code) {
if (index >= glyphList.size()) {
return;
}
FontDefinition.Glyph g = (FontDefinition.Glyph) glyphList.get(index);
g.setCode(code);
Integer codeI = new Integer(code);
indices.put(codeI, new Integer(index));
glyphs.put(codeI, g);
}
/**
* Description of the Method
*
*@param textFont Description of the Parameter
*@param movie Description of the Parameter
*@param tagwriter Description of the Parameter
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
protected int define(boolean textFont, Movie movie, SWFTagTypes tagwriter)
throws IOException {
Integer integerId = textFont ?
(Integer) movie.definedSymbols.get(font1Key) :
(Integer) movie.definedSymbols.get(font2Key);
if (integerId == null) {
if (textFont) {
integerId = new Integer(defineFont1(movie, tagwriter));
movie.definedSymbols.put(font1Key, integerId);
} else {
integerId = new Integer(defineFont2(movie, tagwriter));
movie.definedSymbols.put(font2Key, integerId);
}
}
id = integerId.intValue();
return id;
}
/**
* Description of the Method
*
*@param movie Description of the Parameter
*@param tagwriter Description of the Parameter
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
protected int defineFont1(Movie movie, SWFTagTypes tagwriter)
throws IOException {
int id = getNextId(movie);
SWFVectors vecs = tagwriter.tagDefineFont(id, glyphList.size());
for (Iterator it = glyphList.iterator(); it.hasNext(); ) {
FontDefinition.Glyph g = (FontDefinition.Glyph) it.next();
Shape s = g.getShape();
s.writeGlyph(vecs);
}
if (fontDef.getName() != null) {
int flags = 0;
if (fontDef.isUnicode()) {
flags |= SWFConstants.FONT_UNICODE;
}
if (fontDef.isShiftJIS()) {
flags |= SWFConstants.FONT_SHIFTJIS;
}
if (fontDef.isAnsi()) {
flags |= SWFConstants.FONT_ANSI;
}
if (fontDef.isItalic()) {
flags |= SWFConstants.FONT_ITALIC;
}
if (fontDef.isBold()) {
flags |= SWFConstants.FONT_BOLD;
}
tagwriter.tagDefineFontInfo(id, fontDef.getName(), flags, getCodes());
}
return id;
}
/**
* Description of the Method
*
*@param movie Description of the Parameter
*@param tagwriter Description of the Parameter
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
protected int defineFont2(Movie movie, SWFTagTypes tagwriter)
throws IOException {
int id = getNextId(movie);
int glyphCount = glyphList.size();
int[] codes = new int[glyphCount];
Rect[] bounds = new Rect[glyphCount];
int[] advances = new int[glyphCount];
//--Gather glyph info
int i = 0;
for (Iterator it = glyphList.iterator(); it.hasNext(); ) {
FontDefinition.Glyph g = (FontDefinition.Glyph) it.next();
codes[i] = g.getCode();
advances[i] = (int) (g.getAdvance() * SWFConstants.TWIPS);
double[] bound = g.getShape().getBoundingRectangle();
bounds[i] = new Rect((int) (bound[0] * SWFConstants.TWIPS),
(int) (bound[1] * SWFConstants.TWIPS),
(int) (bound[2] * SWFConstants.TWIPS),
(int) (bound[3] * SWFConstants.TWIPS));
i++;
}
//--Gather kerning info
List kerns = fontDef.getKerningPairList();
int kernCount = kerns.size();
int[] kern1 = new int[kernCount];
int[] kern2 = new int[kernCount];
int[] kernOff = new int[kernCount];
i = 0;
for (Iterator it = kerns.iterator(); it.hasNext(); ) {
FontDefinition.KerningPair pair = (FontDefinition.KerningPair) it.next();
kern1[i] = pair.getCode1();
kern2[i] = pair.getCode2();
kernOff[i] = (int) (pair.getAdjustment() * SWFConstants.TWIPS);
i++;
}
int flags = 0;
if (fontDef.hasMetrics()) {
flags |= SWFConstants.FONT2_HAS_LAYOUT;
}
if (fontDef.isShiftJIS()) {
flags |= SWFConstants.FONT2_SHIFTJIS;
}
if (fontDef.isUnicode()) {
flags |= SWFConstants.FONT2_UNICODE;
}
if (fontDef.isAnsi()) {
flags |= SWFConstants.FONT2_ANSI;
}
if (fontDef.isItalic()) {
flags |= SWFConstants.FONT2_ITALIC;
}
if (fontDef.isBold()) {
flags |= SWFConstants.FONT2_BOLD;
}
SWFVectors vecs = tagwriter.tagDefineFont2(
id, flags, fontDef.getName(), glyphCount,
(int) (fontDef.getAscent() * SWFConstants.TWIPS),
(int) (fontDef.getDescent() * SWFConstants.TWIPS),
(int) (fontDef.getLeading() * SWFConstants.TWIPS),
codes, advances, bounds, kern1, kern2, kernOff);
for (Iterator it = glyphList.iterator(); it.hasNext(); ) {
FontDefinition.Glyph g = (FontDefinition.Glyph) it.next();
Shape s = g.getShape();
s.writeGlyph(vecs);
}
return id;
}
/**
* Get the codes of the current set of glyphs
*
*@return The codes value
*/
protected int[] getCodes() {
int[] codes = new int[glyphList.size()];
for (int i = 0; i < codes.length; i++) {
FontDefinition.Glyph g = (FontDefinition.Glyph) glyphList.get(i);
codes[i] = g.getCode();
}
return codes;
}
/**
* Description of the Method
*
*@param movie Description of the Parameter
*@param timelineWriter Description of the Parameter
*@param definitionwriter Description of the Parameter
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
protected int defineSymbol(Movie movie,
SWFTagTypes timelineWriter,
SWFTagTypes definitionwriter) throws IOException {
return id;
}
}