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