/*
* AWTDecoder.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.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.LineMetrics;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.DataFormatException;
import com.flagstone.transform.coder.Coder;
import com.flagstone.transform.datatype.Bounds;
import com.flagstone.transform.font.CharacterFormat;
import com.flagstone.transform.shape.Shape;
import com.flagstone.transform.util.shape.Canvas;
/**
* AWTDecoder decodes Java AWT Fonts so they can be used in a Flash file.
*/
public final class AWTDecoder {
/** Number of edge points from a PathIterator segment. */
private static final int SEGMENT_COUNT = 6;
/** x-coordinate of the end point of a move or line. */
private static final int XCOORD = 0;
/** y-coordinate of the end point of a move or line. */
private static final int YCOORD = 1;
/** x-coordinate of the control point for a quadratic curve. */
private static final int QUAD_CTRLX = 0;
/** y-coordinate of the control point for a quadratic curve. */
private static final int QUAD_CTRLY = 1;
/** x-coordinate of the anchor point for a quadratic curve. */
private static final int QUAD_ANCHORX = 2;
/** y-coordinate of the anchor point for a quadratic curve. */
private static final int QUAD_ANCHORY = 3;
/** x-coordinate of the first control point for a cubic curve. */
private static final int CUBE_CTRL1_X = 0;
/** y-coordinate of the first control point for a cubic curve. */
private static final int CUBE_CTRL1_Y = 1;
/** x-coordinate of the second control point for a cubic curve. */
private static final int CUBE_CTRL2_X = 2;
/** y-coordinate of the second control point for a cubic curve. */
private static final int CUBE_CTRL2_Y = 3;
/** x-coordinate of the anchor point for a cubic curve. */
private static final int CUBE_ANCHORX = 4;
/** y-coordinate of the anchor point for a cubic curve. */
private static final int CUBE_ANCHORY = 5;
/** Size of the EM-Square in twips. */
private static final float EM_SQUARE_SIZE = 1024.0f;
/** The list of fonts decoded. */
private final transient List<Font>fonts = new ArrayList<Font>();
/**
* Decode an AWT Font.
* @param font an AWT Font object.
* @throws IOException if an error occurs decoding the font data.
* @throws DataFormatException if the font is in a format not supported by
* the decoder.
*/
public void read(final java.awt.Font font)
throws IOException, DataFormatException {
decode(font);
}
/**
* Get the list of fonts decoded.
* @return a list of fonts.
*/
public List<Font> getFonts() {
return fonts;
}
/**
* Decode the AWT font.
* @param aFont an AWT Font.
*/
private void decode(final java.awt.Font aFont) {
final FontRenderContext fontContext = new FontRenderContext(
new AffineTransform(), true, true);
java.awt.Font awtFont = aFont.deriveFont(1.0f);
final Font font = new Font();
font.setFace(new FontFace(awtFont.getName(),
awtFont.isBold(), awtFont.isItalic()));
font.setEncoding(CharacterFormat.UCS2);
/*
* The new font scaled to the EM Square must be derived using the size
* as well as the transform used for the glyphs otherwise the advance
* values are not scaled accordingly.
*/
final AffineTransform affine = AffineTransform.getTranslateInstance(
0, 0);
awtFont = awtFont.deriveFont(affine);
awtFont = awtFont.deriveFont(EM_SQUARE_SIZE);
final int missingGlyph = awtFont.getMissingGlyphCode();
final int count = awtFont.getNumGlyphs();
font.setMissingGlyph(missingGlyph);
font.setNumberOfGlyphs(count);
font.setHighestChar((char) Coder.USHORT_MAX);
int index = 0;
int code = 0;
char character;
// create the glyph for the characters that cannot be displayed
GlyphVector glyphVector = awtFont.createGlyphVector(fontContext,
new int[] {missingGlyph});
java.awt.Shape outline = glyphVector.getGlyphOutline(0);
int advance = (int) (glyphVector.getGlyphMetrics(0).getAdvance());
font.addGlyph((char) missingGlyph, new Glyph(convertShape(outline),
new Bounds(0, 0, 0, 0), advance));
index = 1;
float ascent = 0.0f;
float descent = 0.0f;
float leading = 0.0f;
/*
* Run through all the unicode character codes looking for a
* corresponding glyph.
*/
while ((index < count) && (code < Coder.USHORT_MAX)) {
if (awtFont.canDisplay(code)) {
character = (char) code;
glyphVector = awtFont.createGlyphVector(fontContext,
new char[] {character});
outline = glyphVector.getGlyphOutline(0);
advance = (int) (glyphVector.getGlyphMetrics(0).getAdvance());
font.addGlyph(character, new Glyph(convertShape(outline),
new Bounds(0, 0, 0, 0), advance));
if (!awtFont.hasUniformLineMetrics()) {
final LineMetrics lineMetrics = awtFont.getLineMetrics(
new char[] {character}, 0, 1, fontContext);
ascent = Math.max(lineMetrics.getAscent(), ascent);
descent = Math.max(lineMetrics.getDescent(), descent);
leading = Math.max(lineMetrics.getLeading(), leading);
}
index++;
} else {
character = (char) missingGlyph;
font.addMissingGlyph((char) code);
}
code++;
}
font.setAscent((int) ascent);
font.setDescent((int) descent);
font.setLeading((int) leading);
fonts.add(font);
}
/**
* Trace the outline of the glyph.
* @param glyph an AWT Shape.
* @return a Flash Shape.
*/
private Shape convertShape(final java.awt.Shape glyph) {
final PathIterator pathIter = glyph.getPathIterator(null);
final Canvas path = new Canvas();
final double[] coords = new double[SEGMENT_COUNT];
while (!pathIter.isDone()) {
switch (pathIter.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
path.close();
path.moveForFont((int) coords[XCOORD], (int) coords[YCOORD]);
break;
case PathIterator.SEG_LINETO:
path.line((int) coords[XCOORD], (int) coords[YCOORD]);
break;
case PathIterator.SEG_QUADTO:
path.curve((int) coords[QUAD_CTRLX], (int) coords[QUAD_CTRLY],
(int) coords[QUAD_ANCHORX], (int) coords[QUAD_ANCHORY]);
break;
case PathIterator.SEG_CUBICTO:
path.curve(
(int) coords[CUBE_CTRL1_X], (int) coords[CUBE_CTRL1_Y],
(int) coords[CUBE_CTRL2_X], (int) coords[CUBE_CTRL2_Y],
(int) coords[CUBE_ANCHORX], (int) coords[CUBE_ANCHORY]);
break;
case PathIterator.SEG_CLOSE:
path.close();
break;
default:
break;
}
pathIter.next();
}
return path.getShape();
}
}