0001: /*
0002: * $Id: Type1CFont.java,v 1.2 2007/12/20 18:33:32 rbair Exp $
0003: *
0004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
0005: * Santa Clara, California 95054, U.S.A. All rights reserved.
0006: *
0007: * This library is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU Lesser General Public
0009: * License as published by the Free Software Foundation; either
0010: * version 2.1 of the License, or (at your option) any later version.
0011: *
0012: * This library is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this library; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
0020: */
0021:
0022: package com.sun.pdfview.font;
0023:
0024: import java.awt.geom.AffineTransform;
0025: import java.awt.geom.GeneralPath;
0026: import java.awt.geom.NoninvertibleTransformException;
0027: import java.io.IOException;
0028:
0029: import com.sun.pdfview.PDFObject;
0030:
0031: /**
0032: * A representation, with parser, of an Adobe Type 1C font.
0033: * @author Mike Wessler
0034: */
0035: public class Type1CFont extends OutlineFont {
0036: String chr2name[] = new String[256];
0037:
0038: byte[] data;
0039: int pos;
0040: byte[] subrs;
0041: float[] stack = new float[100];
0042: int stackptr = 0;
0043:
0044: String names[];
0045: int glyphnames[];
0046: int encoding[] = new int[256];
0047:
0048: String fontname;
0049: AffineTransform at = new AffineTransform(0.001f, 0, 0, 0.001f, 0, 0);
0050:
0051: int num;
0052: float fnum;
0053: int type;
0054: static int CMD = 0;
0055: static int NUM = 1;
0056: static int FLT = 2;
0057:
0058: /**
0059: * create a new Type1CFont based on a font data stream and a descriptor
0060: * @param baseFont the postscript name of this font
0061: * @param src a stream containing the font
0062: * @param descriptor the descriptor for this font
0063: */
0064: public Type1CFont(String baseFont, PDFObject src,
0065: PDFFontDescriptor descriptor) throws IOException {
0066: super (baseFont, src, descriptor);
0067:
0068: PDFObject dataObj = descriptor.getFontFile3();
0069: data = dataObj.getStream();
0070: pos = 0;
0071: parse();
0072:
0073: // TODO: free up (set to null) unused structures (data, subrs, stack)
0074: }
0075:
0076: /**
0077: * a debug method for printing the data
0078: */
0079: private void printData() {
0080: char[] parts = new char[17];
0081: int partsloc = 0;
0082: for (int i = 0; i < data.length; i++) {
0083: int d = ((int) data[i]) & 0xff;
0084: if (d == 0) {
0085: parts[partsloc++] = '.';
0086: } else if (d < 32 || d >= 127) {
0087: parts[partsloc++] = '?';
0088: } else {
0089: parts[partsloc++] = (char) d;
0090: }
0091: if (d < 16) {
0092: System.out.print("0" + Integer.toHexString(d));
0093: } else {
0094: System.out.print(Integer.toHexString(d));
0095: }
0096: if ((i & 15) == 15) {
0097: System.out.println(" " + new String(parts));
0098: partsloc = 0;
0099: } else if ((i & 7) == 7) {
0100: System.out.print(" ");
0101: parts[partsloc++] = ' ';
0102: } else if ((i & 1) == 1) {
0103: System.out.print(" ");
0104: }
0105: }
0106: System.out.println();
0107: }
0108:
0109: /**
0110: * read the next decoded value from the stream
0111: * @param charstring ????
0112: */
0113: private int readNext(boolean charstring) {
0114: num = (int) (data[pos++]) & 0xff;
0115: if (num == 30 && !charstring) { // goofy floatingpoint rep
0116: readFNum();
0117: return type = FLT;
0118: } else if (num == 28) {
0119: num = (((int) data[pos]) << 8)
0120: + (((int) data[pos + 1]) & 0xff);
0121: pos += 2;
0122: return type = NUM;
0123: } else if (num == 29 && !charstring) {
0124: num = (((int) data[pos] & 0xff) << 24)
0125: | (((int) data[pos + 1] & 0xff) << 16)
0126: | (((int) data[pos + 2] & 0xff) << 8)
0127: | (((int) data[pos + 3] & 0xff));
0128: pos += 4;
0129: return type = NUM;
0130: } else if (num == 12) { // two-byte command
0131: num = 1000 + ((int) (data[pos++]) & 0xff);
0132: return type = CMD;
0133: } else if (num < 32) {
0134: return type = CMD;
0135: } else if (num < 247) {
0136: num -= 139;
0137: return type = NUM;
0138: } else if (num < 251) {
0139: num = (num - 247) * 256 + (((int) data[pos++]) & 0xff)
0140: + 108;
0141: return type = NUM;
0142: } else if (num < 255) {
0143: num = -(num - 251) * 256 - (((int) data[pos++]) & 0xff)
0144: - 108;
0145: return type = NUM;
0146: } else if (!charstring) { // dict shouldn't have a 255 code
0147: printData();
0148: throw new RuntimeException(
0149: "Got a 255 code while reading dict");
0150: } else { // num was 255
0151: fnum = ((((int) data[pos] & 0xff) << 24)
0152: | (((int) data[pos + 1] & 0xff) << 16)
0153: | (((int) data[pos + 2] & 0xff) << 8) | (((int) data[pos + 3] & 0xff))) / 65536f;
0154: pos += 4;
0155: return type = FLT;
0156: }
0157: }
0158:
0159: /**
0160: * read the next funky floating point number from the input stream.
0161: * value gets put into the fnum field.
0162: */
0163: public void readFNum() {
0164: // work in nybbles: 0-9=0-9, a=. b=E, c=E-, d=rsvd e=neg f=end
0165: float f = 0;
0166: boolean neg = false;
0167: int exp = 0;
0168: int eval = 0;
0169: float mul = 1;
0170: byte work = data[pos++];
0171: while (true) {
0172: if (work == (byte) 0xdd) {
0173: work = data[pos++];
0174: }
0175: int nyb = (work >> 4) & 0xf;
0176: work = (byte) ((work << 4) | 0xd);
0177: if (nyb < 10) {
0178: if (exp != 0) { // working on the exponent
0179: eval = eval * 10 + nyb;
0180: } else if (mul == 1) { // working on an int
0181: f = f * 10 + nyb;
0182: } else { // working on decimal part
0183: f += nyb * mul;
0184: mul /= 10f;
0185: }
0186: } else if (nyb == 0xa) { // decimal
0187: mul = 0.1f;
0188: } else if (nyb == 0xb) { // E+
0189: exp = 1;
0190: } else if (nyb == 0xc) { // E-
0191: exp = -1;
0192: } else if (nyb == 0xe) { // neg
0193: neg = true;
0194: } else {
0195: break;
0196: }
0197: }
0198: fnum = (neg ? -1 : 1) * f * (float) Math.pow(10, eval * exp);
0199: }
0200:
0201: /**
0202: * read an integer from the input stream
0203: * @param len the number of bytes in the integer
0204: * @return the integer
0205: */
0206: private int readInt(int len) {
0207: int n = 0;
0208: for (int i = 0; i < len; i++) {
0209: n = (n << 8) | (((int) data[pos++]) & 0xff);
0210: }
0211: return n;
0212: }
0213:
0214: /**
0215: * read the next byte from the stream
0216: * @return the byte
0217: */
0218: private int readByte() {
0219: return ((int) data[pos++]) & 0xff;
0220: }
0221:
0222: // DICT structure:
0223: // operand operator operand operator ...
0224:
0225: // INDEX structure:
0226: // count(2) offsize [offset offset ... offset] data
0227: // offset array has count+1 entries
0228: // data starts at 3+(count+1)*offsize
0229: // offset for data is offset+2+(count+1)*offsize
0230:
0231: /**
0232: * get the size of the dictionary located within the stream at
0233: * some offset.
0234: * @param loc the index of the start of the dictionary
0235: * @return the size of the dictionary, in bytes.
0236: */
0237: public int getIndexSize(int loc) {
0238: // System.out.println("Getting size of index at "+loc);
0239: int hold = pos;
0240: pos = loc;
0241: int count = readInt(2);
0242: if (count == 0) {
0243: return 2;
0244: }
0245: int encsz = readByte();
0246: // pos is now at the first offset. last offset is at count*encsz
0247: pos += count * encsz;
0248: int end = readInt(encsz);
0249: pos = hold;
0250: return 2 + (count + 1) * encsz + end;
0251: }
0252:
0253: /**
0254: * A range. There's probably a version of this class floating around
0255: * somewhere already in Java.
0256: */
0257: class Range {
0258: private int start;
0259: private int len;
0260:
0261: public Range(int start, int len) {
0262: this .start = start;
0263: this .len = len;
0264: }
0265:
0266: public final int getStart() {
0267: return start;
0268: }
0269:
0270: public final int getLen() {
0271: return len;
0272: }
0273:
0274: public final int getEnd() {
0275: return start + len;
0276: }
0277: }
0278:
0279: /**
0280: * Get the range of a particular index in a dictionary.
0281: * @param index the start of the dictionary.
0282: * @param id the index of the entry in the dictionary
0283: * @return a range describing the offsets of the start and end of
0284: * the entry from the start of the file, not the dictionary
0285: */
0286: Range getIndexEntry(int index, int id) {
0287: int hold = pos;
0288: pos = index;
0289: int count = readInt(2);
0290: int encsz = readByte();
0291: pos += encsz * id;
0292: int from = readInt(encsz);
0293: Range r = new Range(from + 2 + index + encsz * (count + 1),
0294: readInt(encsz) - from);
0295: pos = hold;
0296: return r;
0297: }
0298:
0299: // Top DICT: NAME CODE DEFAULT
0300: // charstringtype 12 6 2
0301: // fontmatrix 12 7 0.001 0 0 0.001
0302: // charset 15 - (offset) names of glyphs (ref to name idx)
0303: // encoding 16 - (offset) array of codes
0304: // CharStrings 17 - (offset)
0305: // Private 18 - (size, offset)
0306:
0307: // glyph at position i in CharStrings has name charset[i]
0308: // and code encoding[i]
0309:
0310: int charstringtype = 2;
0311: float temps[] = new float[32];
0312: int charsetbase = 0;
0313: int encodingbase = 0;
0314: int charstringbase = 0;
0315: int privatebase = 0;
0316: int privatesize = 0;
0317: int gsubrbase = 0;
0318: int lsubrbase = 0;
0319: int gsubrsoffset = 0;
0320: int lsubrsoffset = 0;
0321:
0322: int nglyphs = 1;
0323:
0324: /**
0325: * read a dictionary that exists within some range, parsing the entries
0326: * within the dictionary.
0327: */
0328: private void readDict(Range r) {
0329: // System.out.println("reading dictionary from "+r.getStart()+" to "+r.getEnd());
0330: pos = r.getStart();
0331: while (pos < r.getEnd()) {
0332: int cmd = readCommand(false);
0333: if (cmd == 1006) { // charstringtype, default=2
0334: charstringtype = (int) stack[0];
0335: } else if (cmd == 1007) { // fontmatrix
0336: if (stackptr == 4) {
0337: at = new AffineTransform((float) stack[0],
0338: (float) stack[1], (float) stack[2],
0339: (float) stack[3], 0, 0);
0340: } else {
0341: at = new AffineTransform((float) stack[0],
0342: (float) stack[1], (float) stack[2],
0343: (float) stack[3], (float) stack[4],
0344: (float) stack[5]);
0345: }
0346: } else if (cmd == 15) { // charset
0347: charsetbase = (int) stack[0];
0348: } else if (cmd == 16) { // encoding
0349: encodingbase = (int) stack[0];
0350: } else if (cmd == 17) { // charstrings
0351: charstringbase = (int) stack[0];
0352: } else if (cmd == 18) { // private
0353: privatesize = (int) stack[0];
0354: privatebase = (int) stack[1];
0355: } else if (cmd == 19) { // subrs (in Private dict)
0356: lsubrbase = (int) stack[0];
0357: lsubrsoffset = calcoffset(lsubrbase);
0358: }
0359: stackptr = 0;
0360: }
0361: }
0362:
0363: /**
0364: * read a complete command. this may involve several numbers
0365: * which go onto a stack before an actual command is read.
0366: * @param charstring ????
0367: * @return the command. Some numbers may also be on the stack.
0368: */
0369: private int readCommand(boolean charstring) {
0370: while (true) {
0371: int t = readNext(charstring);
0372: if (t == CMD) {
0373: /*
0374: System.out.print("CMD= "+num+", args=");
0375: for (int i=0; i<stackptr; i++) {
0376: System.out.print(" "+stack[i]);
0377: }
0378: System.out.println();
0379: */
0380: return num;
0381: } else {
0382: stack[stackptr++] = (t == NUM) ? (float) num
0383: : (float) fnum;
0384: }
0385: }
0386: }
0387:
0388: /**
0389: * parse information about the encoding of this file.
0390: * @param base the start of the encoding data
0391: */
0392: private void readEncodingData(int base) {
0393: if (base == 0) { // this is the StandardEncoding
0394: // System.out.println("**** STANDARD ENCODING!");
0395: System.arraycopy(FontSupport.standardEncoding, 0, encoding,
0396: 0, FontSupport.standardEncoding.length);
0397: } else if (base == 1) { // this is the expert encoding
0398: System.out.println("**** EXPERT ENCODING!");
0399: // TODO: copy ExpertEncoding
0400: } else {
0401: pos = base;
0402: int encodingtype = readByte();
0403: if ((encodingtype & 127) == 0) {
0404: int ncodes = readByte();
0405: for (int i = 1; i < ncodes + 1; i++) {
0406: int idx = readByte() & 0xff;
0407: encoding[idx] = i;
0408: }
0409: } else if ((encodingtype & 127) == 1) {
0410: int nranges = readByte();
0411: int p = 1;
0412: for (int i = 0; i < nranges; i++) {
0413: int start = readByte();
0414: int more = readByte();
0415: for (int j = start; j < start + more + 1; j++) {
0416: encoding[j] = p++;
0417: }
0418: }
0419: } else {
0420: System.out
0421: .println("Bad encoding type: " + encodingtype);
0422: }
0423: // TODO: now check for supplemental encoding data
0424: }
0425: }
0426:
0427: /**
0428: * read the names of the glyphs.
0429: * @param base the start of the glyph name table
0430: */
0431: private void readGlyphNames(int base) {
0432: if (base == 0) {
0433: glyphnames = new int[229];
0434: for (int i = 0; i < glyphnames.length; i++) {
0435: glyphnames[i] = i;
0436: }
0437: return;
0438: } else if (base == 1) {
0439: glyphnames = FontSupport.type1CExpertCharset;
0440: return;
0441: } else if (base == 2) {
0442: glyphnames = FontSupport.type1CExpertSubCharset;
0443: return;
0444: }
0445: // nglyphs has already been set.
0446: glyphnames = new int[nglyphs];
0447: glyphnames[0] = 0;
0448: pos = base;
0449: int t = readByte();
0450: if (t == 0) {
0451: for (int i = 1; i < nglyphs; i++) {
0452: glyphnames[i] = readInt(2);
0453: }
0454: } else if (t == 1) {
0455: int n = 1;
0456: while (n < nglyphs) {
0457: int sid = readInt(2);
0458: int range = readByte() + 1;
0459: for (int i = 0; i < range; i++) {
0460: glyphnames[n++] = sid++;
0461: }
0462: }
0463: } else if (t == 2) {
0464: int n = 1;
0465: while (n < nglyphs) {
0466: int sid = readInt(2);
0467: int range = readInt(2) + 1;
0468: for (int i = 0; i < range; i++) {
0469: glyphnames[n++] = sid++;
0470: }
0471: }
0472: }
0473: }
0474:
0475: /**
0476: * read a list of names
0477: * @param base the start of the name table
0478: */
0479: private void readNames(int base) {
0480: pos = base;
0481: int nextra = readInt(2);
0482: names = new String[nextra];
0483: // safenames= new String[nextra];
0484: for (int i = 0; i < nextra; i++) {
0485: Range r = getIndexEntry(base, i);
0486: names[i] = new String(data, r.getStart(), r.getLen());
0487: // System.out.println("Read name: "+i+" from "+r.getStart()+" to "+r.getEnd()+": "+safe(names[i]));
0488: }
0489: }
0490:
0491: /**
0492: * parse the font data.
0493: * @param encdif a dictionary describing the encoding.
0494: */
0495: private void parse() throws IOException {
0496: int majorVersion = readByte();
0497: int minorVersion = readByte();
0498: int hdrsz = readByte();
0499: int offsize = readByte();
0500: // jump over rest of header: base of font names index
0501: int fnames = hdrsz;
0502: // offset in the file of the array of font dicts
0503: int topdicts = fnames + getIndexSize(fnames);
0504: // offset in the file of local names
0505: int theNames = topdicts + getIndexSize(topdicts);
0506: // offset in the file of the array of global subroutines
0507: gsubrbase = theNames + getIndexSize(theNames);
0508: gsubrsoffset = calcoffset(gsubrbase);
0509: // read extra names
0510: readNames(theNames);
0511: // does this file have more than one font?
0512: pos = topdicts;
0513: if (readInt(2) != 1) {
0514: printData();
0515: throw new RuntimeException(
0516: "More than one font in this file!");
0517: }
0518: Range r = getIndexEntry(fnames, 0);
0519: fontname = new String(data, r.getStart(), r.getLen());
0520: // read first dict
0521: // System.out.println("TOPDICT[0]:");
0522: readDict(getIndexEntry(topdicts, 0));
0523: // read the private dictionary
0524: // System.out.println("PRIVATE DICT:");
0525: readDict(new Range(privatebase, privatesize));
0526: // calculate the number of glyphs
0527: pos = charstringbase;
0528: nglyphs = readInt(2);
0529: // now get the glyph names
0530: // System.out.println("GLYPHNAMES:");
0531: readGlyphNames(charsetbase);
0532: // now figure out the encoding
0533: // System.out.println("ENCODING:");
0534: readEncodingData(encodingbase);
0535: }
0536:
0537: /**
0538: * get the index of a particular name. The name table starts with
0539: * the standard names in FontSupport.stdNames, and is appended by
0540: * any names in the name table from this font's dictionary.
0541: */
0542: private int getNameIndex(String name) {
0543: int val = FontSupport.findName(name, FontSupport.stdNames);
0544: if (val == -1) {
0545: val = FontSupport.findName(name, names)
0546: + FontSupport.stdNames.length;
0547: }
0548: if (val == -1) {
0549: val = 0;
0550: }
0551: return val;
0552: }
0553:
0554: /**
0555: * convert a string to one in which any non-printable bytes are
0556: * replaced by "<###>" where ## is the value of the byte.
0557: */
0558: private String safe(String src) {
0559: StringBuffer sb = new StringBuffer();
0560: for (int i = 0; i < src.length(); i++) {
0561: char c = src.charAt(i);
0562: if (c >= 32 && c < 128) {
0563: sb.append(c);
0564: } else {
0565: sb.append("<" + (int) c + ">");
0566: }
0567: }
0568: return sb.toString();
0569: }
0570:
0571: /**
0572: * Read the data for a glyph from the glyph table, and transform
0573: * it based on the current transform.
0574: *
0575: * @param base the start of the glyph table
0576: * @param offset the index of this glyph in the glyph table
0577: */
0578: private synchronized GeneralPath readGlyph(int base, int offset) {
0579: FlPoint pt = new FlPoint();
0580:
0581: // find this entry
0582: Range r = getIndexEntry(base, offset);
0583:
0584: // create a path
0585: GeneralPath gp = new GeneralPath();
0586:
0587: // rember the start position (for recursive calls due to seac)
0588: int hold = pos;
0589:
0590: // read the glyph itself
0591: stackptr = 0;
0592: parseGlyph(r, gp, pt);
0593:
0594: // restore the start position
0595: pos = hold;
0596:
0597: gp.transform(at);
0598:
0599: return gp;
0600: }
0601:
0602: /**
0603: * calculate an offset code for a dictionary
0604: * @param base the index of the start of the dictionary
0605: */
0606: public int calcoffset(int base) {
0607: int len = getIndexSize(base);
0608: if (len < 1240) {
0609: return -107;
0610: } else if (len < 33900) {
0611: return -1131;
0612: } else {
0613: return -32768;
0614: }
0615: }
0616:
0617: /**
0618: * get the name associated with an ID.
0619: * @param id the index of the name
0620: * @return the name from the FontSupport.stdNames table augmented
0621: * by the local name table
0622: */
0623: public String getSID(int id) {
0624: if (id < FontSupport.stdNames.length) {
0625: return FontSupport.stdNames[id];
0626: } else {
0627: id -= FontSupport.stdNames.length;
0628: return names[id];
0629: }
0630: }
0631:
0632: /**
0633: * build an accented character out of two pre-defined glyphs.
0634: * @param x the x offset of the accent
0635: * @param y the y offset of the accent
0636: * @param b the index of the base glyph
0637: * @param a the index of the accent glyph
0638: * @param gp the GeneralPath into which the combined glyph will be
0639: * written.
0640: */
0641: private void buildAccentChar(float x, float y, char b, char a,
0642: GeneralPath gp) {
0643: // get the outline of the accent
0644: GeneralPath pathA = getOutline(a, getWidth(a, null));
0645:
0646: // undo the effect of the transform applied in read
0647: AffineTransform xformA = AffineTransform.getTranslateInstance(
0648: x, y);
0649: try {
0650: xformA.concatenate(at.createInverse());
0651: } catch (NoninvertibleTransformException nte) {
0652: // oh well ...
0653: }
0654: pathA.transform(xformA);
0655:
0656: GeneralPath pathB = getOutline(b, getWidth(b, null));
0657:
0658: try {
0659: AffineTransform xformB = at.createInverse();
0660: pathB.transform(xformB);
0661: } catch (NoninvertibleTransformException nte) {
0662: // ignore
0663: }
0664:
0665: gp.append(pathB, false);
0666: gp.append(pathA, false);
0667: }
0668:
0669: /**
0670: * parse a glyph defined in a particular range
0671: * @param r the range of the glyph definition
0672: * @param gp a GeneralPath in which to store the glyph outline
0673: * @param pt a FlPoint representing the end of the current path
0674: */
0675: void parseGlyph(Range r, GeneralPath gp, FlPoint pt) {
0676: pos = r.getStart();
0677: int i;
0678: float x1, y1, x2, y2, x3, y3, ybase;
0679: int hold;
0680: int stemhints = 0;
0681: while (pos < r.getEnd()) {
0682: int cmd = readCommand(true);
0683: hold = 0;
0684: switch (cmd) {
0685: case 1: // hstem
0686: case 3: // vstem
0687: stackptr = 0;
0688: break;
0689: case 4: // vmoveto
0690: if (stackptr > 1) { // this is the first call, arg1 is width
0691: stack[0] = stack[1];
0692: }
0693: pt.y += stack[0];
0694: if (pt.open) {
0695: gp.closePath();
0696: }
0697: pt.open = false;
0698: gp.moveTo(pt.x, pt.y);
0699: stackptr = 0;
0700: break;
0701: case 5: // rlineto
0702: for (i = 0; i < stackptr;) {
0703: pt.x += stack[i++];
0704: pt.y += stack[i++];
0705: gp.lineTo(pt.x, pt.y);
0706: }
0707: pt.open = true;
0708: stackptr = 0;
0709: break;
0710: case 6: // hlineto
0711: for (i = 0; i < stackptr;) {
0712: if ((i & 1) == 0) {
0713: pt.x += stack[i++];
0714: } else {
0715: pt.y += stack[i++];
0716: }
0717: gp.lineTo(pt.x, pt.y);
0718: }
0719: pt.open = true;
0720: stackptr = 0;
0721: break;
0722: case 7: // vlineto
0723: for (i = 0; i < stackptr;) {
0724: if ((i & 1) == 0) {
0725: pt.y += stack[i++];
0726: } else {
0727: pt.x += stack[i++];
0728: }
0729: gp.lineTo(pt.x, pt.y);
0730: }
0731: pt.open = true;
0732: stackptr = 0;
0733: break;
0734: case 8: // rrcurveto
0735: for (i = 0; i < stackptr;) {
0736: x1 = pt.x + stack[i++];
0737: y1 = pt.y + stack[i++];
0738: x2 = x1 + stack[i++];
0739: y2 = y1 + stack[i++];
0740: pt.x = x2 + stack[i++];
0741: pt.y = y2 + stack[i++];
0742: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
0743: }
0744: pt.open = true;
0745: stackptr = 0;
0746: break;
0747: case 10: // callsubr
0748: hold = pos;
0749: i = (int) stack[--stackptr] + lsubrsoffset;
0750: Range lsubr = getIndexEntry(lsubrbase, i);
0751: parseGlyph(lsubr, gp, pt);
0752: pos = hold;
0753: break;
0754: case 11: // return
0755: return;
0756: case 14: // endchar
0757: // width x y achar bchar endchar == x y achar bchar seac
0758: if (stackptr == 5) {
0759: buildAccentChar(stack[1], stack[2],
0760: (char) stack[3], (char) stack[4], gp);
0761: }
0762: if (pt.open) {
0763: gp.closePath();
0764: }
0765: pt.open = false;
0766: stackptr = 0;
0767: break;
0768: case 18: // hstemhm
0769: stemhints += stackptr / 2;
0770: stackptr = 0;
0771: break;
0772: case 19: // hintmask
0773: case 20: // cntrmask
0774: stemhints += stackptr / 2;
0775: pos += (stemhints - 1) / 8 + 1;
0776: stackptr = 0;
0777: break;
0778: case 21: // rmoveto
0779: if (stackptr > 2) {
0780: stack[0] = stack[1];
0781: stack[1] = stack[2];
0782: }
0783: pt.x += stack[0];
0784: pt.y += stack[1];
0785: if (pt.open) {
0786: gp.closePath();
0787: }
0788: gp.moveTo(pt.x, pt.y);
0789: pt.open = false;
0790: stackptr = 0;
0791: break;
0792: case 22: // hmoveto
0793: if (stackptr > 1) {
0794: stack[0] = stack[1];
0795: }
0796: pt.x += stack[0];
0797: if (pt.open) {
0798: gp.closePath();
0799: }
0800: gp.moveTo(pt.x, pt.y);
0801: pt.open = false;
0802: stackptr = 0;
0803: break;
0804: case 23: // vstemhm
0805: stemhints += stackptr / 2;
0806: stackptr = 0;
0807: break;
0808: case 24: // rcurveline
0809: for (i = 0; i < stackptr - 2;) {
0810: x1 = pt.x + stack[i++];
0811: y1 = pt.y + stack[i++];
0812: x2 = x1 + stack[i++];
0813: y2 = y1 + stack[i++];
0814: pt.x = x2 + stack[i++];
0815: pt.y = y2 + stack[i++];
0816: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
0817: }
0818: pt.x += stack[i++];
0819: pt.y += stack[i++];
0820: gp.lineTo(pt.x, pt.y);
0821: pt.open = true;
0822: stackptr = 0;
0823: break;
0824: case 25: // rlinecurve
0825: for (i = 0; i < stackptr - 6;) {
0826: pt.x += stack[i++];
0827: pt.y += stack[i++];
0828: gp.lineTo(pt.x, pt.y);
0829: }
0830: x1 = pt.x + stack[i++];
0831: y1 = pt.y + stack[i++];
0832: x2 = x1 + stack[i++];
0833: y2 = y1 + stack[i++];
0834: pt.x = x2 + stack[i++];
0835: pt.y = y2 + stack[i++];
0836: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
0837: pt.open = true;
0838: stackptr = 0;
0839: break;
0840: case 26: // vvcurveto
0841: i = 0;
0842: if ((stackptr & 1) == 1) { // odd number of arguments
0843: pt.x += stack[i++];
0844: }
0845: while (i < stackptr) {
0846: x1 = pt.x;
0847: y1 = pt.y + stack[i++];
0848: x2 = x1 + stack[i++];
0849: y2 = y1 + stack[i++];
0850: pt.x = x2;
0851: pt.y = y2 + stack[i++];
0852: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
0853: }
0854: pt.open = true;
0855: stackptr = 0;
0856: break;
0857: case 27: // hhcurveto
0858: i = 0;
0859: if ((stackptr & 1) == 1) { // odd number of arguments
0860: pt.y += stack[i++];
0861: }
0862: while (i < stackptr) {
0863: x1 = pt.x + stack[i++];
0864: y1 = pt.y;
0865: x2 = x1 + stack[i++];
0866: y2 = y1 + stack[i++];
0867: pt.x = x2 + stack[i++];
0868: pt.y = y2;
0869: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
0870: }
0871: pt.open = true;
0872: stackptr = 0;
0873: break;
0874: case 29: // callgsubr
0875: hold = pos;
0876: i = (int) stack[--stackptr] + gsubrsoffset;
0877: Range gsubr = getIndexEntry(gsubrbase, i);
0878: parseGlyph(gsubr, gp, pt);
0879: pos = hold;
0880: break;
0881: case 30: // vhcurveto
0882: hold = 4;
0883: case 31: // hvcurveto
0884: for (i = 0; i < stackptr;) {
0885: boolean hv = (((i + hold) & 4) == 0);
0886: x1 = pt.x + (hv ? stack[i++] : 0);
0887: y1 = pt.y + (hv ? 0 : stack[i++]);
0888: x2 = x1 + stack[i++];
0889: y2 = y1 + stack[i++];
0890: pt.x = x2 + (hv ? 0 : stack[i++]);
0891: pt.y = y2 + (hv ? stack[i++] : 0);
0892: if (i == stackptr - 1) {
0893: if (hv) {
0894: pt.x += stack[i++];
0895: } else {
0896: pt.y += stack[i++];
0897: }
0898: }
0899: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
0900: }
0901: pt.open = true;
0902: stackptr = 0;
0903: break;
0904: case 1000: // old dotsection command. ignore.
0905: stackptr = 0;
0906: break;
0907: case 1003: // and
0908: x1 = stack[--stackptr];
0909: y1 = stack[--stackptr];
0910: stack[stackptr++] = ((x1 != 0) && (y1 != 0)) ? 1 : 0;
0911: break;
0912: case 1004: // or
0913: x1 = stack[--stackptr];
0914: y1 = stack[--stackptr];
0915: stack[stackptr++] = ((x1 != 0) || (y1 != 0)) ? 1 : 0;
0916: break;
0917: case 1005: // not
0918: x1 = stack[--stackptr];
0919: stack[stackptr++] = (x1 == 0) ? 1 : 0;
0920: break;
0921: case 1009: // abs
0922: stack[stackptr - 1] = Math.abs(stack[stackptr - 1]);
0923: break;
0924: case 1010: // add
0925: x1 = stack[--stackptr];
0926: y1 = stack[--stackptr];
0927: stack[stackptr++] = x1 + y1;
0928: break;
0929: case 1011: // sub
0930: x1 = stack[--stackptr];
0931: y1 = stack[--stackptr];
0932: stack[stackptr++] = y1 - x1;
0933: break;
0934: case 1012: // div
0935: x1 = stack[--stackptr];
0936: y1 = stack[--stackptr];
0937: stack[stackptr++] = y1 / x1;
0938: break;
0939: case 1014: // neg
0940: stack[stackptr - 1] = -stack[stackptr - 1];
0941: break;
0942: case 1015: // eq
0943: x1 = stack[--stackptr];
0944: y1 = stack[--stackptr];
0945: stack[stackptr++] = (x1 == y1) ? 1 : 0;
0946: break;
0947: case 1018: // drop
0948: stackptr--;
0949: break;
0950: case 1020: // put
0951: i = (int) stack[--stackptr];
0952: x1 = stack[--stackptr];
0953: temps[i] = x1;
0954: break;
0955: case 1021: // get
0956: i = (int) stack[--stackptr];
0957: stack[stackptr++] = temps[i];
0958: break;
0959: case 1022: // ifelse
0960: if (stack[stackptr - 2] > stack[stackptr - 1]) {
0961: stack[stackptr - 4] = stack[stackptr - 3];
0962: }
0963: stackptr -= 3;
0964: break;
0965: case 1023: // random
0966: stack[stackptr++] = (float) Math.random();
0967: break;
0968: case 1024: // mul
0969: x1 = stack[--stackptr];
0970: y1 = stack[--stackptr];
0971: stack[stackptr++] = y1 * x1;
0972: break;
0973: case 1026: // sqrt
0974: stack[stackptr - 1] = (float) Math
0975: .sqrt(stack[stackptr - 1]);
0976: break;
0977: case 1027: // dup
0978: x1 = stack[stackptr - 1];
0979: stack[stackptr++] = x1;
0980: break;
0981: case 1028: // exch
0982: x1 = stack[stackptr - 1];
0983: stack[stackptr - 1] = stack[stackptr - 2];
0984: stack[stackptr - 2] = x1;
0985: break;
0986: case 1029: // index
0987: i = (int) stack[stackptr - 1];
0988: if (i < 0)
0989: i = 0;
0990: stack[stackptr - 1] = stack[stackptr - 2 - i];
0991: break;
0992: case 1030: // roll
0993: i = (int) stack[--stackptr];
0994: int n = (int) stack[--stackptr];
0995: // roll n number by i (+ = upward)
0996: if (i > 0) {
0997: i = i % n;
0998: } else {
0999: i = n - (-i % n);
1000: }
1001: // x x x x i y y y -> y y y x x x x i (where i=3)
1002: if (i > 0) {
1003: float roll[] = new float[n];
1004: System.arraycopy(stack, stackptr - 1 - i, roll, 0,
1005: i);
1006: System.arraycopy(stack, stackptr - 1 - n, roll, i,
1007: n - i);
1008: System.arraycopy(roll, 0, stack, stackptr - 1 - n,
1009: n);
1010: }
1011: break;
1012: case 1034: // hflex
1013: x1 = pt.x + stack[0];
1014: y1 = ybase = pt.y;
1015: x2 = x1 + stack[1];
1016: y2 = y1 + stack[2];
1017: pt.x = x2 + stack[3];
1018: pt.y = y2;
1019: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1020: x1 = pt.x + stack[4];
1021: y1 = pt.y;
1022: x2 = x1 + stack[5];
1023: y2 = ybase;
1024: pt.x = x2 + stack[6];
1025: pt.y = y2;
1026: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1027: pt.open = true;
1028: stackptr = 0;
1029: break;
1030: case 1035: // flex
1031: x1 = pt.x + stack[0];
1032: y1 = pt.y + stack[1];
1033: x2 = x1 + stack[2];
1034: y2 = y1 + stack[3];
1035: pt.x = x2 + stack[4];
1036: pt.y = y2 + stack[5];
1037: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1038: x1 = pt.x + stack[6];
1039: y1 = pt.y + stack[7];
1040: x2 = x1 + stack[8];
1041: y2 = y1 + stack[9];
1042: pt.x = x2 + stack[10];
1043: pt.y = y2 + stack[11];
1044: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1045: pt.open = true;
1046: stackptr = 0;
1047: break;
1048: case 1036: // hflex1
1049: ybase = pt.y;
1050: x1 = pt.x + stack[0];
1051: y1 = pt.y + stack[1];
1052: x2 = x1 + stack[2];
1053: y2 = y1 + stack[3];
1054: pt.x = x2 + stack[4];
1055: pt.y = y2;
1056: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1057: x1 = pt.x + stack[5];
1058: y1 = pt.y;
1059: x2 = x1 + stack[6];
1060: y2 = y1 + stack[7];
1061: pt.x = x2 + stack[8];
1062: pt.y = ybase;
1063: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1064: pt.open = true;
1065: stackptr = 0;
1066: break;
1067: case 1037: // flex1
1068: ybase = pt.y;
1069: float xbase = pt.x;
1070: x1 = pt.x + stack[0];
1071: y1 = pt.y + stack[1];
1072: x2 = x1 + stack[2];
1073: y2 = y1 + stack[3];
1074: pt.x = x2 + stack[4];
1075: pt.y = y2 + stack[5];
1076: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1077: x1 = pt.x + stack[6];
1078: y1 = pt.y + stack[7];
1079: x2 = x1 + stack[8];
1080: y2 = y1 + stack[9];
1081: if (Math.abs(x2 - xbase) > Math.abs(y2 - ybase)) {
1082: pt.x = x2 + stack[10];
1083: pt.y = ybase;
1084: } else {
1085: pt.x = xbase;
1086: pt.y = y2 + stack[10];
1087: }
1088: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1089: pt.open = true;
1090: stackptr = 0;
1091: break;
1092: default:
1093: System.out.println("ERROR! TYPE1C CHARSTRING CMD IS "
1094: + cmd);
1095: break;
1096: }
1097: }
1098: }
1099:
1100: /**
1101: * Get a glyph outline by name
1102: *
1103: * @param name the name of the desired glyph
1104: * @return the glyph outline, or null if unavailable
1105: */
1106: protected GeneralPath getOutline(String name, float width) {
1107: // first find the index of this name
1108: int index = getNameIndex(name);
1109:
1110: // now find the glyph with that name
1111: for (int i = 0; i < glyphnames.length; i++) {
1112: if (glyphnames[i] == index) {
1113: return readGlyph(charstringbase, i);
1114: }
1115: }
1116:
1117: // not found -- return the unknown glyph
1118: return readGlyph(charstringbase, 0);
1119: }
1120:
1121: /**
1122: * Get a glyph outline by character code
1123: *
1124: * Note this method must always return an outline
1125: *
1126: * @param src the character code of the desired glyph
1127: * @return the glyph outline
1128: */
1129: protected GeneralPath getOutline(char src, float width) {
1130: // ignore high bits
1131: int index = (int) (src & 0xff);
1132:
1133: // if we use a standard encoding, the mapping is from glyph to SID
1134: // therefore we must find the glyph index in the name table
1135: if (encodingbase == 0 || encodingbase == 1) {
1136: for (int i = 0; i < glyphnames.length; i++) {
1137: if (glyphnames[i] == encoding[index]) {
1138: return readGlyph(charstringbase, i);
1139: }
1140: }
1141: } else {
1142: // for a custom encoding, the mapping is from glyph to GID, so
1143: // we can just map the glyph directly
1144: if (index > 0 && index < encoding.length) {
1145: return readGlyph(charstringbase, encoding[index]);
1146: }
1147: }
1148:
1149: // for some reason the glyph was not found, return the empty glyph
1150: return readGlyph(charstringbase, 0);
1151: }
1152: }
|