commit 900ea78da3b57a7c03a76609c671676c2bd13dd1
Author: tomas <tomas@logand.com>
Date:   Sat, 23 Jan 2010 14:21:35 +0100
Initial commit
Diffstat:
| A | wps.js |  |  | 578 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | wps.wps |  |  | 362 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
2 files changed, 940 insertions(+), 0 deletions(-)
diff --git a/wps.js b/wps.js
@@ -0,0 +1,578 @@
+function PdfT(V) {
+   this.V = V;
+   return this;
+}
+function isPdfT(V) {
+   return V.constructor == PdfT; // TODO better test
+}
+
+function ps0(L, F, S) {
+   var N = L.length;
+   var I = 0;
+   if(!S) S = [];
+
+   function member(C, L) {return 0 <= L.indexOf(C);}
+   function peek() {return I < N && L[I];}
+   function xchar() {return I < N && L[I++];}
+   function skip() {while(I < N && member(L[I], " \t\n")) I++;}
+
+   function comment() {
+      while("%" == peek()) {
+         while(peek() && "\n" != peek())
+            xchar();
+         skip();
+      }
+   }
+
+   function text() {
+      xchar();
+      var L = [];
+      var N = 1;
+      while(0 < N && peek()) {
+         var C = xchar();
+         switch(C) {
+            case "(":
+               N++;
+               break;
+            case ")":
+               N--;
+               if(N <= 0) C = false;
+               break;
+            case "\\":
+               C = xchar();
+               switch(C) {
+                  case "(": break;
+                  case ")": break;
+                  case "\\": break;
+                  case "n": C = "\n"; break;
+                  case "r": C = "\r"; break;
+                  case "t": C = "\t"; break;
+                  default:
+                     C = false;
+               }
+               break;
+         }
+         if(C !== false) L.push(C);
+      }
+      return new PdfT(L.join(""));
+   }
+
+   function symbol() {
+      var C = xchar();
+      var N = member(C, "+-0123456789.");
+      var F = "." == C;
+      var L = [C];
+      while(peek() && !member(peek(), "%/[]{}<>( \t\n")) {
+         C = xchar();
+         L.push(C);
+         if(N && !member(C, "0123456789")) {
+            if(!F && "." == C) F = true;
+            else N = false;
+         }
+      }
+      L = L.join("");
+      if(1 == L.length && member(L, "+-.")) N = false;
+      return N ? (F ? parseFloat(L) : parseInt(L, 10)) : L;
+   }
+
+   function code() {
+      xchar();
+      var L = [];
+      while(peek()) {
+         var T = token();
+         if("}" == T) break;
+         if(T || T == 0) L.push(T);
+      }
+      return L;
+   }
+
+   function token() {
+      skip();
+      switch(peek()) {
+         case false: return undefined;
+         case "%": return comment();
+         case "[": return xchar();
+         case "]": return xchar();
+         case "{": return code();
+         case "}": return xchar();
+         case "(": return text();
+         default: return symbol();
+      }
+   }
+
+//   function quoted(T) {
+//      return typeof T == "string" && "/" == T.charAt(0);
+//   }
+
+   function parse(E) {
+      var G = true;
+      while(G && peek()) {
+         var T = token();
+         if(T || T == 0) {
+            if(typeof T == "number" || typeof T == "object" || quoted(T))
+               S.push(T);
+            else {
+               if(F[T]) F[T]();
+               else throw "Unknown operator '" + T + "' " + typeof T;
+               if(E == T) G = false;
+            }
+         }
+      }
+      return S;
+   }
+
+   return parse();
+}
+
+function quoted(T) {
+   return typeof T == "string" && "/" == T.charAt(0);
+}
+
+function wps(E, W, H, T) {
+   var S = [];
+   var F = {};
+   var C = E.getContext("2d");
+
+   E.setAttribute("width", W);
+   E.setAttribute("height", H);
+
+   function min(X, Y) {
+      return X < Y ? X : Y;
+   }
+   function max(X, Y) {
+      return X < Y ? Y : X;
+   }
+
+   // basic operators
+
+   F["true"] = function() {S.push(true);};
+   F["false"] = function() {S.push(false);};
+   F["null"] = function() {S.push(null);};
+
+   F["neg"] = function() {S.push(- S.pop());};
+   F["add"] = function() {S.push(S.pop() + S.pop());};
+   F["sub"] = function() {F["neg"](); F["add"]();};
+   F["mul"] = function() {S.push(S.pop() * S.pop());};
+   F["div"] = function() {
+      var X = S.pop();
+      S.push(S.pop() / X);
+   };
+   F["idiv"] = function() {
+      var X = S.pop();
+      S.push(Math.floor(S.pop() / X));
+   };
+   F["mod"] = function() {
+      var X = S.pop();
+      S.push(S.pop() % X);
+   };
+   // TODO sqrt, exp, ceiling, sin
+   F["exch"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      S.push(Y);
+      S.push(X);
+   };
+
+   F["dup"] = function() {var X = S.pop(); S.push(X); S.push(X);};
+   F["clear"] = function() {S = [];};
+   F["pop"] = function() {S.pop();};
+   // TODO roll
+
+   F["eq"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      S.push(X == Y);
+   };
+   F["ne"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      S.push(X != Y);
+   };
+   F["gt"] = 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);
+   };
+   F["ge"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      S.push(X >= Y);
+   };
+   F["le"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      S.push(X <= Y);
+   };
+
+   F["if"] = function() {
+      var B = S.pop();
+      var C = S.pop();
+      if(C == true) run(B);
+   };
+   F["ifelse"] = function() {
+      var N = S.pop();
+      var P = S.pop();
+      var C = S.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();
+      if(K < 0) {
+         for(var I = J; L <= I; I += K) {
+            S.push(I);
+            run(B);
+         }
+      } else {
+         for(var I = J; I <= L; I += K) {
+            S.push(I);
+            run(B);
+         }
+      }
+   };
+
+   F["."] = function() {alert(S.pop());};
+   F["=="] = function() {alert(S[0]);};
+   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(typeof T == "number" || typeof T == "object" || quoted(T))
+                  S.push(T);
+               else {
+                  if(F[T]) F[T]();
+                  else throw "Unknown operator '" + T + "' " + typeof T;
+               }
+            }
+         }
+   }
+
+   F["def"] = function() {
+      var C = S.pop();
+      var N = S.pop();
+      if(quoted(N)) F[N.substring(1)] = function() {run(C);};
+      else throw "Wrong operator name " + N + " for " + C;
+   };
+
+   // html5 graphic operators
+
+   //transform
+   //setTransform
+   //createLinearGradient
+   //createRadialGradient
+   //createPatternI
+   //createPatternC
+   //createPatternV
+   F["clearRect"] = function() {
+      var H = S.pop();
+      var W = S.pop();
+      var Y = S.pop();
+      var X = S.pop();
+      C.clearRect(X, Y, W, H);
+   };
+   F["fillRect"] = function() {
+      var H = S.pop();
+      var W = S.pop();
+      var Y = S.pop();
+      var X = S.pop();
+      C.fillRect(X, Y, W, H);
+   };
+   F["strokeRect"] = function() {
+      var H = S.pop();
+      var W = S.pop();
+      var Y = S.pop();
+      var X = S.pop();
+      C.strokeRect(X, Y, W, H);
+   };
+   //quadraticCurveTo
+   F["rect"] = function() {
+      var H = S.pop();
+      var W = S.pop();
+      var Y = S.pop();
+      var X = S.pop();
+      C.strokeRect(X, Y, W, H);
+   };
+   //isPointInPath
+   //fillText
+   //strokeText
+   //measureText
+   //drawImageI1
+   //drawImageI2
+   //drawImageC1
+   //drawImageC2
+   //drawImageV1
+   //drawImageV2
+   //createImageData1
+   //createImageData2
+   //getImageData
+   //putImageData
+
+   // html5 utility operators
+
+   F["gput"] = function() {
+      var K = S.pop();
+      var V = S.pop();
+      C[K.substring(1)] = isPdfT(V) ? V.V : V;
+   };
+   F["gget"] = function() {
+      var K = S.pop();
+      S.push(C[K.substring(1)]);
+   };
+
+   F["rgb"] = function() {
+      var B = S.pop();
+      var G = S.pop();
+      var R = S.pop();
+      S.push(new PdfT("rgba(" + R + "," + G + "," + B + ")"));
+   };
+   F["rgba"] = function() {
+      var A = S.pop();
+      var B = S.pop();
+      var G = S.pop();
+      var R = S.pop();
+      S.push(new PdfT("rgba(" + R + "," + G + "," + B + "," + A + ")"));
+   };
+
+   // ps graphic operators
+
+   F["gsave"] = function() {C.save();};
+   F["grestore"] = function() {C.restore();};
+   F["scale"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      C.scale(X, Y);
+   };
+   F["rotate"] = function() {
+      var A = S.pop();
+      C.rotate(A);
+   };
+   F["translate"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      C.translate(X, Y);
+   };
+   F["newpath"] = function() {C.beginPath();};
+   F["closepath"] = function() {C.closePath();};
+   F["moveto"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      C.moveTo(X, Y);
+   };
+   F["lineto"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      C.lineTo(X, Y);
+   };
+   F["arcto"] = function() {
+      var R = S.pop();
+      var Y2 = S.pop();
+      var X2 = S.pop();
+      var Y1 = S.pop();
+      var X1 = S.pop();
+      C.arcTo(X1, Y1, X2, Y2, R);
+   };
+   F["arc"] = function() {
+      var A = S.pop();
+      var E = S.pop();
+      var B = S.pop();
+      var R = S.pop();
+      var Y = S.pop();
+      var X = S.pop();
+      C.arc(X, Y, R, B, E, A);
+   };
+   F["fill"] = function() {C.fill();};
+   F["stroke"] = function() {C.stroke();};
+   F["clip"] = function() {C.clip();};
+   F["setgray"] = function() {
+      var G = S.pop();
+      C.fillStyle = "rgb(" + G * 255 + "," + G * 255 + "," + G * 255 + ")";
+   };
+
+   // TODO eps operators
+
+   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() {};
+   F["setflat"] = function() {};
+   F["_"] = function() {};
+   F["clippath"] = function() {};
+
+   // pdf graphic operators
+
+   F["w"] = function() {C.lineWidth = S.pop();};
+   F["J"] = function() {C.lineCap = S.pop();};
+   F["j"] = function() {C.lineJoin = S.pop();};
+   F["M"] = function() {C.mitterLimit = S.pop();};
+   F["d"] = function() {
+      var P = S.pop();
+      var A = S.pop();
+      alert("TODO d");
+   };
+   F["ri"] = function() {alert("TODO ri");};
+   F["i"] = function() {alert("TODO i");};
+   F["gs"] = function() {alert("TODO gs");};
+
+   F["q"] = function() {C.save();};
+   F["Q"] = function() {C.restore();};
+   F["cm"] = function() { // TODO fix cm
+      var Dy = S.pop();
+      var Dx = S.pop();
+      var M22 = S.pop();
+      var M21 = S.pop();
+      var M12 = S.pop();
+      var M11 = S.pop();
+      //alert(M11 +"|"+ M12 +"|"+ M21 +"|"+ M22 +"|"+ Dx +"|"+ Dy);
+      //C.setTransform(M11, M12, M21, M22, Dx, Dy);
+      C.transform(M11, M12, M21, M22, Dx, Dy);
+   };
+
+   F["m"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      C.beginPath(); // TODO only if not m previously
+      C.moveTo(X, Y);
+   };
+   F["l"] = function() {
+      var Y = S.pop();
+      var X = S.pop();
+      C.lineTo(X, Y);
+   };
+   F["c"] = function() {
+      var Y3 = S.pop();
+      var X3 = S.pop();
+      var Y2 = S.pop();
+      var X2 = S.pop();
+      var Y1 = S.pop();
+      var X1 = S.pop();
+      C.bezierCurveTo(X1, Y1, X2, Y2, X3, Y3); // TODO the right c method
+   };
+//   F["c2"] = function() { // not in pdf
+//      var Y3 = S.pop();
+//      var X3 = S.pop();
+//      var Y2 = S.pop();
+//      var X2 = S.pop();
+//      var Y1 = S.pop();
+//      var X1 = S.pop();
+//      C.bezierCurveTo(X1, Y1, X2, Y2, X3, Y3); // TODO the right c method
+//   };
+   F["v"] = function() {alert("TODO v");};
+   F["y"] = function() {alert("TODO y");};
+   F["h"] = function() {C.closePath();};
+   F["re"] = function() {
+      var Y2 = S.pop();
+      var X2 = S.pop();
+      var Y1 = S.pop();
+      var X1 = S.pop();
+      C.rect(X1, Y1, X2, Y2);
+   };
+
+   F["S"] = function() {C.stroke();};
+   F["s"] = function() {F["h"](); F["S"]();};
+   F["f"] = function() {C.fill();};
+   F["F"] = function() {C.fill();};
+   F["f*"] = function() {alert("TODO f*");};
+   F["B"] = function() {F["f"](); F["S"]();};
+   F["B*"] = function() {F["f*"](); F["S"]();};
+   F["b"] = function() {F["h"](); F["B"]();};
+   F["b*"] = function() {F["h"](); F["B*"]();};
+   F["n"] = function() {alert("TODO n");};
+
+   F["W"] = function() {C.clip();};
+   F["W*"] = function() {alert("TODO W*");};
+
+   F["BT"] = function() {alert("TODO BT");};
+   F["ET"] = function() {alert("TODO ET");};
+
+   F["Tc"] = function() {alert("TODO Tc");};
+   F["Tw"] = function() {alert("TODO Tw");};
+   F["Tz"] = function() {alert("TODO Tz");};
+   F["TL"] = function() {alert("TODO Tf");};
+   F["Tf"] = function() {
+      var N = S.pop();
+      var F = S.pop();
+      C.font = N + "pt " + F.V;
+   };
+   F["Tr"] = function() {alert("TODO Tr");};
+   F["Ts"] = function() {alert("TODO Ts");};
+
+   F["Td"] = function() {alert("TODO Td");};
+   F["TD"] = function() {alert("TODO TD");};
+   F["Tm"] = function() {alert("TODO Tm");};
+   F["T*"] = function() {alert("TODO T*");};
+
+   F["Tj"] = function() {
+      var T = S.pop();
+      //alert(T.V);
+      //if(C.strokeText) C.strokeText(T.V, 0, 0);
+      if(C.fillText) C.fillText(T.V, 0, 0);
+   };
+   F["TJ"] = function() {alert("TODO TJ");};
+   F["'"] = function() {alert("TODO '");};
+   F["\""] = function() {alert("TODO \"");};
+
+   F["d0"] = function() {alert("TODO d0");};
+   F["d1"] = function() {alert("TODO d1");};
+
+   F["CS"] = function() {alert("TODO CS");};
+   F["cs"] = function() {alert("TODO cs");};
+   F["SC"] = function() {alert("TODO SC");};
+   F["SCN"] = function() {alert("TODO SCN");};
+   F["sc"] = function() {alert("TODO sc");};
+   F["scn"] = function() {alert("TODO scn");};
+   F["G"] = function() {alert("TODO G");};
+   F["g"] = function() {alert("TODO g");};
+   F["RG"] = function() {alert("TODO RG");};
+   F["rg"] = function() { // TODO color spaces
+      var B = S.pop();
+      var G = S.pop();
+      var R = S.pop();
+      C.fillStyle = "rgb(" + R + "," + G + "," + B + ")";
+   };
+   F["K"] = function() {alert("TODO K");};
+   F["k"] = function() {alert("TODO k");};
+
+   F["sh"] = function() {alert("TODO sh");};
+
+   F["BI"] = function() {alert("TODO BI");};
+   F["ID"] = function() {alert("TODO ID");};
+   F["EI"] = function() {alert("TODO EI");};
+
+   F["Do"] = function() {alert("TODO Do");};
+
+   F["MP"] = function() {alert("TODO MP");};
+   F["DP"] = function() {alert("TODO DP");};
+   F["BMC"] = function() {alert("TODO BMC");};
+   F["BDC"] = function() {alert("TODO BDC");};
+   F["EMC"] = function() {alert("TODO EMC");};
+
+   F["BX"] = function() {alert("TODO BX");};
+   F["EX"] = function() {alert("TODO EX");};
+
+  if(T.length)
+    for(var I = 0; I < T.length; I++)
+      ps0(T[I], F, S);
+  else ps0(T, F, S);
+}
diff --git a/wps.wps b/wps.wps
@@ -0,0 +1,362 @@
+%%% wps.wps -- postscript and pdf operators for html 5 canvas
+%%% (c) 2009 Tomas Hlavaty
+
+%% basic operators
+
+/sub { % num num -- num
+  neg add
+} def
+
+%% html5 operators
+
+/.gget { % key --
+  gc exch
+  get
+} def
+
+/.gput { % any key --
+  gc 3 1 roll exch
+  put
+} def
+
+/.gcall { % key nargs --
+  gc 3 1 roll call
+} def
+
+/.gcanvas { % -- canvas
+  /canvas .gget
+} def
+
+/.gdim { % w h --
+  .gcanvas exch /height exch put
+  .gcanvas exch /width exch put
+} def
+
+/gbox { % x0 y0 x1 y1 --
+  % TODO compute properly
+  .gdim
+  pop
+  pop
+} def
+
+/.save    {/save 0 .gcall} def
+/.restore {/restore 0 .gcall} def
+
+/scale { % x y --
+  /scale 2 .gcall
+} def
+
+/rotate { % angle --
+  /rotate 1 .gcall
+} def
+
+/translate { % x y --
+  /translate 2 .gcall
+} def
+
+/transform { % m11 m12 m21 m22 dx dy --
+  /transform 6 .gcall
+} def
+
+/setTransform { % m11 m12 m21 m22 dx dy --
+  /setTransform 6 .gcall
+} def
+
+/createLinearGradient { % x0 y0 x1 y1 -- CanvasGradient
+  /createLinearGradient 4 .gcall
+} def
+
+/createRadialGradient { % x0 y0 r0 x1 y1 r1 -- CanvasGradient
+  /createRadialGradient 6 .gcall
+} def
+
+/createPattern { % image repetition -- CanvasPattern
+  /createPattern 2 .gcall
+} def
+
+/.clearRect { % x y w h --
+  /clearRect 4 .gcall
+} def
+
+/.fillRect { % x y w h --
+  /fillRect 4 .gcall
+} def
+
+/.strokeRect { % x y w h --
+  /strokeRect 4 .gcall
+} def
+
+/.beginPath {/.beginPath 0 .gcall} def
+/.closePath {/.closePath 0 .gcall} def
+
+/.moveTo { % x y --
+  /moveTo 2 .gcall
+} def
+
+/.lineTo { % x y --
+  /lineTo 2 .gcall
+} def
+
+/quadraticCurveTo { % cpx cpy x y --
+  /quadraticCurveTo 4 .gcall
+} def
+
+/bezierCurveTo { % cp1x cp1y cp2x cp2y x y --
+  /bezierCurveTo 6 .gcall
+} def
+
+/.arcTo { % x1 y1 x2 y2 radius --
+  /arcTo 5 .gcall
+} def
+
+/rect { % x y w h --
+  /rect 4 .gcall
+} def
+
+/arc { % x y radius startAngle endAngle anticlockwise --
+  /arc 6 .gcall
+} def
+
+/fill { % --
+  /fill 0 .gcall
+} def
+
+/stroke { % --
+  /stroke 0 .gcall
+} def
+
+/clip { % --
+  /clip 0 .gcall
+} def
+
+/isPointInPath { % x y -- boolean
+  /isPointInPath 2 .gcall
+} def
+
+/fillText { % text x y maxWidth --
+  /fillText 4 .gcall
+} def
+
+/strokeText { % text x y maxWidth --
+  /strokeText 4 .gcall
+} def
+
+/measureText { % text -- TextMetrics
+  /measureText 1 .gcall
+} def
+
+/drawImage1 { % image dx dy dw dh --
+  /drawImage1 5 .gcall
+} def
+
+/drawImage2 { % image sx sy sw sh dx dy dw dh --
+  /drawImage2 9 .gcall
+} def
+
+/createImageData1 { % imagedata -- ImageData
+  /createImageData1 1 .gcall
+} def
+
+/createImageData2 { % sw sh -- ImageData
+  /createImageData2 2 .gcall
+} def
+
+/getImageData { % sx sy sw sh --
+  /getImageData 4 .gcall
+} def
+
+/putImageData { % imagedata dx dy dirtyX dirtyY dirtyWidth dirtyHeight --
+  /putImageData 7 .gcall
+} def
+
+/getGlobalAlpha              {/globalAlpha .gget} def
+/getGlobalCompositeOperation {/globalCompositeOperation .gget} def % TODO str
+/getStrokeStyle              {/strokeStyle .gget} def % TODO str
+/getFillStyle                {/fillStyle .gget} def % TODO str
+/.getLineWidth               {/lineWidth .gget} def
+/.getLineCap                 {/lineCap .gget} def % TODO str
+/.getLineJoin                {/lineJoin .gget} def % TODO str
+/.getMiterLimit              {/miterLimit .gget} def
+/getShadowOffsetX            {/shadowOffsetX .gget} def
+/getShadowOffsetY            {/shadowOffsetY .gget} def
+/getShadowBlur               {/shadowBlur .gget} def
+/getShadowColor              {/shadowColor .gget} def % TODO str
+/.getFont                    {/font .gget} def % TODO str
+/getTextAlign                {/textAlign .gget} def % TODO str
+/getTextBaseline             {/textBaseline .gget} def % TODO str
+
+/setGlobalAlpha              {/globalAlpha .gput} def
+/setGlobalCompositeOperation {/globalCompositeOperation .gput} def % TODO str
+/setStrokeStyle              {/strokeStyle .gput} def % TODO str
+/setFillStyle                {/fillStyle .gput} def % TODO str
+/.setLineWidth               {/lineWidth .gput} def
+/.setLineCap                 {/lineCap .gput} def % TODO str
+/.setLineJoin                {/lineJoin .gput} def % TODO str
+/.setMiterLimit              {/miterLimit .gput} def
+/setShadowOffsetX            {/shadowOffsetX .gput} def
+/setShadowOffsetY            {/shadowOffsetY .gput} def
+/setShadowBlur               {/shadowBlur .gput} def
+/setShadowColor              {/shadowColor .gput} def % TODO str
+/.setFont                    {/font .gput} def % TODO str
+/setTextAlign                {/textAlign .gput} def % TODO str
+/setTextBaseline             {/textBaseline .gput} def % TODO str
+
+%% PostScript operators
+
+/gsave     {.save} def
+/grestore  {.restore} def
+
+/rectclip { % x y w h --
+  .clipRect
+} def
+
+/rectfill { % x y w h --
+  .fillRect
+} def
+
+/rectstroke { % x y w h --
+  .strokeRect
+} def
+
+/newpath   {.beginPath} def
+/closepath {.closePath} def
+
+/moveto { % x y --
+  .moveTo
+} def
+
+/lineto { % x y --
+  .lineTo
+} def
+
+/arcto { % x1 y1 x2 y2 radius --
+  .arcTo
+} def
+
+/setlinewidth { % width --
+  .setLineWidth
+} def
+
+/setlinecap { % linecap --
+  .setLineCap
+} def
+
+/setlinejoin { % linejoin --
+  .setLineJoin
+} def
+
+/setmiterlimit { % miterlimit --
+  .setMiterLimit
+} def
+
+/setgray { % gray --
+  255 mul dup dup
+  rgb setFillStyle
+} def
+
+/setrgbcolor { % r g b --
+  3 {255 * 3 1 roll} repeat
+  rgb setFillStyle
+} def
+
+/setfont { % font --
+  % TODO C.font = N + "pt " + F.V;
+} def
+
+%    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() {};
+%    F["setflat"] = function() {};
+%    F["_"] = function() {};
+%    F["clippath"] = function() {};
+
+%% PDF operators
+
+/w {
+  setlinewidth
+} def
+
+/J {
+  setlinecap
+} def
+
+/j {
+  setlinejoin
+} def
+
+/M {
+  setmitterlimit
+} def
+
+/q {gsave} def
+/Q {grestore} def
+
+/cm {
+  transform
+} def
+
+/m {
+  newpath % TODO only if not m previously
+  moveto
+} def
+
+/l {
+  lineto
+} def
+
+/c {
+  bezierCurveTo
+} def
+
+/h {closepath} def
+
+/re {
+  rect
+} def
+
+/S  {stroke} def
+/s  {h S} def
+/f  {fill} def
+/F  {fill} def
+/B  {f S} def
+/B* {f* S} def
+/b  {h B} def
+/b* {h B*} def
+/W  {clip} def
+
+/Tf {
+  setfont
+} def
+
+/Tj {
+  fillText
+} def
+
+/rg {
+  % TODO other color spaces
+  3 {255 * 3 1 roll} repeat
+  rgb setFillStyle
+} def
+
+%% other operators
+
+/pi 3.141592653589 def
+
+/inch { % num --
+  72 mul
+} def
+
+%/black   {0 0 0 setrgbcolor} def
+%/red     {1 0 0 setrgbcolor} def
+%/green   {0 1 0 setrgbcolor} def
+%/blue    {0 0 1 setrgbcolor} def
+%/yellow  {1 1 0 setrgbcolor} def
+%/cyan    {0 1 1 setrgbcolor} def
+%/magenta {1 0 1 setrgbcolor} def
+%/white   {1 1 1 setrgbcolor} def