commit b9d59e1774d94fa691cdb8921e1d97c1634f1ce4
parent 816e5bef74761f547024a8e25ce1b3f0387d97ee
Author: tomas <tomas@logand.com>
Date:   Sat, 23 Jan 2010 14:27:13 +0100
Changes from 2009-06-25
Diffstat:
| M | wps.js |  |  | 444 | +++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ | 
| M | wps.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