summaryrefslogtreecommitdiff
path: root/libjava/classpath/gnu/java/awt/font/opentype/truetype
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/gnu/java/awt/font/opentype/truetype')
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java176
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java447
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java187
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java228
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java287
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java380
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java1815
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java291
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java393
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.diabin0 -> 1572 bytes
-rw-r--r--libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.pngbin0 -> 11278 bytes
11 files changed, 4204 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java
new file mode 100644
index 000000000..87dfebd41
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java
@@ -0,0 +1,176 @@
+/* Fixed.java -- Fixed-point arithmetics for TrueType coordinates.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.awt.font.opentype.truetype;
+
+import gnu.java.lang.CPStringBuilder;
+
+/**
+ * A utility class for fixed-point arithmetics, where numbers are
+ * represented with 26 dot 6 digits. This representation is used by
+ * TrueType coordinates.
+ *
+ * <p>A good compiler will inline calls of methods in this class.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public final class Fixed
+{
+ public static final int ONE = 1<<6;
+
+
+ /**
+ * The constructor is private so nobody can use it.
+ */
+ private Fixed()
+ {
+ }
+
+
+ /**
+ * Multiplies two fixed-point numbers.
+ */
+ public static int mul(int a, int b)
+ {
+ return (int) ((((long) a) * b) >> 6);
+ }
+
+ public static int mul16(int a, int b)
+ {
+ return (int) ((((long) a) * b) >> 16);
+ }
+
+ public static int div(int a, int b)
+ {
+ return (int) ((((long) a) << 6) / b);
+ }
+
+ public static int div16(int a, int b)
+ {
+ return (int) ((((long) a) << 16) / b);
+ }
+
+ public static int ceil(int a)
+ {
+ return (a + 63) & -64;
+ }
+
+
+ public static int floor(int a)
+ {
+ return a & -64;
+ }
+
+
+ /**
+ * Calculates the length of a fixed-point vector.
+ */
+ public static int vectorLength(int x, int y)
+ {
+ int shift;
+ float fx, fy;
+
+ if (x == 0)
+ return Math.abs(y);
+ else if (y == 0)
+ return Math.abs(x);
+
+ /* Use the FPU. */
+ fx = ((float) x) / 64.0f;
+ fy = ((float) y) / 64.0f;
+ return (int) (Math.sqrt(fx * fx + fy * fy) * 64.0);
+ }
+
+
+ public static int intValue(int f)
+ {
+ return f >> 6;
+ }
+
+
+ public static float floatValue(int f)
+ {
+ return ((float) f) / 64;
+ }
+ public static float floatValue16(int f)
+ {
+ return ((float) f) / 65536;
+ }
+
+ public static double doubleValue(int f)
+ {
+ return ((double) f) / 64;
+ }
+
+
+ public static int valueOf(float f)
+ {
+ return (int) (f * 64);
+ }
+
+
+ public static int valueOf(double d)
+ {
+ return (int) (d * 64);
+ }
+
+ public static int valueOf16(double d)
+ {
+ return (int) (d * (1 << 16));
+ }
+
+ /**
+ * Makes a string representation of a fixed-point number.
+ */
+ public static String toString(int f)
+ {
+ return String.valueOf(floatValue(f));
+ }
+
+
+ public static String toString(int x, int y)
+ {
+ CPStringBuilder sbuf = new CPStringBuilder(40);
+ sbuf.append('(');
+ sbuf.append(((float) x) / 64);
+ sbuf.append(", ");
+ sbuf.append(((float) y) / 64);
+ sbuf.append(')');
+ return sbuf.toString();
+ }
+}
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java
new file mode 100644
index 000000000..8a3c56665
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java
@@ -0,0 +1,447 @@
+/* GlyphLoader.java -- Helper for loading TrueType glyph outlines.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.awt.font.opentype.truetype;
+
+import gnu.java.awt.font.opentype.Hinter;
+
+import java.awt.geom.AffineTransform;
+import java.nio.ByteBuffer;
+
+
+/**
+ * A class for loading scaled and hinted glyph outlines.
+ *
+ * <p><b>Lack of Thread Safety:</b> Glyph loaders are intentionally
+ * <i>not</i> safe to access from multiple concurrent
+ * threads. Synchronization needs to be performed externally. Usually,
+ * the font has already obtained a lock before calling the scaler,
+ * which in turn calls the GlyphLoader. It would thus be wasteful to
+ * acquire additional locks for the GlyphLoader.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+final class GlyphLoader
+{
+ /**
+ * A helper object for locating glyph data. GlyphLocator is an
+ * abstract superclass, and there is a concretization for each glyph
+ * location table ('loca') format.
+ */
+ private final GlyphLocator glyphLocator;
+
+
+ /**
+ * A helper object for measuring the advance width and height of a
+ * glyph.
+ */
+ private final GlyphMeasurer glyphMeasurer;
+
+
+ /**
+ * The virtual machine for executing TrueType bytecodes.
+ */
+ private final VirtualMachine vm;
+
+
+ /**
+ * The number of font units in one em. A typical value is 2048,
+ * but this depends on the font.
+ */
+ private final int unitsPerEm;
+
+ private final int[] contourEndPoints;
+ private final byte[] pointFlags;
+
+
+ /**
+ * Constructs a GlyphLoader.
+ */
+ GlyphLoader(GlyphLocator glyphLocator, VirtualMachine vm,
+ int unitsPerEm, int maxContours, int maxPoints,
+ GlyphMeasurer glyphMeasurer)
+ {
+ this.glyphLocator = glyphLocator;
+ this.glyphMeasurer = glyphMeasurer;
+ this.unitsPerEm = unitsPerEm;
+ this.vm = vm;
+
+ contourEndPoints = new int[maxContours];
+ pointFlags = new byte[maxPoints];
+ }
+
+
+ /**
+ * @param glyphIndex the number of the glyph whose outlines are to be
+ * retrieved.
+ */
+ public void loadGlyph(int glyphIndex,
+ double pointSize,
+ AffineTransform transform,
+ boolean antialias,
+ Zone glyphZone, Hinter hinter)
+ {
+ glyphZone.setNumPoints(4);
+ loadSubGlyph(glyphIndex, pointSize, transform, antialias, glyphZone,
+ 0, 0, hinter);
+ }
+
+ public void loadGlyph(int glyphIndex, AffineTransform transform,
+ Zone glyphZone, Hinter hinter)
+ {
+ loadGlyph(glyphIndex, unitsPerEm, transform, false, glyphZone, hinter);
+ }
+
+ private void loadSubGlyph(int glyphIndex,
+ double pointSize,
+ AffineTransform transform,
+ boolean antialias,
+ Zone glyphZone,
+ int preTranslateX,
+ int preTranslateY,
+ Hinter hinter)
+ {
+ ByteBuffer glyph;
+ int numContours;
+ int xMin, yMin, xMax, yMax;
+ byte flag;
+
+ glyph = glyphLocator.getGlyphData(glyphIndex);
+
+ if (glyph == null)
+ {
+ glyphZone.setNumPoints(4);
+ setPhantomPoints(glyphIndex, 0, glyphZone);
+ glyphZone.transform(pointSize, transform, unitsPerEm,
+ preTranslateX, preTranslateY);
+ return;
+ }
+
+ numContours = glyph.getShort();
+ xMin = glyph.getChar();
+ yMin = glyph.getChar();
+ xMax = glyph.getChar();
+ yMax = glyph.getChar();
+
+
+ if (numContours >= 0)
+ loadSimpleGlyph(glyphIndex, pointSize, transform, antialias,
+ numContours, glyph, glyphZone,
+ preTranslateX, preTranslateY, hinter);
+ else
+ loadCompoundGlyph(glyphIndex, pointSize, transform, antialias,
+ glyph, glyphZone,
+ preTranslateX, preTranslateY, hinter);
+ }
+
+
+ private void loadSimpleGlyph(int glyphIndex,
+ double pointSize, AffineTransform transform,
+ boolean antialias,
+ int numContours, ByteBuffer glyph,
+ Zone glyphZone,
+ int preTranslateX, int preTranslateY,
+ Hinter hinter)
+ {
+ int numPoints;
+ int posInstructions, numInstructions;
+ boolean execInstructions;
+
+ execInstructions = vm.setup(pointSize, transform, antialias);
+
+ /* Load the contour end points and determine the number of
+ * points.
+ */
+ for (int i = 0; i < numContours; i++)
+ contourEndPoints[i] = glyph.getChar();
+ if (numContours > 0)
+ numPoints = 1 + contourEndPoints[numContours - 1];
+ else
+ numPoints = 0;
+ glyphZone.setNumPoints(numPoints + 4);
+
+ numInstructions = glyph.getChar();
+ posInstructions = glyph.position();
+ glyph.position(posInstructions + numInstructions);
+ loadFlags(numPoints, glyph);
+ loadCoordinates(numPoints, glyph, glyphZone);
+ for (int i = 0; i < numContours; i++)
+ glyphZone.setContourEnd(contourEndPoints[i], true);
+
+ setPhantomPoints(glyphIndex, numPoints, glyphZone);
+ glyphZone.transform(pointSize, transform, unitsPerEm,
+ preTranslateX, preTranslateY);
+
+ if (execInstructions && hinter != null)
+ {
+ hinter.applyHints(glyphZone);
+ }
+ }
+
+
+ private static final short ARGS_ARE_WORDS = 1;
+ private static final short ARGS_ARE_XY_VALUES = 2;
+ private static final short ROUND_XY_TO_GRID = 4;
+ private static final short WE_HAVE_A_SCALE = 8;
+ private static final short MORE_COMPONENTS = 32;
+ private static final short WE_HAVE_AN_X_AND_Y_SCALE = 64;
+ private static final short WE_HAVE_A_TWO_BY_TWO = 128;
+ private static final short WE_HAVE_INSTRUCTIONS = 256;
+ private static final short USE_MY_METRICS = 512;
+ private static final short OVERLAP_COMPOUND = 1024;
+ private static final short SCALED_COMPONENT_OFFSET = 2048;
+ private static final short UNSCALED_COMPONENT_OFFSET = 4096;
+
+ private void loadCompoundGlyph(int glyphIndex,
+ double pointSize,
+ AffineTransform transform,
+ boolean antialias,
+ ByteBuffer glyph,
+ Zone glyphZone,
+ int preTranslateX, int preTranslateY,
+ Hinter hinter)
+ {
+ short flags;
+ int subGlyphIndex;
+ int metricsGlyphIndex;
+ Zone subGlyphZone = new Zone(glyphZone.getCapacity());
+ int arg1, arg2;
+ double a, b, c, d, e, f;
+ AffineTransform componentTransform = new AffineTransform();
+
+ /* By default, use the metrics of the compound glyph. The default
+ * is overridden if some component glyph has the USE_MY_METRICS
+ * flag set.
+ */
+ metricsGlyphIndex = glyphIndex;
+
+ do
+ {
+ flags = glyph.getShort();
+ subGlyphIndex = glyph.getChar();
+
+ if ((flags & USE_MY_METRICS) != 0)
+ metricsGlyphIndex = subGlyphIndex;
+
+ if ((flags & ARGS_ARE_WORDS) != 0)
+ {
+ arg1 = glyph.getShort();
+ arg2 = glyph.getShort();
+ }
+ else
+ {
+ arg1 = glyph.get();
+ arg2 = glyph.get();
+ }
+
+ if ((flags & WE_HAVE_A_SCALE) != 0)
+ {
+ a = d = getDouble214(glyph);
+ b = c = 0.0;
+ }
+ else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0)
+ {
+ a = getDouble214(glyph);
+ d = getDouble214(glyph);
+ b = c = 0.0;
+ }
+ else if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0)
+ {
+ a = getDouble214(glyph);
+ b = getDouble214(glyph);
+ c = getDouble214(glyph);
+ d = getDouble214(glyph);
+ }
+ else
+ {
+ a = d = 1.0;
+ b = c = 0.0;
+ }
+
+ double m = Math.max(Math.abs(a), Math.abs(b));
+ double n = Math.max(Math.abs(c), Math.abs(d));
+
+ /* The Apple TrueType specification actually says that m is
+ * multiplied by two if
+ *
+ * abs(abs(a) - abs(c)) <= 33/65536,
+ *
+ * but this is probably a typo. On 2003-07-23, Sascha Brawer
+ * wrote an e-mail message to applefonts@apple.com, asking
+ * whether this might possibly be an error in the specification.
+ */
+ if (Math.abs(Math.abs(a) - Math.abs(b)) <= 33.0/65536.0)
+ m = m * 2;
+
+ if (Math.abs(Math.abs(c) - Math.abs(d)) <= 33.0/65536.0)
+ n = n * 2;
+
+ if ((flags & ARGS_ARE_XY_VALUES) != 0)
+ {
+ e = m * arg1;
+ f = n * arg2;
+ }
+ else
+ e = f = 0.0;
+
+ componentTransform.setTransform(a, b, c, d, 0.0, 0.0);
+
+ // System.out.println("componentTransform = " + componentTransform
+ // + ", e=" + e + ", f=" + f);
+ componentTransform.concatenate(transform);
+
+ int pos = glyph.position();
+ int lim = glyph.limit();
+
+ loadSubGlyph(subGlyphIndex, pointSize, componentTransform,
+ antialias, subGlyphZone,
+ Math.round((float) e + preTranslateX),
+ Math.round(-((float) f + preTranslateY)), hinter);
+ glyphZone.combineWithSubGlyph(subGlyphZone, 4);
+ glyph.limit(lim).position(pos);
+ }
+ while ((flags & MORE_COMPONENTS) != 0);
+
+ setPhantomPoints(metricsGlyphIndex, glyphZone.getSize() - 4, glyphZone);
+ }
+
+
+ private double getDouble214(ByteBuffer buf)
+ {
+ return ((double) buf.getShort()) / (1 << 14);
+ }
+
+
+ /**
+ * Loads the per-point flags of a glyph into the
+ * <code>pointFlags</code> field.
+ */
+ private void loadFlags(int numPoints, ByteBuffer glyph)
+ {
+ byte flag;
+ int numRepetitions;
+
+ for (int i = 0; i < numPoints; i++)
+ {
+ pointFlags[i] = flag = glyph.get();
+ if ((flag & 8) != 0)
+ {
+ numRepetitions = ((int) glyph.get()) & 0xff;
+ while (numRepetitions > 0)
+ {
+ pointFlags[++i] = flag;
+ --numRepetitions;
+ }
+ }
+ }
+ }
+
+
+ private void loadCoordinates(int numPoints, ByteBuffer glyph,
+ Zone glyphZone)
+ {
+ int x, y;
+ byte flag;
+
+ x = 0;
+ for (int i = 0; i < numPoints; i++)
+ {
+ flag = pointFlags[i];
+ if ((flag & 2) == 0)
+ {
+ if ((flag & 16) == 0)
+ x += glyph.getShort();
+ }
+ else
+ {
+ if ((flag & 16) != 0)
+ x += (glyph.get() & 0xff);
+ else
+ x -= (glyph.get() & 0xff);
+ }
+ glyphZone.setOriginalX(i, x);
+ glyphZone.setOnCurve(i, (flag & 1) == 1);
+ }
+
+ y = 0;
+ for (int i = 0; i < numPoints; i++)
+ {
+ flag = pointFlags[i];
+ if ((flag & 4) == 0)
+ {
+ if ((flag & 32) == 0)
+ y += glyph.getShort();
+ }
+ else
+ {
+ if ((flag & 32) != 0)
+ y += (glyph.get() & 0xff);
+ else
+ y -= (glyph.get() & 0xff);
+ }
+ glyphZone.setOriginalY(i, -y);
+ }
+ }
+
+
+ private void setPhantomPoints(int glyphIndex, int numPoints,
+ Zone glyphZone)
+ {
+ /* Phantom point 0: Character origin. */
+ glyphZone.setOriginalX(numPoints, 0);
+ glyphZone.setOriginalY(numPoints, 0);
+
+ /* Phantom point 1: Horizontal advance point. */
+ glyphZone.setOriginalX(numPoints + 1,
+ glyphMeasurer.getAdvanceWidth(glyphIndex, true));
+ glyphZone.setOriginalY(numPoints + 1,
+ glyphMeasurer.getAdvanceHeight(glyphIndex, true));
+
+ /* Phantom point 2: Vertical origin. */
+ int vertX = glyphMeasurer.getAscent(/* vertical */ false);
+ int vertY = glyphMeasurer.getAscent(/* horizontal */ true);
+ glyphZone.setOriginalX(numPoints + 2, vertX);
+ glyphZone.setOriginalY(numPoints + 2, vertY);
+
+ /* Phantom point 3: Vertical advance point. */
+ glyphZone.setOriginalX(numPoints + 3,
+ vertX + glyphMeasurer.getAdvanceWidth(glyphIndex, false));
+ glyphZone.setOriginalY(numPoints + 3,
+ vertY + glyphMeasurer.getAdvanceHeight(glyphIndex, false));
+ }
+}
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java
new file mode 100644
index 000000000..fc2256b15
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java
@@ -0,0 +1,187 @@
+/* GlyphLocator.java -- Locates outlines of TrueType glyphs.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.java.awt.font.opentype.truetype;
+
+import java.awt.FontFormatException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.IntBuffer;
+
+
+/**
+ * Locates glyph outlines in a TrueType or OpenType <code>glyf</code>
+ * table.
+ *
+ * @see <a href=
+ * "http://partners.adobe.com/asn/tech/type/opentype/loca.html"
+ * >Adobe&#x2019;s specification of the OpenType &#x2018;loca&#x2019;
+ * table</a>
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+abstract class GlyphLocator
+{
+ /**
+ * The actual glyph data of the font, which is contained in the
+ * 'glyf' table.
+ */
+ protected ByteBuffer glyfTable;
+
+
+ /**
+ * Creates a new GlyphLocator for a <code>loca</code> table.
+ *
+ * @param format the format of the <code>loca</code> table. The
+ * value must be 0 for two-byte offsets, or 1 for four-byte
+ * offsets. TrueType and OpenType fonts indicate the format in the
+ * <code>indexToLoc</code> field of the <a href=
+ * "http://partners.adobe.com/asn/tech/type/opentype/head.html"
+ * >font header</a>.
+ *
+ * @param loca the <code>loca</code> table of the font, which
+ * contains the position of each glyph in the <code>glyf</code>
+ * table.
+ *
+ * @param glyf the <code>glyf</code> table of the font, which
+ * contains the outline data of each glyph.
+ *
+ * @throws FontFormatException if <code>format</code> is neither 0
+ * nor 1.
+ */
+ public static GlyphLocator forTable(int format, ByteBuffer loca,
+ ByteBuffer glyf)
+ throws FontFormatException
+ {
+ switch (format)
+ {
+ case 0:
+ return new GlyphLocator.TwoByte(loca, glyf);
+
+ case 1:
+ return new GlyphLocator.FourByte(loca, glyf);
+
+ default:
+ throw new FontFormatException("unsupported loca format");
+ }
+ }
+
+
+ /**
+ * Locates the outline data for a glyph.
+ *
+ * <p>For efficiency, the glyph locator does not create a new buffer
+ * for each invocation. Instead, this method always returns the same
+ * buffer object. Therefore, the data of a glyph must have been read
+ * completely before another glyph of the same font gets requested
+ * through this method.
+ *
+ * @param glyph the number of the glyph whose outlines are to be
+ * retrieved.
+ *
+ * @return a buffer whose position is set to the first byte of glyph
+ * data, and whose limit is set to disallow accessing any data that
+ * does not belong to the glyph. If there is no outline data for the
+ * requested glyph, as would be the case for the space glyph, the
+ * result will be <code>null</code>.
+ */
+ public abstract ByteBuffer getGlyphData(int glyph);
+
+
+ /**
+ * A GlyphLocator that locates glyphs using two-byte offsets,
+ * interpreting <code>loca</code> tables of format 0.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ private final static class TwoByte
+ extends GlyphLocator
+ {
+ final CharBuffer indexToLoc;
+
+ TwoByte(ByteBuffer loca, ByteBuffer glyf)
+ {
+ this.glyfTable = glyf;
+ indexToLoc = loca.asCharBuffer();
+ }
+
+
+ public ByteBuffer getGlyphData(int glyph)
+ {
+ int offset, limit;
+ offset = ((int) indexToLoc.get(glyph)) << 1;
+ limit = ((int) indexToLoc.get(glyph + 1)) << 1;
+ if (offset >= limit)
+ return null;
+
+ glyfTable.limit(limit).position(offset);
+ return glyfTable;
+ }
+ }
+
+
+ /**
+ * A GlyphLocator that locates glyphs using four-byte offsets,
+ * interpreting <code>loca</code> tables of format 1.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ private final static class FourByte
+ extends GlyphLocator
+ {
+ final IntBuffer indexToLoc;
+
+ FourByte(ByteBuffer loca, ByteBuffer glyf)
+ {
+ this.glyfTable = glyf;
+ indexToLoc = loca.asIntBuffer();
+ }
+
+
+ public ByteBuffer getGlyphData(int glyph)
+ {
+ int offset, limit;
+ offset = indexToLoc.get(glyph);
+ limit = indexToLoc.get(glyph + 1);
+ if (offset >= limit)
+ return null;
+
+ glyfTable.limit(limit).position(offset);
+ return glyfTable;
+ }
+ }
+}
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java
new file mode 100644
index 000000000..452456d13
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java
@@ -0,0 +1,228 @@
+/* GlyphMeasurer.java -- Helper for measuring TrueType glyphs.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.awt.font.opentype.truetype;
+
+import java.awt.FontFormatException;
+import java.nio.ByteBuffer;
+import java.nio.ShortBuffer;
+
+
+/**
+ * A class for measuring TrueType and OpenType glyphs.
+ *
+ * <p><b>Lack of Thread Safety:</b> Glyph measurers are intentionally
+ * <i>not</i> safe to access from multiple concurrent
+ * threads. Synchronization needs to be performed externally. Usually,
+ * the font has already obtained a lock before calling the scaler,
+ * which in turn calls the GlyphMeasurer. It would thus be wasteful to
+ * acquire additional locks for the GlyphMeasurer.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+final class GlyphMeasurer
+{
+ /**
+ * A view buffer that allows accessing the contents of the
+ * font&#x2019;s <code>hmtx</code> table as shorts.
+ */
+ private final ShortBuffer horizontalGlyphMetrics;
+
+
+ /**
+ * A view buffer that allows accessing the contents of the
+ * font&#x2019;s <code>vmtx</code> table as shorts.
+ */
+ private final ShortBuffer verticalGlyphMetrics;
+
+
+ private final int numLongHorizontalMetricsEntries;
+ private final int numLongVerticalMetricsEntries;
+
+ private final int horizontalAscent;
+ private final int verticalAscent;
+
+ private final int horizontalDescent;
+ private final int verticalDescent;
+
+ private final int horizontalLineGap;
+
+
+ /**
+ * Constructs a GlyphMeasurer from TrueType/OpenType font tables.
+ *
+ * @param hhea the <code>hhea</code> table, which contains
+ * information about horizontal metrics that is common to all
+ * glyphs.
+ *
+ * @param hmtx the <code>hmtx</code> table, which contains
+ * glyph-specific information about horizontal metrics.
+ *
+ * @param vhea the <code>vhea</code> table, which contains
+ * information about vertical metrics that is common to all
+ * glyphs. If a font does not provide such a table, pass
+ * <code>null</code>.
+ *
+ * @param vmtx the <code>vmtx</code> table, which contains
+ * glyph-specific information about vertical metrics. If a font
+ * does not provide such a table, pass <code>null</code>.
+ */
+ GlyphMeasurer(ByteBuffer hhea, ByteBuffer hmtx,
+ ByteBuffer vhea, ByteBuffer vmtx)
+ throws FontFormatException
+ {
+ if ((hhea.getInt(0) != 0x00010000) || (hhea.getInt(30) != 0))
+ throw new FontFormatException("unsupported hhea format");
+
+ horizontalAscent = hhea.getShort(4);
+ horizontalDescent = hhea.getShort(6);
+ horizontalLineGap = hhea.getShort(8);
+
+ numLongHorizontalMetricsEntries = hhea.getChar(34);
+ horizontalGlyphMetrics = hmtx.asShortBuffer();
+
+ if (vhea != null)
+ {
+ verticalAscent = vhea.getShort(4);
+ verticalDescent = vhea.getShort(6);
+ numLongVerticalMetricsEntries = vhea.getChar(34);
+ verticalGlyphMetrics = vmtx.asShortBuffer();
+ }
+ else
+ {
+ verticalAscent = /* advanceWidthMax */ hhea.getChar(10) / 2;
+ verticalDescent = -verticalAscent;
+ numLongVerticalMetricsEntries = 0;
+ verticalGlyphMetrics = null;
+ }
+ }
+
+
+ /**
+ * Returns the distance from the baseline to the highest ascender.
+ *
+ * @param horizontal <code>true</code> for horizontal line layout,
+ * <code>false</code> for vertical line layout.
+ *
+ * @return the maximal ascent, in font units.
+ */
+ public int getAscent(boolean horizontal)
+ {
+ return horizontal ? horizontalAscent : verticalAscent;
+ }
+
+
+ /**
+ * Returns the distance from the baseline to the lowest descender.
+ *
+ * @param horizontal <code>true</code> for horizontal line layout,
+ * <code>false</code> for vertical line layout.
+ *
+ * @return the maximal descent, in font units.
+ */
+ public int getDescent(boolean horizontal)
+ {
+ return horizontal ? horizontalDescent : verticalDescent;
+ }
+
+
+ /**
+ * Returns the typographic line gap.
+ *
+ * @param horizontal <code>true</code> for horizontal line layout,
+ * <code>false</code> for vertical line layout.
+ *
+ * @return the line gap, in font units.
+ */
+ public int getLineGap(boolean horizontal)
+ {
+ return horizontalLineGap;
+ }
+
+
+ /**
+ * Determines the advance width of a glyph, without considering
+ * hinting.
+ *
+ * @param glyphIndex the index of the glyph whose advance width is
+ * to be determined.
+ *
+ * @param horizontal <code>true</code> for horizontal line layout,
+ * <code>false</code> for vertical line layout.
+ *
+ * @return the advance width, in font units.
+ */
+ public int getAdvanceWidth(int glyphIndex, boolean horizontal)
+ {
+ if (!horizontal)
+ return 0;
+
+ glyphIndex = Math.min(glyphIndex,
+ numLongHorizontalMetricsEntries - 1);
+ return horizontalGlyphMetrics.get(glyphIndex << 1);
+ }
+
+
+ /**
+ * Determines the advance width of a glyph, without considering
+ * hinting.
+ *
+ * @param glyphIndex the index of the glyph whose advance width is
+ * to be determined.
+ *
+ * @param horizontal <code>true</code> for horizontal line layout,
+ * <code>false</code> for vertical line layout.
+ *
+ * @return the advance width, in font units.
+ */
+ public int getAdvanceHeight(int glyphIndex, boolean horizontal)
+ {
+ if (horizontal)
+ return 0;
+
+ /* If a font does not provide vertical glyph metrics, advance
+ * by the height of one horizontal line.
+ */
+ if (verticalGlyphMetrics == null)
+ return horizontalAscent - horizontalDescent + horizontalLineGap;
+
+ glyphIndex = Math.min(glyphIndex,
+ numLongVerticalMetricsEntries - 1);
+ return verticalGlyphMetrics.get(glyphIndex << 1);
+ }
+}
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java
new file mode 100644
index 000000000..438eb6558
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java
@@ -0,0 +1,287 @@
+/* Point.java -- Holds information for one point on a glyph outline
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.awt.font.opentype.truetype;
+
+import gnu.java.lang.CPStringBuilder;
+
+/**
+ * Encapsulates information regarding one point on a glyph outline.
+ */
+public class Point
+{
+ public static final short FLAG_TOUCHED_X = 1;
+ public static final short FLAG_TOUCHED_Y = 2;
+ public static final short FLAG_ON_CURVE = 4;
+ public static final short FLAG_CONTOUR_END = 8;
+ public static final short FLAG_WEAK_INTERPOLATION = 16;
+ public static final short FLAG_INFLECTION = 32;
+ public static final short FLAG_DONE_X = 64;
+ public static final short FLAG_DONE_Y = 128;
+
+ /**
+ * Right direction.
+ */
+ public static final int DIR_RIGHT = 1;
+
+ /**
+ * Left direction.
+ */
+ public static final int DIR_LEFT = -1;
+
+ /**
+ * Up direction.
+ */
+ public static final int DIR_UP = 2;
+
+ /**
+ * Down direction.
+ */
+ public static final int DIR_DOWN = -2;
+
+ /**
+ * The original x coordinate in font units.
+ */
+ int origX;
+
+ /**
+ * The original y coordinate in font units.
+ */
+ int origY;
+
+ /**
+ * The x coordinate scaled to the target.
+ */
+ int scaledX;
+
+ /**
+ * The y coordinate scaled to the target.
+ */
+ int scaledY;
+
+ /**
+ * The final hinted and scaled x coordinate.
+ */
+ int x;
+
+ /**
+ * The final hinted and scaled y coordinate.
+ */
+ int y;
+
+ int u;
+ int v;
+
+ /**
+ * The glyph flags.
+ */
+ short flags;
+
+ /**
+ * The previous point in the contour.
+ */
+ private Point prev;
+
+ /**
+ * The next point in the contour.
+ */
+ private Point next;
+
+ /**
+ * The in-direction of the point, according to the DIR_* constants of this
+ * class.
+ */
+ int inDir;
+
+ /**
+ * The out-direction of the point, according to the DIR_* constants of this
+ * class.
+ */
+ int outDir;
+
+ public Point getNext()
+ {
+ return next;
+ }
+
+ public void setNext(Point next)
+ {
+ this.next = next;
+ }
+
+ public Point getPrev()
+ {
+ return prev;
+ }
+
+ public void setPrev(Point prev)
+ {
+ this.prev = prev;
+ }
+
+ public int getOrigX()
+ {
+ return origX;
+ }
+
+ public void setOrigX(int origX)
+ {
+ this.origX = origX;
+ }
+
+ public int getOrigY()
+ {
+ return origY;
+ }
+
+ public void setOrigY(int origY)
+ {
+ this.origY = origY;
+ }
+
+ public int getInDir()
+ {
+ return inDir;
+ }
+
+ public void setInDir(int inDir)
+ {
+ this.inDir = inDir;
+ }
+
+ public int getOutDir()
+ {
+ return outDir;
+ }
+
+ public void setOutDir(int outDir)
+ {
+ this.outDir = outDir;
+ }
+
+ public short getFlags()
+ {
+ return flags;
+ }
+
+ public void setFlags(short flags)
+ {
+ this.flags = flags;
+ }
+
+ public void addFlags(short flags)
+ {
+ this.flags |= flags;
+ }
+
+ public boolean isControlPoint()
+ {
+ return (flags & FLAG_ON_CURVE) == 0;
+ }
+
+ public int getU()
+ {
+ return u;
+ }
+
+ public void setU(int u)
+ {
+ this.u = u;
+ }
+
+ public int getV()
+ {
+ return v;
+ }
+
+ public void setV(int v)
+ {
+ this.v = v;
+ }
+
+ public String toString()
+ {
+ CPStringBuilder s = new CPStringBuilder();
+ s.append("[Point] origX: ");
+ s.append(origX);
+ s.append(", origY: ");
+ s.append(origY);
+ // TODO: Add more info when needed.
+ return s.toString();
+ }
+
+ public int getX()
+ {
+ return x;
+ }
+
+ public void setX(int x)
+ {
+ this.x = x;
+ }
+
+ public int getY()
+ {
+ return y;
+ }
+
+ public void setY(int y)
+ {
+ this.y = y;
+ }
+
+ public int getScaledX()
+ {
+ return scaledX;
+ }
+
+ public void setScaledX(int scaledX)
+ {
+ this.scaledX = scaledX;
+ }
+
+ public int getScaledY()
+ {
+ return scaledY;
+ }
+
+ public void setScaledY(int scaledY)
+ {
+ this.scaledY = scaledY;
+ }
+}
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java
new file mode 100644
index 000000000..1d5c53f9d
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java
@@ -0,0 +1,380 @@
+/* TrueTypeScaler.java -- Font scaler for TrueType outlines.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.java.awt.font.opentype.truetype;
+
+import gnu.java.awt.font.opentype.Hinter;
+import gnu.java.awt.font.opentype.Scaler;
+
+import java.awt.FontFormatException;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Point2D;
+import java.nio.ByteBuffer;
+
+
+/**
+ * A scaler for fonts whose outlines are described in the TrueType
+ * format.
+ *
+ * <p><b>Lack of Thread Safety:</b> Font scalers are intentionally
+ * <i>not</i> safe to access from multiple concurrent threads.
+ * Synchronization needs to be performed externally. Usually, the font
+ * that uses this scaler already has obtained a lock before calling
+ * the scaler.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public final class TrueTypeScaler
+ extends Scaler
+{
+ /**
+ * The TrueType or OpenType table that contains the glyph outlines.
+ */
+ private ByteBuffer glyfTable;
+
+
+ /**
+ * A helper object for loading glyph outlines.
+ */
+ private GlyphLoader glyphLoader;
+
+
+ /**
+ * A helper object for measuring the advance width and height of a
+ * glyph.
+ */
+ private final GlyphMeasurer glyphMeasurer;
+
+ private final Zone glyphZone;
+
+
+ /**
+ * The number of units per em. A typical value is 2048, but some
+ * font use other numbers as well.
+ */
+ private int unitsPerEm;
+
+
+ /**
+ * Constructs a new TrueTypeScaler.
+ *
+ * @param unitsPerEm the number of font units per em. This value can
+ * be retrieved from the font&#x2019;s <code>head</code> table.
+ *
+ * @param maxp the <code>maxp</code> table of the font, which
+ * contains various constants needed for setting up the virtual
+ * machine that interprets TrueType bytecodes.
+ *
+ * @param controlValueTable the <code>cvt</code> table of the font,
+ * which contains the initial values of the control value table.
+ *
+ * @param fpgm the <code>fpgm</code> table of the font, which
+ * contains a font program that is executed exactly once. The
+ * purpose of the font program is to define functions and to patch
+ * the interpreter.
+ *
+ * @param locaFormat the format of the <code>loca</code> table. The
+ * value must be 0 for two-byte offsets, or 1 for four-byte
+ * offsets. TrueType and OpenType fonts indicate the format in the
+ * <code>indexToLoc</code> field of the <a href=
+ * "http://partners.adobe.com/asn/tech/type/opentype/head.html"
+ * >font header</a>.
+ *
+ * @param loca the <code>loca</code> table of the font, which
+ * contains for each glyph the offset of its outline data
+ * in <code>glyf</code>.
+ *
+ * @param glyf the <code>glyf</code> table of the font, which
+ * contains the outline data for all glyphs in the font.
+ *
+ * @param preProgram the <code>prep</code> table of the font, which
+ * contains a program that is executed whenever the point size or
+ * the device transform have changed. This program is called
+ * pre-program because it gets executed before the instructions of
+ * the individual glyphs. If the font does not contain a
+ * pre-program, pass <code>null</code>.
+ *
+ * @throws FontFormatException if <code>format</code> is neither 0
+ * nor 1.
+ */
+ public TrueTypeScaler(int unitsPerEm,
+ ByteBuffer hhea,
+ ByteBuffer htmx,
+ ByteBuffer vhea,
+ ByteBuffer vtmx,
+ ByteBuffer maxp,
+ ByteBuffer controlValueTable,
+ ByteBuffer fpgm,
+ int locaFormat, ByteBuffer loca,
+ ByteBuffer glyf,
+ ByteBuffer preProgram)
+ throws FontFormatException
+ {
+ int maxContours, maxPoints;
+ VirtualMachine vm;
+
+ maxContours = Math.max(/* maxContours */ (int) maxp.getChar(8),
+ /* maxCompositeContours */ (int) maxp.getChar(12))
+ + /* fix for some broken fonts */ 8;
+ maxPoints = Math.max(/* maxPoints */ (int) maxp.getChar(6),
+ /* maxCompositePoints */ (int) maxp.getChar(10))
+ + /* fix for some broken fonts */ 12;
+
+
+ glyphZone = new Zone(maxPoints + /* four phantom points */ 4);
+ this.glyfTable = glyf;
+ vm = new VirtualMachine(unitsPerEm, maxp,
+ controlValueTable, fpgm,
+ preProgram);
+
+ GlyphLocator locator = GlyphLocator.forTable(locaFormat, loca, glyf);
+ glyphMeasurer = new GlyphMeasurer(hhea, htmx, vhea, vtmx);
+ glyphLoader = new GlyphLoader(locator, vm, unitsPerEm,
+ maxContours, maxPoints,
+ glyphMeasurer);
+
+ this.unitsPerEm = unitsPerEm;
+ }
+
+
+ /**
+ * Retrieves the scaled outline of a glyph, adjusting control points
+ * to the raster grid if necessary.
+ *
+ * @param glyphIndex the glyph number whose outline is retrieved.
+ *
+ * @param pointSize the point size for the glyph.
+ *
+ * @param deviceTransform an affine transformation for the device.
+ *
+ * @param antialias whether or not the rasterizer will perform
+ * anti-aliasing on the returned path.
+ *
+ * @param fractionalMetrics <code>false</code> for adjusting glyph
+ * positions to the raster grid of device space.
+ */
+ public GeneralPath getOutline(int glyphIndex,
+ float pointSize,
+ AffineTransform deviceTransform,
+ boolean antialias,
+ boolean fractionalMetrics, Hinter hinter,
+ int type)
+ {
+ glyphLoader.loadGlyph(glyphIndex, pointSize, deviceTransform,
+ antialias, glyphZone, hinter);
+ return glyphZone.getPath(type);
+ }
+
+ public Zone getRawOutline(int glyphIndex, AffineTransform transform)
+ {
+ Zone zone = new Zone(glyphZone.getCapacity());
+ glyphLoader.loadGlyph(glyphIndex, transform, zone, null);
+ return zone;
+ }
+
+ /**
+ * Determines the advance width and height for a glyph.
+ *
+ * @param glyphIndex the glyph whose advance width and height is to
+ * be determined.
+ *
+ * @param pointSize the point size of the font.
+ *
+ * @param transform a transform that is applied in addition to
+ * scaling to the specified point size. This is often used for
+ * scaling according to the device resolution. Those who lack any
+ * aesthetic sense may also use the transform to slant or stretch
+ * glyphs.
+ *
+ * @param antialias <code>true</code> for anti-aliased rendering,
+ * <code>false</code> for normal rendering. For hinted fonts,
+ * this parameter may indeed affect the result.
+ *
+ * @param fractionalMetrics <code>true</code> for fractional metrics,
+ * <code>false</code> for rounding the result to a pixel boundary.
+ *
+ * @param horizontal <code>true</code> for horizontal line layout,
+ * <code>false</code> for vertical line layout.
+ *
+ * @param advance a point whose <code>x</code> and <code>y</code>
+ * fields will hold the advance in each direction. It is possible
+ * that both values are non-zero, for example if
+ * <code>transform</code> is a rotation, or in the case of Urdu
+ * fonts.
+ */
+ public void getAdvance(int glyphIndex,
+ float pointSize,
+ AffineTransform transform,
+ boolean antialias,
+ boolean fractionalMetrics,
+ boolean horizontal,
+ Point2D advance)
+ {
+ double x, y;
+ double scaleFactor = (double) pointSize / unitsPerEm;
+
+ /* FIXME: Should grid-fit if needed. Also, use cache if present
+ * in the font.
+ */
+ advance.setLocation(
+ scaleFactor * glyphMeasurer.getAdvanceWidth(glyphIndex, horizontal),
+ scaleFactor * glyphMeasurer.getAdvanceHeight(glyphIndex, horizontal));
+
+ transform.transform(advance, advance);
+ }
+
+
+ /**
+ * Scales a value from font units to pixels, given the point size
+ * and the transform.
+ *
+ * @param pointSize the point size of the font.
+ *
+ * @param transform a transform that is applied in addition to
+ * scaling to the specified point size. This is often used for
+ * scaling according to the device resolution.
+ *
+ * @param fractionalMetrics <code>true</code> for fractional
+ * metrics, <code>false</code> for rounding the result to a pixel
+ * boundary.
+ *
+ * @param horizontal <code>true</code> if the <code>funits</code>
+ * value is along the x axis, <code>false</code> if it is along the
+ * y axis.
+ */
+ private float scaleFromFUnits(int funits,
+ float pointSize,
+ AffineTransform transform,
+ boolean fractionalMetrics,
+ boolean horizontal)
+ {
+ double s;
+
+ s = (double) pointSize / unitsPerEm;
+ if (transform != null)
+ s *= horizontal ? transform.getScaleY() : transform.getScaleX();
+ s *= funits;
+ if (!fractionalMetrics)
+ s = Math.round(s);
+ return (float) s;
+ }
+
+
+ /**
+ * Determines the distance between the base line and the highest
+ * ascender.
+ *
+ * @param pointSize the point size of the font.
+ *
+ * @param transform a transform that is applied in addition to
+ * scaling to the specified point size. This is often used for
+ * scaling according to the device resolution. Those who lack any
+ * aesthetic sense may also use the transform to slant or stretch
+ * glyphs.
+ *
+ * @param antialias <code>true</code> for anti-aliased rendering,
+ * <code>false</code> for normal rendering. For hinted fonts,
+ * this parameter may indeed affect the result.
+ *
+ * @param fractionalMetrics <code>true</code> for fractional metrics,
+ * <code>false</code> for rounding the result to a pixel boundary.
+ *
+ * @param horizontal <code>true</code> for horizontal line layout,
+ * <code>false</code> for vertical line layout.
+ *
+ * @return the ascent, which usually is a positive number.
+ */
+ public float getAscent(float pointSize,
+ AffineTransform transform,
+ boolean antialias,
+ boolean fractionalMetrics,
+ boolean horizontal)
+ {
+ /* Note that the ascent is orthogonal to the direction of line
+ * layout: If the line direction is horizontal, the measurement of
+ * ascent is along the vertical axis, and vice versa.
+ */
+ return scaleFromFUnits(glyphMeasurer.getAscent(horizontal),
+ pointSize,
+ transform,
+ fractionalMetrics,
+ /* reverse */ !horizontal);
+ }
+
+
+ /**
+ * Determines the distance between the base line and the lowest
+ * descender.
+ *
+ * @param pointSize the point size of the font.
+ *
+ * @param transform a transform that is applied in addition to
+ * scaling to the specified point size. This is often used for
+ * scaling according to the device resolution. Those who lack any
+ * aesthetic sense may also use the transform to slant or stretch
+ * glyphs.
+ *
+ * @param antialiased <code>true</code> for anti-aliased rendering,
+ * <code>false</code> for normal rendering. For hinted fonts,
+ * this parameter may indeed affect the result.
+ *
+ * @param fractionalMetrics <code>true</code> for fractional metrics,
+ * <code>false</code> for rounding the result to a pixel boundary.
+ *
+ * @param horizontal <code>true</code> for horizontal line layout,
+ * <code>false</code> for vertical line layout.
+ *
+ * @return the descent, which usually is a nagative number.
+ */
+ public float getDescent(float pointSize,
+ AffineTransform transform,
+ boolean antialiased,
+ boolean fractionalMetrics,
+ boolean horizontal)
+ {
+ /* Note that the descent is orthogonal to the direction of line
+ * layout: If the line direction is horizontal, the measurement of
+ * descent is along the vertical axis, and vice versa.
+ */
+ return scaleFromFUnits(glyphMeasurer.getDescent(horizontal),
+ pointSize,
+ transform,
+ fractionalMetrics,
+ /* reverse */ !horizontal);
+ }
+}
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java
new file mode 100644
index 000000000..512c39ffd
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java
@@ -0,0 +1,1815 @@
+/* VirtualMachine.java -- Virtual machine for TrueType bytecodes.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.java.awt.font.opentype.truetype;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.FontFormatException;
+import java.awt.geom.AffineTransform;
+import java.nio.ByteBuffer;
+import java.nio.ShortBuffer;
+
+
+/**
+ * A virtual machine for interpreting TrueType bytecodes.
+ *
+ * <p><b>Lack of Thread Safety:</b> The virtual machine is
+ * intentionally <i>not</i> safe to access from multiple concurrent
+ * threads. Synchronization needs to be performed externally. Usually,
+ * the font has already obtained a lock before calling the scaler,
+ * which in turn calls the VM. It would be wasteful to acquire
+ * additional locks for the VM.
+ *
+ * <p><b>Implementation Status:</b> The current implementation can
+ * execute pre-programs of fonts, but it does not yet actually move
+ * any points. Control flow and arithmeti instructions are
+ * implemented, but most geometric instructions are not working
+ * yet. So, the VirtualMachine class is currently a no-op. However,
+ * not very much is missing. You are more than welcome to complete the
+ * implementation.
+ *
+ * <p><b>Patents:</b> Apple Computer holds three United States Patents
+ * for the mathematical algorithms that are used by TrueType
+ * instructions. The monopoly granted by these patents will expire in
+ * October 2009. Before the expiration date, a license must be
+ * obtained from Apple Computer to use the patented technology inside
+ * the United States. For other countries, different dates might
+ * apply, or no license might be needed.
+ *
+ * <p>The default build of this class does not use the patented
+ * algorithms. If you have obtained a license from Apple, or if the
+ * patent protection has expired, or if no license is required for
+ * your contry, you can set a flag in the source file which will
+ * enable the use of the patented mathematical algorithms.</p>
+ *
+ * <p>The relevant patents are listed subsequently.</p>
+ *
+ * <p><ol><li>United States Patent 5155805, <i>Method and Apparatus
+ * for Moving Control Points in Displaying Digital Typeface on Raster
+ * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple
+ * Computer. Filing date: May 8, 1989. Date of patent: October 13,
+ * 1992.</li>
+ *
+ * <li>United States Patent 5159668, <i>Method and Apparatus for
+ * Manipulating Outlines in Improving Digital Typeface on Raster
+ * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple
+ * Computer. Filing date: May 8, 1989. Date of patent: October 27,
+ * 1992.</li>
+ *
+ * <li>United States Patent 5325479, <i>Method and Apparatus for
+ * Moving Control Points in Displaying Digital Typeface on Raster
+ * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple
+ * Computer. Filing date: May 28, 1989. Date of patent: June 28, 1994
+ * (with a statement that &#x201c;[t]he portion of the term of this
+ * patent subsequent to Oct. 13, 2009 has been
+ * disclaimed&#x201d;).</li></ol>
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+class VirtualMachine
+{
+ /**
+ * Indicates whether or not to perform hinting operations that are
+ * protected by a number of US patents, two of which will expire on
+ * October 13, 2009, and one of which will expire on October 27,
+ * 2009.
+ */
+ private final static boolean PATENTED_HINTING = false;
+
+
+ /**
+ * Indicates whether the execution of the Virtual Machine is traced
+ * to System.out.
+ */
+ private final static boolean TRACE_EXECUTION = false;
+
+
+ /**
+ * The value 1 in 2-dot-14 fixed notation.
+ */
+ private static final short ONE_214 = 0x4000; // 1 << 14
+
+
+ /**
+ * The storage area of the virtual machine.
+ */
+ private final int[] storage;
+
+
+ /**
+ * The stack. The stack grows from bottom to top, so
+ * <code>sp[0]</code> gets used before <code>sp[1]</code>.
+ */
+ private int[] stack;
+
+
+ /**
+ * The maximum number of stack elements.
+ */
+ private final int maxStackElements;
+
+
+ /**
+ * The current stack pointer of the virtual machine.
+ */
+ private int sp;
+
+
+ /**
+ * fdefBuffer[i] is the buffer that contains the TrueType
+ * instructions of function #i. Most of the time, functions are
+ * defined in the font program, but a font may also re-define
+ * functions in its CVT program.
+ */
+ private ByteBuffer[] fdefBuffer;
+
+
+ /**
+ * fdefEntryPoint[i] is the position in fdefBuffer[i] where the
+ * first TrueType instruction after the FDEF is located.
+ */
+ private int[] fdefEntryPoint;
+
+
+ /**
+ * The original Control Value Table, sometimes abbreviated as CVT.
+ * The table contains signed 16-bit FUnits. Some fonts have no CVT,
+ * in which case the field will be <code>null</code>.
+ */
+ private ShortBuffer controlValueTable;
+
+
+ /**
+ * The scaled values inside the control value table.
+ */
+ private int[] cvt;
+
+
+ /**
+ * A value that is used by rounding operations to compensate for dot
+ * gain.
+ */
+ private int engineCompensation = 0;
+
+
+ /**
+ * The contents of the font&#x2019;s <code>fpgm</code> table, or
+ * <code>null</code> after the font program has been executed once.
+ */
+ private ByteBuffer fontProgram;
+
+
+ /**
+ * The <code>prep</code> table of the font, which contains a program
+ * that is executed whenever the point size or the device transform
+ * have changed. This program is called pre-program because it gets
+ * executed before the instructions of the individual glyphs. If
+ * the font does not contain a pre-program, the value of this field
+ * is <code>null</code>.
+ */
+ private ByteBuffer preProgram;
+
+
+ /**
+ * The number of points in the Twilight Zone.
+ */
+ private int numTwilightPoints;
+
+
+ /**
+ * The current point size of the scaled font. The value is in Fixed
+ * 26.6 notation.
+ */
+ private int pointSize; // 26.6
+
+ private AffineTransform deviceTransform;
+
+ private int scaleX, scaleY, shearX, shearY; // 26.6
+
+
+ /**
+ * Indicates whether or not scan-line conversion will use
+ * anti-aliasing (with gray levels). Font programs can ask for this
+ * value with the <code>GETINFO</code> instruction, and some
+ * programs may behave differently according to this setting.
+ */
+ private boolean antialiased;
+
+
+ /* Graphics State. FIXME: Move this to its own class? Some
+ * documentation would not hurt, either.
+ */
+ private int cvtCutIn; // 26.6
+ private int deltaBase; // uint32
+ private int deltaShift; // uint32
+ private short freeX; // 2.14
+ private short freeY; // 2.14
+ private int loop; // int
+ private int minimumDistance; // 26.6
+ private short projX; // 2.14
+ private short projY; // 2.14
+ private short dualX; // 2.14
+ private short dualY; // 2.14
+ private int rp0, rp1, rp2; // point numbers
+ private boolean scanControl;
+ private int scanType;
+ private int singleWidthValue; // 26.6
+ private Zone zp0, zp1, zp2;
+
+ private Zone twilightZone;
+ private Zone glyphZone;
+
+
+ /**
+ * Indicates whether or not the instructions that are associated
+ * with individual glyphs shall be executed. Set as a side effect
+ * of executing the pre-program when the point size, device
+ * transform or some other relevant parameter have changed.
+ */
+ private boolean executeGlyphInstructions;
+
+
+ /**
+ * Indicates whether to ignore any modifications to the control
+ * value table that the font&#x2019;s pre-program might have
+ * performed. Set as a side effect of executing the pre-program
+ * when the point size, device transform or some other relevant
+ * parameter have changed.
+ */
+ private boolean ignoreCVTProgram;
+
+
+ /**
+ * The length of the space between rounded values. A value
+ * of zero means that rounding has been switched off.
+ */
+ private int roundPeriod; // 26.6
+
+
+ /**
+ * The offset of the rounded values from multiples of
+ * <code>roundPeriod</code>.
+ */
+ private int roundPhase; // 26.6
+
+
+ private int roundThreshold; // 26.6
+
+
+ /**
+ * A cache for the number of pixels per EM. The value is a normal
+ * integer, not a fixed point notation.
+ *
+ * @see #getPixelsPerEM()
+ */
+ private int cachedPixelsPerEM;
+
+
+ /**
+ * The number of font units per EM.
+ */
+ private int unitsPerEm;
+
+
+ /**
+ * Constructs a new Virtual Machine for executing TrueType
+ * instructions.
+ *
+ * @param unitsPerEm the number of font units in one typographic
+ * em.
+ *
+ * @param preProgram the <code>prep</code> table of the font, which
+ * contains a program that is executed whenever the point size or
+ * the device transform have changed. This program is called
+ * pre-program because it gets executed before the instructions of
+ * the individual glyphs. If the font does not contain a
+ * pre-program, pass <code>null</code>.
+ */
+ VirtualMachine(int unitsPerEm,
+ ByteBuffer maxp,
+ ByteBuffer controlValueTable,
+ ByteBuffer fontProgram,
+ ByteBuffer preProgram)
+ throws FontFormatException
+ {
+ int maxStorage, numFunctionDefs, maxInstructionDefs;
+
+ if (maxp.getInt(0) != 0x00010000)
+ throw new FontFormatException("unsupported maxp version");
+
+ this.unitsPerEm = unitsPerEm;
+ maxStorage = maxp.getChar(18);
+
+ /* FreeType says that there exist some broken fonts (like
+ * "Keystrokes MT") that contain function defs, but have a zero
+ * value in their maxp table.
+ */
+ numFunctionDefs = maxp.getChar(20);
+ if (numFunctionDefs == 0)
+ numFunctionDefs = 64;
+ fdefBuffer = new ByteBuffer[numFunctionDefs];
+ fdefEntryPoint = new int[numFunctionDefs];
+
+ /* Read the contents of the Control Value Table. */
+ if (controlValueTable != null)
+ this.controlValueTable = controlValueTable.asShortBuffer();
+
+ maxInstructionDefs = maxp.getChar(22);
+ maxStackElements = maxp.getChar(24);
+ storage = new int[maxStorage];
+ this.fontProgram = fontProgram;
+ this.preProgram = preProgram;
+ numTwilightPoints = maxp.getChar(16);
+ }
+
+
+ /**
+ * Sets the graphics state to default values.
+ */
+ private void resetGraphicsState()
+ {
+ /* The freedom, projection and dual vector default to the x axis. */
+ freeX = projX = dualX = ONE_214;
+ freeY = projY = dualX = 0;
+ cachedPixelsPerEM = 0;
+
+ cvtCutIn = 68; // 17/16 in 26.6 notation
+ deltaBase = 9;
+ deltaShift = 3;
+ loop = 1;
+ minimumDistance = Fixed.ONE;
+ singleWidthValue = 0;
+ rp0 = rp1 = rp2 = 0;
+ scanControl = false;
+ scanType = 2;
+ zp0 = zp1 = zp2 = getZone(1);
+
+ setRoundingMode(Fixed.ONE, 0x48); // round to grid
+ }
+
+
+ /**
+ * Reloads the control value table and scales each entry from font
+ * units to pixel values.
+ */
+ private void reloadControlValueTable()
+ {
+ /* Some TrueType fonts have no control value table. */
+ if (controlValueTable == null)
+ return;
+
+ /* Read in the Control Value Table. */
+ if (cvt == null)
+ cvt = new int[controlValueTable.capacity()];
+
+ /* Scale the entries. */
+ for (int i = 0; i < cvt.length; i++)
+ cvt[i] = funitsToPixels(controlValueTable.get(i));
+ }
+
+
+ /**
+ * Scales a value from font unites to pixels.
+ *
+ * @return the scaled value.
+ */
+ private int funitsToPixels(int funits)
+ {
+ return (int) (((long) funits * scaleY + (unitsPerEm>>1))
+ / unitsPerEm);
+ }
+
+
+ /**
+ * Sets up the virtual machine for the specified parameters. If
+ * there is no change to the last set-up, the method will quickly
+ * return. Otherwise, the font&#x2019;s pre-program will be
+ * executed.
+ *
+ * @param pointSize the point size of the scaled font.
+ *
+ * @param deviceTransform an affine transformation which gets
+ * applied in addition to scaling by <code>pointSize</code>. Font
+ * programs can separately inquire about the point size. For this
+ * reason, it is not recommended to pre-multiply the point size to
+ * the device transformation.
+ *
+ * @param antialiased <code>true</code> if the scan-line conversion
+ * algorithm will use gray levels to give a smoother appearance,
+ * <code>false</code> otherwise. Font programs can ask for this
+ * value with the <code>GETINFO</code> instruction, and some
+ * programs may behave differently according to this setting.
+ */
+ public boolean setup(double pointSize,
+ AffineTransform deviceTransform,
+ boolean antialiased)
+ {
+ boolean changeCTM;
+ int pointSize_Fixed;
+
+ if (stack == null)
+ stack = new int[maxStackElements];
+
+ if (twilightZone == null)
+ twilightZone = new Zone(numTwilightPoints);
+
+ /* If the font program has not yet been executed, do so. */
+ if (fontProgram != null)
+ {
+ resetGraphicsState();
+ sp = -1;
+ execute(fontProgram, 0);
+ fontProgram = null; // prevent further execution
+ }
+
+ /* Determine whether the transformation matrix has changed. */
+ pointSize_Fixed = Fixed.valueOf(pointSize);
+ changeCTM = ((pointSize_Fixed != this.pointSize)
+ || !deviceTransform.equals(this.deviceTransform)
+ || (antialiased != this.antialiased));
+
+ if (changeCTM)
+ {
+ this.pointSize = pointSize_Fixed;
+ this.deviceTransform = deviceTransform;
+ this.antialiased = antialiased;
+ scaleX = (int) (deviceTransform.getScaleX() * pointSize * 64);
+ scaleY = (int) (deviceTransform.getScaleY() * pointSize * 64);
+ shearX = (int) (deviceTransform.getShearX() * pointSize * 64);
+ shearY = (int) (deviceTransform.getShearY() * pointSize * 64);
+
+ resetGraphicsState();
+ reloadControlValueTable();
+ executeGlyphInstructions = true;
+ ignoreCVTProgram = false;
+
+ if (preProgram != null)
+ {
+ sp = -1;
+ execute(preProgram, 0);
+ if (ignoreCVTProgram)
+ reloadControlValueTable();
+ }
+ }
+
+ return executeGlyphInstructions;
+ }
+
+
+ /**
+ * Executes a stream of TrueType instructions.
+ */
+ private void execute(ByteBuffer instructions, int pos)
+ {
+ instructions.position(pos);
+
+ // FIXME: SECURITY: Possible denial-of-service attack
+ // via instructions that have an endless loop.
+ while (instructions.hasRemaining()
+ && executeInstruction(instructions))
+ ;
+ }
+
+
+ /**
+ * Writes a textual description of the current TrueType instruction,
+ * including the top stack elements, to <code>System.out</code>.
+ * This is useful for debugging.
+ *
+ * @param inst the instruction stream, positioned at the current
+ * instruction.
+ */
+ private void dumpInstruction(ByteBuffer inst)
+ {
+ CPStringBuilder sbuf = new CPStringBuilder(40);
+ int pc = inst.position();
+ int bcode = inst.get(pc) & 0xff;
+ int count;
+ int delta;
+
+ char pcPrefix = 'c';
+ for (int i = 0; i < fdefBuffer.length; i++)
+ {
+ if (fdefBuffer[i] == inst)
+ {
+ pcPrefix = 'f';
+ break;
+ }
+ }
+ sbuf.append(pcPrefix);
+
+
+ sbuf.append(getHex((short) inst.position()));
+ sbuf.append(": ");
+ sbuf.append(getHex((byte) bcode));
+ sbuf.append(" ");
+ sbuf.append(INST_NAME[bcode]);
+
+ if (bcode == 0x40) // NPUSHB
+ {
+ count = inst.get(pc + 1) & 0xff;
+ sbuf.append(" (");
+ sbuf.append(count);
+ sbuf.append(") ");
+ for (int i = 0; i < count; i++)
+ {
+ if (i > 0)
+ sbuf.append(" ");
+ sbuf.append('$');
+ sbuf.append(getHex(inst.get(pc + 2 + i)));
+ }
+ }
+ if (bcode == 0x41) // NPUSHW
+ {
+ count = inst.get(pc + 1) & 0xff;
+ sbuf.append(" (");
+ sbuf.append(count);
+ sbuf.append(") ");
+ for (int i = 0; i < count; i++)
+ {
+ if (i > 0)
+ sbuf.append(' ');
+ sbuf.append('$');
+ sbuf.append(getHex(inst.getShort(pc + 2 + 2*i)));
+ }
+ }
+ else
+ {
+ count = getInstructionLength(bcode) - 1;
+ for (int i = 0; i < count; i++)
+ {
+ sbuf.append(" $");
+ sbuf.append(getHex(inst.get(pc + 1 + i)));
+ }
+ }
+
+ while (sbuf.length() < 30)
+ sbuf.append(' ');
+ sbuf.append('|');
+ sbuf.append(sp + 1);
+ sbuf.append("| ");
+ for (int i = sp; i >= Math.max(0, sp - 5); i = i - 1)
+ {
+ if (i < sp)
+ sbuf.append(" ");
+ if ((stack[i] >> 16) != 0)
+ sbuf.append(getHex((short) (stack[i] >> 16)));
+ sbuf.append(getHex((short) stack[i]));
+ }
+ System.out.println(sbuf);
+ }
+
+
+ private static char getNibble(int i, int rightShift)
+ {
+ i = (i >> rightShift) & 15;
+ if (i < 10)
+ return (char) (i + '0');
+ else
+ return (char) (i + 'a' - 10);
+ }
+
+
+ private static String getHex(byte b)
+ {
+ char[] a = new char[2];
+ a[0] = getNibble(b, 4);
+ a[1] = getNibble(b, 0);
+ return new String(a);
+ }
+
+
+ private static String getHex(short b)
+ {
+ char[] a = new char[4];
+ a[0] = getNibble(b, 12);
+ a[1] = getNibble(b, 8);
+ a[2] = getNibble(b, 4);
+ a[3] = getNibble(b, 0);
+ return new String(a);
+ }
+
+
+ /**
+ * Skips any instructions until the specified opcode has been
+ * encoutered.
+ *
+ * @param inst the current instruction stream. After the call,
+ * the position of <code>inst</code> is right after the first
+ * occurence of <code>opcode</code>.
+ *
+ * @param opcode1 the opcode for which to look.
+ *
+ * @param opcode2 another opcode for which to look. Pass -1
+ * if only <code>opcode1</code> would terminate skipping.
+ *
+ * @param illegalCode1 an opcode that must not be encountered
+ * while skipping. Pass -1 if any opcode is acceptable.
+ *
+ * @param illegalCode2 another opcode that must not be encountered
+ * while skipping. Pass -1 to perform no check.
+ *
+ * @param handleNestedIfClauses <code>true</code> to handle
+ * nested <code>IF [ELSE] EIF</code> clauses, <code>false</code>
+ * to ignore them. From the TrueType specification document,
+ * one would think that nested if clauses would not be valid,
+ * but they do appear in some fonts.
+ *
+ * @throws IllegalStateException if <code>illegalCode1</code> or
+ * <code>illegalCode2</code> has been encountered while skipping.
+ */
+ private static void skipAfter(ByteBuffer inst,
+ int opcode1, int opcode2,
+ int illegalCode1, int illegalCode2,
+ boolean handleNestedIfClauses)
+ {
+ int pos = inst.position();
+ int curOpcode;
+ int instLen;
+ int nestingLevel = 0; // increased inside IF [ELSE] EIF sequences
+
+ while (true)
+ {
+ curOpcode = inst.get(pos) & 0xff;
+ instLen = getInstructionLength(curOpcode);
+
+ if (false && TRACE_EXECUTION)
+ {
+ for (int i = 0; i < nestingLevel; i++)
+ System.out.print("--");
+ System.out.print("--" + pos + "-" + INST_NAME[curOpcode]);
+ if (nestingLevel > 0)
+ System.out.print(", ifNestingLevel=" + nestingLevel);
+ System.out.println();
+ }
+
+ if (curOpcode == 0x40) // NPUSHB
+ pos += 1 + (inst.get(pos + 1) & 0xff);
+ else if (curOpcode == 0x41) // NPUSHW
+ pos += 1 + 2 * (inst.get(pos + 1) & 0xff);
+ else
+ pos += instLen;
+
+ if ((nestingLevel == 0)
+ && ((curOpcode == opcode1) || (curOpcode == opcode2)))
+ break;
+
+ if (handleNestedIfClauses)
+ {
+ if (curOpcode == /* IF */ 0x58)
+ ++nestingLevel;
+ else if (curOpcode == /* EIF */ 0x59)
+ --nestingLevel;
+ }
+
+ if ((nestingLevel < 0)
+ || (curOpcode == illegalCode1)
+ || (curOpcode == illegalCode2))
+ throw new IllegalStateException();
+ }
+
+ inst.position(pos);
+ }
+
+
+ /**
+ * Returns the number of bytes that a TrueType instruction occupies.
+ *
+ * @param opcode the instruction.
+ *
+ * @return the number of bytes occupied by the instructions and its
+ * operands. For <code>NPUSHB</code> and <code>NPUSHW</code>, where
+ * the instruction length depends on the first operand byte, the
+ * result is -1.
+ */
+ private static int getInstructionLength(int opcode)
+ {
+ /* NPUSHB, NPUSHW --> see following byte */
+ if ((opcode == 0x40) || (opcode == 0x41))
+ return -1;
+
+ /* PUSHB[0] .. PUSHB[7] --> 2, 3, 4, 5, 6, 7, 8, 9 */
+ if ((opcode >= 0xb0) && (opcode <= 0xb7))
+ return opcode - 0xae;
+
+ /* PUSHW[0] .. PUSHW[7] --> 3, 5, 6, 7, 11, 13, 15, 17*/
+ if ((opcode >= 0xb8) && (opcode <= 0xbf))
+ return 1 + ((opcode - 0xb7) << 1);
+
+ return 1;
+ }
+
+
+ /**
+ * Executes a single TrueType instruction. This is the core
+ * routine of the Virtual Machine.
+ *
+ * @return <code>true</code> if another instruction shall be
+ * executed in the same call frame; <code>false</code> if the
+ * current call frame shall be popped.
+ */
+ private boolean executeInstruction(ByteBuffer inst)
+ {
+ if (TRACE_EXECUTION)
+ dumpInstruction(inst);
+
+ int i, count, e1, e2, e3, e4, x, y;
+ int bcode = inst.get() & 0xff;
+
+ switch (bcode)
+ {
+ case 0x00: // SVTCA[0], Set freedom and proj. Vectors To Coord. Axis [y]
+ setFreedomVector((short) 0, ONE_214);
+ setProjectionVector((short) 0, ONE_214);
+ break;
+
+ case 0x01: // SVTCA[1], Set freedom and proj. Vectors To Coord. Axis [x]
+ setFreedomVector(ONE_214, (short) 0);
+ setProjectionVector(ONE_214, (short) 0);
+ break;
+
+ case 0x02: // SPVTCA[0], Set Projection Vector To Coordinate Axis [y]
+ setProjectionVector((short) 0, ONE_214);
+ break;
+
+ case 0x03: // SPVTCA[1], Set Projection Vector To Coordinate Axis [x]
+ setProjectionVector(ONE_214, (short) 0);
+ break;
+
+ case 0x0c: // GPV, Get Projection Vector
+ stack[++sp] = projX;
+ stack[++sp] = projY;
+ break;
+
+ case 0x0d: // GPV, Get Freedom Vector
+ stack[++sp] = freeX;
+ stack[++sp] = freeY;
+ break;
+
+ case 0x0F: // ISECT, move point p to the InterSECTION of two lines
+ sp -= 4;
+ handleISECT(stack[sp], stack[sp+1], stack[sp+2],
+ stack[sp+3], stack[sp+4]);
+ break;
+
+ case 0x10: // SRP0, Set Reference Point 0
+ rp0 = stack[sp--];
+ break;
+
+ case 0x11: // SRP1, Set Reference Point 1
+ rp1 = stack[sp--];
+ break;
+
+ case 0x12: // SRP2, Set Reference Point 2
+ rp2 = stack[sp--];
+ break;
+
+ case 0x13: // SZP0, Set Zone Pointer 0
+ zp0 = getZone(stack[sp--]);
+ break;
+
+ case 0x14: // SZP1, Set Zone Pointer 1
+ zp1 = getZone(stack[sp--]);
+ break;
+
+ case 0x15: // SZP2, Set Zone Pointer 2
+ zp2 = getZone(stack[sp--]);
+ break;
+
+ case 0x16: // SZPS, Set Zone PointerS
+ zp0 = zp1 = zp2 = getZone(stack[sp--]);
+ break;
+
+ case 0x17: // SLOOP, Set LOOP variable
+ loop = stack[sp--];
+ break;
+
+ case 0x18: // RTG, Round To Grid
+ setRoundingMode(Fixed.ONE, 0x48);
+ break;
+
+ case 0x19: // RTHG, Round To Half Grid
+ setRoundingMode(Fixed.ONE, 0x68);
+ break;
+
+ case 0x1a: // SMD, Set Minimum Distance
+ minimumDistance = stack[sp--];
+ break;
+
+ case 0x1B: // ELSE, ELSE clause
+ skipAfter(inst,
+ /* look for: EIF, -- */ 0x59, -1,
+ /* illegal: --, -- */ -1, -1,
+ /* handle nested if clauses */ true);
+ break;
+
+ case 0x1C: // JMPR, JuMP Relative
+ inst.position(inst.position() - 1 + stack[sp--]);
+ break;
+
+ case 0x1D: // SCVTCI, Set Control Value Table Cut-In
+ cvtCutIn = stack[sp--];
+ break;
+
+ case 0x1F: // SSW, Set Single Width
+ singleWidthValue = stack[sp--];
+ break;
+
+ case 0x20: // DUP, DUPlicate top stack element
+ e1 = stack[sp];
+ stack[++sp] = e1;
+ break;
+
+ case 0x21: // POP, POP top stack element
+ sp--;
+ break;
+
+ case 0x22: // CLEAR, CLEAR the stack
+ sp = -1;
+ break;
+
+ case 0x23: // SWAP, SWAP the top two elements on the stack
+ e1 = stack[sp--];
+ e2 = stack[sp];
+ stack[sp] = e1;
+ stack[++sp] = e2;
+ break;
+
+ case 0x24: // DEPTH, DEPTH of the stack
+ stack[++sp] = sp + 1;
+ break;
+
+ case 0x25: // CINDEX, Copy the INDEXed element to the top of the stack
+ stack[sp] = stack[sp - stack[sp]];
+ break;
+
+ case 0x26: // MINDEX, Move the INDEXed element to the top of the stack
+ i = stack[sp];
+ e1 = stack[sp - i];
+ System.arraycopy(/* src */ stack, /* srcPos */ sp - i + 1,
+ /* dest */ stack, /* destPos*/ sp - i,
+ /* length */ i - 1);
+ --sp;
+ stack[sp] = e1;
+ break;
+
+ case 0x2a: // LOOPCALL, LOOP and CALL function
+ i = stack[sp--];
+ count = stack[sp--];
+ e1 = inst.position();
+ e2 = sp;
+ for (int j = 0; j < count; j++)
+ execute(fdefBuffer[i], fdefEntryPoint[i]);
+ inst.position(e1);
+ break;
+
+ case 0x2B: // CALL, CALL function
+ i = stack[sp--];
+ e1 = inst.position();
+ e2 = sp;
+ execute(fdefBuffer[i], fdefEntryPoint[i]);
+ inst.position(e1);
+ break;
+
+ case 0x2C: // FDEF, Function DEFinition
+ i = stack[sp--];
+ fdefBuffer[i] = inst;
+ fdefEntryPoint[i] = inst.position();
+ skipAfter(inst,
+ /* look for: ENDF */ 0x2d,
+ /* look for: --- */ -1,
+ /* illegal: IDEF */ 0x89,
+ /* illegal: FDEF */ 0x2c,
+ /* do not handle nested if clauses */ false);
+ break;
+
+ case 0x2D: // ENDF, END Function definition
+ /* Pop the current stack frame. */
+ return false;
+
+ case 0x2e: // MDAP[0], Move Direct Absolute Point
+ handleMDAP(stack[sp--], /* round */ false);
+ break;
+
+ case 0x2f: // MDAP[1], Move Direct Absolute Point
+ handleMDAP(stack[sp--], /* round */ true);
+ break;
+
+ case 0x39: // IP, Interpolate Point by the last relative stretch
+ handleIP();
+ break;
+
+ case 0x3d: // RTDG, Round To Double Grid
+ setRoundingMode(Fixed.ONE, 0x08);
+ roundThreshold = roundThreshold / 64; // period/128
+ break;
+
+ case 0x3e: // MIAP[0], Move Indirect Absolute Point
+ e1 = stack[sp--];
+ handleMIAP(e1, stack[sp--], /* round */ false);
+ break;
+
+ case 0x3f: // MIAP[1], Move Indirect Absolute Point
+ e1 = stack[sp--];
+ handleMIAP(e1, stack[sp--], /* round */ true);
+ break;
+
+ case 0x40: // NPUSHB
+ count = inst.get() & 0xff;
+ for (i = 0; i < count; i++)
+ stack[++sp] = inst.get() & 0xff;
+ break;
+
+ case 0x41: // NPUSHW
+ count = inst.get() & 0xff;
+ for (i = 0; i < count; i++)
+ stack[++sp] = inst.getShort();
+ break;
+
+ case 0x42: // WS, Write Store
+ e1 = stack[sp--]; i = stack[sp--];
+ storage[i] = e1;
+ break;
+
+ case 0x43: // RS, Read Store
+ stack[sp] = storage[stack[sp]];
+ break;
+
+ case 0x44: // WCVTP, Write Control Value Table in Pixel units
+ e1 = stack[sp--];
+ i = stack[sp--];
+ if (i < cvt.length)
+ cvt[i] = e1;
+ break;
+
+ case 0x45: // RCVT, Read Control Value Table entry
+ if (stack[sp] < cvt.length)
+ stack[sp] = cvt[stack[sp]];
+ else
+ stack[sp] = 0;
+ break;
+
+ case 0x46: // GC[0], Get Coordinate projected onto the projection vector
+ stack[sp] = getProjection(zp2, stack[sp]);
+ break;
+
+ case 0x47: // GC[1], Get Coordinate projected onto the projection vector
+ stack[sp] = getOriginalProjection(zp2, stack[sp]);
+ break;
+
+ case 0x4B: // MPPEM, Measure Pixels Per EM
+ stack[++sp] = getPixelsPerEM();
+ break;
+
+ case 0x4c: // MPS, Measure Point Size
+ /* FreeType2 returns pixels per em here, because they think that
+ * the point size would be irrelevant in a given font program.
+ * This is extremely surprising, because the appearance of good
+ * fonts _should_ change with point size. For example, a good
+ * font should be wider at small point sizes, and the holes
+ * inside glyphs ("Punzen" in German, I do not know the correct
+ * English expression) should be larger. Note that this change
+ * of appearance is dependent on point size, _not_ the
+ * resolution of the display device.
+ */
+ stack[++sp] = pointSize;
+ break;
+
+ case 0x4f: // DEBUG, DEBUG call
+ sp--;
+ break;
+
+ case 0x50: // LT, Less Than
+ e1 = stack[sp--];
+ stack[sp] = (stack[sp] < e1) ? 1 : 0;
+ break;
+
+ case 0x51: // LTEQ, Greater Than or EQual
+ e1 = stack[sp--];
+ stack[sp] = (stack[sp] <= e1) ? 1 : 0;
+ break;
+
+ case 0x52: // GT, Greater Than
+ e1 = stack[sp--];
+ stack[sp] = (stack[sp] > e1) ? 1 : 0;
+ break;
+
+ case 0x53: // GTEQ, Greater Than or EQual
+ e1 = stack[sp--];
+ stack[sp] = (stack[sp] >= e1) ? 1 : 0;
+ break;
+
+ case 0x54: // EQ, EQual
+ e1 = stack[sp--];
+ stack[sp] = (stack[sp] == e1) ? 1 : 0;
+ break;
+
+ case 0x55: // NEQ, Not EQual
+ e1 = stack[sp--];
+ stack[sp] = (stack[sp] != e1) ? 1 : 0;
+ break;
+
+ case 0x58: // IF, IF test
+ if (stack[sp--] == 0)
+ skipAfter(inst,
+ /* look for: ELSE */ 0x1B,
+ /* look for: EIF */ 0x59,
+ /* illegal: -- */ -1,
+ /* illegal: -- */ -1,
+ /* handle nested if clauses */ true);
+ break;
+
+ case 0x59: // EIF, End IF
+ // Do nothing.
+ break;
+
+ case 0x5A: // AND
+ e1 = stack[sp--];
+ stack[sp] = ((e1 != 0) && (stack[sp] != 0)) ? 1 : 0;
+ break;
+
+ case 0x5B: // OR
+ e1 = stack[sp--];
+ stack[sp] = ((e1 != 0) || (stack[sp] != 0)) ? 1 : 0;
+ break;
+
+ case 0x5C: // NOT
+ stack[sp] = (stack[sp] != 0) ? 0 : 1;
+ break;
+
+ case 0x5e: // SDB, Set Delta Base in the graphics state
+ deltaBase = stack[sp--];
+ break;
+
+ case 0x5f: // SDS, Set Delta Shift in the graphics state
+ deltaShift = stack[sp--];
+ break;
+
+ case 0x60: // ADD
+ e1 = stack[sp--];
+ stack[sp] += e1;
+ break;
+
+ case 0x61: // SUB, SUBtract
+ e1 = stack[sp--];
+ stack[sp] -= e1;
+ break;
+
+ case 0x62: // DIV, DIVide
+ e1 = stack[sp--];
+ stack[sp] = Fixed.div(e1, stack[sp]);
+ break;
+
+ case 0x63: // MUL, MULtiply
+ e1 = stack[sp--];
+ stack[sp] = Fixed.mul(e1, stack[sp]);
+ break;
+
+ case 0x64: // ABS, ABSolute value
+ stack[sp] = Math.abs(stack[sp]);
+ break;
+
+ case 0x65: // NEG, NEGate
+ stack[sp] = -stack[sp];
+ break;
+
+ case 0x66: // FLOOR
+ stack[sp] = Fixed.floor(stack[sp]);
+ break;
+
+ case 0x67: // CEILING
+ stack[sp] = Fixed.ceil(stack[sp]);
+ break;
+
+ case 0x68: // ROUND[0] -- round grey distance
+ stack[sp] = round(stack[sp], /* no engine compensation */ 0);
+ break;
+
+ case 0x69: // ROUND[1] -- round black distance
+ stack[sp] = round(stack[sp], -engineCompensation);
+ break;
+
+ case 0x6a: // ROUND[2] -- round white distance
+ stack[sp] = round(stack[sp], engineCompensation);
+ break;
+
+ case 0x6b: // ROUND[3] -- round distance (not yet defined)
+ stack[sp] = round(stack[sp], /* no engine compensation */ 0);
+ break;
+
+ case 0x6c: // NROUND[0] -- compensate grey distance
+ stack[sp] = nround(stack[sp], 0);
+ break;
+
+ case 0x6d: // NROUND[1] -- compensate black distance
+ stack[sp] = nround(stack[sp], -engineCompensation);
+ break;
+
+ case 0x6e: // NROUND[2] -- compensate white distance
+ stack[sp] = nround(stack[sp], engineCompensation);
+ break;
+
+ case 0x6f: // NROUND[3] -- compensate distance (not yet defined)
+ stack[sp] = nround(stack[sp], 0);
+ break;
+
+ case 0x70: // WCVTF, Write Control Value Table in Funits
+ e1 = stack[sp--];
+ cvt[stack[sp--]] = e1 * getPixelsPerEM();
+ break;
+
+ case 0x73: // DELTAC1, DELTA exception C1
+ count = stack[sp--];
+ sp -= 2 * count;
+ deltaC(stack, sp + 1, count, 0);
+ break;
+
+ case 0x74: // DELTAC2, DELTA exception C2
+ count = stack[sp--];
+ sp -= 2 * count;
+ deltaC(stack, sp + 1, count, 16);
+ break;
+
+ case 0x75: // DELTAC3, DELTA exception C3
+ count = stack[sp--];
+ sp -= 2 * count;
+ deltaC(stack, sp + 1, count, 32);
+ break;
+
+ case 0x76: // SROUND, Super ROUND
+ setRoundingMode(Fixed.ONE, stack[sp--]);
+ break;
+
+ case 0x77: // S45ROUND, Super ROUND 45 degrees
+ setRoundingMode(/* sqrt(2)/2 */ 0x2d, stack[sp--]);
+ break;
+
+ case 0x78: // JROT, Jump Relative On True
+ e1 = stack[sp--];
+ i = inst.position() - 1 + stack[sp--];
+ if (e1 != 0)
+ inst.position(i);
+ break;
+
+ case 0x79: // JROF, Jump Relative On False
+ e1 = stack[sp--];
+ i = inst.position() - 1 + stack[sp--];
+ if (e1 == 0)
+ inst.position(i);
+ break;
+
+ case 0x7a: // ROFF, Round OFF
+ roundPeriod = 0;
+ break;
+
+ case 0x7c: // RUTG, Round Up To Grid
+ setRoundingMode(Fixed.ONE, 0x40);
+ break;
+
+ case 0x7d: // RDTG, Round Down To Grid
+ setRoundingMode(Fixed.ONE, 0x40);
+ roundThreshold = 0;
+ break;
+
+ case 0x7e: // SANGW, Set ANGle Weight (no-op according to TrueType spec)
+ case 0x7f: // AA, Adjust Angle (no-op according to TrueType spec)
+ sp--;
+ break;
+
+ case 0x85: // SCANCTRL, SCAN conversion ConTRoL
+ e1 = stack[sp--];
+ int ppemThreshold = e1 & 255;
+ scanControl = false;
+ boolean ppemCondition = (ppemThreshold == 255)
+ || ((ppemThreshold != 0) && (getPixelsPerEM() > ppemThreshold));
+ if (((e1 & (1<<8)) != 0) && ppemCondition)
+ scanControl = true;
+ if (((e1 & (1<<9)) != 0) && isRotated())
+ scanControl = true;
+ if (((e1 & (1<<10)) != 0) && isStretched())
+ scanControl = true;
+ if (((e1 & (1<<11)) != 0) && !ppemCondition)
+ scanControl = false;
+ if (((e1 & (1<<12)) != 0) && !isRotated())
+ scanControl = false;
+ if (((e1 & (1<<13)) != 0) && !isStretched())
+ scanControl = false;
+ break;
+
+ case 0x88: // GETINFO, GET INFOrmation
+ e1 = 0;
+ if ((stack[sp] & 1) != 0) // ask for rasterizer version
+ e1 |= 35; // "Microsoft Rasterizer version 1.7" (grayscale-capable)
+ if (((stack[sp] & 2) != 0) && isRotated())
+ e1 |= 1 << 8; // bit 8: glyph has been rotated
+ if (((stack[sp] & 4) != 0) && isStretched())
+ e1 |= 1 << 9; // bit 9: glyph has been stretched
+ if (((stack[sp] & 32) != 0) && antialiased)
+ e1 |= 1 << 12; // bit 12: antialiasing is active
+ stack[sp] = e1;
+ break;
+
+ case 0x8a: // ROLL, ROLL the top three stack elements
+ e1 = stack[sp - 2];
+ stack[sp - 2] = stack[sp - 1];
+ stack[sp - 1] = stack[sp];
+ stack[sp] = e1;
+ break;
+
+ case 0x8b: // MAX, MAXimum of top two stack elements
+ e1 = stack[sp--];
+ stack[sp] = Math.max(e1, stack[sp]);
+ break;
+
+ case 0x8c: // MIN, MINimum of top two stack elements
+ e1 = stack[sp--];
+ stack[sp] = Math.min(e1, stack[sp]);
+ break;
+
+ case 0x8d: // SCANTYPE
+ scanType = stack[sp--];
+ break;
+
+ case 0x8e: // INSTCTRL, INSTRuction execution ConTRoL
+ e1 = stack[sp--]; // selector
+ e2 = stack[sp--]; // value
+ switch (e1)
+ {
+ case 1:
+ executeGlyphInstructions = (e2 == 0);
+ break;
+
+ case 2:
+ ignoreCVTProgram = (e2 != 0);
+ break;
+ }
+ break;
+
+ case 0xb0: // PUSHB[0]
+ case 0xb1: // PUSHB[1]
+ case 0xb2: // PUSHB[2]
+ case 0xb3: // PUSHB[3]
+ case 0xb4: // PUSHB[4]
+ case 0xb5: // PUSHB[5]
+ case 0xb6: // PUSHB[6]
+ case 0xb7: // PUSHB[7]
+ count = bcode - 0xb0 + 1;
+ for (i = 0; i < count; i++)
+ stack[++sp] = inst.get() & 0xff;
+ break;
+
+ case 0xb8: // PUSHW[0]
+ case 0xb9: // PUSHW[1]
+ case 0xba: // PUSHW[2]
+ case 0xbb: // PUSHW[3]
+ case 0xbc: // PUSHW[4]
+ case 0xbd: // PUSHW[5]
+ case 0xbe: // PUSHW[6]
+ case 0xbf: // PUSHW[7]
+ count = bcode - 0xb8 + 1;
+ for (i = 0; i < count; i++)
+ stack[++sp] = inst.getShort();
+ break;
+
+ // MIRPxxxx, Move Indirect Relative Point
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3:
+ case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb:
+ case 0xec: case 0xed: case 0xee: case 0xef:
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3:
+ case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb:
+ case 0xfc: case 0xfd: case 0xfe: case 0xff:
+ e1 = stack[sp--];
+ handleMIRP(bcode, /* point */ e1, /* cvtIndex */ stack[sp--]);
+ break;
+
+ default:
+ throw new IllegalStateException();
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Sets the rounding mode.
+ *
+ * @param period the grid period in fixed-point notation, such as
+ * {@link Fixed#ONE} for the <code>SROUND</code> instruction or
+ * <code>sqrt(2)/2</code> for the <code>S45ROUND</code> instruction.
+ *
+ * @param mode a byte whose bits are set according to the TrueType
+ * specification for SROUND and S45ROUND parameters.
+ */
+ private void setRoundingMode(int period, int mode)
+ {
+ /* Set the period. */
+ switch ((mode & 0xc0) >> 6)
+ {
+ case 0:
+ roundPeriod = period / 2;
+ break;
+
+ case 2:
+ roundPeriod = period * 2;
+ break;
+
+ default:
+ roundPeriod = period;
+ break;
+ }
+
+ /* Set the phase. */
+ switch ((mode & 0x30) >> 4)
+ {
+ case 0:
+ roundPhase = 0;
+ break;
+
+ case 1:
+ roundPhase = roundPeriod >> 2; // period/4
+ break;
+
+ case 2:
+ roundPhase = roundPeriod >> 1; // period/2
+ break;
+
+ case 3:
+ roundPhase = (roundPeriod >> 1) + (roundPeriod >> 2); // period * 3/4
+ break;
+ }
+
+ /* Set the threshold. */
+ int threshold = mode & 0x0f;
+ if (threshold == 0)
+ roundThreshold = roundPeriod - Fixed.ONE;
+ else
+ roundThreshold = ((threshold - 4) * roundPeriod) / 8;
+ }
+
+
+
+ /**
+ * Implements the DELTAC instructions. These instructions check
+ * whether the current number of pixels per em is contained in an
+ * exception table. If it is, a delta value is determined, and the
+ * specified entry in the Control Value Table is modified according
+ * to the delta.
+ *
+ * @param pairs the delta table. Because the delta table is on
+ * the stack, callers usually just want to pass the stack array.
+ *
+ * @param offset the offset of the first pair in <code>pairs</code>.
+ *
+ * @param numPairs the number of pairs.
+ *
+ * @param base 0 for <code>DELTAC1</code>, 16 for <code>DELTAC2</code>,
+ * or 32 for <code>DELTAC2</code>.
+ *
+ * @see <a href=
+ * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC1"
+ * >Apple&#x2019;s documentation for <code>DELTAC1</code></a>, <a href=
+ * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC2"
+ * ><code>DELTAC2</code></a>, and <a href=
+ * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC3"
+ * ><code>DELTAC3</code></a>
+ */
+ private void deltaC(int[] pairs, int offset, int numPairs, int base)
+ {
+ int arg, relativePpem;
+ int ppemTrigger = getPixelsPerEM() - (deltaBase + base);
+ int delta, cvtIndex, rightShift;
+ for (int i = 0; i < numPairs; i++)
+ {
+ arg = pairs[offset + 2 * i];
+ relativePpem = (arg >> 4) & 15;
+ if (relativePpem == ppemTrigger)
+ {
+ delta = (arg & 15) - 8;
+ if (delta >= 0)
+ ++delta;
+
+ rightShift = deltaShift - 6;
+ if (rightShift > 0)
+ delta = delta >> rightShift;
+ else if (rightShift < 0)
+ delta = delta << (-rightShift);
+ cvt[pairs[offset + 2 * i + 1]] += delta;
+
+ break;
+ }
+ }
+ }
+
+
+ private Zone getZone(int zoneNumber)
+ {
+ return (zoneNumber == 0) ? twilightZone : glyphZone;
+ }
+
+
+ /**
+ * Projects the specified vector along the current projection
+ * vector.
+ *
+ * @param x the x component of the input vector, in 26.6 fixed-point
+ * notation.
+ *
+ * @param y the y component of the input vector, in 26.6 fixed-point
+ * notation.
+ *
+ * @return the projected distance, in 26.6 fixed-point notation.
+ */
+ private int getProjection(int x, int y)
+ {
+ return (int) (((((long) x) * projX + ((long) y) * projY)) >> 14);
+ }
+
+
+ /**
+ * Projects the specified vector along the current dual projection
+ * vector.
+ *
+ * @param x the x component of the input vector, in 26.6 fixed-point
+ * notation.
+ *
+ * @param y the y component of the input vector, in 26.6 fixed-point
+ * notation.
+ *
+ * @return the projected distance, in 26.6 fixed-point notation.
+ */
+ private int getDualProjection(int x, int y)
+ {
+ return (int) (((((long) x) * dualX + ((long) y) * dualY)) >> 14);
+ }
+
+
+ private int getProjection(Zone zone, int point)
+ {
+ return getProjection(zone.getX(point), zone.getY(point));
+ }
+
+
+ private int getOriginalProjection(Zone zone, int point)
+ {
+ return getDualProjection(zone.getOriginalX(point),
+ zone.getOriginalY(point));
+ }
+
+
+ private void handleISECT(int a0, int a1, int b0, int b1, int p)
+ {
+ System.out.println("FIXME: Unimplemented ISECT " + p);
+ }
+
+
+ private static int muldiv(int a, int b, int c)
+ {
+ int s;
+ s = a; a = Math.abs(a);
+ s ^= b; b = Math.abs(b);
+ s ^= c; c = Math.abs(c);
+ a = (int) ((((long) a) * b + (c>>1)) / c);
+ return (s < 0) ? -a : a;
+ }
+
+
+ private int getFreeDotProj()
+ {
+ int result;
+
+ result = ((((int) projX) * freeX) << 2)
+ + ((((int) projY) * freeY) << 2);
+
+ /* FIXME: This seems somewhat bogus. Need to contact the
+ * developers of FreeType.
+ */
+ if (Math.abs(result) < 0x4000000)
+ result = 0x40000000;
+ return result;
+ }
+
+
+ private void movePoint(Zone zone, int point, int distance)
+ {
+ int freeDotProj = getFreeDotProj();
+ int c;
+
+ if (freeX != 0)
+ {
+ c = zone.getX(point);
+ c += muldiv(distance, freeX << 16, freeDotProj);
+ zone.setX(point, c, /* touch */ true);
+ }
+
+ if (freeY != 0)
+ {
+ c = zone.getY(point);
+ c += muldiv(distance, freeY << 16, freeDotProj);
+ zone.setY(point, c, /* touch */ true);
+ }
+
+ if (TRACE_EXECUTION)
+ {
+ System.out.println("point[" + point + "] moved to "
+ + Fixed.toString(zone.getX(point),
+ zone.getY(point)));
+ dumpVectors();
+ }
+ }
+
+ private void dumpVectors()
+ {
+ System.out.println(" proj=" + Fixed.toString(projX>>8, projY>>8)
+ + ", free=" + Fixed.toString(freeX>>8, freeY>>8));
+ }
+
+
+ private void handleIP()
+ {
+ // Implementation taken from FreeType.
+ int p, org_a, org_b, org_x, cur_a, cur_b, cur_x, distance;
+ int freeDotProj;
+
+ org_a = getOriginalProjection(zp0, rp1);
+ cur_a = getProjection(zp0, rp1);
+
+ org_b = getOriginalProjection(zp1, rp2);
+ cur_b = getProjection(zp1, rp2);
+
+ while (--loop >= 0)
+ {
+ p = stack[sp--];
+ org_x = getOriginalProjection(zp2, p);
+ cur_x = getProjection(zp2, p);
+
+ if (((org_a <= org_b) && (org_x <= org_a))
+ || ((org_a > org_b) && (org_x >= org_a)))
+ distance = (cur_a - org_a) + (org_x - cur_x);
+ else if (((org_a <= org_b) && (org_x >= org_b))
+ || ((org_a > org_b) && (org_x < org_b)))
+ distance = (cur_b - org_b) + (org_x - cur_x);
+ else
+ distance = muldiv(cur_b - cur_a, org_x - org_a, org_b - org_a)
+ + (cur_a - cur_x);
+ movePoint(zp2, p, distance);
+ }
+ loop = 1;
+ }
+
+
+ private void handleMDAP(int point, boolean round)
+ {
+ System.out.println("FIXME: Unimplemented MDAP: point "
+ + point + "/" + zp0);
+ }
+
+
+ private void handleMIAP(int cvtIndex, int point, boolean round)
+ {
+ int previousPos, pos;
+
+ previousPos = getProjection(zp0, point);
+ pos = cvt[cvtIndex];
+
+ if (round)
+ {
+ if (Math.abs(pos - previousPos) > cvtCutIn)
+ pos = previousPos;
+ pos = round(pos, /* no engine compensation */ 0);
+ }
+ movePoint(zp0, point, pos - previousPos);
+ rp0 = rp1 = point;
+ }
+
+
+ private void handleMIRP(int bcode, int point, int cvtIndex)
+ {
+ System.out.println("FIXME: Unimplemented mirp " + point + ", " + cvtIndex);
+ }
+
+
+
+ private int round(int distance, int compensation)
+ {
+ int result;
+
+ if (roundPeriod == 0)
+ return nround(distance, compensation);
+
+ if (distance >= 0)
+ {
+ result = distance + compensation - roundPhase + roundThreshold;
+ result &= -roundPeriod; // truncate to the next lowest periodic value
+ return Math.max(result, 0) + roundPhase;
+ }
+ else
+ {
+ result = compensation - roundPhase + roundThreshold - distance;
+ result &= -roundPeriod;
+ return Math.max(-result, 0) - roundPhase;
+ }
+ }
+
+
+ private static int nround(int distance, int compensation)
+ {
+ if (distance >= 0)
+ return Math.max(distance + compensation, 0);
+ else
+ return Math.min(distance - compensation, 0);
+ }
+
+
+ /**
+ * Determines whether the current glyph is rotated.
+ *
+ * @return <code>false</code> if the shearing factors for the
+ * <i>x</i> and <i>y</i> axes are zero; <code>true</code> if they
+ * are non-zero.
+ */
+ private boolean isRotated()
+ {
+ return (shearX != 0) || (shearY != 0);
+ }
+
+
+ /**
+ * Determines whether the current glyph is stretched.
+ *
+ * @return <code>false</code> if the scaling factors for the
+ * <i>x</i> and <i>y</i> axes are are equal; <code>true</code> if
+ * they differ.
+ */
+ private boolean isStretched()
+ {
+ return scaleX != scaleY;
+ }
+
+
+ /**
+ * Returns how many pixels there are per EM, in direction of the
+ * current projection vector. The result is a normal integer,
+ * not a Fixed.
+ */
+ private int getPixelsPerEM()
+ {
+ if (cachedPixelsPerEM == 0)
+ {
+ cachedPixelsPerEM = Fixed.intValue(Fixed.vectorLength(
+ applyCTM_x(projX >> 8, projY >> 8),
+ applyCTM_y(projX >> 8, projY >> 8)));
+ }
+
+ return cachedPixelsPerEM;
+ }
+
+
+ private void setProjectionVector(short x, short y)
+ {
+ if (PATENTED_HINTING)
+ {
+ if ((x != projX) || (y != projY))
+ cachedPixelsPerEM = 0;
+
+ projX = x;
+ projY = y;
+ }
+ }
+
+
+ private void setFreedomVector(short x, short y)
+ {
+ if (PATENTED_HINTING)
+ {
+ freeX = x;
+ freeY = y;
+ }
+ }
+
+
+ private void setDualVector(short x, short y)
+ {
+ if (PATENTED_HINTING)
+ {
+ dualX = x;
+ dualY = y;
+ }
+ }
+
+
+ private int applyCTM_x(int x, int y)
+ {
+ return (int) (((long) scaleX * x + (long) shearX * y) >> 6);
+ }
+
+ private int applyCTM_y(int x, int y)
+ {
+ return (int) (((long) shearY * x + (long) scaleY * y) >> 6);
+ }
+
+
+ private static final String[] INST_NAME =
+ {
+ /* 00 */ "SVTCA[0]", "SVTCA[1]", "SPVTCA[0]", "SPVTCA[1]",
+ /* 04 */ "INST_04", "INST_05", "INST_06", "INST_07",
+ /* 08 */ "INST_08", "INST_09", "INST_0A", "INST_0B",
+ /* 0c */ "GPV", "GFV", "INST_0E", "ISECT",
+ /* 10 */ "SRP0", "SRP1", "SRP2", "SZP0",
+ /* 14 */ "SZP1", "SZP2", "SZPS", "SLOOP",
+ /* 18 */ "RTG", "RTHG", "SMD", "ELSE",
+ /* 1c */ "JMPR", "SCVTCI", "INST_1E", "SSW",
+ /* 20 */ "DUP", "POP", "CLEAR", "SWAP",
+ /* 24 */ "DEPTH", "CINDEX", "MINDEX", "INST_27",
+ /* 28 */ "INST_28", "INST_29", "LOOPCALL", "CALL",
+ /* 2c */ "FDEF", "ENDF", "MDAP[0]", "MDAP[1]",
+ /* 30 */ "IUP[0]", "IUP[1]", "SHP[0]", "SHP[1]",
+ /* 34 */ "INST_34", "INST_35", "INST_36", "INST_37",
+ /* 38 */ "INST_38", "IP", "INST_3A", "INST_3B",
+ /* 3c */ "INST_3C", "RTDG", "MIAP[0]", "MIAP[1]",
+ /* 40 */ "NPUSHB", "NPUSHW", "WS", "RS",
+ /* 44 */ "WCVTP", "RCVT", "GC[0]", "GC[1]",
+ /* 48 */ "INST_48", "INST_49", "INST_4A", "MPPEM",
+ /* 4c */ "MPS", "FLIPON", "FLIPOFF", "DEBUG",
+ /* 50 */ "LT", "LTEQ", "GT", "GTEQ",
+ /* 54 */ "EQ", "NEQ", "INST_56", "INST_57",
+ /* 58 */ "IF", "EIF", "AND", "OR",
+ /* 5c */ "NOT", "INST_5D", "SDB", "SDS",
+ /* 60 */ "ADD", "SUB", "DIV", "MUL",
+ /* 64 */ "ABS", "NEG", "FLOOR", "CEILING",
+ /* 68 */ "ROUND[0]", "ROUND[1]", "ROUND[2]", "ROUND[3]",
+ /* 6c */ "NROUND[0]", "NROUND[1]", "NROUND[2]", "NROUND[3]",
+ /* 70 */ "WCVTF", "INST_71", "INST_72", "DELTAC1",
+ /* 74 */ "DELTAC2", "DELTAC3", "SROUND", "S45ROUND",
+ /* 78 */ "JROT", "JROF", "ROFF", "INST_7B",
+ /* 7c */ "RUTG", "RDTG", "SANGW", "AA",
+ /* 80 */ "FLIPPT", "FLIPRGON", "FLIPRGOFF", "INST_83",
+ /* 84 */ "INST_84", "SCANCTRL", "INST_86", "INST_87",
+ /* 88 */ "GETINFO", "INST_89", "ROLL", "MAX",
+ /* 8c */ "MIN", "SCANTYPE", "INSTCTRL", "INST_8F",
+ /* 90 */ "INST_90", "INST_91", "INST_92", "INST_93",
+ /* 94 */ "INST_94", "INST_95", "INST_96", "INST_97",
+ /* 98 */ "INST_98", "INST_99", "INST_9A", "INST_9B",
+ /* 9c */ "INST_9C", "INST_9D", "INST_9E", "INST_9F",
+ /* a0 */ "INST_A0", "INST_A1", "INST_A2", "INST_A3",
+ /* a4 */ "INST_A4", "INST_A5", "INST_A6", "INST_A7",
+ /* a8 */ "INST_A8", "INST_A9", "INST_AA", "INST_AB",
+ /* ac */ "INST_AC", "INST_AD", "INST_AE", "INST_AF",
+ /* b0 */ "PUSHB[0]", "PUSHB[1]", "PUSHB[2]", "PUSHB[3]",
+ /* b4 */ "PUSHB[4]", "PUSHB[5]", "PUSHB[6]", "PUSHB[7]",
+ /* b8 */ "PUSHW[0]", "PUSHW[1]", "PUSHW[2]", "PUSHW[3]",
+ /* bc */ "PUSHW[4]", "PUSHW[5]", "PUSHW[6]", "PUSHW[7]",
+ /* c0 */ "INST_C0", "INST_C1", "INST_C2", "INST_C3",
+ /* c4 */ "INST_C4", "INST_C5", "INST_C6", "INST_C7",
+ /* c8 */ "INST_C8", "INST_C9", "INST_CA", "INST_CB",
+ /* cc */ "INST_CC", "INST_CD", "INST_CE", "INST_CF",
+ /* d0 */ "INST_D0", "INST_D1", "INST_D2", "INST_D3",
+ /* d4 */ "INST_D4", "INST_D5", "INST_D6", "INST_D7",
+ /* d8 */ "INST_D8", "INST_D9", "INST_DA", "INST_DB",
+ /* dc */ "INST_DC", "INST_DD", "INST_DE", "INST_DF",
+ /* e0 */ "MIRP00000", "MIRP00001", "MIRP00010", "MIRP00011",
+ /* e4 */ "MIRP00100", "MIRP00101", "MIRP00110", "MIRP00111",
+ /* e8 */ "MIRP01000", "MIRP01001", "MIRP01010", "MIRP01011",
+ /* ec */ "MIRP01100", "MIRP01101", "MIRP01110", "MIRP01111",
+ /* f0 */ "MIRP10000", "MIRP10001", "MIRP10010", "MIRP10011",
+ /* f4 */ "MIRP10100", "MIRP10101", "MIRP10110", "MIRP10111",
+ /* f8 */ "MIRP11000", "MIRP11001", "MIRP11010", "MIRP11011",
+ /* fc */ "MIRP11100", "MIRP11101", "MIRP11110", "MIRP11111"
+ };
+}
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java
new file mode 100644
index 000000000..b0559e0e3
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java
@@ -0,0 +1,291 @@
+/* Zone.java -- A collection of points with some additional information.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.java.awt.font.opentype.truetype;
+
+import gnu.java.awt.font.FontDelegate;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.PathIterator;
+
+
+/**
+ * A collection of points with some additional information.
+ */
+public final class Zone
+{
+ private Point[] points;
+ private int numPoints;
+
+ public double scaleX, scaleY, shearX, shearY;
+
+ public Zone(int maxNumPoints)
+ {
+ points = new Point[maxNumPoints];
+ }
+
+ public int getCapacity()
+ {
+ return points.length;
+ }
+
+
+ public int getSize()
+ {
+ return numPoints;
+ }
+
+
+ public int getX(int point)
+ {
+ return getX(point, FontDelegate.FLAG_FITTED);
+ }
+
+ public int getX(int point, int flags)
+ {
+ int x;
+ if ((flags & FontDelegate.FLAG_FITTED) != 0)
+ x = points[point].x;
+ else
+ x = points[point].scaledX;
+ return x;
+ }
+
+
+ public void setX(int point, int value, boolean touch)
+ {
+ points[point].scaledX = value;
+ points[point].x = value;
+ if (touch)
+ points[point].flags |= Point.FLAG_TOUCHED_X;
+ }
+
+
+ public void setY(int point, int value, boolean touch)
+ {
+ points[point].scaledY = value;
+ points[point].y = value;
+ if (touch)
+ points[point].flags |= Point.FLAG_TOUCHED_Y;
+ }
+
+ public int getY(int point)
+ {
+ return getY(point, FontDelegate.FLAG_FITTED);
+ }
+
+ public int getY(int point, int flags)
+ {
+ int y;
+ if ((flags & FontDelegate.FLAG_FITTED) != 0)
+ y = points[point].y;
+ else
+ y = points[point].scaledY;
+ return y;
+ }
+
+
+ public int getOriginalX(int point)
+ {
+ return points[point].origX;
+ }
+
+
+ public int getOriginalY(int point)
+ {
+ return points[point].origY;
+ }
+
+
+ public void setOriginalX(int point, int x)
+ {
+ points[point].origX = x;
+ }
+
+ public void setOriginalY(int point, int y)
+ {
+ points[point].origY = y;
+ }
+
+ public void setNumPoints(int numPoints)
+ {
+ for (int i = 0; i < numPoints; i++)
+ points[i] = new Point();
+ this.numPoints = numPoints;
+ }
+
+
+ public boolean isOnCurve(int point)
+ {
+ return (points[point].flags & Point.FLAG_ON_CURVE) != 0;
+ }
+
+
+ public void setOnCurve(int point, boolean onCurve)
+ {
+ if (onCurve)
+ points[point].flags |= Point.FLAG_ON_CURVE;
+ else
+ points[point].flags &= ~Point.FLAG_ON_CURVE;
+ }
+
+
+ public boolean isContourEnd(int point)
+ {
+ return (points[point].flags & Point.FLAG_CONTOUR_END) != 0;
+ }
+
+
+ public void setContourEnd(int point, boolean segEnd)
+ {
+ if (segEnd)
+ points[point].flags |= Point.FLAG_CONTOUR_END;
+ else
+ points[point].flags &= ~Point.FLAG_CONTOUR_END;
+ }
+
+
+
+
+ void transform(double pointSize, AffineTransform deviceTransform,
+ int unitsPerEm, int preTranslateX, int preTranslateY)
+ {
+ double factor;
+
+ factor = pointSize / (double) unitsPerEm;
+ scaleX = deviceTransform.getScaleX() * factor;
+ scaleY = deviceTransform.getScaleY() * factor;
+ shearX = deviceTransform.getShearX() * factor;
+ shearY = deviceTransform.getShearY() * factor;
+
+ for (int i = 0; i < numPoints; i++)
+ {
+ int x = points[i].origX + preTranslateX;
+ int y = points[i].origY + preTranslateY;
+
+ points[i].scaledX = points[i].x = Fixed.valueOf(scaleX * x
+ + shearX * y);
+ points[i].scaledY = points[i].y = Fixed.valueOf(shearY * x
+ + scaleY * y);
+ }
+ }
+
+
+
+ void combineWithSubGlyph(Zone zone, int numPhantomPoints)
+ {
+ int offset = this.numPoints - numPhantomPoints;
+ int count = zone.numPoints;
+ System.arraycopy(zone.points, 0, this.points, offset, count);
+ this.numPoints += count - numPhantomPoints;
+ }
+
+
+ private void dump()
+ {
+ for (int i = 0; i < numPoints; i++)
+ {
+ System.out.print(" " + i + ": ");
+ System.out.print(Fixed.toString(points[i].scaledX, points[i].scaledY));
+ System.out.print(' ');
+ System.out.print(Fixed.toString(points[i].origX, points[i].origY));
+ System.out.print(' ');
+ if (isOnCurve(i))
+ System.out.print('.');
+ else
+ System.out.print('c');
+ if (isContourEnd(i))
+ System.out.print('E');
+ System.out.println();
+ if (isContourEnd(i))
+ System.out.println();
+ }
+ }
+
+
+ public PathIterator getPathIterator(int type)
+ {
+ return new ZonePathIterator(this, type);
+ }
+
+
+ public GeneralPath getPath(int type)
+ {
+ GeneralPath p = new GeneralPath(GeneralPath.WIND_NON_ZERO, numPoints);
+ p.append(getPathIterator(type), /* connect */ false);
+ return p;
+ }
+
+ /**
+ * Returns the number of contours in this outline.
+ *
+ * @return the number of contours in this outline
+ */
+ public int getNumContours()
+ {
+ int num = 0;
+ for (int i = 0; i < numPoints; i++)
+ {
+ if (isContourEnd(i))
+ num++;
+ }
+ return num;
+ }
+
+ public int getContourEnd(int n)
+ {
+ int idx = -1;
+ int num = 0;
+ for (int i = 0; i < numPoints; i++)
+ {
+ if (isContourEnd(i))
+ {
+ idx = i;
+ if (num == n)
+ break;
+ num++;
+ }
+ }
+ return idx;
+ }
+
+ public Point[] getPoints()
+ {
+ return points;
+ }
+}
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java
new file mode 100644
index 000000000..f4534f36f
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java
@@ -0,0 +1,393 @@
+/* ZonePathIterator.java -- A PathIterator over glyph zones.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.java.awt.font.opentype.truetype;
+
+import java.awt.geom.PathIterator;
+
+
+/**
+ * A PathIterator that enumerates the non-phantom points in a zone.
+ *
+ * <p><b>Lack of thread safety:</b> Instances of this class are
+ * <i>not</i> safe to access from multiple concurrent threads.
+ *
+ * @see Zone
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+final class ZonePathIterator
+ implements PathIterator
+{
+ /**
+ * If <code>state</code> has this value, <code>currentSegment</code>
+ * will emit a <code>SEG_LINETO</code> or <code>SEG_QUADTO</code> segment
+ * to the current point. For a discussion of subtleties of on-curve
+ * and off-curve points, please refer to the documentation for
+ * {@link #getSegment}.
+ */
+ private static final int EMIT_SEGMENT = 0;
+
+
+ /**
+ * If <code>state</code> has this value, <code>currentSegment</code>
+ * will emit a <code>SEG_CLOSE</code> in order to close the sub-path
+ * for the current contour.
+ */
+ private static final int EMIT_CLOSE = 1;
+
+
+ /**
+ * If <code>state</code> has this value, <code>currentSegment</code>
+ * will emit a <code>SEG_MOVETO</code> segment to the first point in
+ * the current contour. If the first point is off-curve, a suitable
+ * on-curve point is calculated.
+ *
+ * @see #getStartSegment
+ */
+ private static final int EMIT_MOVETO = 2;
+
+
+ /**
+ * The state of the iterator, which is one of
+ * <code>EMIT_SEGMENT</code>, <code>EMIT_CLOSE</code>, or
+ * <code>EMIT_MOVETO</code>.
+ */
+ private int state;
+
+
+
+ /**
+ * The zone whose segments are enumerated by this iterator.
+ */
+ private Zone zone;
+
+
+ /**
+ * The total number of points in the zone, not including the four
+ * phantom points at its end.
+ */
+ private int numPoints;
+
+
+ /**
+ * The number of the current point.
+ */
+ private int curPoint;
+
+
+ /**
+ * The number of the first point in the current contour.
+ */
+ private int contourStart;
+
+
+ private int type;
+
+ /**
+ * Constructs a ZonePathIterator for the specified zone.
+ *
+ * @param zone the zone whose segments will be enumerated
+ * by this iterator.
+ */
+ ZonePathIterator(Zone zone, int t)
+ {
+ this.zone = zone;
+ type = t;
+ numPoints = zone.getSize() - /* four phantom points */ 4;
+
+ // The first segment that needs to be emitted is a SEG_MOVETO.
+ state = EMIT_MOVETO;
+ }
+
+
+ /**
+ * Returns the winding rule. TrueType glyphs always use the non-zero
+ * winding rule, so this method will always return {@link
+ * PathIterator#WIND_NON_ZERO}.
+ */
+ public int getWindingRule()
+ {
+ return PathIterator.WIND_NON_ZERO;
+ }
+
+
+
+ public boolean isDone()
+ {
+ return (state != EMIT_CLOSE) && (curPoint >= numPoints);
+ }
+
+
+ public void next()
+ {
+ boolean onCurve;
+
+ /* If the current point is the end of a segment, and no SEG_CLOSE
+ * has been emitted yet, this will be the next segment.
+ */
+ if (zone.isContourEnd(curPoint) && (state != EMIT_CLOSE))
+ {
+ state = EMIT_CLOSE;
+ return;
+ }
+
+ /* If the previously emitted segment was a SEG_CLOSE, we are now
+ * at the beginning of a new contour.
+ */
+ if (state == EMIT_CLOSE)
+ {
+ contourStart = ++curPoint;
+ state = EMIT_MOVETO;
+ return;
+ }
+
+ onCurve = zone.isOnCurve(curPoint);
+
+ /* If the last segment was a moveto, and the current point
+ * (which is the first point in the contour) is off-curve,
+ * we need to emit a quadto segment for the first point.
+ */
+ if ((state == EMIT_MOVETO) && !onCurve)
+ {
+ state = EMIT_SEGMENT;
+ return;
+ }
+
+
+ curPoint++;
+
+ /* If the last point has been off-curve, and the now current
+ * point is on-curve, the last segment was a quadto that
+ * had the now current point at its end. In this case, we can
+ * skip a segment.
+ */
+ if (!onCurve && zone.isOnCurve(curPoint))
+ {
+ /* But if the skipped point is the end of a contour, we must not
+ * skip the SEG_CLOSE. An example where this matters is the 'o'
+ * glyph in the Helvetica font face that comes with MacOS X
+ * 10.1.5.
+ */
+ if (zone.isContourEnd(curPoint))
+ {
+ state = EMIT_CLOSE;
+ return;
+ }
+
+ curPoint++;
+ }
+
+ state = EMIT_SEGMENT;
+ }
+
+
+ /**
+ * Determines the successor of the current point in the current
+ * contour. The successor of the last point in a contour is the
+ * start of that contour.
+ *
+ * @return the number of the point that follows the current point in
+ * the same contour.
+ */
+ private int getSuccessor(int p)
+ {
+ if (zone.isContourEnd(p))
+ return contourStart;
+ else
+ return p + 1;
+ }
+
+
+
+ /**
+ * Retrieves the current path segment using single-precision
+ * coordinate values.
+ */
+ public int currentSegment(float[] coords)
+ {
+ switch (state)
+ {
+ case EMIT_CLOSE:
+ return PathIterator.SEG_CLOSE;
+
+ case EMIT_MOVETO:
+ return getStartSegment(curPoint, coords);
+
+ default:
+ return getSegment(curPoint, coords);
+ }
+ }
+
+
+ /**
+ * A helper array that is used by {@link
+ * #currentSegment(double[])}.
+ */
+ float[] floats;
+
+
+ /**
+ * Retrieves the current path segment using double-precision
+ * coordinate values.
+ */
+ public int currentSegment(double[] coords)
+ {
+ if (floats == null)
+ floats = new float[6];
+ int result;
+
+ result = currentSegment(floats);
+ for (int i = 0; i < 6; i++)
+ coords[i] = floats[i];
+ return result;
+ }
+
+
+ /**
+ * Returns the segment for the specified point.
+ *
+ * <p><img src="doc-files/ZonePathIterator-1.png" width="426"
+ * height="194" alt="An example curve" /></p>
+ *
+ * <p>If <code>cur</code> is an on-curve point, the returned segment
+ * is a straight line to <code>cur</code>. In the illustration, this
+ * would be the case for <code>cur = 4</code>.</p>
+ *
+ * <p>If <code>cur</code> is an off-curve point, and
+ * <code>cur</code>&#x2019;s successor <code>succ</code> is also
+ * off-curve, the returned segment is a quadratic B&eacute;zier
+ * spline whose control point is <code>cur</code>, and whose end
+ * point is located at the middle of the line connecting
+ * <code>cur</code> and <code>succ</code>. In the illustration,
+ * this would be the case for <code>cur = 5</code>.</p>
+ *
+ * <p>If <code>cur</code> is an off-curve point, and
+ * <code>cur</code>&#x2019;s successor <code>succ</code> is
+ * on-curve, the returned segment is a quadratic B&eacute;zier
+ * spline whose control point is <code>cur</code>, and whose end
+ * point is <code>succ</code>. In the illustration, this would
+ * be the case for <code>cur = 6</code>.</p>
+ *
+ * @return either <code>PathIterator.SEG_LINETO</code> or
+ * <code>PathIterator.SEG_QUADTO</code>.
+ */
+ private int getSegment(int cur, float[] coords)
+ {
+ int curX, curY;
+ int succ, succX, succY;
+
+ curX = zone.getX(cur, type);
+ curY = zone.getY(cur, type);
+ coords[0] = Fixed.floatValue(curX);
+ coords[1] = Fixed.floatValue(curY);
+
+ if (zone.isOnCurve(cur))
+ return PathIterator.SEG_LINETO;
+
+ succ = getSuccessor(cur);
+ succX = zone.getX(succ, type);
+ succY = zone.getY(succ, type);
+
+ if (zone.isOnCurve(succ))
+ {
+ coords[2] = Fixed.floatValue(succX);
+ coords[3] = Fixed.floatValue(succY);
+ }
+ else
+ {
+ coords[2] = Fixed.floatValue((curX + succX) / 2);
+ coords[3] = Fixed.floatValue((curY + succY) / 2);
+ }
+ return PathIterator.SEG_QUADTO;
+ }
+
+
+ /**
+ * Returns the start segment for the contour which starts
+ * at the specified point.
+ *
+ * <p>If the contour starts with an on-curve point, the returned
+ * segment is a <code>SEG_MOVETO</code> to that point.</p>
+ *
+ * <p>If the contour starts with an off-curve point, and the contour
+ * ends with an on-curve point, the returned segment is a
+ * <code>SEG_MOVETO</code> to the end point.</p>
+ *
+ * <p>If the contour starts with an off-curve point, and the contour
+ * also ends with an off-curve point, the returned segment is a
+ * <code>SEG_MOVETO</code> to the location at the middle between the
+ * start and end points of the contour.</p>
+ *
+ * @return <code>PathIterator.SEG_MOVETO</code>.
+ */
+ private int getStartSegment(int contourStart, float[] coords)
+ {
+ int x, y;
+
+ if (zone.isOnCurve(contourStart))
+ {
+ x = zone.getX(contourStart, type);
+ y = zone.getY(contourStart, type);
+ }
+ else
+ {
+ /* Find the last point of the current contour. */
+ int contourEnd = contourStart;
+ while (!zone.isContourEnd(contourEnd))
+ ++contourEnd;
+
+ if (zone.isOnCurve(contourEnd))
+ {
+ /* An example is the 'o' glyph of the Helvetica which comes
+ * with Apple MacOS X 10.1.5.
+ */
+ x = zone.getX(contourEnd, type);
+ y = zone.getY(contourEnd, type);
+ }
+ else
+ {
+ x = (zone.getX(contourStart, type) + zone.getX(contourEnd, type)) / 2;
+ y = (zone.getY(contourStart, type) + zone.getY(contourEnd, type)) / 2;
+ }
+ }
+
+ coords[0] = Fixed.floatValue(x);
+ coords[1] = Fixed.floatValue(y);
+ return PathIterator.SEG_MOVETO;
+ }
+}
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia
new file mode 100644
index 000000000..b715ea02c
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia
Binary files differ
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png
new file mode 100644
index 000000000..81d09d839
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png
Binary files differ