// Copyright 2001-2004, FreeHEP. package org.freehep.postscript; import java.io.EOFException; import java.io.FileInputStream; import java.io.IOException; import java.io.PushbackInputStream; import org.freehep.util.io.ASCII85InputStream; import org.freehep.util.io.ASCIIHexInputStream; import org.freehep.util.io.EncodingException; /** * Scanner for PostScript input * * @author Mark Donszelmann * @version $Id: Scanner.java 10178 2006-12-08 09:03:07Z duns $ */ public class Scanner { private PushbackInputStream in; private ByteBuffer buffer; private int lineNo; private int openCurly, openSquare; private PostScriptStack procStack; private boolean nextIsComment; private boolean nextIsDSC; private DSC dsc; public Scanner(PushbackInputStream in, DSC dsc) throws IOException { this.in = in; buffer = new ByteBuffer(80); lineNo = 1; openCurly = 0; openSquare = 0; procStack = new PostScriptStack(); nextIsComment = false; nextIsDSC = false; this.dsc = dsc; } public void close() throws IOException { in.close(); } public PSObject nextToken(boolean packingMode, NameLookup lookup) throws IOException, SyntaxException, NameNotFoundException { PSObject obj = nextObject(packingMode, lookup); // deferred execution ? while ((openCurly > 0) && (!(obj instanceof PSComment)) && (!(obj instanceof PSDSC))) { procStack.push(obj); obj = nextObject(packingMode, lookup); } return obj; } private PSObject nextObject(boolean packingMode, NameLookup lookup) throws IOException, SyntaxException, NameNotFoundException { buffer.reset(); // needed in case DSC reads too far searching for continuation DSC comments. if (nextIsComment) return readComment(); if (nextIsDSC) return readDSC(); int next = readNext(); switch (next) { case -1: return null; case '(': return readString(); case ')': throw new SyntaxException(lineNo, "Unbalanced Parenthesis"); case '~': next = in.read(); if (next == '>') { throw new SyntaxException(lineNo, "Non matching '~>'"); } if (next > 0) { in.unread(next); } return readName('~', false); case '>': next = in.read(); if (next == '>') { return new PSName(">>"); } if (next > 0) { in.unread(next); } throw new SyntaxException(lineNo, "Non matching '>'"); case '<': return readEncoding(); case '[': openSquare++; return new PSName("["); case ']': if (openSquare > 0) { openSquare--; return new PSName("]"); } throw new SyntaxException(lineNo, "Non matching ']'"); case '{': openCurly++; return new PSMark(); case '}': if (openCurly > 0) openCurly--; int n = procStack.countToMark(); if (n < 0) { throw new SyntaxException(lineNo, "Unmatched '{'"); } PSObject[] array = new PSObject[n]; for (int i=n-1; i>=0; i--) { array[i] = procStack.popObject(); } procStack.popMark(); PSPackedArray proc; if (packingMode) { proc = new PSPackedArray(array); } else { proc = new PSArray(array); } proc.setExecutable(); return proc; case '/': return readLiteral(lookup); case '%': // normal comment or DSC next = in.read(); if (((next == '%') || (next == '!')) && (dsc != null)) { return readDSC(); } if (next > 0) { in.unread(next); } return readComment(); case '@': // special name we do not handle at this time, normally associated to PCL commands // FIXME, look up the PCL details somewhere. return readComment(); case '+': case '-': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return readNumber(next); default: return readName(next, false); } } public int getLineNo() { return lineNo; } private PSString readString() throws IOException { int balance = 1; int next = in.read();; while ((balance > 0) && (next != -1)) { switch (next) { case '\\': escapeChar(); break; case '(': balance++; buffer.append(next); break; case ')': balance--; if (balance > 0) { buffer.append(next); } break; case '\r': // replaced by LF in string next = in.read(); if ((next != '\n') && (next > 0)) { in.unread(next); } buffer.append('\n'); lineNo++; break; case '\n': buffer.append(next); lineNo++; break; default: buffer.append(next); break; } next = in.read(); } if ((balance == 0) && (next > 0)) { in.unread(next); } return new PSString(buffer.getChars()); } private void escapeChar() throws IOException { int next = in.read(); if (next == -1) throw new EOFException("Unexpected End Of File"); switch (next) { case 'n': buffer.append('\n'); break; case 'r': buffer.append('\r'); break; case 't': buffer.append('\t'); break; case 'b': buffer.append('\b'); break; case 'f': buffer.append('\f'); break; case '\\': buffer.append('\\'); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': StringBuffer octal = new StringBuffer(3); octal.append(next-'0'); next = in.read(); if (next == -1) throw new EOFException("Unexpected End Of File"); if (('0' <= next) && (next <= '7')) { octal.append(next-'0'); next = in.read(); if (next == -1) throw new EOFException("Unexpected End Of File"); if (('0' <= next) && (next <= '7')) { octal.append(next-'0'); // next = in.read(); } else if (next > 0) { in.unread(next); } } else if (next > 0) { in.unread(next); } buffer.append(Integer.parseInt(octal.toString(), 8)); return; case '\n': next = in.read(); lineNo++; return; case '\r': next = in.read(); if (next == '\n') { next = in.read(); } lineNo++; return; case '(': case ')': default: buffer.append(next); break; } } private PSComment readComment() throws IOException { nextIsComment = false; int next = in.read(); while ((next != -1) && (next != '\r') && (next != '\n')) { buffer.append(next); next = in.read(); } return new PSComment(buffer.getString()); } private PSDSC readDSC() throws IOException { nextIsDSC = false; while (true) { int next = in.read(); while ((next != -1) && (next != '\r') && (next != '\n')) { buffer.append(next); next = in.read(); } // read next ignoring '\r' next = in.read(); if (next == '\r') next = in.read(); if (next != '%') { if (next > 0) in.unread(next); break; } next = in.read(); if (next != '%') { if (next > 0) in.unread(next); nextIsComment = true; break; } next = in.read(); if (next != '+') { if (next > 0) in.unread(next); nextIsDSC = true; break; } } return new PSDSC(buffer.getString(), dsc); } private PSObject readLiteral(NameLookup lookup) throws IOException, SyntaxException, NameNotFoundException { int next = in.read(); if (next == -1) throw new IOException("Unexpected EOF reached"); if (next == '/') { // immediate next = in.read(); PSName name = readName(next, true); PSObject obj = lookup.lookup(name); if (obj == null) throw new NameNotFoundException("Immediate name /'"+name+" not found."); return obj; } return readName(next, true); } private PSName readName(int next, boolean literal) throws IOException, SyntaxException { boolean found = false; while(!found) { switch(next) { case -1: case '\000': case '\b': case '\t': case '\n': case '\f': case '\r': case ' ': found = true; break; case ')': throw new SyntaxException(lineNo, "Unbalanced Parenthesis"); case '%': case '(': case '/': case '<': case '>': case '[': case ']': case '{': case '}': found = true; in.unread(next); break; default: buffer.append(next); next = in.read(); break; } } PSName name = new PSName(buffer.getString(), literal); return name; } private PSObject readNumber(int next) throws IOException, SyntaxException { String name = readName(next, false).getValue(); try { return PSUtils.parseNumber(name); } catch (NumberFormatException e) { } return new PSName(name); } private PSObject readEncoding() throws IOException, SyntaxException { int next = in.read(); switch (next) { case '~': ASCII85InputStream in85 = new ASCII85InputStream(in); try { next = in85.read(); while (next != -1) { buffer.append(next); next = in85.read(); } } catch (EncodingException e) { throw new SyntaxException(lineNo, e.getMessage()); } lineNo += in85.getLineNo() - 1; return new PSString(buffer.getChars()); case '<': return new PSName("<<"); default: if (next > 0) { in.unread(next); } ASCIIHexInputStream inHex = new ASCIIHexInputStream(in); try { next = inHex.read(); while (next != -1) { buffer.append(next); next = inHex.read(); } } catch (EncodingException e) { throw new SyntaxException(lineNo, e.getMessage()); } lineNo += inHex.getLineNo() - 1; return new PSString(buffer.getChars()); } } // skips whitespace until next token private int readNext() throws IOException { boolean ws; int next; do { next = in.read(); switch(next) { case '\r': next = in.read(); if ((next != '\n') && (next > 0)) { in.unread(next); } // fallthrough case '\n': lineNo++; ws = true; break; case '\000': case '\b': case '\t': case '\f': case ' ': ws = true; break; default: ws = false; } } while ((next != -1) && ws); return (ws) ? -1 : next; } public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("Usage: Scanner filename.ps"); System.exit(1); } DSC dsc = new DSC(); Scanner scanner = new Scanner(new PushbackInputStream(new FileInputStream(args[0])), dsc); PSObject token; while ((token = scanner.nextToken(true, null)) != null) { System.out.println(scanner.getLineNo()+": "+token.getClass().getName()+": "+token); } scanner.close(); } }