wps

PostScript for the Web
git clone https://logand.com/git/wps.git/
Log | Files | Refs | LICENSE

commit b9d59e1774d94fa691cdb8921e1d97c1634f1ce4
parent 816e5bef74761f547024a8e25ce1b3f0387d97ee
Author: tomas <tomas@logand.com>
Date:   Sat, 23 Jan 2010 14:27:13 +0100

Changes from 2009-06-25

Diffstat:
Mwps.js | 444+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mwps.wps | 156++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
2 files changed, 415 insertions(+), 185 deletions(-)

diff --git a/wps.js b/wps.js @@ -1,27 +1,49 @@ +// TODO one run, exec function or name !quoted + +function isQuoted(V) { + return V.q; +} + +function quote(V) { + V.q = true; + return V; +} + +function unquote(V) { + delete V.q; + return V; +} + function Symbol(N) { - if("/" == N[0]) { - this.nm = N.substring(1); - this.q = true; - } else this.nm = N; + this.nm = N; return this; } function isSymbol(V) { - return V && V.constructor == Symbol; -} - -function isQuoted(V) { - return V.q; + return V && V.constructor === Symbol; } function symbolName(V) { return V.nm; } -function ps0(L, F, S) { +function isArray(V) { + return V && V.constructor === Array; +} + +function inDs(Ds, K) { + for(var I = Ds.length - 1; 0 <= I; --I) { + if("undefined" != typeof Ds[I][K]) + return Ds[I]; + } + return false; +} + +function ps0(L, Os, Ds) { // TODO Nd name dict name=>sym? var N = L.length; var I = 0; + // TODO white space ffeed + null??? function member(C, L) {return 0 <= L.indexOf(C);} function peek() {return I < N && L[I];} function xchar() {return I < N && L[I++];} @@ -35,7 +57,9 @@ function ps0(L, F, S) { } } - function text() { // TODO hex text in <> + function text() { + // TODO hex text in <> + // TODO ASCII base-85 <~ and ~> xchar(); var L = []; var N = 1; @@ -58,6 +82,7 @@ function ps0(L, F, S) { case "n": C = "\n"; break; case "r": C = "\r"; break; case "t": C = "\t"; break; + // TODO \n (ignore \n) \b \f \ddd octal default: C = false; } @@ -69,11 +94,14 @@ function ps0(L, F, S) { } function symbol() { + // TODO 1e10 1E-5 real numbers + // TODO radix numbers 8#1777 16#FFFE 2#1000 var C = xchar(); + if(member(C, "()<>/% \t\n")) throw "Symbol expected"; var N = member(C, "+-0123456789."); var F = "." == C; var L = [C]; - while(peek() && !member(peek(), "%/[]{}<>( \t\n")) { + while(peek() && !member(peek(), "()<>[]{}/% \t\n")) { C = xchar(); L.push(C); if(N && !member(C, "0123456789")) { @@ -86,228 +114,310 @@ function ps0(L, F, S) { return N ? (F ? parseFloat(L) : parseInt(L, 10)) : new Symbol(L); } - function proc() { - xchar(); - var L = []; - while(peek()) { - var T = token(); - if("}" == T) break; - if(T || T == 0) L.push(T); - } - return L; - } + var D = 0; function token() { skip(); - switch(peek()) { // TODO read dict in <<>> + switch(peek()) { // TODO read dict in <> <~~> <<>> case false: return undefined; case "%": return comment(); - case "[": return xchar(); - case "]": return xchar(); - case "{": return proc(); - case "}": return xchar(); + case "[": return new Symbol(xchar()); + case "]": return new Symbol(xchar()); + case "{": D++; return new Symbol(xchar()); + case "}": D--; return new Symbol(xchar()); + case "/": xchar(); var X = symbol(); return quote(X); case "(": return text(); default: return symbol(); } } - function parse(E) { - var G = true; - while(G && peek()) { +// var Es = []; + + function exec() { + var X = Os.pop(); + if(isSymbol(X) && !isQuoted(X)) { // executable name + var K = symbolName(X); + var D = inDs(Ds, K); + var V = D && D[K]; + if(V) { + Os.push(V); + exec(); +// Es.push(V); +// if("function" == typeof V) V(); +// //else Os.push(V); +// else run(V); + } else throw "Unknown operator 1 '" + K + "'"; + } else if(isArray(X) && isQuoted(X)) { // proc + var M = X.length; + for(var I = 0; I < M; I++) { + //Es.push(X[I]); + Os.push(X[I]); + exec(); + } + } else if("function" == typeof X) X(); // operator + else Os.push(X); + } + +// function run(T) { +// if(T || T == 0) { +// // if(isSymbol(T) && !isQuoted(T)) { +// // var K = symbolName(T); +// // var D = inDs(Ds, K); +// // var V = D && D[K]; +// // if(V) { +// // if("function" == typeof V) V(); +// // //else Os.push(V); +// // else run(V); +// // } else throw "Unknown operator 1 '" + K + "'"; +// // } else Os.push(T); +// } +// } + + function parse() { + while(peek()) { var T = token(); if(T || T == 0) { - if(isSymbol(T) && !isQuoted(T)) { - var X = symbolName(T); - if(F[X]) F[X](); - else throw "Unknown operator '" + X + "'"; - if(E == X) G = false; - } else S.push(T); + Os.push(T); + if(D <= 0 || 1 == D && isSymbol(T) && "{" == symbolName(T)) + exec(); } } - return S; + return Os; } return parse(); } function wps(E, T) { - var S = []; - var F = {}; - var C = E.getContext("2d"); + var Os = []; + var Sd = {}; + var Ds = [Sd]; // trivial - F["true"] = function() {S.push(true);}; - F["false"] = function() {S.push(false);}; - F["null"] = function() {S.push(null);}; + Sd[".true"] = function() {Os.push(true);}; + Sd[".false"] = function() {Os.push(false);}; + Sd[".null"] = function() {Os.push(null);}; + + Sd["cvx"] = function() { + var X = Os.pop(); + if(isSymbol(X) && isQuoted(X)) Os.push(unquote(X)); // executable name + else if(isArray(X) && !isQuoted(X)) Os.push(quote(X)); // proc + // TODO string -> parse + else Os.push(X); + }; // math - F["neg"] = function() {S.push(-1 * S.pop());}; - F["add"] = function() {S.push(S.pop() + S.pop());}; - F["mul"] = function() {S.push(S.pop() * S.pop());}; - F["div"] = function() {var X = S.pop();S.push(S.pop() / X);}; - F["mod"] = function() {var X = S.pop();S.push(S.pop() % X);}; - - // stack - F["exch"] = function() { - var Y = S.pop(); - var X = S.pop(); - S.push(Y); - S.push(X); + Sd["neg"] = function() {Os.push(-1 * Os.pop());}; + Sd["add"] = function() {Os.push(Os.pop() + Os.pop());}; + Sd["mul"] = function() {Os.push(Os.pop() * Os.pop());}; + Sd["div"] = function() {var X = Os.pop(); Os.push(Os.pop() / X);}; + Sd["mod"] = function() {var X = Os.pop(); Os.push(Os.pop() % X);}; + + // stack and array + var M = {}; + Sd["mark"] = function() {Os.push(M);}; + Sd["counttomark"] = function() { + var N = 0; + for(var I = Os.length - 1; 0 <= I; I--) + if(M === Os[I]) return N; + else N++; + throw "Mark not found"; }; - F["dup"] = function() {var X = S.pop(); S.push(X); S.push(X);}; - F["clear"] = function() {S.length = 0;}; - F["pop"] = function() {S.pop();}; - F["index"] = function() {S.push(S[S.pop()]);}; // TODO from other end!!! - F["roll"] = function() { - var J = S.pop(); - var N = S.pop(); + Sd["exch"] = function() { + var Y = Os.pop(); + var X = Os.pop(); + Os.push(Y); + Os.push(X); + }; + Sd["clear"] = function() {Os.length = 0;}; + Sd["pop"] = function() {Os.pop();}; + Sd["index"] = function() { + Os.push(Os[Os.length - 2 - Os.pop()]); + }; + Sd["roll"] = function() { + var J = Os.pop(); + var N = Os.pop(); var X = []; var Y = []; for(var I = 0; I < N; I++) - if(I < J) X.unshift(S.pop()); - else Y.unshift(S.pop()); - for(I = 0; I < J; I++) S.push(X.shift()); - for(I = 0; I < N - J; I++) S.push(Y.shift()); + if(I < J) X.unshift(Os.pop()); + else Y.unshift(Os.pop()); + for(I = 0; I < J; I++) Os.push(X.shift()); + for(I = 0; I < N - J; I++) Os.push(Y.shift()); }; - F["copy"] = function() { - var N = S.pop(); + Sd["copy"] = function() { + var N = Os.pop(); + if("object" == typeof N) { + var X = Os.pop(); + for(var I in X) + N[I]=X[i]; + } else { + var X = Os.length - N; + for(var I = 0; I < N; I++) + Os.push(X + I); + } + }; + + Sd["length"] = function() {Os.push(Os.pop().length);}; + Sd["astore"] = function() { + var A = Os.pop(); + var N = A.length; + var X = Os.length - N; for(var I = 0; I < N; I++) - S.push(S[N - 1]); // TODO from other end!!! + A[I] = Os[X + I]; }; - F["eq"] = function() {var Y = S.pop(); var X = S.pop(); S.push(X == Y);}; - F["lt"] = function() {var Y = S.pop(); var X = S.pop(); S.push(X < Y);}; + Sd["eq"] = function() {var Y = Os.pop(); var X = Os.pop(); Os.push(X == Y);}; + Sd["lt"] = function() {var Y = Os.pop(); var X = Os.pop(); Os.push(X < Y);}; - //F["not"] = function() {var X = S.pop(); S.push(X == undefined || X == false);}; - //F[".nand"] = function() {S.push(F["not"]() || F["not"]());}; + //Sd["not"] = function() {var X = Os.pop(); Os.push(X == undefined || X == false);}; + //Sd[".nand"] = function() {Os.push(Sd["not"]() || Sd["not"]());}; - F["ifelse"] = function() { - var N = S.pop(); - var P = S.pop(); - var C = S.pop(); + Sd["ifelse"] = function() { + var N = Os.pop(); + var P = Os.pop(); + var C = Os.pop(); if(C == true) run(P); else run(N); }; - F["repeat"] = function() { - var B = S.pop(); - var N = S.pop(); - for(var I = 0; I < N; I++) run(B); - }; - F["for"] = function() { - var B = S.pop(); - var L = S.pop(); - var K = S.pop(); - var J = S.pop(); + Sd["for"] = function() { + var B = Os.pop(); + var L = Os.pop(); + var K = Os.pop(); + var J = Os.pop(); if(K < 0) { for(var I = J; L <= I; I += K) { - S.push(I); + Os.push(I); run(B); } } else { for(var I = J; I <= L; I += K) { - S.push(I); + Os.push(I); run(B); } } }; - F["."] = function() {alert(S.pop());}; - F["pstack"] = function() {alert(S);}; - - function run(C) { - if(!C.length) S.push(C); - else { - var M = C.length; - for(var I = 0; I < M; I++) { - var T = C[I]; - if(isSymbol(T) && !isQuoted(T)) { - var X = symbolName(T); - if(F[X]) F[X](); - else throw "Unknown operator '" + X + "'"; - } else S.push(T); - } - } - } - - F["get"] = function() { // dict key -- any - var K = S.pop(); - var D = S.pop(); + Sd["="] = function() {alert(Os.pop());}; + Sd["=="] = function() {alert(Os.pop());}; // TODO + Sd["stack"] = function() {alert(Os);}; // TODO + Sd["pstack"] = function() {alert(Os);}; // TODO + +// function run1(T) { +// if(T || T == 0) { +// if(isSymbol(T) && !isQuoted(T)) { +// var K = symbolName(T); +// var D = inDs(Ds, K); +// var V = D && D[K]; +// if(V) { +// if("function" == typeof V) V(); +// //else Os.push(V); +// else run(V); +// } else throw "Unknown operator 1 '" + K + "'"; +// } else Os.push(T); +// } +// } + +// function run(C) { +// if(!C.length) Os.push(C); // TODO run? +// else { +// var M = C.length; +// for(var I = 0; I < M; I++) { +// var T = C[I]; +// run1(T); +// // if(isSymbol(T) && !isQuoted(T)) { +// // var X = symbolName(T); +// // if(Sd[X]) Sd[X](); +// // else throw "Missing operator '" + X + "'"; +// // } else Os.push(T); +// } +// } +// } + + Sd["dict"] = function() {Os.pop(); Os.push({});}; + Sd["get"] = function() { // dict key -- any + var K = Os.pop(); + var D = Os.pop(); // TODO other datatypes http://www.capcode.de/help/get - S.push(D[K]); + if(isSymbol(K)) Os.push(D[symbolName(K)]); + else Os.push(D[K]); }; - F["put"] = function() { // dict key any -- - var V = S.pop(); - var K = S.pop(); - var D = S.pop(); + Sd["put"] = function() { // dict key any -- + var V = Os.pop(); + var K = Os.pop(); + var D = Os.pop(); // TODO other datatypes http://www.capcode.de/help/put - D[K] = V; + if(isSymbol(K)) D[symbolName(K)] = V; + else D[K] = V; }; - - F["def"] = function() { - var C = S.pop(); - var N = S.pop(); - if(isSymbol(N) && isQuoted(N)) F[symbolName(N)] = function() {run(C);}; - else throw "Wrong operator name '" + N + "' as '" + typeof N - + "' for '" + C + "'"; + Sd["begin"] = function() {Ds.push(Os.pop());}; + Sd["end"] = function() {Ds.pop();}; + Sd["currentdict"] = function() {Os.push(Ds[Ds.length - 1]);}; + Sd["where"] = function() { + var K = Os.pop(); + var D = inDs(Ds, K); + if(D) { + Os.push(D); + Os.push(true); + } else Os.push(false); }; +// Sd["def"] = function() { +// var C = Os.pop(); +// var N = Os.pop(); +// if(isSymbol(N) && isQuoted(N)) Sd[symbolName(N)] = function() {run(C);}; +// else throw "Wrong operator name '" + N + "' as '" + typeof N +// + "' for '" + C + "'"; +// }; + + Sd["array"] = function() {Os.push(new Array(Os.pop()));}; + + Sd["restore"] = function() {Os.pop();}; // TODO + Sd["save"] = function() {Os.push([]);}; // TODO + + Sd["bind"] = function() {}; // TODO + Sd["load"] = function() {}; // TODO + + ////////////////////////////////////////////////////////// + // js ffi operators - F[".call"] = function() { // dict key nargs -- result - var N = S.pop(); - var K = S.pop(); - var D = S.pop(); + Sd[".call"] = function() { // dict key nargs -- result + var N = Os.pop(); + var K = Os.pop(); + var D = Os.pop(); var X = []; - for(var I = 0; I < N; I++) X.unshift(S.pop()); - S.push(D[K].apply(D, X)); + for(var I = 0; I < N; I++) X.unshift(Os.pop()); + Os.push(D[K].apply(D, X)); }; - - F[".gc"] = function() { // -- gc - S.push(C); + Sd[".math"] = function() { // -- Math + Os.push(Math); }; - F[".math"] = function() { // -- Math - S.push(Math); + Sd[".gc"] = function() { // -- gc + Os.push(E.getContext("2d")); }; + ///////////////////////////////////////////////////// + // html5 utility operators // TODO js ffi to manipulate strings so the following can be in ps - F[".rgb"] = function() { - var B = S.pop(); - var G = S.pop(); - var R = S.pop(); - S.push("rgb(" + R + "," + G + "," + B + ")"); + Sd[".rgb"] = function() { + var B = Os.pop(); + var G = Os.pop(); + var R = Os.pop(); + Os.push("rgb(" + R + "," + G + "," + B + ")"); }; - F[".rgba"] = function() { - var A = S.pop(); - var B = S.pop(); - var G = S.pop(); - var R = S.pop(); - S.push("rgba(" + R + "," + G + "," + B + "," + A + ")"); + Sd[".rgba"] = function() { + var A = Os.pop(); + var B = Os.pop(); + var G = Os.pop(); + var R = Os.pop(); + Os.push("rgba(" + R + "," + G + "," + B + "," + A + ")"); }; - // "junk" for tiger.eps - - F["save"] = function() {S.push(true);}; - F["restore"] = function() {S.push(true);}; - F["bind"] = function() {}; - F["dict"] = function() {}; - F["load"] = function() {}; - F["begin"] = function() {}; - F["end"] = function() {}; - - F["where"] = function() {}; - //F["currentflat"] = function() {S.push(0);}; - //F["setflat"] = function() {}; - //F["clippath"] = function() {}; - F["transform"] = function() {}; - //F["itransform"] = function() {}; - //F["currentpoint"] = function() {S.push(0); S.push(0);}; - F["*"] = function() {}; - //F["showpage"] = function() {}; - if(T.length) for(var I = 0; I < T.length; I++) - ps0(T[I], F, S); - else ps0(T, F, S); + ps0(T[I], Os, Ds); + else ps0(T, Os, Ds); } diff --git a/wps.wps b/wps.wps @@ -1,5 +1,24 @@ %%% (c) 2009 Tomas Hlavaty +currentdict/systemdict currentdict put + +systemdict/{/mark cvx put +systemdict/[/mark cvx put + +systemdict/] +/counttomark cvx +/array cvx +/astore cvx +/exch cvx +/pop cvx +5 array astore cvx put + +systemdict/}/] cvx/cvx cvx 2 array astore cvx put + +%[1 2 3] = + +systemdict/def{currentdict 2 index 2 index put pop pop} put + %% math /abs{.math(abs)1 .call}def @@ -35,14 +54,25 @@ /sub{neg add}def /idiv{div .floor}def +%% stack + +/dup{0 index}def + %% conditionals +/true .true def +/false .false def +/null .null def + /ne{eq not}def /ge{lt not}def /le{1 index 1 index eq 3 1 roll lt or}def /gt{le not}def /if{{}ifelse}def +/loop{0 exch 0 exch 1 exch for}def +/repeat{1 1 4 2 roll for}def + %% html5 /.gget{.gc exch get}def @@ -55,13 +85,11 @@ %% canvas -% TODO track state, e.g. origin x y - /.save{(save)0 .gcall0}def /.restore{(restore)0 .gcall0}def -/scale{(scale)2 .gcall0}def -/rotate{(rotate)1 .gcall0}def -/translate{(translate)2 .gcall0}def +/.scale{(scale)2 .gcall0}def +/.rotate{(rotate)1 .gcall0}def +/.translate{(translate)2 .gcall0}def /.transform{(transform)6 .gcall0}def /.setTransform{(setTransform)6 .gcall0}def /.createLinearGradient{(createLinearGradient)4 .gcall1}def @@ -78,10 +106,10 @@ /.bezierCurveTo{(bezierCurveTo)6 .gcall0}def /.arcTo{(arcTo)5 .gcall0}def /.rect{(rect)4 .gcall0}def -/arc{(arc)6 .gcall0}def -/fill{(fill)0 .gcall0}def -/stroke{(stroke)0 .gcall0}def -/clip{(clip)0 .gcall0}def +/.arc{(arc)6 .gcall0}def +/.fill{(fill)0 .gcall0}def +/.stroke{(stroke)0 .gcall0}def +/.clip{(clip)0 .gcall0}def /.isPointInPath{(isPointInPath)2 .gcall1}def /.fillText{(fillText)4 .gcall0}def /.strokeText{(strokeText)4 .gcall0}def @@ -127,6 +155,65 @@ %% PostScript +/.deg2rad{.pi 180 div mul}def + +/identmatrix{pop [1 0 0 1 0 0]}def % TODO fill +/matrix{6 array identmatrix}def + +/.cx 0 def +/.cy 0 def +/.px 0 def +/.py 0 def +/.tm0 matrix def +/.tm matrix def +/.tmd matrix def + +/.setPoint{/.cy exch def/.cx exch def}def +/.setPath{/.py exch def/.px exch def}def + +/currentpoint{.cx .cy}def + +/setmatrix{/.tm exch def}def + +/..p{2 dict begin/y exch def/x exch def}def +/..P{end}def +/..tm{6 dict begin/ty exch def/tx exch def/d exch def/c exch def/b exch def/a exch def}def +/..TM{end}def +/.x{a x mul c y mul tx add add}def +/.y{b x mul d y mul ty add add}def +/.mmul{}def % TODO +/.tmu{/.cx .x def/.cy .y def/.tmd .tmd}def % TODO +%– initmatrix – Set CTM to device default +%matrix defaultmatrix matrix Fill matrix with device default matrix +%matrix currentmatrix matrix Fill matrix with CTM + +/translate{2 copy ..p 1 0 0 1 6 4 roll ..tm .tmu ..TM ..P .translate}def % TODO +%tx ty matrix translate matrix Define translation by (tx , ty) + +%sx sy scale – Scale user space by sx and sy +%sx sy matrix scale matrix Define scaling by sx and sy + +%angle rotate – Rotate user space by angle degrees +%angle matrix rotate matrix Define rotation by angle degrees + +%matrix concat – Replace CTM by matrix ´ CTM +%matrix1 matrix2 matrix3 concatmatrix matrix3 Fill matrix3 with matrix1 ´ matrix2 + +/transform{..p .tm ..tm .x .y ..TM ..P}def % TODO +%x y matrix transform x¢ y¢ Transform (x, y) by matrix + +%dx dy dtransform dx¢ dy¢ Transform distance (dx, dy) by CTM +%dx dy matrix dtransform dx¢ dy¢ Transform distance (dx, dy) by matrix + +%x¢ y¢ itransform x y Perform inverse transform of (x¢, y¢) by CTM +%x¢ y¢ matrix itransform x y Perform inverse transform of (x¢, y¢) by matrix + +%dx¢ dy¢ idtransform dx dy Perform inverse transform of distance (dx¢, dy¢) by CTM +%dx¢ dy¢ matrix idtransform dx dy Perform inverse transform of distance (dx¢, dy¢) by matrix + +%matrix1 matrix2 invertmatrix matrix2 Fill matrix2 with inverse of matrix1 + + /gsave{.save}def /grestore{.restore}def /rectclip{.clipRect}def @@ -134,22 +221,50 @@ /rectstroke{.strokeRect}def /newpath{.beginPath}def /closepath{.closePath}def -/moveto{.moveTo}def -/lineto{.lineTo}def +/moveto{2 copy .setPoint 2 copy .setPath .moveTo/.tm0 matrix def}def +/lineto{2 copy .setPoint 2 copy .setPath .lineTo/.tm0 matrix def}def /arcto{.arcTo}def /setlinewidth{.setLineWidth}def -/setlinecap{.setLineCap}def -/setlinejoin{.setLineJoin}def +/setlinecap{.setLineCap}def % TODO +/setlinejoin{.setLineJoin}def % TODO /setmiterlimit{.setMiterLimit}def -/setgray{255 mul dup dup .rgb .setFillStyle}def -/setrgbcolor{3{255 * 3 1 roll} repeat .rgb .setFillStyle}def +/currentlinewidth{.getLineWidth}def +/currentlinecap{<</butt 0/round 1/square 2>> .getLineCap get}def +/currentlinejoin{<</miter 0/round 1/bevel 2>> .getLineJoin get}def +/currentmiterlimit{.getMiterLimit}def + +/setgray{255 mul dup dup .rgb dup .setStrokeStyle .setFillStyle}def +/setrgbcolor{3{255 mul 3 1 roll} repeat .rgb dup .setStrokeStyle .setFillStyle}def /setfont{} def % TODO C.font = N + "pt " + F.V; -/clippath{}def % TODO -/show{.fillText}def % TODO 0 exch 0 exch .fillText +/clippath{0 0 .gcanvas(width)get .gcanvas(height)get .rect}def % TODO +/show{currentpoint 3 2 roll .fillText .strokeText}def % TODO /rlineto{lineto}def % TODO +/currentflat{42}def +/setflat{pop}def + +/transform{}def % TODO +/itransform{}def % TODO + +/currentpoint{0 0}def % TODO + +/showpage{}def + +/arc{.deg2rad exch .deg2rad exch true(arc)6 .gcall0}def % TODO currentpoint +/arcn{.deg2rad exch .deg2rad exch false(arc)6 .gcall0}def % TODO currentpoint + +/fill{.fill newpath currentpoint moveto}def % TODO + +/setdash{pop pop}def + +/stroke{.stroke newpath currentpoint moveto}def + +/curveto{2 copy .setPoint .bezierCurveTo}def + +/grestoreall{}def % TODO + %% PDF /w{setlinewidth}def @@ -210,7 +325,7 @@ /G{}def % TODO /g{}def % TODO /RG{}def % TODO -/rg{3{255 * 3 1 roll} repeat .rgb .setFillStyle}def % TODO +/rg{3{255 mul 3 1 roll} repeat .rgb dup .setStrokeStyle .setFillStyle}def % TODO /K{}def % TODO /k{}def % TODO /sh{}def % TODO @@ -225,3 +340,8 @@ /EMC{}def % TODO /BX{}def % TODO /EX{}def % TODO + +%% finish + +/userdict 1000 dict def +userdict begin