PrePost, a PostScript pre-processing system and/or simplified wrapper language ============================================================================== (C) 2005 Martin J. Fiedler Writing PostScript directly is cool, but it sucks quite hard because of that peculiar stack management stuff. PrePost consists of two parts, each serving one particular purpose: ComPost -- A compiler for an artificial language that makes PostScript development much easier. Instead of managing the stack "by hand", the PrePost artificial language handles variables like in most other languages. Stack management is almost completely transparent. OutPost -- An "optimizer" that strips all comments and unnecessary whitespace from a PostScript file. Additionally, it generates compact names for commonly used functions, compressing the output even further. Both parts are available as stand-alone command-line Python programs. In addition, PrePost itself is a "driver" that performs the tasks of ComPost and OutPost in one step. ComPost, the PrePost compiler ----------------------------- The PrePost language turns PostScript's postfix notation into a more concise prefix notation (hence the name "PrePost"). For example, "1 2 3 mul add" becomes "add(1,mul(2,3))". In addition, PrePost allows to use a Perl-like variable syntax (read: "$var"). Stack management is done automatically, based on variable usage inside the surrounding block. The code generated by PrePost is quite sub-optimal, but the immense savings in development time usually compensate that :) Functions can be defined to create a "/functionname { ... } bind def" block: function foo($bar,$baz):$result $result=mul($bar,add($baz,3)) end This generates "/foo{3 add mul}bind def". Note: - Each statement is written on exactly one line. - The function header specifies input and output variables. - Each statement is written in the form "$destination=". - Function names are exactly those of PostScript. The most important pitfall in PrePost is that it does NOT know whether a function name or the number of parameters or return values is correct. If you write foo($a,$b), then /foo is expected to have exactly 2 parameters. If you write "$a,$b=foo()", then /foo is expected to return exactly 2 parameters on the stack. Also note that inside complex expressions, PrePost implicitly expects functions to return exactly one scalar value. Otherwise, the automatic stack management will fail. In addition, PrePost supports if/then/else and for statements: if(gt($a,0)) % condition moveto($a,0) else moveto(0,0) end for($x,1,1,10) % loop variable $x, count from 1 to 10, increment by 1 rlineto(1,$x) end stroke(closepath()) % some trickery on PrePost's stack manager: % generates "closepath stroke" Constants may be defined to hold numerical(!) values. Here, normal infix formula notation and Python's math library functions may be used: const SQRT_2 = sqrt(2.0) const COS_SIN_45 = 0.5*SQRT_2 $x1=mul(sub($x0,$y0),COS_SIN_45) Any word encountered in the source code that is not a constant name is interpreted as a function name, except for the following situations: - $var = Variables - /Name = PostScript names (are directly passed to the output file) - 123.45 = Numbers (pass-through, too) - &something = verbatim statement. The '&' prefix is stripped away, and the remainder of the is written to the output file For more complex tasks, PrePost offers a 'raw' statement: raw $x,$y:$res mul end Like functions, raw statements accept input values that are put onto the stack, in the order specified, prior to the execution of the raw block. The values left on the stack after execution are then aliased to the result variables. Both variable( set)s may be omitted. The most common use of raw blocks is at the very beginning of the file. Because comments are not stripped inside raw blocks, these can (and need to) be used to generate the PostScript she-bang and/or DSC comments: raw %!PS-Adobe-3.0 %%BoundingBox: 0 0 640 480 end If you want to know more about PrePost, please read its source code or the example PrePost code, or ask me via e-mail. OutPost, the PostScript optimizer --------------------------------- OutPost transforms PostScript input files into a most compact representation, stripping comments and unnecessary whitespace as much as possible. It accepts default PostScript statements inclusing DSC, arrays and strings, but NOT hex strings. Additionally, OutPost supports some preprocessor statements that control automatic remapping of symbol names to shorter representations. For example, "/!{bind def}bind def" saves 7 bytes for each successive use of "bind def". - %#remap If the specified symbol appears often enough in the file that a remapping would save bytes, an appropriate "bind def" statement is added at the position of the %#remap directive. Please note that remapping takes place globally, so the directive MUST precede the first usage of the symbol in question. - %#remap all This automatically remaps ALL statements found in the file into a compact 1- or 2-byte representation. - %#noremap Excludes a symbol from automatic remapping. This is useful if OutPost accidentally remaps symbols that are deliberately used as names, like font names. So a "%#noremap Helvetica" statement might fix some strange problems. - %#symbol Declares a symbol as "privately defined". This means that the symbol will be remapped, but no "bind def" statement will be issued. This is necessary for user-defined functions, constants and variables. Consequently, ComPost automatically adds a %#symbol directive at the beginning of each function.