PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/pripTool/src/pdf-renderer/src/com/sun/pdfview/font/Type1CFont.java

https://bitbucket.org/maxgoebel/examtool
Java | 1190 lines | 883 code | 82 blank | 225 comment | 217 complexity | 6f8a05b559794d4837a0f9b758148dc7 MD5 | raw file
  1. /*
  2. * $Id: Type1CFont.java,v 1.3 2009-03-09 10:18:03 tomoke Exp $
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
  5. * Santa Clara, California 95054, U.S.A. All rights reserved.
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. package com.sun.pdfview.font;
  22. import java.awt.geom.AffineTransform;
  23. import java.awt.geom.GeneralPath;
  24. import java.awt.geom.NoninvertibleTransformException;
  25. import java.io.IOException;
  26. import com.sun.pdfview.PDFObject;
  27. /**
  28. * A representation, with parser, of an Adobe Type 1C font.
  29. * @author Mike Wessler
  30. */
  31. public class Type1CFont extends OutlineFont {
  32. String chr2name[] = new String[256];
  33. byte[] data;
  34. int pos;
  35. byte[] subrs;
  36. float[] stack = new float[100];
  37. int stackptr = 0;
  38. String names[];
  39. int glyphnames[];
  40. int encoding[] = new int[256];
  41. String fontname;
  42. AffineTransform at = new AffineTransform (0.001f, 0, 0, 0.001f, 0, 0);
  43. int num;
  44. float fnum;
  45. int type;
  46. static int CMD = 0;
  47. static int NUM = 1;
  48. static int FLT = 2;
  49. /**
  50. * create a new Type1CFont based on a font data stream and a descriptor
  51. * @param baseFont the postscript name of this font
  52. * @param src a stream containing the font
  53. * @param descriptor the descriptor for this font
  54. */
  55. public Type1CFont (String baseFont, PDFObject src,
  56. PDFFontDescriptor descriptor) throws IOException {
  57. super (baseFont, src, descriptor);
  58. PDFObject dataObj = descriptor.getFontFile3 ();
  59. data = dataObj.getStream ();
  60. pos = 0;
  61. parse ();
  62. // TODO: free up (set to null) unused structures (data, subrs, stack)
  63. }
  64. /**
  65. * a debug method for printing the data
  66. */
  67. private void printData () {
  68. char[] parts = new char[17];
  69. int partsloc = 0;
  70. for (int i = 0; i < data.length; i++) {
  71. int d = ((int) data[i]) & 0xff;
  72. if (d == 0) {
  73. parts[partsloc++] = '.';
  74. } else if (d < 32 || d >= 127) {
  75. parts[partsloc++] = '?';
  76. } else {
  77. parts[partsloc++] = (char) d;
  78. }
  79. if (d < 16) {
  80. System.out.print ("0" + Integer.toHexString (d));
  81. } else {
  82. System.out.print (Integer.toHexString (d));
  83. }
  84. if ((i & 15) == 15) {
  85. System.out.println (" " + new String (parts));
  86. partsloc = 0;
  87. } else if ((i & 7) == 7) {
  88. System.out.print (" ");
  89. parts[partsloc++] = ' ';
  90. } else if ((i & 1) == 1) {
  91. System.out.print (" ");
  92. }
  93. }
  94. System.out.println ();
  95. }
  96. /**
  97. * read the next decoded value from the stream
  98. * @param charstring ????
  99. */
  100. private int readNext (boolean charstring) {
  101. num = (int) (data[pos++]) & 0xff;
  102. if (num == 30 && !charstring) { // goofy floatingpoint rep
  103. readFNum ();
  104. return type = FLT;
  105. } else if (num == 28) {
  106. num = (((int) data[pos]) << 8) + (((int) data[pos + 1]) & 0xff);
  107. pos += 2;
  108. return type = NUM;
  109. } else if (num == 29 && !charstring) {
  110. num = (((int) data[pos] & 0xff) << 24) |
  111. (((int) data[pos + 1] & 0xff) << 16) |
  112. (((int) data[pos + 2] & 0xff) << 8) |
  113. (((int) data[pos + 3] & 0xff));
  114. pos += 4;
  115. return type = NUM;
  116. } else if (num == 12) { // two-byte command
  117. num = 1000 + ((int) (data[pos++]) & 0xff);
  118. return type = CMD;
  119. } else if (num < 32) {
  120. return type = CMD;
  121. } else if (num < 247) {
  122. num -= 139;
  123. return type = NUM;
  124. } else if (num < 251) {
  125. num = (num - 247) * 256 + (((int) data[pos++]) & 0xff) + 108;
  126. return type = NUM;
  127. } else if (num < 255) {
  128. num = -(num - 251) * 256 - (((int) data[pos++]) & 0xff) - 108;
  129. return type = NUM;
  130. } else if (!charstring) { // dict shouldn't have a 255 code
  131. printData ();
  132. throw new RuntimeException ("Got a 255 code while reading dict");
  133. } else { // num was 255
  134. fnum = ((((int) data[pos] & 0xff) << 24) |
  135. (((int) data[pos + 1] & 0xff) << 16) |
  136. (((int) data[pos + 2] & 0xff) << 8) |
  137. (((int) data[pos + 3] & 0xff))) / 65536f;
  138. pos += 4;
  139. return type = FLT;
  140. }
  141. }
  142. /**
  143. * read the next funky floating point number from the input stream.
  144. * value gets put into the fnum field.
  145. */
  146. public void readFNum () {
  147. // work in nybbles: 0-9=0-9, a=. b=E, c=E-, d=rsvd e=neg f=end
  148. float f = 0;
  149. boolean neg = false;
  150. int exp = 0;
  151. int eval = 0;
  152. float mul = 1;
  153. byte work = data[pos++];
  154. while (true) {
  155. if (work == (byte) 0xdd) {
  156. work = data[pos++];
  157. }
  158. int nyb = (work >> 4) & 0xf;
  159. work = (byte) ((work << 4) | 0xd);
  160. if (nyb < 10) {
  161. if (exp != 0) { // working on the exponent
  162. eval = eval * 10 + nyb;
  163. } else if (mul == 1) { // working on an int
  164. f = f * 10 + nyb;
  165. } else { // working on decimal part
  166. f += nyb * mul;
  167. mul /= 10f;
  168. }
  169. } else if (nyb == 0xa) { // decimal
  170. mul = 0.1f;
  171. } else if (nyb == 0xb) { // E+
  172. exp = 1;
  173. } else if (nyb == 0xc) { // E-
  174. exp = -1;
  175. } else if (nyb == 0xe) { // neg
  176. neg = true;
  177. } else {
  178. break;
  179. }
  180. }
  181. fnum = (neg ? -1 : 1) * f * (float) Math.pow (10, eval * exp);
  182. }
  183. /**
  184. * read an integer from the input stream
  185. * @param len the number of bytes in the integer
  186. * @return the integer
  187. */
  188. private int readInt (int len) {
  189. int n = 0;
  190. for (int i = 0; i < len; i++) {
  191. n = (n << 8) | (((int) data[pos++]) & 0xff);
  192. }
  193. return n;
  194. }
  195. /**
  196. * read the next byte from the stream
  197. * @return the byte
  198. */
  199. private int readByte () {
  200. return ((int) data[pos++]) & 0xff;
  201. }
  202. // DICT structure:
  203. // operand operator operand operator ...
  204. // INDEX structure:
  205. // count(2) offsize [offset offset ... offset] data
  206. // offset array has count+1 entries
  207. // data starts at 3+(count+1)*offsize
  208. // offset for data is offset+2+(count+1)*offsize
  209. /**
  210. * get the size of the dictionary located within the stream at
  211. * some offset.
  212. * @param loc the index of the start of the dictionary
  213. * @return the size of the dictionary, in bytes.
  214. */
  215. public int getIndexSize (int loc) {
  216. // System.out.println("Getting size of index at "+loc);
  217. int hold = pos;
  218. pos = loc;
  219. int count = readInt (2);
  220. if (count <= 0) {
  221. return 2;
  222. }
  223. int encsz = readByte ();
  224. if (encsz < 1 || encsz > 4) {
  225. throw new RuntimeException ("Offsize: " + encsz +
  226. ", must be in range 1-4.");
  227. }
  228. // pos is now at the first offset. last offset is at count*encsz
  229. pos += count * encsz;
  230. int end = readInt (encsz);
  231. pos = hold;
  232. return 2 + (count + 1) * encsz + end;
  233. }
  234. /**
  235. * return the number of entries in an Index table.
  236. *
  237. * @param loc
  238. * @return
  239. */
  240. public int getTableLength (int loc) {
  241. int hold = pos;
  242. pos = loc;
  243. int count = readInt (2);
  244. if (count <= 0) {
  245. return 2;
  246. }
  247. pos = hold;
  248. return count;
  249. }
  250. /**
  251. * A range. There's probably a version of this class floating around
  252. * somewhere already in Java.
  253. */
  254. class Range {
  255. private int start;
  256. private int len;
  257. public Range (int start, int len) {
  258. this.start = start;
  259. this.len = len;
  260. }
  261. public final int getStart () {
  262. return start;
  263. }
  264. public final int getLen () {
  265. return len;
  266. }
  267. public final int getEnd () {
  268. return start + len;
  269. }
  270. public String toString () {
  271. return "Range: start: " + start + ", len: " + len;
  272. }
  273. }
  274. /**
  275. * Get the range of a particular index in a dictionary.
  276. * @param index the start of the dictionary.
  277. * @param id the index of the entry in the dictionary
  278. * @return a range describing the offsets of the start and end of
  279. * the entry from the start of the file, not the dictionary
  280. */
  281. Range getIndexEntry (int index, int id) {
  282. int hold = pos;
  283. pos = index;
  284. int count = readInt (2);
  285. int encsz = readByte ();
  286. if (encsz < 1 || encsz > 4) {
  287. throw new RuntimeException ("Offsize: " + encsz +
  288. ", must be in range 1-4.");
  289. }
  290. pos += encsz * id;
  291. int from = readInt (encsz);
  292. Range r = new Range (from + 2 + index + encsz * (count + 1), readInt (
  293. encsz) - from);
  294. pos = hold;
  295. return r;
  296. }
  297. // Top DICT: NAME CODE DEFAULT
  298. // charstringtype 12 6 2
  299. // fontmatrix 12 7 0.001 0 0 0.001
  300. // charset 15 - (offset) names of glyphs (ref to name idx)
  301. // encoding 16 - (offset) array of codes
  302. // CharStrings 17 - (offset)
  303. // Private 18 - (size, offset)
  304. // glyph at position i in CharStrings has name charset[i]
  305. // and code encoding[i]
  306. int charstringtype = 2;
  307. float temps[] = new float[32];
  308. int charsetbase = 0;
  309. int encodingbase = 0;
  310. int charstringbase = 0;
  311. int privatebase = 0;
  312. int privatesize = 0;
  313. int gsubrbase = 0;
  314. int lsubrbase = 0;
  315. int gsubrsoffset = 0;
  316. int lsubrsoffset = 0;
  317. int nglyphs = 1;
  318. /**
  319. * read a dictionary that exists within some range, parsing the entries
  320. * within the dictionary.
  321. */
  322. private void readDict (Range r) {
  323. // System.out.println("reading dictionary from "+r.getStart()+" to "+r.getEnd());
  324. pos = r.getStart ();
  325. while (pos < r.getEnd ()) {
  326. int cmd = readCommand (false);
  327. if (cmd == 1006) { // charstringtype, default=2
  328. charstringtype = (int) stack[0];
  329. } else if (cmd == 1007) { // fontmatrix
  330. if (stackptr == 4) {
  331. at = new AffineTransform ((float) stack[0], (float) stack[1],
  332. (float) stack[2], (float) stack[3],
  333. 0, 0);
  334. } else {
  335. at = new AffineTransform ((float) stack[0], (float) stack[1],
  336. (float) stack[2], (float) stack[3],
  337. (float) stack[4], (float) stack[5]);
  338. }
  339. } else if (cmd == 15) { // charset
  340. charsetbase = (int) stack[0];
  341. } else if (cmd == 16) { // encoding
  342. encodingbase = (int) stack[0];
  343. } else if (cmd == 17) { // charstrings
  344. charstringbase = (int) stack[0];
  345. } else if (cmd == 18) { // private
  346. privatesize = (int) stack[0];
  347. privatebase = (int) stack[1];
  348. } else if (cmd == 19) { // subrs (in Private dict)
  349. lsubrbase = privatebase + (int) stack[0];
  350. lsubrsoffset = calcoffset (lsubrbase);
  351. }
  352. stackptr = 0;
  353. }
  354. }
  355. /**
  356. * read a complete command. this may involve several numbers
  357. * which go onto a stack before an actual command is read.
  358. * @param charstring ????
  359. * @return the command. Some numbers may also be on the stack.
  360. */
  361. private int readCommand (boolean charstring) {
  362. while (true) {
  363. int t = readNext (charstring);
  364. if (t == CMD) {
  365. /*
  366. System.out.print("CMD= "+num+", args=");
  367. for (int i=0; i<stackptr; i++) {
  368. System.out.print(" "+stack[i]);
  369. }
  370. System.out.println();
  371. */
  372. return num;
  373. } else {
  374. stack[stackptr++] = (t == NUM) ? (float) num : fnum;
  375. }
  376. }
  377. }
  378. /**
  379. * parse information about the encoding of this file.
  380. * @param base the start of the encoding data
  381. */
  382. private void readEncodingData (int base) {
  383. if (base == 0) { // this is the StandardEncoding
  384. System.arraycopy (FontSupport.standardEncoding, 0, encoding, 0,
  385. FontSupport.standardEncoding.length);
  386. } else if (base == 1) { // this is the expert encoding
  387. // TODO: copy ExpertEncoding
  388. } else {
  389. pos = base;
  390. int encodingtype = readByte ();
  391. if ((encodingtype & 127) == 0) {
  392. int ncodes = readByte ();
  393. for (int i = 1; i < ncodes + 1; i++) {
  394. int idx = readByte () & 0xff;
  395. encoding[idx] = i;
  396. }
  397. } else if ((encodingtype & 127) == 1) {
  398. int nranges = readByte ();
  399. int p = 1;
  400. for (int i = 0; i < nranges; i++) {
  401. int start = readByte ();
  402. int more = readByte ();
  403. for (int j = start; j < start + more + 1; j++) {
  404. encoding[j] = p++;
  405. }
  406. }
  407. } else {
  408. System.out.println ("Bad encoding type: " + encodingtype);
  409. }
  410. // TODO: now check for supplemental encoding data
  411. }
  412. }
  413. /**
  414. * read the names of the glyphs.
  415. * @param base the start of the glyph name table
  416. */
  417. private void readGlyphNames (int base) {
  418. if (base == 0) {
  419. glyphnames = new int[229];
  420. for (int i = 0; i < glyphnames.length; i++) {
  421. glyphnames[i] = i;
  422. }
  423. return;
  424. } else if (base == 1) {
  425. glyphnames = FontSupport.type1CExpertCharset;
  426. return;
  427. } else if (base == 2) {
  428. glyphnames = FontSupport.type1CExpertSubCharset;
  429. return;
  430. }
  431. // nglyphs has already been set.
  432. glyphnames = new int[nglyphs];
  433. glyphnames[0] = 0;
  434. pos = base;
  435. int t = readByte ();
  436. if (t == 0) {
  437. for (int i = 1; i < nglyphs; i++) {
  438. glyphnames[i] = readInt (2);
  439. }
  440. } else if (t == 1) {
  441. int n = 1;
  442. while (n < nglyphs) {
  443. int sid = readInt (2);
  444. int range = readByte () + 1;
  445. for (int i = 0; i < range; i++) {
  446. glyphnames[n++] = sid++;
  447. }
  448. }
  449. } else if (t == 2) {
  450. int n = 1;
  451. while (n < nglyphs) {
  452. int sid = readInt (2);
  453. int range = readInt (2) + 1;
  454. for (int i = 0; i < range; i++) {
  455. glyphnames[n++] = sid++;
  456. }
  457. }
  458. }
  459. }
  460. /**
  461. * read a list of names
  462. * @param base the start of the name table
  463. */
  464. private void readNames (int base) {
  465. pos = base;
  466. int nextra = readInt (2);
  467. names = new String[nextra];
  468. // safenames= new String[nextra];
  469. for (int i = 0; i < nextra; i++) {
  470. Range r = getIndexEntry (base, i);
  471. names[i] = new String (data, r.getStart (), r.getLen ());
  472. // System.out.println("Read name: "+i+" from "+r.getStart()+" to "+r.getEnd()+": "+safe(names[i]));
  473. }
  474. }
  475. /**
  476. * parse the font data.
  477. * @param encdif a dictionary describing the encoding.
  478. */
  479. private void parse () throws IOException {
  480. int majorVersion = readByte ();
  481. int minorVersion = readByte ();
  482. int hdrsz = readByte ();
  483. int offsize = readByte ();
  484. // jump over rest of header: base of font names index
  485. int fnames = hdrsz;
  486. // offset in the file of the array of font dicts
  487. int topdicts = fnames + getIndexSize (fnames);
  488. // offset in the file of local names
  489. int theNames = topdicts + getIndexSize (topdicts);
  490. // offset in the file of the array of global subroutines
  491. gsubrbase = theNames + getIndexSize (theNames);
  492. gsubrsoffset = calcoffset (gsubrbase);
  493. // read extra names
  494. readNames (theNames);
  495. // does this file have more than one font?
  496. pos = topdicts;
  497. if (readInt (2) != 1) {
  498. printData ();
  499. throw new RuntimeException ("More than one font in this file!");
  500. }
  501. Range r = getIndexEntry (fnames, 0);
  502. fontname = new String (data, r.getStart (), r.getLen ());
  503. // read first dict
  504. // System.out.println("TOPDICT[0]:");
  505. readDict (getIndexEntry (topdicts, 0));
  506. // read the private dictionary
  507. // System.out.println("PRIVATE DICT:");
  508. readDict (new Range (privatebase, privatesize));
  509. // calculate the number of glyphs
  510. pos = charstringbase;
  511. nglyphs = readInt (2);
  512. // now get the glyph names
  513. // System.out.println("GLYPHNAMES:");
  514. readGlyphNames (charsetbase);
  515. // now figure out the encoding
  516. // System.out.println("ENCODING:");
  517. readEncodingData (encodingbase);
  518. }
  519. /**
  520. * get the index of a particular name. The name table starts with
  521. * the standard names in FontSupport.stdNames, and is appended by
  522. * any names in the name table from this font's dictionary.
  523. */
  524. private int getNameIndex (String name) {
  525. int val = FontSupport.findName (name, FontSupport.stdNames);
  526. if (val == -1) {
  527. val = FontSupport.findName (name, names) + FontSupport.stdNames.length;
  528. }
  529. if (val == -1) {
  530. val = 0;
  531. }
  532. return val;
  533. }
  534. /**
  535. * convert a string to one in which any non-printable bytes are
  536. * replaced by "<###>" where ## is the value of the byte.
  537. */
  538. private String safe (String src) {
  539. StringBuffer sb = new StringBuffer ();
  540. for (int i = 0; i < src.length (); i++) {
  541. char c = src.charAt (i);
  542. if (c >= 32 && c < 128) {
  543. sb.append (c);
  544. } else {
  545. sb.append ("<" + (int) c + ">");
  546. }
  547. }
  548. return sb.toString ();
  549. }
  550. /**
  551. * Read the data for a glyph from the glyph table, and transform
  552. * it based on the current transform.
  553. *
  554. * @param base the start of the glyph table
  555. * @param offset the index of this glyph in the glyph table
  556. */
  557. private synchronized GeneralPath readGlyph (int base, int offset) {
  558. FlPoint pt = new FlPoint ();
  559. // find this entry
  560. Range r = getIndexEntry (base, offset);
  561. // create a path
  562. GeneralPath gp = new GeneralPath ();
  563. // rember the start position (for recursive calls due to seac)
  564. int hold = pos;
  565. // read the glyph itself
  566. stackptr = 0;
  567. parseGlyph (r, gp, pt);
  568. // restore the start position
  569. pos = hold;
  570. gp.transform (at);
  571. return gp;
  572. }
  573. /**
  574. * calculate an offset code for a dictionary. Uses the count of entries
  575. * to determine what the offset should be.
  576. *
  577. * @param base the index of the start of the dictionary
  578. */
  579. public int calcoffset (int base) {
  580. int len = getTableLength (base);
  581. if (len < 1240) {
  582. return 107;
  583. } else if (len < 33900) {
  584. return 1131;
  585. } else {
  586. return 32768;
  587. }
  588. }
  589. /**
  590. * get the name associated with an ID.
  591. * @param id the index of the name
  592. * @return the name from the FontSupport.stdNames table augmented
  593. * by the local name table
  594. */
  595. public String getSID (int id) {
  596. if (id < FontSupport.stdNames.length) {
  597. return FontSupport.stdNames[id];
  598. } else {
  599. id -= FontSupport.stdNames.length;
  600. return names[id];
  601. }
  602. }
  603. /**
  604. * build an accented character out of two pre-defined glyphs.
  605. * @param x the x offset of the accent
  606. * @param y the y offset of the accent
  607. * @param b the index of the base glyph
  608. * @param a the index of the accent glyph
  609. * @param gp the GeneralPath into which the combined glyph will be
  610. * written.
  611. */
  612. private void buildAccentChar (float x, float y, char b, char a,
  613. GeneralPath gp) {
  614. // get the outline of the accent
  615. GeneralPath pathA = getOutline (a, getWidth (a, null));
  616. // undo the effect of the transform applied in read
  617. AffineTransform xformA = AffineTransform.getTranslateInstance (x, y);
  618. try {
  619. xformA.concatenate (at.createInverse ());
  620. } catch (NoninvertibleTransformException nte) {
  621. // oh well ...
  622. }
  623. pathA.transform (xformA);
  624. GeneralPath pathB = getOutline (b, getWidth (b, null));
  625. try {
  626. AffineTransform xformB = at.createInverse ();
  627. pathB.transform (xformB);
  628. } catch (NoninvertibleTransformException nte) {
  629. // ignore
  630. }
  631. gp.append (pathB, false);
  632. gp.append (pathA, false);
  633. }
  634. /**
  635. * parse a glyph defined in a particular range
  636. * @param r the range of the glyph definition
  637. * @param gp a GeneralPath in which to store the glyph outline
  638. * @param pt a FlPoint representing the end of the current path
  639. */
  640. void parseGlyph (Range r, GeneralPath gp, FlPoint pt) {
  641. pos = r.getStart ();
  642. int i;
  643. float x1, y1, x2, y2, x3, y3, ybase;
  644. int hold;
  645. int stemhints = 0;
  646. while (pos < r.getEnd ()) {
  647. int cmd = readCommand (true);
  648. hold = 0;
  649. switch (cmd) {
  650. case 1: // hstem
  651. case 3: // vstem
  652. stackptr = 0;
  653. break;
  654. case 4: // vmoveto
  655. if (stackptr > 1) { // this is the first call, arg1 is width
  656. stack[0] = stack[1];
  657. }
  658. pt.y += stack[0];
  659. if (pt.open) {
  660. gp.closePath ();
  661. }
  662. pt.open = false;
  663. gp.moveTo (pt.x, pt.y);
  664. stackptr = 0;
  665. break;
  666. case 5: // rlineto
  667. for (i = 0; i < stackptr;) {
  668. pt.x += stack[i++];
  669. pt.y += stack[i++];
  670. gp.lineTo (pt.x, pt.y);
  671. }
  672. pt.open = true;
  673. stackptr = 0;
  674. break;
  675. case 6: // hlineto
  676. for (i = 0; i < stackptr;) {
  677. if ((i & 1) == 0) {
  678. pt.x += stack[i++];
  679. } else {
  680. pt.y += stack[i++];
  681. }
  682. gp.lineTo (pt.x, pt.y);
  683. }
  684. pt.open = true;
  685. stackptr = 0;
  686. break;
  687. case 7: // vlineto
  688. for (i = 0; i < stackptr;) {
  689. if ((i & 1) == 0) {
  690. pt.y += stack[i++];
  691. } else {
  692. pt.x += stack[i++];
  693. }
  694. gp.lineTo (pt.x, pt.y);
  695. }
  696. pt.open = true;
  697. stackptr = 0;
  698. break;
  699. case 8: // rrcurveto
  700. for (i = 0; i < stackptr;) {
  701. x1 = pt.x + stack[i++];
  702. y1 = pt.y + stack[i++];
  703. x2 = x1 + stack[i++];
  704. y2 = y1 + stack[i++];
  705. pt.x = x2 + stack[i++];
  706. pt.y = y2 + stack[i++];
  707. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  708. }
  709. pt.open = true;
  710. stackptr = 0;
  711. break;
  712. case 10: // callsubr
  713. hold = pos;
  714. i = (int) stack[--stackptr] + lsubrsoffset;
  715. Range lsubr = getIndexEntry (lsubrbase, i);
  716. parseGlyph (lsubr, gp, pt);
  717. pos = hold;
  718. break;
  719. case 11: // return
  720. return;
  721. case 14: // endchar
  722. // width x y achar bchar endchar == x y achar bchar seac
  723. if (stackptr == 5) {
  724. buildAccentChar (stack[1], stack[2], (char) stack[3],
  725. (char) stack[4], gp);
  726. }
  727. if (pt.open) {
  728. gp.closePath ();
  729. }
  730. pt.open = false;
  731. stackptr = 0;
  732. break;
  733. case 18: // hstemhm
  734. stemhints += stackptr / 2;
  735. stackptr = 0;
  736. break;
  737. case 19: // hintmask
  738. case 20: // cntrmask
  739. stemhints += stackptr / 2;
  740. pos += (stemhints - 1) / 8 + 1;
  741. stackptr = 0;
  742. break;
  743. case 21: // rmoveto
  744. if (stackptr > 2) {
  745. stack[0] = stack[1];
  746. stack[1] = stack[2];
  747. }
  748. pt.x += stack[0];
  749. pt.y += stack[1];
  750. if (pt.open) {
  751. gp.closePath ();
  752. }
  753. gp.moveTo (pt.x, pt.y);
  754. pt.open = false;
  755. stackptr = 0;
  756. break;
  757. case 22: // hmoveto
  758. if (stackptr > 1) {
  759. stack[0] = stack[1];
  760. }
  761. pt.x += stack[0];
  762. if (pt.open) {
  763. gp.closePath ();
  764. }
  765. gp.moveTo (pt.x, pt.y);
  766. pt.open = false;
  767. stackptr = 0;
  768. break;
  769. case 23: // vstemhm
  770. stemhints += stackptr / 2;
  771. stackptr = 0;
  772. break;
  773. case 24: // rcurveline
  774. for (i = 0; i < stackptr - 2;) {
  775. x1 = pt.x + stack[i++];
  776. y1 = pt.y + stack[i++];
  777. x2 = x1 + stack[i++];
  778. y2 = y1 + stack[i++];
  779. pt.x = x2 + stack[i++];
  780. pt.y = y2 + stack[i++];
  781. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  782. }
  783. pt.x += stack[i++];
  784. pt.y += stack[i++];
  785. gp.lineTo (pt.x, pt.y);
  786. pt.open = true;
  787. stackptr = 0;
  788. break;
  789. case 25: // rlinecurve
  790. for (i = 0; i < stackptr - 6;) {
  791. pt.x += stack[i++];
  792. pt.y += stack[i++];
  793. gp.lineTo (pt.x, pt.y);
  794. }
  795. x1 = pt.x + stack[i++];
  796. y1 = pt.y + stack[i++];
  797. x2 = x1 + stack[i++];
  798. y2 = y1 + stack[i++];
  799. pt.x = x2 + stack[i++];
  800. pt.y = y2 + stack[i++];
  801. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  802. pt.open = true;
  803. stackptr = 0;
  804. break;
  805. case 26: // vvcurveto
  806. i = 0;
  807. if ((stackptr & 1) == 1) { // odd number of arguments
  808. pt.x += stack[i++];
  809. }
  810. while (i < stackptr) {
  811. x1 = pt.x;
  812. y1 = pt.y + stack[i++];
  813. x2 = x1 + stack[i++];
  814. y2 = y1 + stack[i++];
  815. pt.x = x2;
  816. pt.y = y2 + stack[i++];
  817. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  818. }
  819. pt.open = true;
  820. stackptr = 0;
  821. break;
  822. case 27: // hhcurveto
  823. i = 0;
  824. if ((stackptr & 1) == 1) { // odd number of arguments
  825. pt.y += stack[i++];
  826. }
  827. while (i < stackptr) {
  828. x1 = pt.x + stack[i++];
  829. y1 = pt.y;
  830. x2 = x1 + stack[i++];
  831. y2 = y1 + stack[i++];
  832. pt.x = x2 + stack[i++];
  833. pt.y = y2;
  834. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  835. }
  836. pt.open = true;
  837. stackptr = 0;
  838. break;
  839. case 29: // callgsubr
  840. hold = pos;
  841. i = (int) stack[--stackptr] + gsubrsoffset;
  842. Range gsubr = getIndexEntry (gsubrbase, i);
  843. parseGlyph (gsubr, gp, pt);
  844. pos = hold;
  845. break;
  846. case 30: // vhcurveto
  847. hold = 4;
  848. case 31: // hvcurveto
  849. for (i = 0; i < stackptr;) {
  850. boolean hv = (((i + hold) & 4) == 0);
  851. x1 = pt.x + (hv ? stack[i++] : 0);
  852. y1 = pt.y + (hv ? 0 : stack[i++]);
  853. x2 = x1 + stack[i++];
  854. y2 = y1 + stack[i++];
  855. pt.x = x2 + (hv ? 0 : stack[i++]);
  856. pt.y = y2 + (hv ? stack[i++] : 0);
  857. if (i == stackptr - 1) {
  858. if (hv) {
  859. pt.x += stack[i++];
  860. } else {
  861. pt.y += stack[i++];
  862. }
  863. }
  864. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  865. }
  866. pt.open = true;
  867. stackptr = 0;
  868. break;
  869. case 1000: // old dotsection command. ignore.
  870. stackptr = 0;
  871. break;
  872. case 1003: // and
  873. x1 = stack[--stackptr];
  874. y1 = stack[--stackptr];
  875. stack[stackptr++] = ((x1 != 0) && (y1 != 0)) ? 1 : 0;
  876. break;
  877. case 1004: // or
  878. x1 = stack[--stackptr];
  879. y1 = stack[--stackptr];
  880. stack[stackptr++] = ((x1 != 0) || (y1 != 0)) ? 1 : 0;
  881. break;
  882. case 1005: // not
  883. x1 = stack[--stackptr];
  884. stack[stackptr++] = (x1 == 0) ? 1 : 0;
  885. break;
  886. case 1009: // abs
  887. stack[stackptr - 1] = Math.abs (stack[stackptr - 1]);
  888. break;
  889. case 1010: // add
  890. x1 = stack[--stackptr];
  891. y1 = stack[--stackptr];
  892. stack[stackptr++] = x1 + y1;
  893. break;
  894. case 1011: // sub
  895. x1 = stack[--stackptr];
  896. y1 = stack[--stackptr];
  897. stack[stackptr++] = y1 - x1;
  898. break;
  899. case 1012: // div
  900. x1 = stack[--stackptr];
  901. y1 = stack[--stackptr];
  902. stack[stackptr++] = y1 / x1;
  903. break;
  904. case 1014: // neg
  905. stack[stackptr - 1] = -stack[stackptr - 1];
  906. break;
  907. case 1015: // eq
  908. x1 = stack[--stackptr];
  909. y1 = stack[--stackptr];
  910. stack[stackptr++] = (x1 == y1) ? 1 : 0;
  911. break;
  912. case 1018: // drop
  913. stackptr--;
  914. break;
  915. case 1020: // put
  916. i = (int) stack[--stackptr];
  917. x1 = stack[--stackptr];
  918. temps[i] = x1;
  919. break;
  920. case 1021: // get
  921. i = (int) stack[--stackptr];
  922. stack[stackptr++] = temps[i];
  923. break;
  924. case 1022: // ifelse
  925. if (stack[stackptr - 2] > stack[stackptr - 1]) {
  926. stack[stackptr - 4] = stack[stackptr - 3];
  927. }
  928. stackptr -= 3;
  929. break;
  930. case 1023: // random
  931. stack[stackptr++] = (float) Math.random ();
  932. break;
  933. case 1024: // mul
  934. x1 = stack[--stackptr];
  935. y1 = stack[--stackptr];
  936. stack[stackptr++] = y1 * x1;
  937. break;
  938. case 1026: // sqrt
  939. stack[stackptr - 1] = (float) Math.sqrt (stack[stackptr - 1]);
  940. break;
  941. case 1027: // dup
  942. x1 = stack[stackptr - 1];
  943. stack[stackptr++] = x1;
  944. break;
  945. case 1028: // exch
  946. x1 = stack[stackptr - 1];
  947. stack[stackptr - 1] = stack[stackptr - 2];
  948. stack[stackptr - 2] = x1;
  949. break;
  950. case 1029: // index
  951. i = (int) stack[stackptr - 1];
  952. if (i < 0) {
  953. i = 0;
  954. }
  955. stack[stackptr - 1] = stack[stackptr - 2 - i];
  956. break;
  957. case 1030: // roll
  958. i = (int) stack[--stackptr];
  959. int n = (int) stack[--stackptr];
  960. // roll n number by i (+ = upward)
  961. if (i > 0) {
  962. i = i % n;
  963. } else {
  964. i = n - (-i % n);
  965. }
  966. // x x x x i y y y -> y y y x x x x i (where i=3)
  967. if (i > 0) {
  968. float roll[] = new float[n];
  969. System.arraycopy (stack, stackptr - 1 - i, roll, 0, i);
  970. System.arraycopy (stack, stackptr - 1 - n, roll, i,
  971. n - i);
  972. System.arraycopy (roll, 0, stack, stackptr - 1 - n, n);
  973. }
  974. break;
  975. case 1034: // hflex
  976. x1 = pt.x + stack[0];
  977. y1 = ybase = pt.y;
  978. x2 = x1 + stack[1];
  979. y2 = y1 + stack[2];
  980. pt.x = x2 + stack[3];
  981. pt.y = y2;
  982. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  983. x1 = pt.x + stack[4];
  984. y1 = pt.y;
  985. x2 = x1 + stack[5];
  986. y2 = ybase;
  987. pt.x = x2 + stack[6];
  988. pt.y = y2;
  989. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  990. pt.open = true;
  991. stackptr = 0;
  992. break;
  993. case 1035: // flex
  994. x1 = pt.x + stack[0];
  995. y1 = pt.y + stack[1];
  996. x2 = x1 + stack[2];
  997. y2 = y1 + stack[3];
  998. pt.x = x2 + stack[4];
  999. pt.y = y2 + stack[5];
  1000. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  1001. x1 = pt.x + stack[6];
  1002. y1 = pt.y + stack[7];
  1003. x2 = x1 + stack[8];
  1004. y2 = y1 + stack[9];
  1005. pt.x = x2 + stack[10];
  1006. pt.y = y2 + stack[11];
  1007. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  1008. pt.open = true;
  1009. stackptr = 0;
  1010. break;
  1011. case 1036: // hflex1
  1012. ybase = pt.y;
  1013. x1 = pt.x + stack[0];
  1014. y1 = pt.y + stack[1];
  1015. x2 = x1 + stack[2];
  1016. y2 = y1 + stack[3];
  1017. pt.x = x2 + stack[4];
  1018. pt.y = y2;
  1019. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  1020. x1 = pt.x + stack[5];
  1021. y1 = pt.y;
  1022. x2 = x1 + stack[6];
  1023. y2 = y1 + stack[7];
  1024. pt.x = x2 + stack[8];
  1025. pt.y = ybase;
  1026. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  1027. pt.open = true;
  1028. stackptr = 0;
  1029. break;
  1030. case 1037: // flex1
  1031. ybase = pt.y;
  1032. float xbase = pt.x;
  1033. x1 = pt.x + stack[0];
  1034. y1 = pt.y + stack[1];
  1035. x2 = x1 + stack[2];
  1036. y2 = y1 + stack[3];
  1037. pt.x = x2 + stack[4];
  1038. pt.y = y2 + stack[5];
  1039. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  1040. x1 = pt.x + stack[6];
  1041. y1 = pt.y + stack[7];
  1042. x2 = x1 + stack[8];
  1043. y2 = y1 + stack[9];
  1044. if (Math.abs (x2 - xbase) > Math.abs (y2 - ybase)) {
  1045. pt.x = x2 + stack[10];
  1046. pt.y = ybase;
  1047. } else {
  1048. pt.x = xbase;
  1049. pt.y = y2 + stack[10];
  1050. }
  1051. gp.curveTo (x1, y1, x2, y2, pt.x, pt.y);
  1052. pt.open = true;
  1053. stackptr = 0;
  1054. break;
  1055. default:
  1056. System.out.println ("ERROR! TYPE1C CHARSTRING CMD IS " + cmd);
  1057. break;
  1058. }
  1059. }
  1060. }
  1061. /**
  1062. * Get a glyph outline by name
  1063. *
  1064. * @param name the name of the desired glyph
  1065. * @return the glyph outline, or null if unavailable
  1066. */
  1067. protected GeneralPath getOutline (String name, float width) {
  1068. // first find the index of this name
  1069. int index = getNameIndex (name);
  1070. // now find the glyph with that name
  1071. for (int i = 0; i < glyphnames.length; i++) {
  1072. if (glyphnames[i] == index) {
  1073. return readGlyph (charstringbase, i);
  1074. }
  1075. }
  1076. // not found -- return the unknown glyph
  1077. return readGlyph (charstringbase, 0);
  1078. }
  1079. /**
  1080. * Get a glyph outline by character code
  1081. *
  1082. * Note this method must always return an outline
  1083. *
  1084. * @param src the character code of the desired glyph
  1085. * @return the glyph outline
  1086. */
  1087. protected GeneralPath getOutline (char src, float width) {
  1088. // ignore high bits
  1089. int index = (int) (src & 0xff);
  1090. // if we use a standard encoding, the mapping is from glyph to SID
  1091. // therefore we must find the glyph index in the name table
  1092. if (encodingbase == 0 || encodingbase == 1) {
  1093. for (int i = 0; i < glyphnames.length; i++) {
  1094. if (glyphnames[i] == encoding[index]) {
  1095. return readGlyph (charstringbase, i);
  1096. }
  1097. }
  1098. } else {
  1099. // for a custom encoding, the mapping is from glyph to GID, so
  1100. // we can just map the glyph directly
  1101. if (index > 0 && index < encoding.length) {
  1102. return readGlyph (charstringbase, encoding[index]);
  1103. }
  1104. }
  1105. // for some reason the glyph was not found, return the empty glyph
  1106. return readGlyph (charstringbase, 0);
  1107. }
  1108. }