0001: /*
0002: * $Id: TestType1CFont.java,v 1.3 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 test;
0023:
0024: import javax.swing.*;
0025: import java.awt.*;
0026: import java.awt.event.*;
0027: import java.io.*;
0028: import java.util.*;
0029: import java.awt.geom.*;
0030:
0031: import com.sun.pdfview.font.*;
0032:
0033: public class TestType1CFont extends JPanel implements KeyListener {
0034: int encoding[] = new int[256]; // glyph at position i has code encoding[i]
0035: int glyphnames[]; // glyph at position i has name SID glyphnames[i]
0036:
0037: String names[]; // extra names for this font (others in FontSupport)
0038: // String safenames[]; // names without high-bit characters
0039:
0040: HashMap charset = new HashMap();
0041: ArrayList charnames = new ArrayList();
0042: int charcounter = -1;
0043:
0044: byte[] data;
0045: int pos;
0046: byte[] subrs[];
0047: float[] stack = new float[100];
0048: int stackptr = 0;
0049:
0050: AffineTransform at = new AffineTransform(0.001f, 0, 0, 0.001f, 0, 0);
0051:
0052: int num;
0053: float fnum;
0054: int type;
0055: static int CMD = 0;
0056: static int NUM = 1;
0057: static int FLT = 2;
0058:
0059: public boolean isRequestFocusEnabled() {
0060: return true;
0061: }
0062:
0063: public TestType1CFont(InputStream is) throws IOException {
0064: super ();
0065: setPreferredSize(new Dimension(800, 800));
0066: addKeyListener(this );
0067: BufferedInputStream bis = new BufferedInputStream(is);
0068: int count = 0;
0069: ArrayList al = new ArrayList();
0070: byte b[] = new byte[32000];
0071: int len;
0072: while ((len = bis.read(b, 0, b.length)) >= 0) {
0073: byte[] c = new byte[len];
0074: System.arraycopy(b, 0, c, 0, len);
0075: al.add(c);
0076: count += len;
0077: b = new byte[32000];
0078: }
0079: data = new byte[count];
0080: len = 0;
0081: for (int i = 0; i < al.size(); i++) {
0082: byte from[] = (byte[]) al.get(i);
0083: System.arraycopy(from, 0, data, len, from.length);
0084: len += from.length;
0085: }
0086: pos = 0;
0087: // printData();
0088: parse();
0089: // TODO: free up (set to null) unused structures (data, subrs, stack)
0090: }
0091:
0092: GlyphData showing;
0093: String showname;
0094: Font gfont = new Font("Sans-serif", Font.PLAIN, 24)
0095: .deriveFont(AffineTransform.getScaleInstance(1, -1));
0096: Color fillColor = new Color(0xe0, 0xff, 0xff);
0097:
0098: public void keyTyped(KeyEvent evt) {
0099: }
0100:
0101: public void keyReleased(KeyEvent evt) {
0102: }
0103:
0104: public void keyPressed(KeyEvent evt) {
0105: if (evt.getKeyCode() == evt.VK_RIGHT) {
0106: charcounter++;
0107: if (charcounter >= charnames.size()) {
0108: charcounter = 0;
0109: }
0110: showing = readGlyph((String) charnames.get(charcounter));
0111: } else if (evt.getKeyCode() == evt.VK_LEFT) {
0112: charcounter--;
0113: if (charcounter < 0) {
0114: charcounter = charnames.size() - 1;
0115: }
0116: showing = readGlyph((String) charnames.get(charcounter));
0117: } else {
0118: char c = evt.getKeyChar();
0119: // System.out.println("Got char: "+name);
0120: showing = readGlyph(FontSupport.stdNames[FontSupport.standardEncoding[(int) c & 0xff]]);
0121: }
0122: repaint();
0123: }
0124:
0125: public void paint(Graphics g) {
0126: Graphics2D g2 = (Graphics2D) g;
0127: g2.setColor(Color.white);
0128: g2.fillRect(0, 0, getWidth(), getHeight());
0129: AffineTransform at = new AffineTransform(0.5, 0, 0, -0.5, 30,
0130: getHeight() * 3 / 4);
0131: g2.transform(at);
0132: g2.setColor(Color.black);
0133: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0134: RenderingHints.VALUE_ANTIALIAS_ON);
0135: g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
0136: RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
0137: // System.out.println("Showing="+showing);
0138: if (showing != null) {
0139: showing.draw(g2);
0140: }
0141: }
0142:
0143: private void printData() {
0144: char[] parts = new char[17];
0145: int partsloc = 0;
0146: for (int i = 0; i < data.length; i++) {
0147: int d = ((int) data[i]) & 0xff;
0148: if (d == 0) {
0149: parts[partsloc++] = '.';
0150: } else if (d < 32 || d >= 127) {
0151: parts[partsloc++] = '?';
0152: } else {
0153: parts[partsloc++] = (char) d;
0154: }
0155: if (d < 16) {
0156: System.out.print("0" + Integer.toHexString(d));
0157: } else {
0158: System.out.print(Integer.toHexString(d));
0159: }
0160: if ((i & 15) == 15) {
0161: System.out.println(" " + new String(parts));
0162: partsloc = 0;
0163: } else if ((i & 7) == 7) {
0164: System.out.print(" ");
0165: parts[partsloc++] = ' ';
0166: } else if ((i & 1) == 1) {
0167: System.out.print(" ");
0168: }
0169: }
0170: System.out.println();
0171: }
0172:
0173: private int readNext(boolean charstring) {
0174: num = (int) (data[pos++]) & 0xff;
0175: if (num == 30 && !charstring) { // goofy floatingpoint rep
0176: readFNum();
0177: return type = FLT;
0178: } else if (num == 28) {
0179: num = (((int) data[pos]) << 8)
0180: + (((int) data[pos + 1]) & 0xff);
0181: pos += 2;
0182: return type = NUM;
0183: } else if (num == 29 && !charstring) {
0184: num = (((int) data[pos] & 0xff) << 24)
0185: | (((int) data[pos + 1] & 0xff) << 16)
0186: | (((int) data[pos + 2] & 0xff) << 8)
0187: | (((int) data[pos + 3] & 0xff));
0188: pos += 4;
0189: return type = NUM;
0190: } else if (num == 12) { // two-byte command
0191: num = 1000 + ((int) (data[pos++]) & 0xff);
0192: return type = CMD;
0193: } else if (num < 32) {
0194: return type = CMD;
0195: } else if (num < 247) {
0196: num -= 139;
0197: return type = NUM;
0198: } else if (num < 251) {
0199: num = (num - 247) * 256 + (((int) data[pos++]) & 0xff)
0200: + 108;
0201: return type = NUM;
0202: } else if (num < 255) {
0203: num = -(num - 251) * 256 - (((int) data[pos++]) & 0xff)
0204: - 108;
0205: return type = NUM;
0206: } else if (!charstring) { // dict shouldn't have a 255 code
0207: printData();
0208: throw new RuntimeException(
0209: "Got a 255 code while reading dict");
0210: } else { // num was 255
0211: fnum = ((((int) data[pos] & 0xff) << 24)
0212: | (((int) data[pos + 1] & 0xff) << 16)
0213: | (((int) data[pos + 2] & 0xff) << 8) | (((int) data[pos + 3] & 0xff))) / 65536f;
0214: pos += 4;
0215: return type = FLT;
0216: }
0217: }
0218:
0219: public void readFNum() {
0220: // work in nybbles: 0-9=0-9, a=. b=E, c=E-, d=rsvd e=neg f=end
0221: float f = 0;
0222: boolean neg = false;
0223: int exp = 0;
0224: int eval = 0;
0225: float mul = 1;
0226: byte work = data[pos++];
0227: while (true) {
0228: if (work == (byte) 0xdd) {
0229: work = data[pos++];
0230: }
0231: int nyb = (work >> 4) & 0xf;
0232: work = (byte) ((work << 4) | 0xd);
0233: if (nyb < 10) {
0234: if (exp != 0) { // working on the exponent
0235: eval = eval * 10 + nyb;
0236: } else if (mul == 1) { // working on an int
0237: f = f * 10 + nyb;
0238: } else { // working on decimal part
0239: f += nyb * mul;
0240: mul /= 10f;
0241: }
0242: } else if (nyb == 0xa) { // decimal
0243: mul = 0.1f;
0244: } else if (nyb == 0xb) { // E+
0245: exp = 1;
0246: } else if (nyb == 0xc) { // E-
0247: exp = -1;
0248: } else if (nyb == 0xe) { // neg
0249: neg = true;
0250: } else {
0251: break;
0252: }
0253: }
0254: fnum = (neg ? -1 : 1) * f * (float) Math.pow(10, eval * exp);
0255: }
0256:
0257: private int readInt(int len) {
0258: int n = 0;
0259: for (int i = 0; i < len; i++) {
0260: n = (n << 8) | (((int) data[pos++]) & 0xff);
0261: }
0262: return n;
0263: }
0264:
0265: private int readByte() {
0266: return ((int) data[pos++]) & 0xff;
0267: }
0268:
0269: // DICT structure:
0270: // operand operator operand operator ...
0271:
0272: // INDEX structure:
0273: // count(2) offsize [offset offset ... offset] data
0274: // offset array has count+1 entries
0275: // data starts at 3+(count+1)*offsize
0276: // offset for data is offset+2+(count+1)*offsize
0277:
0278: public int getIndexSize(int loc) {
0279: // System.out.println("Getting size of index at "+loc);
0280: int hold = pos;
0281: pos = loc;
0282: int count = readInt(2);
0283: if (count == 0) {
0284: return 2;
0285: }
0286: int encsz = readByte();
0287: // pos is now at the first offset. last offset is at count*encsz
0288: pos += count * encsz;
0289: int end = readInt(encsz);
0290: pos = hold;
0291: return 2 + (count + 1) * encsz + end;
0292: }
0293:
0294: class Range {
0295: private int start;
0296: private int len;
0297:
0298: public Range(int start, int len) {
0299: this .start = start;
0300: this .len = len;
0301: }
0302:
0303: public final int getStart() {
0304: return start;
0305: }
0306:
0307: public final int getLen() {
0308: return len;
0309: }
0310:
0311: public final int getEnd() {
0312: return start + len;
0313: }
0314: }
0315:
0316: public Range getIndexEntry(int index, int id) {
0317: int hold = pos;
0318: pos = index;
0319: int count = readInt(2);
0320: int encsz = readByte();
0321: pos += encsz * id;
0322: int from = readInt(encsz);
0323: Range r = new Range(from + 2 + index + encsz * (count + 1),
0324: readInt(encsz) - from);
0325: pos = hold;
0326: return r;
0327: }
0328:
0329: // Top DICT: NAME CODE DEFAULT
0330: // charstringtype 12 6 2
0331: // fontmatrix 12 7 0.001 0 0 0.001
0332: // charset 15 - (offset) names of glyphs (ref to name idx)
0333: // encoding 16 - (offset) array of codes
0334: // CharStrings 17 - (offset)
0335: // Private 18 - (size, offset)
0336:
0337: // glyph at position i in CharStrings has name charset[i]
0338: // and code encoding[i]
0339:
0340: int charstringtype = 2;
0341: float temps[] = new float[32];
0342: int charsetbase = 0;
0343: int encodingbase = 0;
0344: int charstringbase = 0;
0345: int privatebase = 0;
0346: int privatesize = 0;
0347: int gsubrbase = 0;
0348: int lsubrbase = 0;
0349: int gsubrsoffset = 0;
0350: int lsubrsoffset = 0;
0351: int defaultWidthX = 0;
0352: int nominalWidthX = 0;
0353:
0354: int nglyphs = 1;
0355:
0356: private void readDict(Range r) {
0357: // System.out.println("reading dictionary from "+r.getStart()+" to "+r.getEnd());
0358: pos = r.getStart();
0359: while (pos < r.getEnd()) {
0360: int cmd = readCommand(false);
0361: if (cmd == 1006) { // charstringtype, default=2
0362: charstringtype = (int) stack[0];
0363: } else if (cmd == 1007) { // fontmatrix
0364: if (stackptr == 4) {
0365: at = new AffineTransform((float) stack[0],
0366: (float) stack[1], (float) stack[2],
0367: (float) stack[3], 0, 0);
0368: } else {
0369: at = new AffineTransform((float) stack[0],
0370: (float) stack[1], (float) stack[2],
0371: (float) stack[3], (float) stack[4],
0372: (float) stack[5]);
0373: }
0374: } else if (cmd == 15) { // charset
0375: charsetbase = (int) stack[0];
0376: } else if (cmd == 16) { // encoding
0377: encodingbase = (int) stack[0];
0378: } else if (cmd == 17) { // charstrings
0379: charstringbase = (int) stack[0];
0380: } else if (cmd == 18) { // private
0381: privatesize = (int) stack[0];
0382: privatebase = (int) stack[1];
0383: } else if (cmd == 19) { // subrs (in Private dict)
0384: lsubrbase = (int) stack[0];
0385: lsubrsoffset = calcoffset(lsubrbase);
0386: } else if (cmd == 20) { // defaultWidthX (in Private dict)
0387: defaultWidthX = (int) stack[0];
0388: } else if (cmd == 21) { // nominalWidthX (in Private dict)
0389: nominalWidthX = (int) stack[0];
0390: }
0391: stackptr = 0;
0392: }
0393: }
0394:
0395: private int readCommand(boolean charstring) {
0396: while (true) {
0397: int type = readNext(charstring);
0398: if (type == CMD) {
0399: System.out.print("CMD= " + num + ", args=");
0400: for (int i = 0; i < stackptr; i++) {
0401: System.out.print(" " + stack[i]);
0402: }
0403: System.out.println();
0404: return num;
0405: } else {
0406: stack[stackptr++] = (type == NUM) ? (float) num
0407: : (float) fnum;
0408: }
0409: }
0410: }
0411:
0412: private void readEncodingData(int base) {
0413: if (base == 0) { // this is the StandardEncoding
0414: System.out.println("**** STANDARD ENCODING!");
0415: // TODO: copy standard encoding
0416: } else if (base == 1) { // this is the expert encoding
0417: System.out.println("**** EXPERT ENCODING!");
0418: // TODO: copy ExpertEncoding
0419: } else {
0420: pos = base;
0421: int encodingtype = readByte();
0422: if ((encodingtype & 127) == 0) {
0423: System.out.println("**** Type 0 Encoding:");
0424:
0425: int ncodes = readByte();
0426: for (int i = 1; i < ncodes + 1; i++) {
0427: encoding[i] = readByte();
0428: System.out.println("Encoding[" + i + "] = "
0429: + encoding[i]);
0430: }
0431: } else if ((encodingtype & 127) == 1) {
0432: System.out.println("**** Type 1 Encoding:");
0433:
0434: int nranges = readByte();
0435: int pos = 0;
0436: for (int i = 0; i < nranges; i++) {
0437: int start = readByte();
0438: int more = readByte();
0439: for (int j = start; j < start + more + 1; j++) {
0440: System.out.println("Encoding[" + pos + "] = "
0441: + j);
0442: encoding[pos++] = j;
0443: }
0444: }
0445: } else {
0446: System.out
0447: .println("Bad encoding type: " + encodingtype);
0448: }
0449: // TODO: now check for supplemental encoding data
0450: }
0451: }
0452:
0453: private void readGlyphNames(int base) {
0454: if (base == 0) {
0455: System.out.println("**** Identity glyphNames");
0456:
0457: glyphnames = new int[229];
0458: for (int i = 0; i < glyphnames.length; i++) {
0459: glyphnames[i] = i;
0460: }
0461: return;
0462: } else if (base == 1) {
0463: System.out.println("**** Type1CExpertCharset glyphNames");
0464: glyphnames = FontSupport.type1CExpertCharset;
0465: return;
0466: } else if (base == 2) {
0467: System.out
0468: .println("**** Type1CExpertSubCharset glyphNames");
0469: glyphnames = FontSupport.type1CExpertSubCharset;
0470: return;
0471: }
0472:
0473: System.out.print("**** Custom glyphNames Type ");
0474:
0475: // nglyphs has already been set.
0476: glyphnames = new int[nglyphs];
0477: glyphnames[0] = 0;
0478: pos = base;
0479: int type = readByte();
0480: if (type == 0) {
0481: System.out.println("0");
0482:
0483: for (int i = 1; i < nglyphs; i++) {
0484: glyphnames[i] = readInt(2);
0485: System.out.println("glyphnames[" + i + "] = "
0486: + glyphnames[i]);
0487: }
0488: } else if (type == 1) {
0489: System.out.println("1");
0490:
0491: int n = 1;
0492: while (n < nglyphs) {
0493: int sid = readInt(2);
0494: int range = readByte() + 1;
0495: for (int i = 0; i < range; i++) {
0496: System.out
0497: .println("glyphnames[" + n + "] = " + sid);
0498: glyphnames[n++] = sid++;
0499: }
0500: }
0501: } else if (type == 2) {
0502: System.out.println("2");
0503:
0504: int n = 1;
0505: while (n < nglyphs) {
0506: int sid = readInt(2);
0507: int range = readInt(2) + 1;
0508: for (int i = 0; i < range; i++) {
0509: System.out
0510: .println("glyphnames[" + n + "] = " + sid);
0511: glyphnames[n++] = sid++;
0512: }
0513: }
0514: }
0515: }
0516:
0517: private void readNames(int base) {
0518: pos = base;
0519: int nextra = readInt(2);
0520: names = new String[nextra];
0521: // safenames= new String[nextra];
0522: for (int i = 0; i < nextra; i++) {
0523: Range r = getIndexEntry(base, i);
0524: names[i] = new String(data, r.getStart(), r.getLen());
0525: System.out.println("Read name: " + i + " from "
0526: + r.getStart() + " to " + r.getEnd() + ": "
0527: + safe(names[i]));
0528: }
0529: }
0530:
0531: private void parse() {
0532: int majorVersion = readByte();
0533: int minorVersion = readByte();
0534: int hdrsz = readByte();
0535: int offsize = readByte();
0536: // jump over rest of header: base of font names index
0537: int fnames = hdrsz;
0538: // offset in the file of the array of font dicts
0539: int topdicts = fnames + getIndexSize(fnames);
0540: // offset in the file of local names
0541: int names = topdicts + getIndexSize(topdicts);
0542: // offset in the file of the array of global subroutines
0543: gsubrbase = names + getIndexSize(names);
0544: gsubrsoffset = calcoffset(gsubrbase);
0545: // read extra names
0546: readNames(names);
0547: // does this file have more than one font?
0548: pos = topdicts;
0549: if (readInt(2) != 1) {
0550: printData();
0551: throw new RuntimeException(
0552: "More than one font in this file!");
0553: }
0554: // read first dict
0555: System.out.println("TOPDICT[0]:");
0556: readDict(getIndexEntry(topdicts, 0));
0557: // read the private dictionary
0558: System.out.println("PRIVATE DICT:");
0559: readDict(new Range(privatebase, privatesize));
0560: // calculate the number of glyphs
0561: pos = charstringbase;
0562: nglyphs = readInt(2);
0563: // now get the glyph names
0564: System.out.println("GLYPHNAMES:");
0565: readGlyphNames(charsetbase);
0566: // now figure out the encoding
0567: System.out.println("ENCODING:");
0568: readEncodingData(encodingbase);
0569: // now get the glyphs
0570: System.out.println("GLYPHS:");
0571: readGlyphs(charstringbase);
0572: }
0573:
0574: private String safe(String src) {
0575: StringBuffer sb = new StringBuffer();
0576: for (int i = 0; i < src.length(); i++) {
0577: char c = src.charAt(i);
0578: if (c >= 32 && c < 128) {
0579: sb.append(c);
0580: } else {
0581: sb.append("<" + (int) c + ">");
0582: }
0583: }
0584: return sb.toString();
0585: }
0586:
0587: class GlyphPoint {
0588: float x, y;
0589: boolean curvecontrol;
0590: GeneralPath gp;
0591:
0592: public GlyphPoint(float x, float y, boolean curvectrl) {
0593: this .x = x;
0594: this .y = y;
0595: this .curvecontrol = curvectrl;
0596: gp = new GeneralPath();
0597: if (curvectrl) {
0598: gp.moveTo(x - 4, y - 4);
0599: gp.lineTo(x + 4, y + 4);
0600: gp.moveTo(x - 4, y + 4);
0601: gp.lineTo(x + 4, y - 4);
0602: } else {
0603: gp.moveTo(x - 4, y - 4);
0604: gp.lineTo(x - 4, y + 4);
0605: gp.lineTo(x + 4, y + 4);
0606: gp.lineTo(x + 4, y - 4);
0607: gp.closePath();
0608: }
0609: }
0610: }
0611:
0612: class GlyphData {
0613: GeneralPath gp;
0614: GeneralPath advp;
0615: ArrayList points;
0616: float x, y;
0617: float advance;
0618: String name;
0619:
0620: public GlyphData() {
0621: gp = new GeneralPath();
0622: // advp= new GeneralPath();
0623: points = new ArrayList();
0624: x = y = 0;
0625: }
0626:
0627: public void setName(String name) {
0628: this .name = name;
0629: }
0630:
0631: public void lineTo(float x, float y) {
0632: gp.lineTo(x, y);
0633: this .x = x;
0634: this .y = y;
0635: points.add(new GlyphPoint(x, y, false));
0636: }
0637:
0638: public void moveTo(float x, float y) {
0639: gp.moveTo(x, y);
0640: this .x = x;
0641: this .y = y;
0642: points.add(new GlyphPoint(x, y, false));
0643: }
0644:
0645: public void curveTo(float x1, float y1, float x2, float y2,
0646: float x3, float y3) {
0647: gp.curveTo(x1, y1, x2, y2, x3, y3);
0648: this .x = x3;
0649: this .y = y3;
0650: points.add(new GlyphPoint(x1, y1, true));
0651: points.add(new GlyphPoint(x2, y2, true));
0652: points.add(new GlyphPoint(x3, y3, false));
0653: }
0654:
0655: public void closePath() {
0656: gp.closePath();
0657: }
0658:
0659: public void addGlyph(GlyphData gv, float x, float y) {
0660: AffineTransform at = AffineTransform.getTranslateInstance(
0661: x, y);
0662: PathIterator pi = gv.gp.getPathIterator(at);
0663:
0664: float[] coords = new float[6];
0665:
0666: while (!pi.isDone()) {
0667: int type = pi.currentSegment(coords);
0668:
0669: switch (type) {
0670: case PathIterator.SEG_MOVETO:
0671: moveTo(coords[0], coords[1]);
0672: break;
0673: case PathIterator.SEG_LINETO:
0674: lineTo(coords[0], coords[1]);
0675: break;
0676: case PathIterator.SEG_CUBICTO:
0677: curveTo(coords[0], coords[1], coords[2], coords[3],
0678: coords[4], coords[5]);
0679: break;
0680: case PathIterator.SEG_CLOSE:
0681: closePath();
0682: break;
0683: default:
0684: System.out.println("Unknown path type: " + type);
0685: break;
0686: }
0687:
0688: pi.next();
0689: }
0690: }
0691:
0692: public void setAdvance(float adv) {
0693: advance = adv;
0694: advp = new GeneralPath();
0695: advp.moveTo(-2, -2);
0696: advp.lineTo(2, 2);
0697: advp.moveTo(-2, 2);
0698: advp.lineTo(2, -2);
0699: advp.moveTo(adv - 2, -2);
0700: advp.lineTo(adv, 0);
0701: advp.lineTo(adv + 2, -2);
0702: advp.moveTo(adv, 0);
0703: advp.lineTo(adv, -8);
0704: }
0705:
0706: public void draw(Graphics2D g) {
0707: g.setColor(fillColor);
0708: g.fill(gp);
0709: g.setColor(Color.black);
0710: g.draw(gp);
0711: for (int i = 0; i < points.size(); i++) {
0712: GlyphPoint p = (GlyphPoint) points.get(i);
0713: g.setColor(Color.red);
0714: g.draw(p.gp);
0715: g.setColor(Color.blue);
0716: g.setFont(gfont);
0717: g.drawString(String.valueOf(i), p.x + 3, p.y + 3);
0718: }
0719: g.setColor(Color.black);
0720: // System.out.println("Advance: "+advance);
0721: g.draw(advp);
0722: if (name != null) {
0723: g.setFont(gfont);
0724: g.drawString(name, 0, -40);
0725: }
0726: }
0727: }
0728:
0729: private void readGlyphs(int base) {
0730: for (int i = 1; i < nglyphs; i++) {
0731: System.out.println("Reading glyph "
0732: + safe(getSID(glyphnames[i])));
0733: charnames.add(getSID(glyphnames[i]));
0734: }
0735: }
0736:
0737: /**
0738: * Read a single glyph, given its offset
0739: */
0740: public GlyphData readGlyph(String sid) {
0741: int index = getNameIndex(sid);
0742:
0743: // now find the glyph with that name
0744: for (int i = 0; i < glyphnames.length; i++) {
0745: if (glyphnames[i] == index) {
0746: return readGlyph(i);
0747: }
0748: }
0749:
0750: // not found -- return the unknown glyph
0751: return readGlyph(0);
0752: }
0753:
0754: /**
0755: * Read a glyph, given an index
0756: */
0757: public GlyphData readGlyph(int index) {
0758: String sid = getSID(index);
0759:
0760: if (charset.containsKey(sid)) {
0761: return (GlyphData) charset.get(sid);
0762: }
0763:
0764: Range r = getIndexEntry(charstringbase, index);
0765:
0766: FlPoint pt = new FlPoint();
0767: GlyphData gd = new GlyphData();
0768:
0769: parseGlyph(r, gd, pt);
0770: gd.setName(sid);
0771:
0772: charset.put(sid, gd);
0773: return gd;
0774: }
0775:
0776: /**
0777: * build an accented character out of two pre-defined glyphs.
0778: * @param x the x offset of the accent
0779: * @param y the y offset of the accent
0780: * @param a the index of the base glyph
0781: * @param b the index of the accent glyph
0782: * @param gp the GeneralPath into which the combined glyph will be
0783: * written.
0784: */
0785: private void buildAccentChar(float x, float y, float a, float b,
0786: GlyphData gv) {
0787: System.out.println("Building accent character!!!!!!");
0788:
0789: for (int i = 0; i < nglyphs; i++) {
0790: if (encoding[i] == (int) a) {
0791: gv.addGlyph(readGlyph(i + 1), x, y);
0792: } else if (encoding[i] == (int) b) {
0793: gv.addGlyph(readGlyph(i + 1), 0, 0);
0794: }
0795: }
0796:
0797: /* if (shapes[b]!=null) {
0798: gp.append(shapes[b], false);
0799: }
0800: if (shapes[a]!=null) {
0801: gp.append(shapes[a], false);
0802: }*/
0803: }
0804:
0805: public int calcoffset(int base) {
0806: int len = getIndexSize(base);
0807: if (len < 1240) {
0808: return -107;
0809: } else if (len < 33900) {
0810: return -1131;
0811: } else {
0812: return -32768;
0813: }
0814: }
0815:
0816: public String getSID(int id) {
0817: if (id < FontSupport.stdNames.length) {
0818: return FontSupport.stdNames[id];
0819: } else {
0820: id -= FontSupport.stdNames.length;
0821: return names[id];
0822: }
0823: }
0824:
0825: /**
0826: * get the index of a particular name. The name table starts with
0827: * the standard names in FontSupport.stdNames, and is appended by
0828: * any names in the name table from this font's dictionary.
0829: */
0830: private int getNameIndex(String name) {
0831: int val = FontSupport.findName(name, FontSupport.stdNames);
0832: if (val == -1) {
0833: val = FontSupport.findName(name, names)
0834: + FontSupport.stdNames.length;
0835: }
0836: if (val == -1) {
0837: val = 0;
0838: }
0839: return val;
0840: }
0841:
0842: public void parseGlyph(Range r, GlyphData gp, FlPoint pt) {
0843: pos = r.getStart();
0844: int i;
0845: float x1, y1, x2, y2, x3, y3, ybase;
0846: int hold;
0847: int stemhints = 0;
0848: gp.setAdvance(defaultWidthX);
0849: while (pos < r.getEnd()) {
0850: int cmd = readCommand(true);
0851: hold = 0;
0852: switch (cmd) {
0853: case 1: // hstem
0854: case 3: // vstem
0855: if ((stackptr & 1) == 1) {
0856: gp.setAdvance(nominalWidthX + stack[0]);
0857: }
0858: stackptr = 0;
0859: break;
0860: case 4: // vmoveto
0861: if (stackptr > 1) { // this is the first call, arg1 is width
0862: gp.setAdvance(nominalWidthX + stack[0]);
0863: stack[0] = stack[1];
0864: }
0865: pt.y += stack[0];
0866: if (pt.open) {
0867: gp.closePath();
0868: }
0869: pt.open = false;
0870: gp.moveTo(pt.x, pt.y);
0871: stackptr = 0;
0872: break;
0873: case 5: // rlineto
0874: for (i = 0; i < stackptr;) {
0875: pt.x += stack[i++];
0876: pt.y += stack[i++];
0877: gp.lineTo(pt.x, pt.y);
0878: }
0879: pt.open = true;
0880: stackptr = 0;
0881: break;
0882: case 6: // hlineto
0883: for (i = 0; i < stackptr;) {
0884: if ((i & 1) == 0) {
0885: pt.x += stack[i++];
0886: } else {
0887: pt.y += stack[i++];
0888: }
0889: gp.lineTo(pt.x, pt.y);
0890: }
0891: pt.open = true;
0892: stackptr = 0;
0893: break;
0894: case 7: // vlineto
0895: for (i = 0; i < stackptr;) {
0896: if ((i & 1) == 0) {
0897: pt.y += stack[i++];
0898: } else {
0899: pt.x += stack[i++];
0900: }
0901: gp.lineTo(pt.x, pt.y);
0902: }
0903: pt.open = true;
0904: stackptr = 0;
0905: break;
0906: case 8: // rrcurveto
0907: for (i = 0; i < stackptr;) {
0908: x1 = pt.x + stack[i++];
0909: y1 = pt.y + stack[i++];
0910: x2 = x1 + stack[i++];
0911: y2 = y1 + stack[i++];
0912: pt.x = x2 + stack[i++];
0913: pt.y = y2 + stack[i++];
0914: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
0915: }
0916: pt.open = true;
0917: stackptr = 0;
0918: break;
0919: case 10: // callsubr
0920: hold = pos;
0921: i = (int) stack[--stackptr] + lsubrsoffset;
0922: Range lsubr = getIndexEntry(lsubrbase, i);
0923: parseGlyph(lsubr, gp, pt);
0924: pos = hold;
0925: break;
0926: case 11: // return
0927: return;
0928: case 14: // endchar
0929: if (stackptr == 5) {
0930: buildAccentChar(stack[1], stack[2], stack[3],
0931: stack[4], gp);
0932: }
0933:
0934: if (pt.open) {
0935: gp.closePath();
0936: }
0937: pt.open = false;
0938: stackptr = 0;
0939: return;
0940: case 18: // hstemhm
0941: if ((stackptr & 1) == 1) {
0942: gp.setAdvance(nominalWidthX + stack[0]);
0943: }
0944: stemhints += stackptr / 2;
0945: stackptr = 0;
0946: break;
0947: case 19: // hintmask
0948: case 20: // cntrmask
0949: if ((stackptr & 1) == 1) {
0950: gp.setAdvance(nominalWidthX + stack[0]);
0951: }
0952: stemhints += stackptr / 2;
0953: System.out.println("Added " + stackptr
0954: + " extra bits; skipping "
0955: + ((stemhints - 1) / 8 + 1) + " from "
0956: + stemhints);
0957: pos += (stemhints - 1) / 8 + 1;
0958: stackptr = 0;
0959: break;
0960: case 21: // rmoveto
0961: if (stackptr > 2) {
0962: gp.setAdvance(nominalWidthX + stack[0]);
0963: stack[0] = stack[1];
0964: stack[1] = stack[2];
0965: }
0966: pt.x += stack[0];
0967: pt.y += stack[1];
0968: if (pt.open) {
0969: gp.closePath();
0970: }
0971: gp.moveTo(pt.x, pt.y);
0972: pt.open = false;
0973: stackptr = 0;
0974: break;
0975: case 22: // hmoveto
0976: if (stackptr > 1) {
0977: gp.setAdvance(nominalWidthX + stack[0]);
0978: stack[0] = stack[1];
0979: }
0980: pt.x += stack[0];
0981: if (pt.open) {
0982: gp.closePath();
0983: }
0984: gp.moveTo(pt.x, pt.y);
0985: pt.open = false;
0986: stackptr = 0;
0987: break;
0988: case 23: // vstemhm
0989: if ((stackptr & 1) == 1) {
0990: gp.setAdvance(nominalWidthX + stack[0]);
0991: }
0992: stemhints += stackptr / 2;
0993: stackptr = 0;
0994: break;
0995: case 24: // rcurveline
0996: for (i = 0; i < stackptr - 2;) {
0997: x1 = pt.x + stack[i++];
0998: y1 = pt.y + stack[i++];
0999: x2 = x1 + stack[i++];
1000: y2 = y1 + stack[i++];
1001: pt.x = x2 + stack[i++];
1002: pt.y = y2 + stack[i++];
1003: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1004: }
1005: pt.x += stack[i++];
1006: pt.y += stack[i++];
1007: gp.lineTo(pt.x, pt.y);
1008: pt.open = true;
1009: stackptr = 0;
1010: break;
1011: case 25: // rlinecurve
1012: for (i = 0; i < stackptr - 6;) {
1013: pt.x += stack[i++];
1014: pt.y += stack[i++];
1015: gp.lineTo(pt.x, pt.y);
1016: }
1017: x1 = pt.x + stack[i++];
1018: y1 = pt.y + stack[i++];
1019: x2 = x1 + stack[i++];
1020: y2 = y1 + stack[i++];
1021: pt.x = x2 + stack[i++];
1022: pt.y = y2 + stack[i++];
1023: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1024: pt.open = true;
1025: stackptr = 0;
1026: break;
1027: case 26: // vvcurveto
1028: i = 0;
1029: if ((stackptr & 1) == 1) { // odd number of arguments
1030: pt.x += stack[i++];
1031: }
1032: while (i < stackptr) {
1033: x1 = pt.x;
1034: y1 = pt.y + stack[i++];
1035: x2 = x1 + stack[i++];
1036: y2 = y1 + stack[i++];
1037: pt.x = x2;
1038: pt.y = y2 + stack[i++];
1039: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1040: }
1041: pt.open = true;
1042: stackptr = 0;
1043: break;
1044: case 27: // hhcurveto
1045: i = 0;
1046: if ((stackptr & 1) == 1) { // odd number of arguments
1047: pt.y += stack[i++];
1048: }
1049: while (i < stackptr) {
1050: x1 = pt.x + stack[i++];
1051: y1 = pt.y;
1052: x2 = x1 + stack[i++];
1053: y2 = y1 + stack[i++];
1054: pt.x = x2 + stack[i++];
1055: pt.y = y2;
1056: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1057: }
1058: pt.open = true;
1059: stackptr = 0;
1060: break;
1061: case 29: // callgsubr
1062: hold = pos;
1063: i = (int) stack[--stackptr] + gsubrsoffset;
1064: Range gsubr = getIndexEntry(gsubrbase, i);
1065: parseGlyph(gsubr, gp, pt);
1066: pos = hold;
1067: break;
1068: case 30: // vhcurveto
1069: hold = 4;
1070: case 31: // hvcurveto
1071: for (i = 0; i < stackptr;) {
1072: boolean hv = (((i + hold) & 4) == 0);
1073: x1 = pt.x + (hv ? stack[i++] : 0);
1074: y1 = pt.y + (hv ? 0 : stack[i++]);
1075: x2 = x1 + stack[i++];
1076: y2 = y1 + stack[i++];
1077: pt.x = x2 + (hv ? 0 : stack[i++]);
1078: pt.y = y2 + (hv ? stack[i++] : 0);
1079: if (i == stackptr - 1) {
1080: if (hv) {
1081: pt.x += stack[i++];
1082: } else {
1083: pt.y += stack[i++];
1084: }
1085: }
1086: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1087: }
1088: pt.open = true;
1089: stackptr = 0;
1090: break;
1091: case 1003: // and
1092: x1 = stack[--stackptr];
1093: y1 = stack[--stackptr];
1094: stack[stackptr++] = ((x1 != 0) && (y1 != 0)) ? 1 : 0;
1095: break;
1096: case 1004: // or
1097: x1 = stack[--stackptr];
1098: y1 = stack[--stackptr];
1099: stack[stackptr++] = ((x1 != 0) || (y1 != 0)) ? 1 : 0;
1100: break;
1101: case 1005: // not
1102: x1 = stack[--stackptr];
1103: stack[stackptr++] = (x1 == 0) ? 1 : 0;
1104: break;
1105: case 1009: // abs
1106: stack[stackptr - 1] = Math.abs(stack[stackptr - 1]);
1107: break;
1108: case 1010: // add
1109: x1 = stack[--stackptr];
1110: y1 = stack[--stackptr];
1111: stack[stackptr++] = x1 + y1;
1112: break;
1113: case 1011: // sub
1114: x1 = stack[--stackptr];
1115: y1 = stack[--stackptr];
1116: stack[stackptr++] = y1 - x1;
1117: break;
1118: case 1012: // div
1119: x1 = stack[--stackptr];
1120: y1 = stack[--stackptr];
1121: stack[stackptr++] = y1 / x1;
1122: break;
1123: case 1014: // neg
1124: stack[stackptr - 1] = -stack[stackptr - 1];
1125: break;
1126: case 1015: // eq
1127: x1 = stack[--stackptr];
1128: y1 = stack[--stackptr];
1129: stack[stackptr++] = (x1 == y1) ? 1 : 0;
1130: break;
1131: case 1018: // drop
1132: stackptr--;
1133: break;
1134: case 1020: // put
1135: i = (int) stack[--stackptr];
1136: x1 = stack[--stackptr];
1137: temps[i] = x1;
1138: break;
1139: case 1021: // get
1140: i = (int) stack[--stackptr];
1141: stack[stackptr++] = temps[i];
1142: break;
1143: case 1022: // ifelse
1144: if (stack[stackptr - 2] > stack[stackptr - 1]) {
1145: stack[stackptr - 4] = stack[stackptr - 3];
1146: }
1147: stackptr -= 3;
1148: break;
1149: case 1023: // random
1150: stack[stackptr++] = (float) Math.random();
1151: break;
1152: case 1024: // mul
1153: x1 = stack[--stackptr];
1154: y1 = stack[--stackptr];
1155: stack[stackptr++] = y1 * x1;
1156: break;
1157: case 1026: // sqrt
1158: stack[stackptr - 1] = (float) Math
1159: .sqrt(stack[stackptr - 1]);
1160: break;
1161: case 1027: // dup
1162: x1 = stack[stackptr - 1];
1163: stack[stackptr++] = x1;
1164: break;
1165: case 1028: // exch
1166: x1 = stack[stackptr - 1];
1167: stack[stackptr - 1] = stack[stackptr - 2];
1168: stack[stackptr - 2] = x1;
1169: break;
1170: case 1029: // index
1171: i = (int) stack[stackptr - 1];
1172: if (i < 0)
1173: i = 0;
1174: stack[stackptr - 1] = stack[stackptr - 2 - i];
1175: break;
1176: case 1030: // roll
1177: i = (int) stack[--stackptr];
1178: int n = (int) stack[--stackptr];
1179: // roll n number by i (+ = upward)
1180: if (i > 0) {
1181: i = i % n;
1182: } else {
1183: i = n - (-i % n);
1184: }
1185: // x x x x i y y y -> y y y x x x x i (where i=3)
1186: if (i > 0) {
1187: float roll[] = new float[n];
1188: System.arraycopy(stack, stackptr - 1 - i, roll, 0,
1189: i);
1190: System.arraycopy(stack, stackptr - 1 - n, roll, i,
1191: n - i);
1192: System.arraycopy(roll, 0, stack, stackptr - 1 - n,
1193: n);
1194: }
1195: break;
1196: case 1034: // hflex
1197: x1 = pt.x + stack[0];
1198: y1 = ybase = pt.y;
1199: x2 = x1 + stack[1];
1200: y2 = y1 + stack[2];
1201: pt.x = x2 + stack[3];
1202: pt.y = y2;
1203: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1204: x1 = pt.x + stack[4];
1205: y1 = pt.y;
1206: x2 = x1 + stack[5];
1207: y2 = ybase;
1208: pt.x = x2 + stack[6];
1209: pt.y = y2;
1210: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1211: pt.open = true;
1212: stackptr = 0;
1213: break;
1214: case 1035: // flex
1215: x1 = pt.x + stack[0];
1216: y1 = pt.y + stack[1];
1217: x2 = x1 + stack[2];
1218: y2 = y1 + stack[3];
1219: pt.x = x2 + stack[4];
1220: pt.y = y2 + stack[5];
1221: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1222: x1 = pt.x + stack[6];
1223: y1 = pt.y + stack[7];
1224: x2 = x1 + stack[8];
1225: y2 = y1 + stack[9];
1226: pt.x = x2 + stack[10];
1227: pt.y = y2 + stack[11];
1228: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1229: pt.open = true;
1230: stackptr = 0;
1231: break;
1232: case 1036: // hflex1
1233: ybase = pt.y;
1234: x1 = pt.x + stack[0];
1235: y1 = pt.y + stack[1];
1236: x2 = x1 + stack[2];
1237: y2 = y1 + stack[3];
1238: pt.x = x2 + stack[4];
1239: pt.y = y2;
1240: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1241: x1 = pt.x + stack[5];
1242: y1 = pt.y;
1243: x2 = x1 + stack[6];
1244: y2 = y1 + stack[7];
1245: pt.x = x2 + stack[8];
1246: pt.y = ybase;
1247: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1248: pt.open = true;
1249: stackptr = 0;
1250: break;
1251: case 1037: // flex1
1252: ybase = pt.y;
1253: float xbase = pt.x;
1254: x1 = pt.x + stack[0];
1255: y1 = pt.y + stack[1];
1256: x2 = x1 + stack[2];
1257: y2 = y1 + stack[3];
1258: pt.x = x2 + stack[4];
1259: pt.y = y2 + stack[5];
1260: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1261: x1 = pt.x + stack[6];
1262: y1 = pt.y + stack[7];
1263: x2 = x1 + stack[8];
1264: y2 = y1 + stack[9];
1265: if (Math.abs(x2 - xbase) > Math.abs(y2 - ybase)) {
1266: pt.x = x2 + stack[10];
1267: pt.y = ybase;
1268: } else {
1269: pt.x = xbase;
1270: pt.y = y2 + stack[10];
1271: }
1272: gp.curveTo(x1, y1, x2, y2, pt.x, pt.y);
1273: pt.open = true;
1274: stackptr = 0;
1275: break;
1276: default:
1277: System.out.println("ERROR! TYPE1C CHARSTRING CMD IS "
1278: + cmd);
1279: break;
1280: }
1281: }
1282: }
1283:
1284: public static void main(String args[]) {
1285: if (args.length != 1) {
1286: System.out.println("Need the name of a cff font.");
1287: System.exit(0);
1288: }
1289: JFrame jf = new JFrame("Font test: " + args[0]);
1290: try {
1291: FileInputStream fis = new FileInputStream(args[0]);
1292: TestType1CFont panel = new TestType1CFont(fis);
1293: jf.getContentPane().add(panel);
1294: jf.pack();
1295: jf.show();
1296: panel.requestFocus();
1297: } catch (IOException ioe) {
1298: }
1299: }
1300: }
|