1 /* VirtualMachine.java -- Virtual machine for TrueType bytecodes.
2    Copyright (C) 2006 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package gnu.java.awt.font.opentype.truetype;
39 
40 import gnu.java.lang.CPStringBuilder;
41 
42 import java.awt.FontFormatException;
43 import java.awt.geom.AffineTransform;
44 import java.nio.ByteBuffer;
45 import java.nio.ShortBuffer;
46 
47 
48 /**
49  * A virtual machine for interpreting TrueType bytecodes.
50  *
51  * <p><b>Lack of Thread Safety:</b> The virtual machine is
52  * intentionally <i>not</i> safe to access from multiple concurrent
53  * threads. Synchronization needs to be performed externally. Usually,
54  * the font has already obtained a lock before calling the scaler,
55  * which in turn calls the VM. It would be wasteful to acquire
56  * additional locks for the VM.
57  *
58  * <p><b>Implementation Status:</b> The current implementation can
59  * execute pre-programs of fonts, but it does not yet actually move
60  * any points. Control flow and arithmeti instructions are
61  * implemented, but most geometric instructions are not working
62  * yet. So, the VirtualMachine class is currently a no-op.  However,
63  * not very much is missing. You are more than welcome to complete the
64  * implementation.
65  *
66  * <p><b>Patents:</b> Apple Computer holds three United States Patents
67  * for the mathematical algorithms that are used by TrueType
68  * instructions. The monopoly granted by these patents will expire in
69  * October 2009. Before the expiration date, a license must be
70  * obtained from Apple Computer to use the patented technology inside
71  * the United States. For other countries, different dates might
72  * apply, or no license might be needed.
73  *
74  * <p>The default build of this class does not use the patented
75  * algorithms.  If you have obtained a license from Apple, or if the
76  * patent protection has expired, or if no license is required for
77  * your contry, you can set a flag in the source file which will
78  * enable the use of the patented mathematical algorithms.</p>
79  *
80  * <p>The relevant patents are listed subsequently.</p>
81  *
82  * <p><ol><li>United States Patent 5155805, <i>Method and Apparatus
83  * for Moving Control Points in Displaying Digital Typeface on Raster
84  * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple
85  * Computer. Filing date: May 8, 1989. Date of patent: October 13,
86  * 1992.</li>
87  *
88  * <li>United States Patent 5159668, <i>Method and Apparatus for
89  * Manipulating Outlines in Improving Digital Typeface on Raster
90  * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple
91  * Computer. Filing date: May 8, 1989. Date of patent: October 27,
92  * 1992.</li>
93  *
94  * <li>United States Patent 5325479, <i>Method and Apparatus for
95  * Moving Control Points in Displaying Digital Typeface on Raster
96  * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple
97  * Computer. Filing date: May 28, 1989. Date of patent: June 28, 1994
98  * (with a statement that &#x201c;[t]he portion of the term of this
99  * patent subsequent to Oct. 13, 2009 has been
100  * disclaimed&#x201d;).</li></ol>
101  *
102  * @author Sascha Brawer (brawer@dandelis.ch)
103  */
104 class VirtualMachine
105 {
106   /**
107    * Indicates whether or not to perform hinting operations that are
108    * protected by a number of US patents, two of which will expire on
109    * October 13, 2009, and one of which will expire on October 27,
110    * 2009.
111    */
112   private final static boolean PATENTED_HINTING = false;
113 
114 
115   /**
116    * Indicates whether the execution of the Virtual Machine is traced
117    * to System.out.
118    */
119   private final static boolean TRACE_EXECUTION = false;
120 
121 
122   /**
123    * The value 1 in 2-dot-14 fixed notation.
124    */
125   private static final short ONE_214 = 0x4000; // 1 << 14
126 
127 
128   /**
129    * The storage area of the virtual machine.
130    */
131   private final int[] storage;
132 
133 
134   /**
135    * The stack. The stack grows from bottom to top, so
136    * <code>sp[0]</code> gets used before <code>sp[1]</code>.
137    */
138   private int[] stack;
139 
140 
141   /**
142    * The maximum number of stack elements.
143    */
144   private final int maxStackElements;
145 
146 
147   /**
148    * The current stack pointer of the virtual machine.
149    */
150   private int sp;
151 
152 
153   /**
154    * fdefBuffer[i] is the buffer that contains the TrueType
155    * instructions of function #i. Most of the time, functions are
156    * defined in the font program, but a font may also re-define
157    * functions in its CVT program.
158    */
159   private ByteBuffer[] fdefBuffer;
160 
161 
162   /**
163    * fdefEntryPoint[i] is the position in fdefBuffer[i] where the
164    * first TrueType instruction after the FDEF is located.
165    */
166   private int[] fdefEntryPoint;
167 
168 
169   /**
170    * The original Control Value Table, sometimes abbreviated as CVT.
171    * The table contains signed 16-bit FUnits. Some fonts have no CVT,
172    * in which case the field will be <code>null</code>.
173    */
174   private ShortBuffer controlValueTable;
175 
176 
177   /**
178    * The scaled values inside the control value table.
179    */
180   private int[] cvt;
181 
182 
183   /**
184    * A value that is used by rounding operations to compensate for dot
185    * gain.
186    */
187   private int engineCompensation = 0;
188 
189 
190   /**
191    * The contents of the font&#x2019;s <code>fpgm</code> table, or
192    * <code>null</code> after the font program has been executed once.
193    */
194   private ByteBuffer fontProgram;
195 
196 
197   /**
198    * The <code>prep</code> table of the font, which contains a program
199    * that is executed whenever the point size or the device transform
200    * have changed.  This program is called pre-program because it gets
201    * executed before the instructions of the individual glyphs.  If
202    * the font does not contain a pre-program, the value of this field
203    * is <code>null</code>.
204    */
205   private ByteBuffer preProgram;
206 
207 
208   /**
209    * The number of points in the Twilight Zone.
210    */
211   private int numTwilightPoints;
212 
213 
214   /**
215    * The current point size of the scaled font. The value is in Fixed
216    * 26.6 notation.
217    */
218   private int pointSize; // 26.6
219 
220   private AffineTransform deviceTransform;
221 
222   private int scaleX, scaleY, shearX, shearY; // 26.6
223 
224 
225   /**
226    * Indicates whether or not scan-line conversion will use
227    * anti-aliasing (with gray levels). Font programs can ask for this
228    * value with the <code>GETINFO</code> instruction, and some
229    * programs may behave differently according to this setting.
230    */
231   private boolean antialiased;
232 
233 
234   /* Graphics State. FIXME: Move this to its own class? Some
235    * documentation would not hurt, either.
236    */
237   private int cvtCutIn; // 26.6
238   private int deltaBase; // uint32
239   private int deltaShift; // uint32
240   private short freeX; // 2.14
241   private short freeY; // 2.14
242   private int loop; // int
243   private int minimumDistance; // 26.6
244   private short projX; // 2.14
245   private short projY; // 2.14
246   private short dualX; // 2.14
247   private short dualY; // 2.14
248   private int rp0, rp1, rp2; // point numbers
249   private boolean scanControl;
250   private int scanType;
251   private int singleWidthValue; // 26.6
252   private Zone zp0, zp1, zp2;
253 
254   private Zone twilightZone;
255   private Zone glyphZone;
256 
257 
258   /**
259    * Indicates whether or not the instructions that are associated
260    * with individual glyphs shall be executed.  Set as a side effect
261    * of executing the pre-program when the point size, device
262    * transform or some other relevant parameter have changed.
263    */
264   private boolean executeGlyphInstructions;
265 
266 
267   /**
268    * Indicates whether to ignore any modifications to the control
269    * value table that the font&#x2019;s pre-program might have
270    * performed.  Set as a side effect of executing the pre-program
271    * when the point size, device transform or some other relevant
272    * parameter have changed.
273    */
274   private boolean ignoreCVTProgram;
275 
276 
277   /**
278    * The length of the space between rounded values. A value
279    * of zero means that rounding has been switched off.
280    */
281   private int roundPeriod; // 26.6
282 
283 
284   /**
285    * The offset of the rounded values from multiples of
286    * <code>roundPeriod</code>.
287    */
288   private int roundPhase; // 26.6
289 
290 
291   private int roundThreshold; // 26.6
292 
293 
294   /**
295    * A cache for the number of pixels per EM. The value is a normal
296    * integer, not a fixed point notation.
297    *
298    * @see #getPixelsPerEM()
299    */
300   private int cachedPixelsPerEM;
301 
302 
303   /**
304    * The number of font units per EM.
305    */
306   private int unitsPerEm;
307 
308 
309   /**
310    * Constructs a new Virtual Machine for executing TrueType
311    * instructions.
312    *
313    * @param unitsPerEm the number of font units in one typographic
314    * em.
315    *
316    * @param preProgram the <code>prep</code> table of the font, which
317    * contains a program that is executed whenever the point size or
318    * the device transform have changed.  This program is called
319    * pre-program because it gets executed before the instructions of
320    * the individual glyphs.  If the font does not contain a
321    * pre-program, pass <code>null</code>.
322    */
VirtualMachine(int unitsPerEm, ByteBuffer maxp, ByteBuffer controlValueTable, ByteBuffer fontProgram, ByteBuffer preProgram)323   VirtualMachine(int unitsPerEm,
324                  ByteBuffer maxp,
325                  ByteBuffer controlValueTable,
326                  ByteBuffer fontProgram,
327                  ByteBuffer preProgram)
328     throws FontFormatException
329   {
330     int maxStorage, numFunctionDefs, maxInstructionDefs;
331 
332     if (maxp.getInt(0) != 0x00010000)
333       throw new FontFormatException("unsupported maxp version");
334 
335     this.unitsPerEm = unitsPerEm;
336     maxStorage = maxp.getChar(18);
337 
338     /* FreeType says that there exist some broken fonts (like
339      * "Keystrokes MT") that contain function defs, but have a zero
340      * value in their maxp table.
341      */
342     numFunctionDefs = maxp.getChar(20);
343     if (numFunctionDefs == 0)
344       numFunctionDefs = 64;
345     fdefBuffer = new ByteBuffer[numFunctionDefs];
346     fdefEntryPoint = new int[numFunctionDefs];
347 
348     /* Read the contents of the Control Value Table. */
349     if (controlValueTable != null)
350       this.controlValueTable = controlValueTable.asShortBuffer();
351 
352     maxInstructionDefs = maxp.getChar(22);
353     maxStackElements = maxp.getChar(24);
354     storage = new int[maxStorage];
355     this.fontProgram = fontProgram;
356     this.preProgram = preProgram;
357     numTwilightPoints = maxp.getChar(16);
358   }
359 
360 
361   /**
362    * Sets the graphics state to default values.
363    */
resetGraphicsState()364   private void resetGraphicsState()
365   {
366     /* The freedom, projection and dual vector default to the x axis. */
367     freeX = projX = dualX = ONE_214;
368     freeY = projY = dualX = 0;
369     cachedPixelsPerEM = 0;
370 
371     cvtCutIn = 68; // 17/16 in 26.6 notation
372     deltaBase = 9;
373     deltaShift = 3;
374     loop = 1;
375     minimumDistance = Fixed.ONE;
376     singleWidthValue = 0;
377     rp0 = rp1 = rp2 = 0;
378     scanControl = false;
379     scanType = 2;
380     zp0 = zp1 = zp2 = getZone(1);
381 
382     setRoundingMode(Fixed.ONE, 0x48); // round to grid
383   }
384 
385 
386   /**
387    * Reloads the control value table and scales each entry from font
388    * units to pixel values.
389    */
reloadControlValueTable()390   private void reloadControlValueTable()
391   {
392     /* Some TrueType fonts have no control value table. */
393     if (controlValueTable == null)
394       return;
395 
396     /* Read in the Control Value Table. */
397     if (cvt == null)
398       cvt = new int[controlValueTable.capacity()];
399 
400     /* Scale the entries. */
401     for (int i = 0; i < cvt.length; i++)
402       cvt[i] = funitsToPixels(controlValueTable.get(i));
403   }
404 
405 
406   /**
407    * Scales a value from font unites to pixels.
408    *
409    * @return the scaled value.
410    */
funitsToPixels(int funits)411   private int funitsToPixels(int funits)
412   {
413     return (int) (((long) funits * scaleY + (unitsPerEm>>1))
414                   / unitsPerEm);
415   }
416 
417 
418   /**
419    * Sets up the virtual machine for the specified parameters.  If
420    * there is no change to the last set-up, the method will quickly
421    * return. Otherwise, the font&#x2019;s pre-program will be
422    * executed.
423    *
424    * @param pointSize the point size of the scaled font.
425    *
426    * @param deviceTransform an affine transformation which gets
427    * applied in addition to scaling by <code>pointSize</code>.  Font
428    * programs can separately inquire about the point size.  For this
429    * reason, it is not recommended to pre-multiply the point size to
430    * the device transformation.
431    *
432    * @param antialiased <code>true</code> if the scan-line conversion
433    * algorithm will use gray levels to give a smoother appearance,
434    * <code>false</code> otherwise.  Font programs can ask for this
435    * value with the <code>GETINFO</code> instruction, and some
436    * programs may behave differently according to this setting.
437    */
setup(double pointSize, AffineTransform deviceTransform, boolean antialiased)438   public boolean setup(double pointSize,
439                        AffineTransform deviceTransform,
440                        boolean antialiased)
441   {
442     boolean changeCTM;
443     int pointSize_Fixed;
444 
445     if (stack == null)
446       stack = new int[maxStackElements];
447 
448     if (twilightZone == null)
449       twilightZone = new Zone(numTwilightPoints);
450 
451     /* If the font program has not yet been executed, do so. */
452     if (fontProgram != null)
453     {
454       resetGraphicsState();
455       sp = -1;
456       execute(fontProgram, 0);
457       fontProgram = null; // prevent further execution
458     }
459 
460     /* Determine whether the transformation matrix has changed. */
461     pointSize_Fixed = Fixed.valueOf(pointSize);
462     changeCTM = ((pointSize_Fixed != this.pointSize)
463                  || !deviceTransform.equals(this.deviceTransform)
464                  || (antialiased != this.antialiased));
465 
466     if (changeCTM)
467     {
468       this.pointSize = pointSize_Fixed;
469       this.deviceTransform = deviceTransform;
470       this.antialiased = antialiased;
471       scaleX = (int) (deviceTransform.getScaleX() * pointSize * 64);
472       scaleY = (int) (deviceTransform.getScaleY() * pointSize * 64);
473       shearX = (int) (deviceTransform.getShearX() * pointSize * 64);
474       shearY = (int) (deviceTransform.getShearY() * pointSize * 64);
475 
476       resetGraphicsState();
477       reloadControlValueTable();
478       executeGlyphInstructions = true;
479       ignoreCVTProgram = false;
480 
481       if (preProgram != null)
482       {
483         sp = -1;
484         execute(preProgram, 0);
485         if (ignoreCVTProgram)
486           reloadControlValueTable();
487       }
488     }
489 
490     return executeGlyphInstructions;
491   }
492 
493 
494   /**
495    * Executes a stream of TrueType instructions.
496    */
execute(ByteBuffer instructions, int pos)497   private void execute(ByteBuffer instructions, int pos)
498   {
499     instructions.position(pos);
500 
501     // FIXME: SECURITY: Possible denial-of-service attack
502     // via instructions that have an endless loop.
503     while (instructions.hasRemaining()
504            && executeInstruction(instructions))
505       ;
506   }
507 
508 
509   /**
510    * Writes a textual description of the current TrueType instruction,
511    * including the top stack elements, to <code>System.out</code>.
512    * This is useful for debugging.
513    *
514    * @param inst the instruction stream, positioned at the current
515    * instruction.
516    */
dumpInstruction(ByteBuffer inst)517   private void dumpInstruction(ByteBuffer inst)
518   {
519     CPStringBuilder sbuf = new CPStringBuilder(40);
520     int pc = inst.position();
521     int bcode = inst.get(pc) & 0xff;
522     int count;
523     int delta;
524 
525     char pcPrefix = 'c';
526     for (int i = 0; i < fdefBuffer.length; i++)
527     {
528       if (fdefBuffer[i] == inst)
529       {
530         pcPrefix = 'f';
531         break;
532       }
533     }
534     sbuf.append(pcPrefix);
535 
536 
537     sbuf.append(getHex((short) inst.position()));
538     sbuf.append(": ");
539     sbuf.append(getHex((byte) bcode));
540     sbuf.append("  ");
541     sbuf.append(INST_NAME[bcode]);
542 
543     if (bcode == 0x40) // NPUSHB
544     {
545       count = inst.get(pc + 1) & 0xff;
546       sbuf.append(" (");
547       sbuf.append(count);
548       sbuf.append(") ");
549       for (int i = 0; i < count; i++)
550       {
551         if (i > 0)
552           sbuf.append(" ");
553         sbuf.append('$');
554         sbuf.append(getHex(inst.get(pc + 2 + i)));
555       }
556     }
557     if (bcode == 0x41) // NPUSHW
558     {
559       count = inst.get(pc + 1) & 0xff;
560       sbuf.append(" (");
561       sbuf.append(count);
562       sbuf.append(") ");
563       for (int i = 0; i < count; i++)
564       {
565         if (i > 0)
566           sbuf.append(' ');
567         sbuf.append('$');
568         sbuf.append(getHex(inst.getShort(pc + 2 + 2*i)));
569       }
570     }
571     else
572     {
573       count = getInstructionLength(bcode) - 1;
574       for (int i = 0; i < count; i++)
575       {
576         sbuf.append(" $");
577         sbuf.append(getHex(inst.get(pc + 1 + i)));
578       }
579     }
580 
581     while (sbuf.length() < 30)
582       sbuf.append(' ');
583     sbuf.append('|');
584     sbuf.append(sp + 1);
585     sbuf.append("| ");
586     for (int i = sp; i >= Math.max(0, sp - 5); i = i - 1)
587     {
588       if (i < sp)
589         sbuf.append(" ");
590       if ((stack[i] >> 16) != 0)
591         sbuf.append(getHex((short) (stack[i] >> 16)));
592       sbuf.append(getHex((short) stack[i]));
593     }
594     System.out.println(sbuf);
595   }
596 
597 
getNibble(int i, int rightShift)598   private static char getNibble(int i, int rightShift)
599   {
600     i = (i >> rightShift) & 15;
601     if (i < 10)
602       return (char) (i + '0');
603     else
604       return (char) (i + 'a' - 10);
605   }
606 
607 
getHex(byte b)608   private static String getHex(byte b)
609   {
610     char[] a = new char[2];
611     a[0] = getNibble(b, 4);
612     a[1] = getNibble(b, 0);
613     return new String(a);
614   }
615 
616 
getHex(short b)617   private static String getHex(short b)
618   {
619     char[] a = new char[4];
620     a[0] = getNibble(b, 12);
621     a[1] = getNibble(b, 8);
622     a[2] = getNibble(b, 4);
623     a[3] = getNibble(b, 0);
624     return new String(a);
625   }
626 
627 
628   /**
629    * Skips any instructions until the specified opcode has been
630    * encoutered.
631    *
632    * @param inst the current instruction stream. After the call,
633    * the position of <code>inst</code> is right after the first
634    * occurence of <code>opcode</code>.
635    *
636    * @param opcode1 the opcode for which to look.
637    *
638    * @param opcode2 another opcode for which to look. Pass -1
639    * if only <code>opcode1</code> would terminate skipping.
640    *
641    * @param illegalCode1 an opcode that must not be encountered
642    * while skipping. Pass -1 if any opcode is acceptable.
643    *
644    * @param illegalCode2 another opcode that must not be encountered
645    * while skipping. Pass -1 to perform no check.
646    *
647    * @param handleNestedIfClauses <code>true</code> to handle
648    * nested <code>IF [ELSE] EIF</code> clauses, <code>false</code>
649    * to ignore them. From the TrueType specification document,
650    * one would think that nested if clauses would not be valid,
651    * but they do appear in some fonts.
652    *
653    * @throws IllegalStateException if <code>illegalCode1</code> or
654    * <code>illegalCode2</code> has been encountered while skipping.
655    */
skipAfter(ByteBuffer inst, int opcode1, int opcode2, int illegalCode1, int illegalCode2, boolean handleNestedIfClauses)656   private static void skipAfter(ByteBuffer inst,
657                                 int opcode1, int opcode2,
658                                 int illegalCode1, int illegalCode2,
659                                 boolean handleNestedIfClauses)
660   {
661     int pos = inst.position();
662     int curOpcode;
663     int instLen;
664     int nestingLevel = 0; // increased inside IF [ELSE] EIF sequences
665 
666     while (true)
667     {
668       curOpcode = inst.get(pos) & 0xff;
669       instLen = getInstructionLength(curOpcode);
670 
671       if (false && TRACE_EXECUTION)
672       {
673         for (int i = 0; i < nestingLevel; i++)
674           System.out.print("--");
675         System.out.print("--" + pos + "-" + INST_NAME[curOpcode]);
676         if (nestingLevel > 0)
677           System.out.print(", ifNestingLevel=" + nestingLevel);
678         System.out.println();
679       }
680 
681       if (curOpcode == 0x40) // NPUSHB
682         pos += 1 + (inst.get(pos + 1) & 0xff);
683       else if (curOpcode == 0x41) // NPUSHW
684         pos += 1 + 2 * (inst.get(pos + 1) & 0xff);
685       else
686         pos += instLen;
687 
688       if ((nestingLevel == 0)
689           && ((curOpcode == opcode1) || (curOpcode == opcode2)))
690         break;
691 
692       if (handleNestedIfClauses)
693       {
694         if (curOpcode == /* IF */ 0x58)
695           ++nestingLevel;
696         else if (curOpcode == /* EIF */ 0x59)
697           --nestingLevel;
698       }
699 
700       if ((nestingLevel < 0)
701           || (curOpcode == illegalCode1)
702           || (curOpcode == illegalCode2))
703         throw new IllegalStateException();
704     }
705 
706     inst.position(pos);
707   }
708 
709 
710   /**
711    * Returns the number of bytes that a TrueType instruction occupies.
712    *
713    * @param opcode the instruction.
714    *
715    * @return the number of bytes occupied by the instructions and its
716    * operands. For <code>NPUSHB</code> and <code>NPUSHW</code>, where
717    * the instruction length depends on the first operand byte, the
718    * result is -1.
719    */
getInstructionLength(int opcode)720   private static int getInstructionLength(int opcode)
721   {
722     /* NPUSHB, NPUSHW --> see following byte */
723     if ((opcode == 0x40) || (opcode == 0x41))
724       return -1;
725 
726     /* PUSHB[0] .. PUSHB[7] --> 2, 3, 4, 5, 6, 7, 8, 9 */
727     if ((opcode >= 0xb0) && (opcode <= 0xb7))
728       return opcode - 0xae;
729 
730     /* PUSHW[0] .. PUSHW[7] --> 3, 5, 6, 7, 11, 13, 15, 17*/
731     if ((opcode >= 0xb8) && (opcode <= 0xbf))
732       return 1 + ((opcode - 0xb7) << 1);
733 
734     return 1;
735   }
736 
737 
738   /**
739    * Executes a single TrueType instruction. This is the core
740    * routine of the Virtual Machine.
741    *
742    * @return <code>true</code> if another instruction shall be
743    * executed in the same call frame; <code>false</code> if the
744    * current call frame shall be popped.
745    */
executeInstruction(ByteBuffer inst)746   private boolean executeInstruction(ByteBuffer inst)
747   {
748     if (TRACE_EXECUTION)
749       dumpInstruction(inst);
750 
751     int i, count, e1, e2, e3, e4, x, y;
752     int bcode = inst.get() & 0xff;
753 
754     switch (bcode)
755     {
756     case 0x00: // SVTCA[0], Set freedom and proj. Vectors To Coord. Axis [y]
757       setFreedomVector((short) 0, ONE_214);
758       setProjectionVector((short) 0, ONE_214);
759       break;
760 
761     case 0x01: // SVTCA[1], Set freedom and proj. Vectors To Coord. Axis [x]
762       setFreedomVector(ONE_214, (short) 0);
763       setProjectionVector(ONE_214, (short) 0);
764       break;
765 
766     case 0x02: // SPVTCA[0], Set Projection Vector To Coordinate Axis [y]
767       setProjectionVector((short) 0, ONE_214);
768       break;
769 
770     case 0x03: // SPVTCA[1], Set Projection Vector To Coordinate Axis [x]
771       setProjectionVector(ONE_214, (short) 0);
772       break;
773 
774     case 0x0c: // GPV, Get Projection Vector
775       stack[++sp] = projX;
776       stack[++sp] = projY;
777       break;
778 
779     case 0x0d: // GPV, Get Freedom Vector
780       stack[++sp] = freeX;
781       stack[++sp] = freeY;
782       break;
783 
784     case 0x0F: // ISECT, move point p to the InterSECTION of two lines
785       sp -= 4;
786       handleISECT(stack[sp], stack[sp+1], stack[sp+2],
787                   stack[sp+3], stack[sp+4]);
788       break;
789 
790     case 0x10: // SRP0, Set Reference Point 0
791       rp0 = stack[sp--];
792       break;
793 
794     case 0x11: // SRP1, Set Reference Point 1
795       rp1 = stack[sp--];
796       break;
797 
798     case 0x12: // SRP2, Set Reference Point 2
799       rp2 = stack[sp--];
800       break;
801 
802     case 0x13: // SZP0, Set Zone Pointer 0
803       zp0 = getZone(stack[sp--]);
804       break;
805 
806     case 0x14: // SZP1, Set Zone Pointer 1
807       zp1 = getZone(stack[sp--]);
808       break;
809 
810     case 0x15: // SZP2, Set Zone Pointer 2
811       zp2 = getZone(stack[sp--]);
812       break;
813 
814     case 0x16: // SZPS, Set Zone PointerS
815       zp0 = zp1 = zp2 = getZone(stack[sp--]);
816       break;
817 
818     case 0x17: // SLOOP, Set LOOP variable
819       loop = stack[sp--];
820       break;
821 
822     case 0x18: // RTG, Round To Grid
823       setRoundingMode(Fixed.ONE, 0x48);
824       break;
825 
826     case 0x19: // RTHG, Round To Half Grid
827       setRoundingMode(Fixed.ONE, 0x68);
828       break;
829 
830     case 0x1a: // SMD, Set Minimum Distance
831       minimumDistance = stack[sp--];
832       break;
833 
834     case 0x1B: // ELSE, ELSE clause
835       skipAfter(inst,
836                 /* look for: EIF, -- */ 0x59, -1,
837                 /* illegal: --, -- */   -1, -1,
838                 /* handle nested if clauses */ true);
839       break;
840 
841     case 0x1C: // JMPR, JuMP Relative
842       inst.position(inst.position() - 1 + stack[sp--]);
843       break;
844 
845     case 0x1D: // SCVTCI, Set Control Value Table Cut-In
846       cvtCutIn = stack[sp--];
847       break;
848 
849     case 0x1F: // SSW, Set Single Width
850       singleWidthValue = stack[sp--];
851       break;
852 
853     case 0x20: // DUP, DUPlicate top stack element
854       e1 = stack[sp];
855       stack[++sp] = e1;
856       break;
857 
858     case 0x21: // POP, POP top stack element
859       sp--;
860       break;
861 
862     case 0x22: // CLEAR, CLEAR the stack
863       sp = -1;
864       break;
865 
866     case 0x23: // SWAP, SWAP the top two elements on the stack
867       e1 = stack[sp--];
868       e2 = stack[sp];
869       stack[sp] = e1;
870       stack[++sp] = e2;
871       break;
872 
873     case 0x24: // DEPTH, DEPTH of the stack
874       stack[++sp] = sp + 1;
875       break;
876 
877     case 0x25: // CINDEX, Copy the INDEXed element to the top of the stack
878       stack[sp] = stack[sp - stack[sp]];
879       break;
880 
881     case 0x26: // MINDEX, Move the INDEXed element to the top of the stack
882       i = stack[sp];
883       e1 = stack[sp - i];
884       System.arraycopy(/* src */ stack,  /* srcPos */ sp - i + 1,
885                        /* dest */ stack, /* destPos*/ sp - i,
886                        /* length */ i - 1);
887       --sp;
888       stack[sp] = e1;
889       break;
890 
891     case 0x2a: // LOOPCALL, LOOP and CALL function
892       i = stack[sp--];
893       count = stack[sp--];
894       e1 = inst.position();
895       e2 = sp;
896       for (int j = 0; j < count; j++)
897         execute(fdefBuffer[i], fdefEntryPoint[i]);
898       inst.position(e1);
899       break;
900 
901     case 0x2B: // CALL, CALL function
902       i = stack[sp--];
903       e1 = inst.position();
904       e2 = sp;
905       execute(fdefBuffer[i], fdefEntryPoint[i]);
906       inst.position(e1);
907       break;
908 
909     case 0x2C: // FDEF, Function DEFinition
910       i = stack[sp--];
911       fdefBuffer[i] = inst;
912       fdefEntryPoint[i] = inst.position();
913       skipAfter(inst,
914                 /* look for: ENDF */ 0x2d,
915                 /* look for: --- */ -1,
916                 /* illegal: IDEF */ 0x89,
917                 /* illegal: FDEF */ 0x2c,
918                 /* do not handle nested if clauses */ false);
919       break;
920 
921     case 0x2D: // ENDF, END Function definition
922       /* Pop the current stack frame. */
923       return false;
924 
925     case 0x2e: // MDAP[0], Move Direct Absolute Point
926       handleMDAP(stack[sp--], /* round */ false);
927       break;
928 
929     case 0x2f: // MDAP[1], Move Direct Absolute Point
930       handleMDAP(stack[sp--], /* round */ true);
931       break;
932 
933     case 0x39: // IP, Interpolate Point by the last relative stretch
934       handleIP();
935       break;
936 
937     case 0x3d: // RTDG, Round To Double Grid
938       setRoundingMode(Fixed.ONE, 0x08);
939       roundThreshold = roundThreshold / 64; // period/128
940       break;
941 
942     case 0x3e: // MIAP[0], Move Indirect Absolute Point
943       e1 = stack[sp--];
944       handleMIAP(e1, stack[sp--], /* round */ false);
945       break;
946 
947     case 0x3f: // MIAP[1], Move Indirect Absolute Point
948       e1 = stack[sp--];
949       handleMIAP(e1, stack[sp--], /* round */ true);
950       break;
951 
952     case 0x40: // NPUSHB
953       count = inst.get() & 0xff;
954       for (i = 0; i < count; i++)
955         stack[++sp] = inst.get() & 0xff;
956       break;
957 
958     case 0x41: // NPUSHW
959       count = inst.get() & 0xff;
960       for (i = 0; i < count; i++)
961         stack[++sp] = inst.getShort();
962       break;
963 
964     case 0x42: // WS, Write Store
965       e1 = stack[sp--]; i = stack[sp--];
966       storage[i] = e1;
967       break;
968 
969     case 0x43: // RS, Read Store
970       stack[sp] = storage[stack[sp]];
971       break;
972 
973     case 0x44: // WCVTP, Write Control Value Table in Pixel units
974       e1 = stack[sp--];
975       i = stack[sp--];
976       if (i < cvt.length)
977         cvt[i] = e1;
978       break;
979 
980     case 0x45: // RCVT, Read Control Value Table entry
981       if (stack[sp] < cvt.length)
982         stack[sp] = cvt[stack[sp]];
983       else
984         stack[sp] = 0;
985       break;
986 
987     case 0x46: // GC[0], Get Coordinate projected onto the projection vector
988       stack[sp] = getProjection(zp2, stack[sp]);
989       break;
990 
991     case 0x47: // GC[1], Get Coordinate projected onto the projection vector
992       stack[sp] = getOriginalProjection(zp2, stack[sp]);
993       break;
994 
995     case 0x4B: // MPPEM, Measure Pixels Per EM
996       stack[++sp] = getPixelsPerEM();
997       break;
998 
999     case 0x4c: // MPS, Measure Point Size
1000       /* FreeType2 returns pixels per em here, because they think that
1001        * the point size would be irrelevant in a given font program.
1002        * This is extremely surprising, because the appearance of good
1003        * fonts _should_ change with point size. For example, a good
1004        * font should be wider at small point sizes, and the holes
1005        * inside glyphs ("Punzen" in German, I do not know the correct
1006        * English expression) should be larger. Note that this change
1007        * of appearance is dependent on point size, _not_ the
1008        * resolution of the display device.
1009        */
1010       stack[++sp] = pointSize;
1011       break;
1012 
1013     case 0x4f: // DEBUG, DEBUG call
1014       sp--;
1015       break;
1016 
1017     case 0x50: // LT, Less Than
1018       e1 = stack[sp--];
1019       stack[sp] = (stack[sp] < e1) ? 1 : 0;
1020       break;
1021 
1022     case 0x51: // LTEQ, Greater Than or EQual
1023       e1 = stack[sp--];
1024       stack[sp] = (stack[sp] <= e1) ? 1 : 0;
1025       break;
1026 
1027     case 0x52: // GT, Greater Than
1028       e1 = stack[sp--];
1029       stack[sp] = (stack[sp] > e1) ? 1 : 0;
1030       break;
1031 
1032     case 0x53: // GTEQ, Greater Than or EQual
1033       e1 = stack[sp--];
1034       stack[sp] = (stack[sp] >= e1) ? 1 : 0;
1035       break;
1036 
1037     case 0x54: // EQ, EQual
1038       e1 = stack[sp--];
1039       stack[sp] = (stack[sp] == e1) ? 1 : 0;
1040       break;
1041 
1042     case 0x55: // NEQ, Not EQual
1043       e1 = stack[sp--];
1044       stack[sp] = (stack[sp] != e1) ? 1 : 0;
1045       break;
1046 
1047     case 0x58: // IF, IF test
1048       if (stack[sp--] == 0)
1049         skipAfter(inst,
1050                   /* look for: ELSE */ 0x1B,
1051                   /* look for: EIF */  0x59,
1052                   /* illegal: -- */    -1,
1053                   /* illegal: -- */    -1,
1054                   /* handle nested if clauses */ true);
1055       break;
1056 
1057     case 0x59: // EIF, End IF
1058       // Do nothing.
1059       break;
1060 
1061     case 0x5A: // AND
1062       e1 = stack[sp--];
1063       stack[sp] = ((e1 != 0) && (stack[sp] != 0)) ? 1 : 0;
1064       break;
1065 
1066     case 0x5B: // OR
1067       e1 = stack[sp--];
1068       stack[sp] = ((e1 != 0) || (stack[sp] != 0)) ? 1 : 0;
1069       break;
1070 
1071     case 0x5C: // NOT
1072       stack[sp] = (stack[sp] != 0) ? 0 : 1;
1073       break;
1074 
1075     case 0x5e: // SDB, Set Delta Base in the graphics state
1076       deltaBase = stack[sp--];
1077       break;
1078 
1079     case 0x5f: // SDS, Set Delta Shift in the graphics state
1080       deltaShift = stack[sp--];
1081       break;
1082 
1083     case 0x60: // ADD
1084       e1 = stack[sp--];
1085       stack[sp] += e1;
1086       break;
1087 
1088     case 0x61: // SUB, SUBtract
1089       e1 = stack[sp--];
1090       stack[sp] -= e1;
1091       break;
1092 
1093     case 0x62: // DIV, DIVide
1094       e1 = stack[sp--];
1095       stack[sp] = Fixed.div(e1, stack[sp]);
1096       break;
1097 
1098     case 0x63:  // MUL, MULtiply
1099       e1 = stack[sp--];
1100       stack[sp] = Fixed.mul(e1, stack[sp]);
1101       break;
1102 
1103     case 0x64: // ABS, ABSolute value
1104       stack[sp] = Math.abs(stack[sp]);
1105       break;
1106 
1107     case 0x65: // NEG, NEGate
1108       stack[sp] = -stack[sp];
1109       break;
1110 
1111     case 0x66: // FLOOR
1112       stack[sp] = Fixed.floor(stack[sp]);
1113       break;
1114 
1115     case 0x67: // CEILING
1116       stack[sp] = Fixed.ceil(stack[sp]);
1117       break;
1118 
1119     case 0x68: // ROUND[0] -- round grey distance
1120       stack[sp] = round(stack[sp], /* no engine compensation */ 0);
1121       break;
1122 
1123     case 0x69: // ROUND[1] -- round black distance
1124       stack[sp] = round(stack[sp], -engineCompensation);
1125       break;
1126 
1127     case 0x6a: // ROUND[2] -- round white distance
1128       stack[sp] = round(stack[sp], engineCompensation);
1129       break;
1130 
1131     case 0x6b: // ROUND[3] -- round distance (not yet defined)
1132       stack[sp] = round(stack[sp], /* no engine compensation */ 0);
1133       break;
1134 
1135     case 0x6c: // NROUND[0] -- compensate grey distance
1136       stack[sp] = nround(stack[sp], 0);
1137       break;
1138 
1139     case 0x6d: // NROUND[1] -- compensate black distance
1140       stack[sp] = nround(stack[sp], -engineCompensation);
1141       break;
1142 
1143     case 0x6e: // NROUND[2] -- compensate white distance
1144       stack[sp] = nround(stack[sp], engineCompensation);
1145       break;
1146 
1147     case 0x6f: // NROUND[3] -- compensate distance (not yet defined)
1148       stack[sp] = nround(stack[sp], 0);
1149       break;
1150 
1151     case 0x70: // WCVTF, Write Control Value Table in Funits
1152       e1 = stack[sp--];
1153       cvt[stack[sp--]] = e1 * getPixelsPerEM();
1154       break;
1155 
1156     case 0x73: // DELTAC1, DELTA exception C1
1157       count = stack[sp--];
1158       sp -= 2 * count;
1159       deltaC(stack, sp + 1, count, 0);
1160       break;
1161 
1162     case 0x74: // DELTAC2, DELTA exception C2
1163       count = stack[sp--];
1164       sp -= 2 * count;
1165       deltaC(stack, sp + 1, count, 16);
1166       break;
1167 
1168     case 0x75: // DELTAC3, DELTA exception C3
1169       count = stack[sp--];
1170       sp -= 2 * count;
1171       deltaC(stack, sp + 1, count, 32);
1172       break;
1173 
1174     case 0x76: // SROUND, Super ROUND
1175       setRoundingMode(Fixed.ONE, stack[sp--]);
1176       break;
1177 
1178     case 0x77: // S45ROUND, Super ROUND 45 degrees
1179       setRoundingMode(/* sqrt(2)/2 */ 0x2d, stack[sp--]);
1180       break;
1181 
1182     case 0x78: // JROT, Jump Relative On True
1183       e1 = stack[sp--];
1184       i = inst.position() - 1 + stack[sp--];
1185       if (e1 != 0)
1186         inst.position(i);
1187       break;
1188 
1189     case 0x79: // JROF, Jump Relative On False
1190       e1 = stack[sp--];
1191       i = inst.position() - 1 + stack[sp--];
1192       if (e1 == 0)
1193         inst.position(i);
1194       break;
1195 
1196     case 0x7a: // ROFF, Round OFF
1197       roundPeriod = 0;
1198       break;
1199 
1200     case 0x7c: // RUTG, Round Up To Grid
1201       setRoundingMode(Fixed.ONE, 0x40);
1202       break;
1203 
1204     case 0x7d: // RDTG, Round Down To Grid
1205       setRoundingMode(Fixed.ONE, 0x40);
1206       roundThreshold = 0;
1207       break;
1208 
1209     case 0x7e: // SANGW, Set ANGle Weight (no-op according to TrueType spec)
1210     case 0x7f: // AA, Adjust Angle (no-op according to TrueType spec)
1211       sp--;
1212       break;
1213 
1214     case 0x85: // SCANCTRL, SCAN conversion ConTRoL
1215       e1 = stack[sp--];
1216       int ppemThreshold = e1 & 255;
1217       scanControl = false;
1218       boolean ppemCondition = (ppemThreshold == 255)
1219         || ((ppemThreshold != 0) && (getPixelsPerEM() > ppemThreshold));
1220       if (((e1 & (1<<8)) != 0) && ppemCondition)
1221         scanControl = true;
1222       if (((e1 & (1<<9)) != 0) && isRotated())
1223         scanControl = true;
1224       if (((e1 & (1<<10)) != 0) && isStretched())
1225         scanControl = true;
1226       if (((e1 & (1<<11)) != 0) && !ppemCondition)
1227         scanControl = false;
1228       if (((e1 & (1<<12)) != 0) && !isRotated())
1229         scanControl = false;
1230       if (((e1 & (1<<13)) != 0) && !isStretched())
1231         scanControl = false;
1232       break;
1233 
1234     case 0x88: // GETINFO, GET INFOrmation
1235       e1 = 0;
1236       if ((stack[sp] & 1) != 0) // ask for rasterizer version
1237         e1 |= 35; // "Microsoft Rasterizer version 1.7" (grayscale-capable)
1238       if (((stack[sp] & 2) != 0) && isRotated())
1239         e1 |= 1 << 8; // bit 8: glyph has been rotated
1240       if (((stack[sp] & 4) != 0) && isStretched())
1241         e1 |= 1 << 9; // bit 9: glyph has been stretched
1242       if (((stack[sp] & 32) != 0) && antialiased)
1243         e1 |= 1 << 12; // bit 12: antialiasing is active
1244       stack[sp] = e1;
1245       break;
1246 
1247     case 0x8a: // ROLL, ROLL the top three stack elements
1248       e1 = stack[sp - 2];
1249       stack[sp - 2] = stack[sp - 1];
1250       stack[sp - 1] = stack[sp];
1251       stack[sp] = e1;
1252       break;
1253 
1254     case 0x8b: // MAX, MAXimum of top two stack elements
1255       e1 = stack[sp--];
1256       stack[sp] = Math.max(e1, stack[sp]);
1257       break;
1258 
1259     case 0x8c: // MIN, MINimum of top two stack elements
1260       e1 = stack[sp--];
1261       stack[sp] = Math.min(e1, stack[sp]);
1262       break;
1263 
1264     case 0x8d: // SCANTYPE
1265       scanType = stack[sp--];
1266       break;
1267 
1268     case 0x8e: // INSTCTRL, INSTRuction execution ConTRoL
1269       e1 = stack[sp--]; // selector
1270       e2 = stack[sp--]; // value
1271       switch (e1)
1272       {
1273       case 1:
1274         executeGlyphInstructions = (e2 == 0);
1275         break;
1276 
1277       case 2:
1278         ignoreCVTProgram = (e2 != 0);
1279         break;
1280       }
1281       break;
1282 
1283     case 0xb0: // PUSHB[0]
1284     case 0xb1: // PUSHB[1]
1285     case 0xb2: // PUSHB[2]
1286     case 0xb3: // PUSHB[3]
1287     case 0xb4: // PUSHB[4]
1288     case 0xb5: // PUSHB[5]
1289     case 0xb6: // PUSHB[6]
1290     case 0xb7: // PUSHB[7]
1291       count = bcode - 0xb0 + 1;
1292       for (i = 0; i < count; i++)
1293         stack[++sp] = inst.get() & 0xff;
1294       break;
1295 
1296     case 0xb8: // PUSHW[0]
1297     case 0xb9: // PUSHW[1]
1298     case 0xba: // PUSHW[2]
1299     case 0xbb: // PUSHW[3]
1300     case 0xbc: // PUSHW[4]
1301     case 0xbd: // PUSHW[5]
1302     case 0xbe: // PUSHW[6]
1303     case 0xbf: // PUSHW[7]
1304       count = bcode - 0xb8 + 1;
1305       for (i = 0; i < count; i++)
1306         stack[++sp] = inst.getShort();
1307       break;
1308 
1309       // MIRPxxxx, Move Indirect Relative Point
1310     case 0xe0: case 0xe1: case 0xe2: case 0xe3:
1311     case 0xe4: case 0xe5: case 0xe6: case 0xe7:
1312     case 0xe8: case 0xe9: case 0xea: case 0xeb:
1313     case 0xec: case 0xed: case 0xee: case 0xef:
1314     case 0xf0: case 0xf1: case 0xf2: case 0xf3:
1315     case 0xf4: case 0xf5: case 0xf6: case 0xf7:
1316     case 0xf8: case 0xf9: case 0xfa: case 0xfb:
1317     case 0xfc: case 0xfd: case 0xfe: case 0xff:
1318       e1 = stack[sp--];
1319       handleMIRP(bcode, /* point */ e1, /* cvtIndex */ stack[sp--]);
1320       break;
1321 
1322     default:
1323       throw new IllegalStateException();
1324     }
1325 
1326     return true;
1327   }
1328 
1329 
1330   /**
1331    * Sets the rounding mode.
1332    *
1333    * @param period the grid period in fixed-point notation, such as
1334    * {@link Fixed#ONE} for the <code>SROUND</code> instruction or
1335    * <code>sqrt(2)/2</code> for the <code>S45ROUND</code> instruction.
1336    *
1337    * @param mode a byte whose bits are set according to the TrueType
1338    * specification for SROUND and S45ROUND parameters.
1339    */
setRoundingMode(int period, int mode)1340   private void setRoundingMode(int period, int mode)
1341   {
1342     /* Set the period. */
1343     switch ((mode & 0xc0) >> 6)
1344     {
1345     case 0:
1346       roundPeriod = period / 2;
1347       break;
1348 
1349     case 2:
1350       roundPeriod = period * 2;
1351       break;
1352 
1353     default:
1354       roundPeriod = period;
1355       break;
1356     }
1357 
1358     /* Set the phase. */
1359     switch ((mode & 0x30) >> 4)
1360     {
1361     case 0:
1362       roundPhase = 0;
1363       break;
1364 
1365     case 1:
1366       roundPhase = roundPeriod >> 2;  // period/4
1367       break;
1368 
1369     case 2:
1370       roundPhase = roundPeriod >> 1; // period/2
1371       break;
1372 
1373     case 3:
1374       roundPhase = (roundPeriod >> 1) + (roundPeriod >> 2); // period * 3/4
1375       break;
1376     }
1377 
1378     /* Set the threshold. */
1379     int threshold = mode & 0x0f;
1380     if (threshold == 0)
1381       roundThreshold = roundPeriod - Fixed.ONE;
1382     else
1383       roundThreshold = ((threshold - 4) * roundPeriod) / 8;
1384   }
1385 
1386 
1387 
1388   /**
1389    * Implements the DELTAC instructions. These instructions check
1390    * whether the current number of pixels per em is contained in an
1391    * exception table.  If it is, a delta value is determined, and the
1392    * specified entry in the Control Value Table is modified according
1393    * to the delta.
1394    *
1395    * @param pairs the delta table. Because the delta table is on
1396    * the stack, callers usually just want to pass the stack array.
1397    *
1398    * @param offset the offset of the first pair in <code>pairs</code>.
1399    *
1400    * @param numPairs the number of pairs.
1401    *
1402    * @param base 0 for <code>DELTAC1</code>, 16 for <code>DELTAC2</code>,
1403    * or 32 for <code>DELTAC2</code>.
1404    *
1405    * @see <a href=
1406    * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC1"
1407    * >Apple&#x2019;s documentation for <code>DELTAC1</code></a>, <a href=
1408    * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC2"
1409    * ><code>DELTAC2</code></a>, and <a href=
1410    * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC3"
1411    * ><code>DELTAC3</code></a>
1412    */
deltaC(int[] pairs, int offset, int numPairs, int base)1413   private void deltaC(int[] pairs, int offset, int numPairs, int base)
1414   {
1415     int arg, relativePpem;
1416     int ppemTrigger = getPixelsPerEM() - (deltaBase + base);
1417     int delta, cvtIndex, rightShift;
1418     for (int i = 0; i < numPairs; i++)
1419     {
1420       arg = pairs[offset + 2 * i];
1421       relativePpem = (arg >> 4) & 15;
1422       if (relativePpem == ppemTrigger)
1423       {
1424         delta = (arg & 15) - 8;
1425         if (delta >= 0)
1426           ++delta;
1427 
1428         rightShift = deltaShift - 6;
1429         if (rightShift > 0)
1430           delta = delta >> rightShift;
1431         else if (rightShift < 0)
1432           delta = delta << (-rightShift);
1433         cvt[pairs[offset + 2 * i + 1]] += delta;
1434 
1435         break;
1436       }
1437     }
1438   }
1439 
1440 
getZone(int zoneNumber)1441   private Zone getZone(int zoneNumber)
1442   {
1443     return (zoneNumber == 0) ? twilightZone : glyphZone;
1444   }
1445 
1446 
1447   /**
1448    * Projects the specified vector along the current projection
1449    * vector.
1450    *
1451    * @param x the x component of the input vector, in 26.6 fixed-point
1452    * notation.
1453    *
1454    * @param y the y component of the input vector, in 26.6 fixed-point
1455    * notation.
1456    *
1457    * @return the projected distance, in 26.6 fixed-point notation.
1458    */
getProjection(int x, int y)1459   private int getProjection(int x, int y)
1460   {
1461     return (int) (((((long) x) * projX + ((long) y) * projY)) >> 14);
1462   }
1463 
1464 
1465   /**
1466    * Projects the specified vector along the current dual projection
1467    * vector.
1468    *
1469    * @param x the x component of the input vector, in 26.6 fixed-point
1470    * notation.
1471    *
1472    * @param y the y component of the input vector, in 26.6 fixed-point
1473    * notation.
1474    *
1475    * @return the projected distance, in 26.6 fixed-point notation.
1476    */
getDualProjection(int x, int y)1477   private int getDualProjection(int x, int y)
1478   {
1479     return (int) (((((long) x) * dualX + ((long) y) * dualY)) >> 14);
1480   }
1481 
1482 
getProjection(Zone zone, int point)1483   private int getProjection(Zone zone, int point)
1484   {
1485     return getProjection(zone.getX(point), zone.getY(point));
1486   }
1487 
1488 
getOriginalProjection(Zone zone, int point)1489   private int getOriginalProjection(Zone zone, int point)
1490   {
1491     return getDualProjection(zone.getOriginalX(point),
1492                              zone.getOriginalY(point));
1493   }
1494 
1495 
handleISECT(int a0, int a1, int b0, int b1, int p)1496   private void handleISECT(int a0, int a1, int b0, int b1, int p)
1497   {
1498     System.out.println("FIXME: Unimplemented ISECT " + p);
1499   }
1500 
1501 
muldiv(int a, int b, int c)1502   private static int muldiv(int a, int b, int c)
1503   {
1504     int s;
1505     s = a; a = Math.abs(a);
1506     s ^= b; b = Math.abs(b);
1507     s ^= c; c = Math.abs(c);
1508     a = (int) ((((long) a) * b + (c>>1)) / c);
1509     return (s < 0) ? -a : a;
1510   }
1511 
1512 
getFreeDotProj()1513   private int getFreeDotProj()
1514   {
1515     int result;
1516 
1517     result = ((((int) projX) * freeX) << 2)
1518       + ((((int) projY) * freeY) << 2);
1519 
1520     /* FIXME: This seems somewhat bogus. Need to contact the
1521      * developers of FreeType.
1522      */
1523     if (Math.abs(result) < 0x4000000)
1524       result = 0x40000000;
1525     return result;
1526   }
1527 
1528 
movePoint(Zone zone, int point, int distance)1529   private void movePoint(Zone zone, int point, int distance)
1530   {
1531     int freeDotProj = getFreeDotProj();
1532     int c;
1533 
1534     if (freeX != 0)
1535     {
1536       c = zone.getX(point);
1537       c += muldiv(distance, freeX << 16, freeDotProj);
1538       zone.setX(point, c, /* touch */ true);
1539     }
1540 
1541     if (freeY != 0)
1542     {
1543       c = zone.getY(point);
1544       c += muldiv(distance, freeY << 16, freeDotProj);
1545       zone.setY(point, c, /* touch */ true);
1546     }
1547 
1548     if (TRACE_EXECUTION)
1549     {
1550       System.out.println("point[" + point + "] moved to "
1551                          + Fixed.toString(zone.getX(point),
1552                                           zone.getY(point)));
1553       dumpVectors();
1554     }
1555   }
1556 
dumpVectors()1557   private void dumpVectors()
1558   {
1559     System.out.println("  proj=" + Fixed.toString(projX>>8, projY>>8)
1560                        + ", free=" + Fixed.toString(freeX>>8, freeY>>8));
1561   }
1562 
1563 
handleIP()1564   private void handleIP()
1565   {
1566     // Implementation taken from FreeType.
1567     int p, org_a, org_b, org_x, cur_a, cur_b, cur_x, distance;
1568     int freeDotProj;
1569 
1570     org_a = getOriginalProjection(zp0, rp1);
1571     cur_a = getProjection(zp0, rp1);
1572 
1573     org_b = getOriginalProjection(zp1, rp2);
1574     cur_b = getProjection(zp1, rp2);
1575 
1576     while (--loop >= 0)
1577     {
1578       p = stack[sp--];
1579       org_x = getOriginalProjection(zp2, p);
1580       cur_x = getProjection(zp2, p);
1581 
1582       if (((org_a <= org_b) && (org_x <= org_a))
1583           || ((org_a > org_b) && (org_x >= org_a)))
1584         distance = (cur_a - org_a) + (org_x - cur_x);
1585       else if (((org_a <= org_b) && (org_x >= org_b))
1586                || ((org_a > org_b) && (org_x < org_b)))
1587         distance = (cur_b - org_b) + (org_x - cur_x);
1588       else
1589         distance = muldiv(cur_b - cur_a, org_x - org_a, org_b - org_a)
1590           + (cur_a - cur_x);
1591       movePoint(zp2, p, distance);
1592     }
1593     loop = 1;
1594   }
1595 
1596 
handleMDAP(int point, boolean round)1597   private void handleMDAP(int point, boolean round)
1598   {
1599     System.out.println("FIXME: Unimplemented MDAP: point "
1600                        + point + "/" + zp0);
1601   }
1602 
1603 
handleMIAP(int cvtIndex, int point, boolean round)1604   private void handleMIAP(int cvtIndex, int point, boolean round)
1605   {
1606     int previousPos, pos;
1607 
1608     previousPos = getProjection(zp0, point);
1609     pos = cvt[cvtIndex];
1610 
1611     if (round)
1612     {
1613       if (Math.abs(pos - previousPos) > cvtCutIn)
1614         pos = previousPos;
1615       pos = round(pos, /* no engine compensation */ 0);
1616     }
1617     movePoint(zp0, point, pos - previousPos);
1618     rp0 = rp1 = point;
1619   }
1620 
1621 
handleMIRP(int bcode, int point, int cvtIndex)1622   private void handleMIRP(int bcode, int point, int cvtIndex)
1623   {
1624     System.out.println("FIXME: Unimplemented mirp " + point + ", " + cvtIndex);
1625   }
1626 
1627 
1628 
round(int distance, int compensation)1629   private int round(int distance, int compensation)
1630   {
1631     int result;
1632 
1633     if (roundPeriod == 0)
1634       return nround(distance, compensation);
1635 
1636     if (distance >= 0)
1637     {
1638       result = distance + compensation - roundPhase + roundThreshold;
1639       result &= -roundPeriod; // truncate to the next lowest periodic value
1640       return Math.max(result, 0) + roundPhase;
1641     }
1642     else
1643     {
1644       result = compensation - roundPhase + roundThreshold - distance;
1645       result &= -roundPeriod;
1646       return Math.max(-result, 0) - roundPhase;
1647     }
1648   }
1649 
1650 
nround(int distance, int compensation)1651   private static int nround(int distance, int compensation)
1652   {
1653     if (distance >= 0)
1654       return Math.max(distance + compensation, 0);
1655     else
1656       return Math.min(distance - compensation, 0);
1657   }
1658 
1659 
1660   /**
1661    * Determines whether the current glyph is rotated.
1662    *
1663    * @return <code>false</code> if the shearing factors for the
1664    * <i>x</i> and <i>y</i> axes are zero; <code>true</code> if they
1665    * are non-zero.
1666    */
isRotated()1667   private boolean isRotated()
1668   {
1669     return (shearX != 0) || (shearY != 0);
1670   }
1671 
1672 
1673   /**
1674    * Determines whether the current glyph is stretched.
1675    *
1676    * @return <code>false</code> if the scaling factors for the
1677    * <i>x</i> and <i>y</i> axes are are equal; <code>true</code> if
1678    * they differ.
1679    */
isStretched()1680   private boolean isStretched()
1681   {
1682     return scaleX != scaleY;
1683   }
1684 
1685 
1686   /**
1687    * Returns how many pixels there are per EM, in direction of the
1688    * current projection vector. The result is a normal integer,
1689    * not a Fixed.
1690    */
getPixelsPerEM()1691   private int getPixelsPerEM()
1692   {
1693     if (cachedPixelsPerEM == 0)
1694     {
1695       cachedPixelsPerEM = Fixed.intValue(Fixed.vectorLength(
1696         applyCTM_x(projX >> 8, projY >> 8),
1697         applyCTM_y(projX >> 8, projY >> 8)));
1698     }
1699 
1700     return cachedPixelsPerEM;
1701   }
1702 
1703 
setProjectionVector(short x, short y)1704   private void setProjectionVector(short x, short y)
1705   {
1706     if (PATENTED_HINTING)
1707     {
1708       if ((x != projX) || (y != projY))
1709         cachedPixelsPerEM = 0;
1710 
1711       projX = x;
1712       projY = y;
1713     }
1714   }
1715 
1716 
setFreedomVector(short x, short y)1717   private void setFreedomVector(short x, short y)
1718   {
1719     if (PATENTED_HINTING)
1720     {
1721       freeX = x;
1722       freeY = y;
1723     }
1724   }
1725 
1726 
setDualVector(short x, short y)1727   private void setDualVector(short x, short y)
1728   {
1729     if (PATENTED_HINTING)
1730     {
1731       dualX = x;
1732       dualY = y;
1733     }
1734   }
1735 
1736 
applyCTM_x(int x, int y)1737   private int applyCTM_x(int x, int y)
1738   {
1739     return (int) (((long) scaleX * x + (long) shearX * y) >> 6);
1740   }
1741 
applyCTM_y(int x, int y)1742   private int applyCTM_y(int x, int y)
1743   {
1744     return (int) (((long) shearY * x + (long) scaleY * y) >> 6);
1745   }
1746 
1747 
1748   private static final String[] INST_NAME =
1749   {
1750     /* 00 */ "SVTCA[0]", "SVTCA[1]", "SPVTCA[0]", "SPVTCA[1]",
1751     /* 04 */ "INST_04", "INST_05", "INST_06", "INST_07",
1752     /* 08 */ "INST_08", "INST_09", "INST_0A", "INST_0B",
1753     /* 0c */ "GPV", "GFV", "INST_0E", "ISECT",
1754     /* 10 */ "SRP0", "SRP1", "SRP2", "SZP0",
1755     /* 14 */ "SZP1", "SZP2", "SZPS", "SLOOP",
1756     /* 18 */ "RTG", "RTHG", "SMD", "ELSE",
1757     /* 1c */ "JMPR", "SCVTCI", "INST_1E", "SSW",
1758     /* 20 */ "DUP", "POP", "CLEAR", "SWAP",
1759     /* 24 */ "DEPTH", "CINDEX", "MINDEX", "INST_27",
1760     /* 28 */ "INST_28", "INST_29", "LOOPCALL", "CALL",
1761     /* 2c */ "FDEF", "ENDF", "MDAP[0]", "MDAP[1]",
1762     /* 30 */ "IUP[0]", "IUP[1]", "SHP[0]", "SHP[1]",
1763     /* 34 */ "INST_34", "INST_35", "INST_36", "INST_37",
1764     /* 38 */ "INST_38", "IP", "INST_3A", "INST_3B",
1765     /* 3c */ "INST_3C", "RTDG", "MIAP[0]", "MIAP[1]",
1766     /* 40 */ "NPUSHB", "NPUSHW", "WS", "RS",
1767     /* 44 */ "WCVTP", "RCVT", "GC[0]", "GC[1]",
1768     /* 48 */ "INST_48", "INST_49", "INST_4A", "MPPEM",
1769     /* 4c */ "MPS", "FLIPON", "FLIPOFF", "DEBUG",
1770     /* 50 */ "LT", "LTEQ", "GT", "GTEQ",
1771     /* 54 */ "EQ", "NEQ", "INST_56", "INST_57",
1772     /* 58 */ "IF", "EIF", "AND", "OR",
1773     /* 5c */ "NOT", "INST_5D", "SDB", "SDS",
1774     /* 60 */ "ADD", "SUB", "DIV", "MUL",
1775     /* 64 */ "ABS", "NEG", "FLOOR", "CEILING",
1776     /* 68 */ "ROUND[0]", "ROUND[1]", "ROUND[2]", "ROUND[3]",
1777     /* 6c */ "NROUND[0]", "NROUND[1]", "NROUND[2]", "NROUND[3]",
1778     /* 70 */ "WCVTF", "INST_71", "INST_72", "DELTAC1",
1779     /* 74 */ "DELTAC2", "DELTAC3", "SROUND", "S45ROUND",
1780     /* 78 */ "JROT", "JROF", "ROFF", "INST_7B",
1781     /* 7c */ "RUTG", "RDTG", "SANGW", "AA",
1782     /* 80 */ "FLIPPT", "FLIPRGON", "FLIPRGOFF", "INST_83",
1783     /* 84 */ "INST_84", "SCANCTRL", "INST_86", "INST_87",
1784     /* 88 */ "GETINFO", "INST_89", "ROLL", "MAX",
1785     /* 8c */ "MIN", "SCANTYPE", "INSTCTRL", "INST_8F",
1786     /* 90 */ "INST_90", "INST_91", "INST_92", "INST_93",
1787     /* 94 */ "INST_94", "INST_95", "INST_96", "INST_97",
1788     /* 98 */ "INST_98", "INST_99", "INST_9A", "INST_9B",
1789     /* 9c */ "INST_9C", "INST_9D", "INST_9E", "INST_9F",
1790     /* a0 */ "INST_A0", "INST_A1", "INST_A2", "INST_A3",
1791     /* a4 */ "INST_A4", "INST_A5", "INST_A6", "INST_A7",
1792     /* a8 */ "INST_A8", "INST_A9", "INST_AA", "INST_AB",
1793     /* ac */ "INST_AC", "INST_AD", "INST_AE", "INST_AF",
1794     /* b0 */ "PUSHB[0]", "PUSHB[1]", "PUSHB[2]", "PUSHB[3]",
1795     /* b4 */ "PUSHB[4]", "PUSHB[5]", "PUSHB[6]", "PUSHB[7]",
1796     /* b8 */ "PUSHW[0]", "PUSHW[1]", "PUSHW[2]", "PUSHW[3]",
1797     /* bc */ "PUSHW[4]", "PUSHW[5]", "PUSHW[6]", "PUSHW[7]",
1798     /* c0 */ "INST_C0", "INST_C1", "INST_C2", "INST_C3",
1799     /* c4 */ "INST_C4", "INST_C5", "INST_C6", "INST_C7",
1800     /* c8 */ "INST_C8", "INST_C9", "INST_CA", "INST_CB",
1801     /* cc */ "INST_CC", "INST_CD", "INST_CE", "INST_CF",
1802     /* d0 */ "INST_D0", "INST_D1", "INST_D2", "INST_D3",
1803     /* d4 */ "INST_D4", "INST_D5", "INST_D6", "INST_D7",
1804     /* d8 */ "INST_D8", "INST_D9", "INST_DA", "INST_DB",
1805     /* dc */ "INST_DC", "INST_DD", "INST_DE", "INST_DF",
1806     /* e0 */ "MIRP00000", "MIRP00001", "MIRP00010", "MIRP00011",
1807     /* e4 */ "MIRP00100", "MIRP00101", "MIRP00110", "MIRP00111",
1808     /* e8 */ "MIRP01000", "MIRP01001", "MIRP01010", "MIRP01011",
1809     /* ec */ "MIRP01100", "MIRP01101", "MIRP01110", "MIRP01111",
1810     /* f0 */ "MIRP10000", "MIRP10001", "MIRP10010", "MIRP10011",
1811     /* f4 */ "MIRP10100", "MIRP10101", "MIRP10110", "MIRP10111",
1812     /* f8 */ "MIRP11000", "MIRP11001", "MIRP11010", "MIRP11011",
1813     /* fc */ "MIRP11100", "MIRP11101", "MIRP11110", "MIRP11111"
1814   };
1815 }
1816