aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.4.3/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.4.3/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java')
-rw-r--r--gcc-4.4.3/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java1815
1 files changed, 1815 insertions, 0 deletions
diff --git a/gcc-4.4.3/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java b/gcc-4.4.3/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java
new file mode 100644
index 000000000..c88d4c649
--- /dev/null
+++ b/gcc-4.4.3/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"
+ };
+}