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 “[t]he portion of the term of this 99 * patent subsequent to Oct. 13, 2009 has been 100 * disclaimed”).</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’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’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’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’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