// Copyright 2004, FreeHEP. package org.freehep.postscript; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * DCS (Document Structuring Conventions) level 3.0 for PostScript Processor * * as specified in PostScript Language Document Structuring Conventions * Specification, version 3.0, 25 September 1992. * * @author Mark Donszelmann * @version $Id: DSC.java 10178 2006-12-08 09:03:07Z duns $ */ public class DSC { private static final String[] mode = {"diablo630", "fx100", "lj2000", "hpgl", "impress", "hplj", "ti855"}; private static final String[] orientation = {"Portrait", "Landscape"}; private static final String[] cmyk = {"Cyan", "Magenta", "Yellow", "Black"}; private final static Object[] keyArgs = { // // 5.1 General Header Comments // "PS-Adobe-3.0", TextLine.class, "PS-Adobe-2.1", NoArgs.class, "PS-Adobe-2.0", NoArgs.class, "PS-Adobe-1.0", NoArgs.class, "BoundingBox:", Rectangle.class, "Copyright:", TextLine.class, "Creator:", TextLine.class, "CreationDate:", TextLine.class, "DocumentData:", new String[] {"Clean7Bit", "Clean8Bit", "Binary"}, "Emulation:", mode, "EndComments", NoArgs.class, "Extensions:", new String[] {"DPS", "CMYK", "Composite", "FileSystem"}, "For:", TextLine.class, "LanguageLevel:", UInt.class, "Orientation:", orientation, "Pages:", UInt.class, "PageOrder:", new String[] {"Ascend", "Descend", "Special"}, "Routing:", TextLine.class, "Title:", TextLine.class, "Version:", Version.class, // // 5.2 General Body Comments // "BeginBinary:", UInt.class, "EndBinary", NoArgs.class, "BeginData:", Unparsed.class, "EndData", NoArgs.class, "BeginDefaults", NoArgs.class, "EndDefaults", NoArgs.class, "BeginEmulation:", mode, "EndEmulation", NoArgs.class, "BeginPreview:", Unparsed.class, "EndPreview", NoArgs.class, "BeginProlog", NoArgs.class, "EndProlog", NoArgs.class, "BeginSetup", NoArgs.class, "EndSetup", NoArgs.class, // // 5.3 General Page Comments // "BeginObject:", Unparsed.class, "EndObject", NoArgs.class, "BeginPageSetup", NoArgs.class, "EndPageSetup", NoArgs.class, "Page:", Page.class, "PageBoundingBox:", Rectangle.class, "PageOrientation:", orientation, // // 5.4 General Trailer Comments // "PageTrailer", NoArgs.class, "Trailer", NoArgs.class, "EOF", NoArgs.class, // // 6.1 Requirement Header Comments // "DocumentMedia:", Unparsed.class, "DocumentNeededResources:", Unparsed.class, "DocumentSuppliedResources:", Unparsed.class, "DocumentPrinterRequired:", Unparsed.class, "DocumentNeededFiles:", Unparsed.class, "DocumentSuppliedFiles:", Unparsed.class, "DocumentFonts:", Unparsed.class, "DocumentNeededFonts:", Unparsed.class, "DocumentSuppliedFonts:", Unparsed.class, "DocumentProcSets:", Unparsed.class, "DocumentNeededProcSets:", Unparsed.class, "DocumentSuppliedProcSets:", Unparsed.class, "OperatorIntervention:", Unparsed.class, "OperatorMessage:", Unparsed.class, "ProofMode:", new String[] {"TrustMe", "Substitute", "NotifyMe"}, "Requirements:", Unparsed.class, "VMlocation:", new String[] {"global", "local"}, "VMusage:", Unparsed.class, // // 6.2 Requirements Body Comments // "BeginDocument:", Unparsed.class, "EndDocument", NoArgs.class, "IncludeDocument:", Unparsed.class, "BeginFeature:", Unparsed.class, "EndFeature", NoArgs.class, "IncludeFeature:", Unparsed.class, "BeginFile:", Unparsed.class, "EndFile", NoArgs.class, "IncludeFile:", Unparsed.class, "BeginFont:", Unparsed.class, "EndFont", NoArgs.class, "IncludeFont:", Unparsed.class, "BeginProcSet:", Unparsed.class, "EndProcSet", NoArgs.class, "IncludeProcSet:", Unparsed.class, "BeginResource:", Unparsed.class, "EndResource", NoArgs.class, "IncludeResource:", Unparsed.class, // // 6.3 Requirements Page Comments // "PageFonts:", Unparsed.class, "PageFiles:", Unparsed.class, "PageMedia:", Unparsed.class, "PageRequirements:", Unparsed.class, "PageResources:", Unparsed.class, // // 7.1 Color Header Comments // "CMYKCustomColor:", Unparsed.class, "DocumentCustomColors:", Unparsed.class, "DocumentProcessColors:", Unparsed.class, "RGBCustomColor:", Unparsed.class, // // 7.2 Color Body Comments // "BeginCustomColor:", Unparsed.class, "EndCustomColor", NoArgs.class, "BeginProcessColor:", cmyk, "EndProcesColor", NoArgs.class, // // 7.3 Color Page Comments // "PageCustomColors:", Unparsed.class, "PageProcessColors:", Unparsed.class, // // 8.2 Query Comments // "?BeginFeatureQuery:", Unparsed.class, "?EndFeatureQuery:", Unparsed.class, "?BeginFileQuery:", Unparsed.class, "?EndFileQuery:", Unparsed.class, "?BeginFontListQuery", NoArgs.class, "?EndFontListQuery:", Unparsed.class, "?BeginFontQuery:", Unparsed.class, "?EndFontQuery:", Unparsed.class, "?BeginPrinterQuery", NoArgs.class, "?EndPrinterQuery:", Unparsed.class, "?BeginQuery:", Unparsed.class, "?EndQuery:", Unparsed.class, "?BeginQuery:", Unparsed.class, "?EndQuery:", Unparsed.class, "?BeginQuery:", Unparsed.class, "?EndQuery:", Unparsed.class, "?BeginResourceListQuery:", new String[] {"font", "file", "procset", "pattern", "form", "encoding"}, "?EndResourceListQuery:", Unparsed.class, "?BeginResourceQuery:", Unparsed.class, "?EndResourceQuery:", Unparsed.class, "?BeginVMStatus", NoArgs.class, "?EndVMStatus:", Unparsed.class, // // 10. Special Structuring Comments // "?BeginExitServer:", Unparsed.class, "?EndExitServer", NoArgs.class, }; private Collection listeners = new ArrayList(); private Map parseTable = new HashMap(); private Map dscTable = new HashMap(); public DSC() { // fill the parseTable for (int i=0; i<keyArgs.length; i+=2) { String token = (String)keyArgs[i]; try { Object obj = keyArgs[i+1]; if (obj instanceof Class) { obj = ((Class)obj).newInstance(); } else if (obj instanceof String[]) { obj = new Enumeration((String[])obj); } addDSCComment(token, obj); } catch (InstantiationException ie) { System.err.println("DSC: could not instantiate class: '"+token); } catch (IllegalAccessException ie) { System.err.println("DSC: no access to class: '"+token); } } } public Object addDSCComment(String comment, Object parse) { Object obj = parseTable.get(comment); parseTable.put(comment, parse); return obj; } public void addDSCEventListener(DSCEventListener listener) { listeners.add(listener); } public void removeDSCEventListener(DSCEventListener listener) { listeners.remove(listener); } // Parses a DCS comment (not pre-pended with the %% signs, but including the appended colon and arguments). public boolean parse(String line, OperandStack os) { String[] tokens = line.split("[ \t]", 2); String key = tokens[0]; String params = ""; if (tokens.length == 1) { // special case if space is missing after colon tokens = line.split(":", 2); if (tokens.length == 2) { key = tokens[0]+":"; params = tokens[1]; } } else { params = tokens[1]; } // lookup comment, parse arguments and derive the state Arguments args = (Arguments)parseTable.get(key); int state; if (params.startsWith("(atend)")) { dscTable.put(key, new Boolean(true)); state = (args != null) ? DSCEvent.PARSED : DSCEvent.UNPARSED; } else { if (args != null) { Object parsed = args.parse(key, params, os); if (parsed != null) { dscTable.put(key, parsed); state = DSCEvent.PARSED; } else { state = DSCEvent.ERROR; } } else { dscTable.put(key, params); state = DSCEvent.UNPARSED; } } // inform the listeners for (Iterator i=listeners.iterator(); i.hasNext(); ) { DSCEventListener listener = (DSCEventListener)i.next(); listener.dscCommentFound(new DSCEvent(this, key, dscTable.get(key), state)); } return state != DSCEvent.ERROR; } public Object get(String key) { return dscTable.get(key); } public static String text(String s) { if (s.startsWith("(") && s.endsWith(")")) { s = s.substring(1, s.length()-2); } return s; } public static interface Arguments { public Object parse(String key, String params, OperandStack os); } public static class NoArgs implements Arguments { public Object parse(String key, String params, OperandStack os) { return new Boolean(true); } } public static class TextLine implements Arguments { public Object parse(String key, String params, OperandStack os) { return text(params); } } public static class Rectangle implements Arguments { public Object parse(String key, String params, OperandStack os) { try { String[] tokens = params.split("[ \t]", 4); if (tokens.length != 4) return null; int[] p = new int[4]; for (int i=0; i<tokens.length; i++) { p[i] = Integer.parseInt(tokens[i]); } return new java.awt.Rectangle(p[0], p[1], p[2]-p[0], p[3]-p[1]); } catch (NumberFormatException e) { return null; } } } public static class UInt implements Arguments { public Object parse(String key, String params, OperandStack os) { try { String[] tokens = params.split("[ \t]", 2); return Long.valueOf(tokens[0]); } catch (NumberFormatException e) { return null; } } } public static class Enumeration implements Arguments { private String[] enumeration; public Enumeration(String[] enumeration) { this.enumeration = enumeration; } public Object parse(String key, String params, OperandStack os) { String[] tokens = params.split("[ \t]", 2); for (int i=0; i<enumeration.length; i++) { if (enumeration[i].equals(tokens[0])) { return tokens[0]; } } return null; } } public static class Unparsed implements Arguments { public Object parse(String key, String params, OperandStack os) { return params; } } // // 5.1 // public static class Version implements Arguments { private double version; private long revision; public Version() { } public Version(double v, long r) { version = v; revision = r; } public double getVersion() { return version; } public long getRevision() { return revision; } public Object parse(String key, String params, OperandStack os) { try { String[] tokens = params.split("[ \t]", 2); if (tokens.length != 2) return null; double v = Double.parseDouble(tokens[0]); long r = Long.parseLong(tokens[1]); return new Version(v, r); } catch (NumberFormatException e) { return null; } } public String toString() { return "v"+version+"r"+revision; } } // // 5.3 // public static class Page implements Arguments { private String label; private long number; public Page() { } public Page(String l, long n) { label = l; number = n; } public String getLabel() { return label; } public long getNumber() { return number; } public Object parse(String key, String params, OperandStack os) { try { String[] tokens = params.split("[ \t]", 2); if (tokens.length != 2) return null; long n = Long.parseLong(tokens[1]); return new Page(text(tokens[0]), n); } catch (NumberFormatException e) { return null; } } public String toString() { return label+" "+number; } } }