commit 7d089e5f79273c3644c826f5f6b9682490a08146
parent c6922a66864e796df87518b4a186f310e84bbaa3
Author: tomas <tomas@logand.com>
Date:   Sat, 23 Jan 2010 15:09:13 +0100
index.org from 2009-07-05
Diffstat:
| M | index.org |  |  | 1213 | ++++++++++++++++++++++++++++++++++++------------------------------------------- | 
1 file changed, 546 insertions(+), 667 deletions(-)
diff --git a/index.org b/index.org
@@ -1,39 +1,28 @@
-#+options: author:nil num:nil creator:nil timestamp:nil
+#+title: WPS: PostScript for the Web
+#+description: PostScript and PDF interpreter for HTML 5 canvas
+#+keywords: PostScript, PDF, interpreter, HTML 5, canvas, JavaScript
+#+options: num:nil toc:t
+#+macro: ps [[http://www.capcode.de/help/$1][$1]]
 
-PostScript and PDF interpreter in JavaScript
+#+BEGIN_HTML:
+<p class="h0">WPS: PostScript for the Web</p>
+#+END_HTML
 
-PostWeb \\
-WebScript
+Welcome to WPS, a PostScript and PDF interpreter for HTML 5 canvas.
 
-TODO reuse parsed wps.wps
+Note that to see and run the examples, JavaScript must be enabled and
+your browser must support HTML 5 canvas (latest Firefox, Opera and
+Chrome should work).
 
-QQQ
-
-Everything is interpreted.  Compilation is translation to lower
-abstraction level or precomputing.
-
-psi/z-matrix.c
-Resource/Init/opdfread.ps
-
-* PostScript interpreter
-
-- stack
-- function dictionary
-- data types
-- reader
-- interpreter/evaluator
-- native bindings (JavaScript FFI)
-- PostScript and PDF API
-
-PDF is stateless?
-
-PostScript is stateful?
+This document allows you to try simple PostScript programs in the WPS
+sandbox.  A few examples are presented here accompanied by a brief
+description of the interpreter and listing some implementation notes
+for my future reference.
 
 #+begin_html
 <style>
 tt {background-color:#fdf}
 canvas {width:12em;height:12em;border:1px dashed black}
-/*canvas {width:10em;height:10em;border:1px dashed black;position:relative;top:0;left:0}*/
 </style>
 #+end_html
 
@@ -48,15 +37,98 @@ function $$(Id) {return $(Id).textContent;}
 </script>
 #+end_html
 
+* WPS sandbox
+
+#+html: <canvas id="xsandbox"></canvas>
+#+begin_html
+<p>Sandbox:</p>
+<p>
+<textarea id="sandbox" style="width:100%" rows="18">
+/n 10 def
+/w 25 def
+
+0 0 n w mul dup .gbox
+
+4 dict begin
+  0 1 n 1 sub {
+    /i exch def
+    /ii 1 1 n div i mul sub def
+    0 1 n 1 sub {
+      /j exch def
+      /jj 1 1 n div j mul sub def
+      ii jj 0 setrgbcolor
+      w j mul w i mul w w rectfill
+    } for
+  } for
+end
+</textarea>
+</p>
+<script>
+function sandbox() {wps($("xsandbox"), [$$("wps"), $("sandbox").value]);}
+</script>
+<button onclick="javascript:sandbox();">Run</button> code from sandbox.
+#+end_html
+
+* PostScript interpreter
+
+A few initial ideas and questions:
+
+- Learn and implement a Forth like language.  PostScript seems like a
+  good choice:
+  - It has the right syntax and stack based evaluation.
+  - It is practical and widely used.
+  - It has long sucessful history in print and publishing (and more).
+  - It is a predecessor of PDF.
+  - Almost everything (e.g. editors, pictures, documentation) can be
+    reused to a great extent.
+  - It is ideal for HTML 5 canvas experiments because from the
+    PostScript point of view, canvas is just another low level device.
+- Flexibility and simplicity first.
+  - Optimize for fast code change, not for raw running speed.  Keep
+    the code small and regular if possible.
+  - Can JavaScript be used as a portable assembler for the Web?  Is
+    building scripting languages on top of JavaScript feasible and
+    efficient enough for real-world use?  If not, why?  Find the
+    limits.
+- Keep the language/environment specific core as small as possible.
+  - Allow to port the interpreter to other languages on both
+    client and server side.
+  - Be open for the possibility of running "the same code" on both the
+    client and server side.
+- Can PDF documents be displayed in web browsers without server-side
+  image rendering?
+  - Implement a canvas based version of PDF page contents in [[http://ondoc.logand.com][OnDoc]].
+- It might be possible to implement different backend devices to be
+  used instead of HTML 5 canvas, for example a SVG device.
+- Investigate the possibility of implementing a Lisp interpreter
+  suitable for production use in web applications.
+
+There are several things WPS is about:
+
+- stack(s)
+- function (operator) dictionary
+- reader
+- interpreter/evaluator
+- data types
+- native bindings (JavaScript FFI)
+- PostScript and PDF API
+
+[[http://en.wikipedia.org/wiki/PostScript][PostScript]] can be seen as a crossover between [[http://en.wikipedia.org/wiki/Forth_(programming_language)][Forth]] and [[http://en.wikipedia.org/wiki/LISP][Lisp]]
+programming languages.  It is (roughly) a programming language with
+[[http://en.wikipedia.org/wiki/Reverse_Polish_notation][RPN]], complex data types, garbage collection and specialized
+drawing operators.
+
 ** Trivial example
 
+The core essence of a RPN calculator is captured in the JavaScript
+code bellow.
+
 #+html: <div id="example1">
 #+begin_src js2
 function example1() {
    // define stack and operators
    var Os = [];
    var Sd = {};
-   var Ds = [Sd];
    Sd["+"] = function() {Os.push(Os.pop() + Os.pop());};
    Sd["="] = function() {alert(Os.pop());};
    // compute 1 2 = 3 + =
@@ -77,11 +149,20 @@ function ex1() {
 }
 </script>
 <button onclick="javascript:ex1()">Eval</button>
-"<tt>1 2 = 3 + =</tt>" from harcoded stack
+"<tt>1 2 = 3 + =</tt>"
 #+end_html
 
+=Os= stands for Operand Stack, which holds arguments for operators.
+=Sd= is a System Dictionary which contains definitions of operators
+(JavaScript functions in this case).
+
 ** Example with PostScript reader
 
+PostScript has simple but non-trivial syntax so a reader which reads
+text and creates internal PostScript objects is necessary.  The reader
+and evaluator is called =ps0= (an empty PostScript interpreter) in the
+JavaScript code bellow.
+
 #+html: <div id="example2">
 #+begin_src js2
 function example2(T) {
@@ -104,177 +185,83 @@ function ex2() {
 }
 </script>
 <button onclick="javascript:ex2()">Eval</button>
-"<tt id="ex2">12 34 + dup = 56 + =</tt>" from string
+"<tt id="ex2">12 34 + dup = 56 + =</tt>"
 #+end_html
 
+=Ds= is a Dictionary Stack allowing users to redefine existing
+operators and revert back to the original ones.  =Es= is an Execution
+Stack which is used to implement a tail recursive evaluator.
+
 ** Example with recursion
 
+It is possible to write recursive code in PostScript.  The following
+PostScript code is from the [[http://www.math.ubc.ca/~cass/graphics/manual/pdf/ch9.pdf][Recursion in PostScript PDF document]].
+
 #+html: <div id="example3">
 #+begin_src ps
-% based on the example from
-% http://en.literateprograms.org/Fibonacci_numbers_(PostScript)
-% 0, 1, 1, 2, 3, 5, 8, 13, 21
-
-%0.1 1.2 2.3 4.5 3 {pstack} repeat
-%0.1 1.2 2.3 4.5 3 {pstack} .repeat
-
-%/fibonacci{0.1 1.2 2.3 4.5 pstack 3{pstack}repeat pstack}def
-%fibonacci
-
-%/fibonacci{0.1 1.2 2.3 4.5 3{1 add pstack}repeat}def
-%fibonacci
-
-%0 1 1 4 {add} for pstack clear % 10
-%1 2 6 { } for pstack clear % 1 3 5
-%3 -.5 1 { } for pstack clear % 3.0 2.5 2.0 1.5 1.0
-
-4 {(abc)} repeat pstack clear % (abc) (abc) (abc) (abc)
-1 2 3 4 3 {pop} repeat pstack clear % 1 % Pops 3 values (down to the 1)
-4 { } repeat pstack clear % % Does nothing four times
-mark 0 {(will not happen)} repeat pstack clear % mark
-
-%false false or =
-%true false or =
-%false true or =
-%true true or =
-
-% from http://www.math.ubc.ca/~cass/graphics/manual/pdf/ch9.pdf
-
-/factorial { 1 dict begin
-  /n exch def
-  n 0 eq {
-    1
-  }{
-    n n 1 sub factorial mul
-  } ifelse
-end } def
-
-%5 factorial pstack clear
-
-% args: array a of numbers
-% effect: sorts the array in order
-/bubblesort { 4 dict begin
-  /a exch def
-  /n a length 1 sub def
-n 0 gt {
-  % at this point only the n+1 items in the bottom of a remain to be sorted
-  % the largest item in that block is to be moved up into position n
-  n {
-    0 1 n 1 sub {
-      /i exch def
-      a i get a i 1 add get gt {
-         % if a[i] > a[i+1] swap a[i] and a[i+1]
-        a i 1 add
-        a i get
-        a i a i 1 add get
-         % set new a[i] = old a[i+1]
-        put
-         % set new a[i+1] = old a[i]
-        put
-      } if
-    } for
-    /n n 1 sub def
-  } repeat
-} if
-end } def
-
-%[5 4 3 2 1 0] bubblesort pstack clear
-
-%{1 2 add {2 mul}} pstack exec pstack exec pstack clear
-
-%1 2 1 {add {(hi)} dup} repeat pstack clear
-%1 2 {} pstack clear
-%1 2 {} exec pstack clear
-
-%(de repeat (N . B)
-%   (when (gt0 N)
-%      (run B 1) )
-%   (when (< 1 N)
-%      (repeat (- N 1) B) ) )
-
-%/fibonacci { % n --
-%  1 sub
-%  1 1 3 2 roll
-%  { 2 copy add 3 2 roll pop pstack} repeat
-%} bind def
-
-%0 fibonacci = % 0
-%1 fibonacci = % 1
-%2 fibonacci = % 1
-%6 fibonacci = % 8
-
-
-% http://en.wikibooks.org/wiki/Transwiki:Fibonacci_number_program#PostScript
-
-%/fib1 {
-%  1 dup
-%  3 -1 roll {
-%    dup
-%    3 -1 roll
-%    dup
-%    4 1 roll
-%    add
-%    3 -1 roll
-%  } repeat
-%} def
-
-%5 fib1 pstack clear
-
-/fib2 {
-  dup dup 1 eq exch 0 eq or not {
-    dup 1 sub fib2
-    exch 2 sub fib2
-    add
-  } if
+/factorial1 {
+  1 dict begin
+    /n exch def
+    n 0 eq {1}{n n 1 sub factorial1 mul} ifelse
+  end
 } def
 
-%5 fib2 pstack clear
+5 factorial1 =
+
+/factorial2 {
+  dup 0 eq {pop 1}{dup 1 sub factorial2 mul} ifelse
+} def
 
+5 factorial2 =
 #+end_src
 #+html: </div>
 #+begin_html
 <script>
 function ex3() {wps(null, [$$("wps"), $$("example3")]);}
 </script>
-<button onclick="javascript:ex3();">Eval</button>
+<button onclick="javascript:ex3();">Run</button> the example.
 #+end_html
 
-* The interpreter
+** Execution stack
 
-A few initial ideas:
+The interpreter manages its Execution Stack explicitly.
 
-- Learn and implement a Forth like language.  PostScript seems a great choice:
-  - It has the right syntax and stack based evaluation.
-  - It is practical and widely used.
-  - It has long sucessful history in print and publishing.
-  - It is a predecessor of PDF.
-  - Almost everything (e.g. editors, pictures, documentation) can be
-    reused to a great extent.
-  - It is ideal for HTML 5 canvas experiments because from the
-    PostScript point of view, canvas is just another low level device.
-- Flexibility and simplicity first.
-  - Optimize for fast change, not for raw running speed.  Keep the
-    code small and regular if possible.
-  - Can JavaScript be used as a portable assembler for web?  Is
-    building scripting languages on top of JavaScript feasible and
-    efficient enough for real-world use?  If not, why?  Find the
-    limits.
-- Keep the language/environment specific core as small as possible.
-  - Allow to port the interpreter to other languages on both
-    client and server side.
-  - Be open for the possibility of running "the same code" on both the
-    client and server side.
-- Can PDF documents be displayed in web browsers without server-side
-  image rendering?
-  - Implement a canvas based version of PDF page contents in [[http://ondoc.logand.com][OnDoc]].
-- Investigate the possibility of implementing a Lisp interpreter
-  suitable for production use in web applications.
+Most operators simply:
+
+1. get their arguments from the Operand Stack
+2. perform some computation and/or side effects
+3. push results to the Operand Stack
 
-Operators beginning with dot are non-standard low level operators
-which are subject to change.
+Some operators are more complex and involve some kind of control flow,
+e.g. {{{ps(if)}}}, {{{ps(repeat)}}}, {{{ps(for)}}}, {{{ps(loop)}}}
+operators.  Such operators:
+
+1. get their arguments from the Operand Stack
+2. perform single step of some computation and/or side effects
+3. push the continuation (code and arguments to be executed next) to
+   the Execution Stack
+
+[[http://en.wikipedia.org/wiki/Tail_call][Tail Call Optimisation]] is implemented using [[http://logand.com/picoWiki/trampoline][trampoline]].  The evaluator
+runs in a loop getting the next [[http://en.wikipedia.org/wiki/Continuation][continuation]] from the Execution Stack.
+Operators that want to "continue" their execution (i.e. use the
+interpreter to run other operators, including themselves) must perform
+only one step at a time and save the remaining steps (continuation) on
+the Execution Stack.
+
+For example, the {{{ps(if)}}} operator saves the "then" or "else" code
+branch to the Execution Stack depending on the value of the "test"
+argument.  It does not "evaluate" the chosen branch directly
+(recursively) but leaves the control to the evaluator loop.
+
+The whole process of interpreting is fed from JavaScript strings which
+are obtained from the content of HTML elements (sometimes hidden from
+this document).
 
 ** PostScript data types
 
+PostScript has quite rich set of data types.
+See [[http://www.adobe.com/devnet/postscript/pdfs/PLRM.pdf][PostScript Language Reference PDF document]] for more details.
+
 | category  | type        | executable | example                | spec               |
 |-----------+-------------+------------+------------------------+--------------------|
 | simple    | boolean     |            | true false             |                    |
@@ -295,7 +282,7 @@ which are subject to change.
 |           | packedarray |            |                        | Level 2            |
 |           | string      | Y          | (hi) <a33f>            |                    |
 
-** Low level data types
+The following data types are implemented in WPS:
 
 | category  | type       | direct | literal | executable |
 |-----------+------------+--------+---------+------------|
@@ -310,7 +297,7 @@ which are subject to change.
 |           | dictionary | Y      | Y       | -          |
 |           | string     | Y      | Y       | -          |
 
-All types are represented directly in JavaScript except:
+All the above types are represented directly in JavaScript except:
 
 | type            | representation  |
 |-----------------+-----------------|
@@ -320,9 +307,15 @@ All types are represented directly in JavaScript except:
 | operator        | function        |
 | proc            | quoted array    |
 
-*** exec
+The interpreter needs to understand when to evaluate an argument.  The
+distinction between a "literal" and "executable" is the key.
+
+** Quoting and execution
 
-pop any, case:
+There are two important operators to control evaluation at the
+PostScript language level.
+
+The {{{ps(exec)}}} operator usually leaves the argument as is except:
 
 | type            | result            |
 |-----------------+-------------------|
@@ -330,9 +323,8 @@ pop any, case:
 | operator        | apply operator    |
 | proc            | exec each element |
 
-otherwise push the original value
-
-*** cvx
+The {{{ps(cvx)}}} operator makes the argument "executable".  Usually
+leaves the argument as is except:
 
 | from         | to              | how     |
 |--------------+-----------------+---------|
@@ -340,13 +332,16 @@ otherwise push the original value
 | array        | proc            | quote   |
 | string       | proc            | ~ parse |
 
-** Stack
-
-
+The ~ (tilde) character in the above table means that the
+functionality has not been implemented yet.
 
 * Drawing with PostScript
 
-Measuring angles:
+As a convention, operators beginning with dot are non-standard, low
+level operators which are subject to change.
+
+There is a difference in how HTML 5 canvas, PostScript and PDF measure
+angles:
 
 | language/device | unit |
 |-----------------+------|
@@ -354,8 +349,29 @@ Measuring angles:
 | PostScript      | deg  |
 | PDF             | rad  |
 
+Many of the examples below set up their bounding box using the
+=.gbox= operator, e.g.
+
+#+begin_src ps
+0 0 180 180 .gbox
+#+end_src
+
+Only the width and height of the canvas clipping rectangle are taken
+into account so far.  The width and height is related to the drawing
+units rather than to the size of the canvas element.
+
+Both PostScript and PDF documents have the origin of the coordinate
+system in the bottom left corner while HTML 5 canvas in the top left
+corner.  Thus, some of the following pictures are displayed upside
+down unless an explicit coordinate transformation was added.  This
+discrepancy between the origin of the coordinate system is a problem
+when drawing text because a simple coordinate transformation on its
+own would draw the text upside-down.
+
 ** Bowtie example
 
+See the [[https://developer.mozilla.org/en/drawing_graphics_with_canvas#section_6][original example]] in JavaScript.
+
 #+html: <canvas id="xbowtie"></canvas>
 #+html: <div id="bowtie">
 #+include "bowtie.wps" src ps
@@ -366,24 +382,14 @@ wps($("xbowtie"), [$$("wps"), $$("bowtie")]);
 </script>
 #+end_html
 
-** Digital clock example
-
-#+html: <canvas id="xclock"></canvas>
-#+html: <div id="clock">
-#+include "clock.wps" src ps
-#+html: </div>
-#+begin_html
-<script>
-wps($("xclock"), [$$("wps"), $$("clock")]);
-</script>
-#+end_html
-
 ** Analog clock example
 
-#+html: <canvas id="xclock2"></canvas>
+See the [[http://oreilly.com/openbook/cgi/ch06_02.html][original example]].
 
 Click on the clock to start/stop it.
 
+#+html: <canvas id="xclock2"></canvas>
+
 #+html: <div id="clock2">
 #+include "clock2.wps" src ps
 #+html: </div>
@@ -393,100 +399,128 @@ wps($("xclock2"), [$$("wps"), $$("clock2")]);
 </script>
 #+end_html
 
-** Tiger example
-
-Text drawing in canvas is not working in Firefox and Opera, works in
-Chrome.
+Running the clock keeps the CPU noticeably busy.  Chrome is best with
+very little overhead, followed by Opera, and Firefox significantly
+worse than the previous two browsers.  WPS seems to be fast enough for
+one-off drawings, but its usability when running the interpreter in a
+tight loop, depends on the efficiency of the host JavaScript
+interpreter.
 
-Firefox throws error about linecap and linejoin not being supported.
-However, it does not throw error about missing support for text
-drawing.
-
-#+plot: title:"tiger.eps drawing times" ind:1 deps:(2) type:2d with:histograms set:"yrange [0:]" set:"xlabel 'browser'" set:"ylabel 'time [s]'" set:"style histogram gap 1" file:"tiger.png" set:"term png size 400, 300"
-| browser | time [s] |
-|---------+----------|
-| Chrome  |      3.8 |
-| Opera   |     13.4 |
-| Firefox |     19.5 |
-
-file:tiger.png
-
-** Fern example
-
-from http://www.pvv.ntnu.no/~andersr/fractal/PostScript.html
+** Fill example
 
-#+html: <canvas id="xfern"></canvas>
+See the [[https://developer.mozilla.org/samples/canvas-tutorial/4_1_canvas_fillstyle.html][original example]] in JavaScript.
 
-#+html: <div id="fern">
-#+include "fern.wps" src ps
+#+html: <canvas id="xfill"></canvas>
+#+html: <div id="fill">
+#+include "fill.wps" src ps
 #+html: </div>
 #+begin_html
 <script>
-//wps($("xfern"), [$$("wps"), $$("fern")]);
+wps($("xfill"), [$$("wps"), $$("fill")]);
 </script>
 #+end_html
 
-** Tree example
+** Tiger example
 
-from http://www.pvv.ntnu.no/~andersr/fractal/PostScript.html
+The [[http://svn.ghostscript.com/viewvc/trunk/gs/examples/tiger.eps?view=co][original example]] is included with [[http://ghostscript.com/][Ghostscript]].
 
-#+html: <canvas id="xtree2"></canvas>
+#+begin_html
+<canvas id="xtiger" style="width:283pt;height:369pt">
+</canvas>
+<p>Drawing took <span id="msg">--</span> seconds.</p>
+#+end_html
 
-#+html: <div id="tree2">
-#+include "tree2.ps" src ps
+#+html: <div id="tiger" style="display:none">
+#+html: <!-- TODO insert tiger.eps automatically -->
 #+html: </div>
+
 #+begin_html
+<div id="tiger1" style="display:none">
+0 0 567 739 .gbox
+1 0 0 -1 0 739 .transform
+/time1 .date (getTime) 0 .call def
+</div>
+
+<div id="tiger2" style="display:none">
+/time2 .date (getTime) 0 .call def
+(msg) .getElementById (textContent) time2 time1 sub 1000 div put
+</div>
+
 <script>
-//wps($("xtree2"), [$$("wps"), $$("tree2")]);
+function tiger() {wps($("xtiger"), [$$("wps"), $$("tiger1"), $$("tiger"), $$("tiger2")]);}
 </script>
+<button onclick="javascript:tiger();">Draw</button> the tiger (be patient).
 #+end_html
 
-** Other
+Is this an interesting JavaScript and canvas benchmark?
 
-1 0 0 0 setcmykcolor    % 100% cyan \\
-0 0 1 0 setcmykcolor    % 100% yellow
+#+plot: title:"tiger.eps drawing times" ind:1 deps:(2 3 4) type:2d with:histograms set:"yrange [0:]" set:"xlabel 'browser'" set:"ylabel 'time [s]'" set:"style histogram gap 3" file:"tiger.png" set:"term png size 600, 300"
+| browser          | WPS time [s] | WPS time (no bind) [s] | PostCanvas time [s] |
+|------------------+--------------+------------------------+---------------------|
+| Chrome           |          2.5 |                    3.8 |                 1.6 |
+| Opera            |         15.9 |                   13.4 |                     |
+| Firefox 3.0      |         15.4 |                   19.5 |                 7.4 |
+| Firefox 3.5      |         11.6 |                        |                     |
 
-** Fill example
+[[http://www.feiri.de/pcan/][PostCanvas]] runs this [[http://www.feiri.de/pcan/example1.html][example]] about 1.5 times (Chrome) to 2 times
+(Firefox) faster.  I am actually surprised that WPS runs only up to 2
+times slower even though it interprets almost everything with minimal
+number of operators coded directly in JavaScript (compared to
+PostCanvas which implements all operators directly in JavaScript).
 
-#+html: <canvas id="xfill"></canvas>
-#+html: <div id="fill">
-#+include "fill.wps" src ps
-#+html: </div>
-#+begin_html
-<script>
-wps($("xfill"), [$$("wps"), $$("fill")]);
-</script>
-#+end_html
+Another surprise to me is that I expected more significant speed up
+after implementing the {{{ps(bind)}}} operator.  Why does Opera get
+slower in this case?
 
-** Chessboard example
+It should be fairly easy to speed up WPS by adding more operators
+implemented directly in JavaScript.  This could be done dynamically by
+redefining/rebinding existing operators to their optimized JavaScript
+version.  The speed of PostCanvas could probably be taken as the best
+case that could be achieved by optimizing WPS though.
 
-#+html: <canvas id="xchess"></canvas>
-#+html: <div id="chess">
-#+include "chess.eps" src ps
-#+html: </div>
-#+begin_html
-<div id="ychess" style="display:none">
-0 0 180 180 .gbox
-%1 0 0 -1 0 180 cm
-</div>
-<script>
-wps($("xchess"), [$$("wps"), $$("ychess"), $$("chess")]);
-</script>
-#+end_html
+file:tiger.png
+
+Firefox throws error about linecap and linejoin not being supported so
+these were not used here.  Opera throws an error when running the
+PostCanvas example.  The tiger does not look the same as rendered by
+[[http://projects.gnome.org/evince/][Evince]] ([[http://poppler.freedesktop.org/][poppler]]/[[http://cairographics.org/][cairo]]) so maybe the linecap and linejoin are
+really needed to get proper image as intended.
+
+It is also interesting to observe that PDF operators and their names
+probably came up from shortening/compressing "user-space" PostScript
+operators in final PostScript files.  The tiger.eps file was created
+in 1990 and contains some "shortcuts" that match PDF operators
+standardised later.
 
 * Drawing with PDF
 
-PDF documents have the origin of the coordinate system in the bottom
-left corner while HTML 5 canvas int the top left corner.  Thus, some
-of the following pictures are displayed upside down (unless an
-explicit coordinate transformation was added which is not part of the
-visible example code).
+PDF is rather complex format.  WPS implements only drawing operators
+that can be present in PDF content streams.  The number of these
+operators is fixed and limited.  Even though the full PostScript
+language is not required, it can be convenient to implement them in
+PostScript.
+
+However, some aspects (e.g. colors) are handled differently in PDF
+compared to PostScript and these differences are not addressed by WPS.
+I imagine that a supporting server-side solution like [[http://logand.com/sw/ondoc/index.html][OnDoc]] would
+provide necessary data (e.g. decomposing PDF into pages and objects,
+providing HTML 5 web fonts and font metrics) and WPS would only draw
+preprocessed page content.
+
+Quoting from [[http://www.adobe.com/print/features/psvspdf/index.html][Adobe]]:
+
+#+begin_quote
+A PDF file is actually a PostScript file which has already been
+interpreted by a RIP and made into clearly defined objects.
+#+end_quote
 
 ** Heart example
 
+See also the [[https://developer.mozilla.org/samples/canvas-tutorial/2_6_canvas_beziercurveto.html][original example]] in JavaScript.
+
 #+html: <canvas id="xheart"></canvas>
 #+html: <div id="heart">
-#+include "heart.wpdf" src ps
+#+include "heart.wps" src ps
 #+html: </div>
 #+begin_html
 <script>
@@ -498,7 +532,7 @@ wps($("xheart"), [$$("wps"), $$("heart")]);
 
 #+html: <canvas id="xrect"></canvas>
 #+html: <div id="rect">
-#+include "rect.wpdf" src ps
+#+include "rect.wps" src ps
 #+html: </div>
 #+begin_html
 <script>
@@ -508,9 +542,11 @@ wps($("xrect"), [$$("wps"), $$("rect")]);
 
 ** Triangles example
 
+See also the [[https://developer.mozilla.org/samples/canvas-tutorial/2_3_canvas_lineto.html][original example]] in JavaScript.
+
 #+html: <canvas id="xtriangles"></canvas>
 #+html: <div id="triangles">
-#+include "triangles.wpdf" src ps
+#+include "triangles.wps" src ps
 #+html: </div>
 #+begin_html
 <script>
@@ -520,9 +556,11 @@ wps($("xtriangles"), [$$("wps"), $$("triangles")]);
 
 ** Smile example
 
+See also the [[http://developer.mozilla.org/samples/canvas-tutorial/2_2_canvas_moveto.html][original example]] in JavaScript.
+
 #+html: <canvas id="xsmile"></canvas>
 #+html: <div id="smile">
-#+include "smile.wpdf" src ps
+#+include "smile.wps" src ps
 #+html: </div>
 #+begin_html
 <script>
@@ -532,9 +570,11 @@ wps($("xsmile"), [$$("wps"), $$("smile")]);
 
 ** Star example
 
+See also the [[http://www.adobe.com/technology/pdfs/presentations/KingPDFTutorial.pdf][original PDF document]] where this example is presented.
+
 #+html: <canvas id="xstar"></canvas>
 #+html: <div id="star">
-#+include "star.wpdf" src ps
+#+include "star.wps" src ps
 #+html: </div>
 #+begin_html
 <script>
@@ -542,325 +582,213 @@ wps($("xstar"), [$$("wps"), $$("star")]);
 </script>
 #+end_html
 
-** Squares 2 example
+** Squares example
 
-#+html: <canvas id="xsquares2"></canvas>
-#+html: <div id="squares2">
-#+include "squares2.wpdf" src ps
+See also the [[https://developer.mozilla.org/samples/canvas-tutorial/5_1_canvas_savestate.html][original example]] in JavaScript.
+
+#+html: <canvas id="xsquares"></canvas>
+#+html: <div id="squares">
+#+include "squares.wps" src ps
 #+html: </div>
 #+begin_html
 <script>
-wps($("xsquares2"), [$$("wps"), $$("squares2")]);
+wps($("xsquares"), [$$("wps"), $$("squares")]);
 </script>
 #+end_html
 
-** Junk
-
-#+begin_html
-<pre>
-
-1 0 0 -1 0 446 cm % 0,0 in bottom left corner
-
-% Ex4
-
-q
-%BT
-%/F1 24 Tf
-%1 0 0 1 260 600 Tm
-%/CS1 cs 
-%63 127 127 sc
-%(Hello World)Tj
-%ET
-%100 0 127 sc
-%/CS2 CS
-%0 0 1 SC
-%315 572 m
-%299 528 l
-%339 554 l
-%291 554 l
-%331 528 l
-%b
-q
-0 255 255 rg
-4 0 0 4 315 550 cm
-0 5.5 m
--4 -5.5 l
- 6  1   l
--6  1   l
- 4 -5.5 l
-f
-Q
-%/CS1 cs
-%63 127 127 sc
-1 0 0 1 315 490 cm
-0 0 m
-  -7  23         -40   19     -15 -17   c
-  -7.5 -27.8   -11 -22       0 -35   c
-  11 -22        7.5 -27.8    15 -17    c
-  40  19          7    23        0   0 c
-b
-Q
-
-% http://developer.mozilla.org/samples/canvas-tutorial/2_5_canvas_quadraticcurveto.html
-
-%75 25 m
-%25 25 25 62 5 c2
-%25 100 50 100 c2
-%50 120 30 125 c2
-%60 120 65 100 c2
-%125 100 125 62.5 c2
-%125 25 75 25 c2
-%S
-
-%3 8 div 6 add .
-%6 3 8 div add .
-
-%8 7 3 mul sub .
-%7 3 mul 8 exch sub .
-
-/box {
-  0 0 m
-  72 0 l
-  0 72 l
-  -72 0 l
-  h f
-} def
-
-1 0 0 1 -452 124 cm
-box
-0 setgray fill
-1 0 0 1 27.0 36.0 cm
-box
-.4 setgray fill
-1 0 0 1 28.8 39.6 cm
-box
-.8 setgray fill
-
-% TODO not, and, or, xor
-% TODO for loop exit
-</pre>
-#+end_html
+** Two squares example
 
-/MediaBox [0 0 612 446]
+See also the [[https://developer.mozilla.org/en/drawing_graphics_with_canvas][original example]] in JavaScript.
 
+#+html: <canvas id="xsquares2"></canvas>
+#+html: <div id="squares2">
+#+include "squares2.wps" src ps
+#+html: </div>
 #+begin_html
 <script>
-function xreq(Url, Cb, Er) {
-//   var req = new XMLHttpRequest();
-//   alert(Url);
-//   req.open('GET', Url, true);
-//   req.onreadystatechange = function (e) {
-//      if(req.readyState == 4) {
-//         if(req.status == 200) Cb(req.responseText);
-//         else if(Er) Er();
-//      }
-//   };
-//   req.send(null);
-
-   try {netscape.security.PrivilegeManager.enablePriviledge(priviledge);}
-   catch(e) {}
-   var req = new XMLHttpRequest();
-   req.open('GET', Url, false); 
-   req.send(null);
-   if(req.status == 200) Cb(req.responseText);
-   else alert("uff");
-}
+wps($("xsquares2"), [$$("wps"), $$("squares2")]);
 </script>
 #+end_html
 
-#+html: <canvas id="c"></canvas>
-
-[[http://svn.ghostscript.com/viewvc/trunk/gs/examples/tiger.eps?view=co][tiger.eps]]:
-#+html: <button onclick="javascript:xreq('http://svn.ghostscript.com/viewvc/trunk/gs/examples/tiger.eps?view=co', wps)">Draw</button>
-#+html: <button onclick="javascript:xreq('file:///home/tomas/src/pdf4web/tiger.eps', alert)">Draw</button>
-
-* JavaScript bindings
-
-http://www.whatwg.org/specs/web-apps/current-work/#the-canvas-element
-
-??? rlineto rmoveto findfont scalefont setfont show rightshow
-stringwidth arcn loop/exit forall cvs array put get length aload
-astore mark ashow widthshow awidthshow kshow currentpoint makefont
-charpath setdash image putinterval dict begin settransfer
-readhexstring flattenpath curveto pathbbox pathforall search transform
-itransform definefont setrgbcolor setcharwidth
-
- setmatrix
-
-octal chars in string \234
+* Operators and JavaScript bindings
 
+WPS implements a minimum core in JavaScript and the rest is
+implemented in PostScript itself.
 
-/Times-Roman findfont typography 15 scalefont setfont
+Many JavaScript data types map quite easily to PostScript data types
+so native bindings can be implemented mostly in PostScript via
+PostScript dictionaries.  [[http://www.whatwg.org/specs/web-apps/current-work/#the-canvas-element][HTML 5 canvas API]] bindings are quite
+straightforward.  Probably the trickiest bit is implementing callbacks
+to handle [[http://en.wikipedia.org/wiki/Document_Object_Model][DOM]] events using PostScript code.
 
 ** Built-in operators
 
-| category       | in                      | operator | out                                                         |
-|----------------+-------------------------+----------+-------------------------------------------------------------|
-| Trivial        |                         | true     | true                                                        |
-|                |                         | false    | false                                                       |
-|                |                         | null     | null                                                        |
-| Math           | x                       | neg      | -x                                                          |
-|                | x y                     | add      | x+y                                                         |
-|                | x y                     | mul      | x*y                                                         |
-|                | x y                     | div      | x/y                                                         |
-|                | x y                     | mod      | x%y                                                         |
-| Stack          |                         | mark     | mark                                                        |
-|                | x y                     | exch     | y x                                                         |
-|                |                         | clear    |                                                             |
-|                | x                       | pop      |                                                             |
-|                | any_n ...any_0 n        | index    | any_n ... any_0 any_n                                       |
-|                | any_(n-1) ... any_0 n j | roll     | any_((j-1) mod n) ... any_0 ... any_(n-1) ... any_(j mod n) |
-|                | any_1 ... any_n n       | copy     | any_1 ... any_n any_1 ... any_n                             |
-| Array          | array                   | length   | n                                                           |
-| Conditionals   | x y                     | eq       | bool                                                        |
-|                | x y                     | lt       | bool                                                        |
-|                | y                       | not      | bool                                                        |
-|                | x y                     | and      | z                                                           |
-|                | x y                     | or       | z                                                           |
-|                | bool then else          | ifelse   |                                                             |
-|                | n proc                  | repeat   |                                                             |
-|                | i j k proc              | for      |                                                             |
-| Debugging      | x                       | .        |                                                             |
-|                |                         | pstack   |                                                             |
-| Dictionaries   | n                       | dict     | dict                                                        |
-|                | dict key                | get      | any                                                         |
-|                | dict key any            | put      |                                                             |
-|                | sym proc                | def      |                                                             |
-| Arrays         | n                       | array    | array                                                       |
-| JavaScript FFI | dict key nargs          | .call    | any                                                         |
-|                |                         | .gc      | gc                                                          |
-|                |                         | .math    | Math                                                        |
-| HTML 5         | r g b                   | .rgb     | text                                                        |
-|                | r g b a                 | .rgba    | text                                                        |
+| category       | in                      | operator         | out                                                         |
+|----------------+-------------------------+------------------+-------------------------------------------------------------|
+| Trivial        |                         | {{{ps(true)}}}   | true                                                        |
+|                |                         | {{{ps(false)}}}  | false                                                       |
+|                |                         | {{{ps(null)}}}   | null                                                        |
+| Math           | x                       | {{{ps(neg)}}}    | -x                                                          |
+|                | x y                     | {{{ps(add)}}}    | x+y                                                         |
+|                | x y                     | {{{ps(mul)}}}    | x*y                                                         |
+|                | x y                     | {{{ps(div)}}}    | x/y                                                         |
+|                | x y                     | {{{ps(mod)}}}    | x%y                                                         |
+| Stack          |                         | {{{ps(mark)}}}   | mark                                                        |
+|                | x y                     | {{{ps(exch)}}}   | y x                                                         |
+|                |                         | {{{ps(clear)}}}  |                                                             |
+|                | x                       | {{{ps(pop)}}}    |                                                             |
+|                | any_n ...any_0 n        | {{{ps(index)}}}  | any_n ... any_0 any_n                                       |
+|                | any_(n-1) ... any_0 n j | {{{ps(roll)}}}   | any_((j-1) mod n) ... any_0 ... any_(n-1) ... any_(j mod n) |
+|                | any_1 ... any_n n       | {{{ps(copy)}}}   | any_1 ... any_n any_1 ... any_n                             |
+| Array          | array                   | {{{ps(length)}}} | n                                                           |
+| Conditionals   | x y                     | {{{ps(eq)}}}     | bool                                                        |
+|                | x y                     | {{{ps(lt)}}}     | bool                                                        |
+|                | y                       | {{{ps(not)}}}    | bool                                                        |
+|                | x y                     | {{{ps(and)}}}    | z                                                           |
+|                | x y                     | {{{ps(or)}}}     | z                                                           |
+|                | bool then else          | {{{ps(ifelse)}}} |                                                             |
+|                | n proc                  | {{{ps(repeat)}}} |                                                             |
+|                | i j k proc              | {{{ps(for)}}}    |                                                             |
+| Debugging      | x                       | {{{ps(=)}}}      |                                                             |
+|                |                         | {{{ps(pstack)}}} |                                                             |
+| Dictionaries   | n                       | {{{ps(dict)}}}   | dict                                                        |
+|                | dict key                | {{{ps(get)}}}    | any                                                         |
+|                | dict key any            | {{{ps(put)}}}    |                                                             |
+|                | sym proc                | {{{ps(def)}}}    |                                                             |
+| Arrays         | n                       | {{{ps(array)}}}  | array                                                       |
+| JavaScript FFI | dict key nargs          | .call            | any                                                         |
+|                |                         | .gc              | gc                                                          |
+|                |                         | .math            | Math                                                        |
+| HTML 5         | r g b                   | .rgb             | text                                                        |
+|                | r g b a                 | .rgba            | text                                                        |
+
+Some of the above operators could be implemented in PostScript instead
+of directly in JavaScript.
 
 ** Core operators
 
-| category     | in          | operator    | out    |
-|--------------+-------------+-------------+--------|
-| Math         |             | abs         |        |
-|              |             | .acos       |        |
-|              |             | .asin       |        |
-|              |             | atan        |        |
-|              |             | .atan2      |        |
-|              |             | ceiling     |        |
-|              |             | cos         |        |
-|              |             | .exp        |        |
-|              |             | floor       |        |
-|              |             | log         |        |
-|              |             | .max        |        |
-|              |             | .min        |        |
-|              |             | .pow        |        |
-|              |             | .random     |        |
-|              |             | rand        |        |
-|              |             | round       |        |
-|              |             | sin         |        |
-|              |             | sqrt        |        |
-|              |             | .tan        |        |
-|              |             | truncate    |        |
-|              |             | .e          |        |
-|              |             | .ln2        |        |
-|              |             | .ln10       |        |
-|              |             | .log2e      |        |
-|              |             | .log10e     |        |
-|              |             | .pi         |        |
-|              |             | .sqrt1_2    |        |
-|              |             | .sqrt2      |        |
-|              |             | sub         |        |
-|              |             | idiv        |        |
-| Stack        | x           | dup         | x x    |
-| Conditionals | x y         | ne          | bool   |
-|              | x y         | ge          | bool   |
-|              | x y         | le          | bool   |
-|              | x y         | gt          | bool   |
-|              | bool proc   | if          |        |
-| HTML 5       | key         | .gget       |        |
-|              | any key     | .gput       |        |
-|              | key nargs   | .gcall0     |        |
-|              | key nargs   | .gcall1     |        |
-|              |             | .gcanvas    | canvas |
-|              | w h         | .gdim       |        |
-|              | x0 y0 x1 y1 | .gbox       |        |
-| PostScript   | gray        | setgray     |        |
-|              | r g b       | setrgbcolor |        |
-|              | ???         | setfont ?   |        |
-|              |             | clippath ?  |        |
-|              | text        | show ?      |        |
-|              | x y         | rlineto     |        |
-
-** Canvas methods
-
-|   | in                                           | canvas                | out            | ps         | pdf         |
-|---+----------------------------------------------+-----------------------+----------------+------------+-------------|
-| / |                                              |                       |                | <          | <           |
-|   |                                              | .save                 |                | gsave      | q           |
-|   |                                              | .restore              |                | grestore   | Q           |
-|   | x y                                          | scale                 |                | scale      | -           |
-|   | angle                                        | rotate                |                | rotate     | -           |
-|   | x y                                          | translate             |                | translate  | -           |
-|   | m11 m12 m21 m22 dx dy                        | .transform            |                | -          | cm          |
-|   | m11 m12 m21 m22 dx dy                        | .setTransform         |                | -          | -           |
-|   | x0 y0 x1 y1                                  | .createLinearGradient | canvasGradient |            |             |
-|   | x0 y0 r0 x1 y1 r1                            | .createRadialGradient | canvasGradient |            |             |
-|   | image repetition                             | .createPattern        | canvasPattern  |            |             |
-|   | x y w h                                      | .clearRect            |                | rectclip   |             |
-|   | x y w h                                      | .fillRect             |                | rectfill   |             |
-|   | x y w h                                      | .strokeRect           |                | rectstroke |             |
-|   |                                              | .beginPath            |                | newpath    | m ?         |
-|   |                                              | .closePath            |                | closepath  | ~ h ? ~ n ? |
-|   | x y                                          | .moveTo               |                | moveto     | m ?         |
-|   | x y                                          | .lineTo               |                | lineto     | l           |
-|   | cpx cpy x y                                  | .quadraticCurveTo     |                |            |             |
-|   | cp1x cp1y cp2x cp2y x y                      | .bezierCurveTo        |                |            | c           |
-|   | x1 y1 x2 y2 radius                           | .arcTo                |                | arcto      |             |
-|   | x y w h                                      | .rect                 |                | -          | ~ re        |
-|   | x y radius startAngle endAngle anticlockwise | .arc                  |                | ~ arc      |             |
-|   |                                              | fill                  |                | fill       | ~ f ?       |
-|   |                                              | stroke                |                | stroke     | S           |
-|   |                                              | clip                  |                | clip       | ~ W ?       |
-|   | x y                                          | .isPointInPath        | boolean        |            |             |
-|   | text x y                                     | .fillText1            |                |            |             |
-|   | text x y maxWidth                            | .fillText2            |                |            |             |
-|   | text x y                                     | .strokeText1          |                |            |             |
-|   | text x y maxWidth                            | .strokeText2          |                |            |             |
-|   | text                                         | .measureText          | textMetrics    |            |             |
-|   | image dx dy                                  | .drawImage1           |                |            |             |
-|   | image dx dy dw dh                            | .drawImage2           |                |            |             |
-|   | image sx sy sw sh dx dy dw dh                | .drawImage3           |                |            |             |
-|   | imagedata                                    | .createImageData1     | imageData      |            |             |
-|   | sw sh                                        | .createImageData1     | imageData      |            |             |
-|   | sx sy sw sh                                  | .getImageData         | imageData      |            |             |
-|   | imagedata dx dy                              | .putImageData1        |                |            |             |
-|   | imagedata dx dy dirtyX dirtyY dirtyW dirtyH  | .putImageData2        |                |            |             |
-
-** Canvas attributes
-
-|   | type | attribute                 | values                                             | ps            | pdf   |
-|---+------+---------------------------+----------------------------------------------------+---------------+-------|
-| / |      | <                         |                                                    | <             | <     |
-|   | num  | .globalAlpha              | (1.0)                                              |               |       |
-|   | str  | .globalCompositeOperation | (source-over)                                      |               |       |
-|   | any  | .strokeStyle              | (black)                                            | ~ setdash ?   | ~ d ? |
-|   | any  | .fillStyle                | (black)                                            |               |       |
-|   | num  | .lineWidth                | (1)                                                | setlinewidth  | w     |
-|   | str  | .lineCap                  | (butt) round square                                | ~ setlinecap  | J     |
-|   | str  | .lineJoin                 | round bevel (miter)                                | ~ setlinejoin | j     |
-|   | num  | .miterLimit               | (10)                                               | setmiterlimit | M     |
-|   | num  | .shadowOffsetX            | (0)                                                |               |       |
-|   | num  | .shadowOffsetY            | (0)                                                |               |       |
-|   | num  | .shadowBlur               | (0)                                                |               |       |
-|   | str  | .shadowColor              | (transparent black)                                |               |       |
-|   | str  | .font                     | (10px sans-serif)                                  |               |       |
-|   | str  | .textAlign                | (start) end left right center                      |               |       |
-|   | str  | .textBaseline             | top hanging middle (alphabetic) ideographic bottom |               |       |
-
-** Other operators
-
-|   | canvas                                      | ps | pdf |
-|---+---------------------------------------------+----+-----|
-| / | <                                           |    |     |
-|   | canvasGradient offset color *.addColorStop* |    |     |
-
-** Other attributes
+| category     | in          | operator              | out    |
+|--------------+-------------+-----------------------+--------|
+| Math         |             | {{{ps(abs)}}}         |        |
+|              |             | .acos                 |        |
+|              |             | .asin                 |        |
+|              |             | {{{ps(atan)}}}        |        |
+|              |             | .atan2                |        |
+|              |             | {{{ps(ceiling)}}}     |        |
+|              |             | {{{ps(cos)}}}         |        |
+|              |             | .exp                  |        |
+|              |             | {{{ps(floor)}}}       |        |
+|              |             | {{{ps(log)}}}         |        |
+|              |             | .max                  |        |
+|              |             | .min                  |        |
+|              |             | .pow                  |        |
+|              |             | .random               |        |
+|              |             | {{{ps(rand)}}}        |        |
+|              |             | {{{ps(round)}}}       |        |
+|              |             | {{{ps(sin)}}}         |        |
+|              |             | {{{ps(sqrt)}}}        |        |
+|              |             | .tan                  |        |
+|              |             | {{{ps(truncate)}}}    |        |
+|              |             | .e                    |        |
+|              |             | .ln2                  |        |
+|              |             | .ln10                 |        |
+|              |             | .log2e                |        |
+|              |             | .log10e               |        |
+|              |             | .pi                   |        |
+|              |             | .sqrt1_2              |        |
+|              |             | .sqrt2                |        |
+|              |             | {{{ps(sub)}}}         |        |
+|              |             | {{{ps(idiv)}}}        |        |
+| Stack        | x           | {{{ps(dup)}}}         | x x    |
+| Conditionals | x y         | {{{ps(ne)}}}          | bool   |
+|              | x y         | {{{ps(ge)}}}          | bool   |
+|              | x y         | {{{ps(le)}}}          | bool   |
+|              | x y         | {{{ps(gt)}}}          | bool   |
+|              | bool proc   | {{{ps(if)}}}          |        |
+| HTML 5       | key         | .gget                 |        |
+|              | any key     | .gput                 |        |
+|              | key nargs   | .gcall0               |        |
+|              | key nargs   | .gcall1               |        |
+|              |             | .gcanvas              | canvas |
+|              | w h         | .gdim                 |        |
+|              | x0 y0 x1 y1 | .gbox                 |        |
+
+** HTML 5 canvas methods and attributes
+
+*** Canvas methods
+
+|   | in                                           | canvas                | out            | ps                             | pdf         |
+|---+----------------------------------------------+-----------------------+----------------+--------------------------------+-------------|
+| / |                                              |                       |                | <                              | <           |
+|   |                                              | .save                 |                | {{{ps(gsave)}}}                | q           |
+|   |                                              | .restore              |                | {{{ps(grestore)}}}             | Q           |
+|   | x y                                          | .scale                |                | {{{ps(scale)}}}                | -           |
+|   | angle                                        | .rotate               |                | {{{ps(rotate)}}}               | -           |
+|   | x y                                          | .translate            |                | {{{ps(translate)}}}            | -           |
+|   | m11 m12 m21 m22 dx dy                        | .transform            |                | -                              | cm          |
+|   | m11 m12 m21 m22 dx dy                        | .setTransform         |                | -                              | -           |
+|   | x0 y0 x1 y1                                  | .createLinearGradient | canvasGradient |                                |             |
+|   | x0 y0 r0 x1 y1 r1                            | .createRadialGradient | canvasGradient |                                |             |
+|   | image repetition                             | .createPattern        | canvasPattern  |                                |             |
+|   | x y w h                                      | .clearRect            |                | {{{ps(rectclip)}}}             |             |
+|   | x y w h                                      | .fillRect             |                | {{{ps(rectfill)}}}             |             |
+|   | x y w h                                      | .strokeRect           |                | {{{ps(rectstroke)}}}           |             |
+|   |                                              | .beginPath            |                | {{{ps(newpath)}}}              | m ?         |
+|   |                                              | .closePath            |                | {{{ps(closepath)}}}            | ~ h ? ~ n ? |
+|   | x y                                          | .moveTo               |                | {{{ps(moveto)}}}               | m ?         |
+|   | x y                                          | .lineTo               |                | {{{ps(lineto)}}}               | l           |
+|   | cpx cpy x y                                  | .quadraticCurveTo     |                |                                |             |
+|   | cp1x cp1y cp2x cp2y x y                      | .bezierCurveTo        |                |                                | c           |
+|   | x1 y1 x2 y2 radius                           | .arcTo                |                | {{{ps(arcto)}}}                |             |
+|   | x y w h                                      | .rect                 |                | -                              | ~ re        |
+|   | x y radius startAngle endAngle anticlockwise | .arc                  |                | ~ {{{ps(arc)}}} {{{ps(arcn)}}} |             |
+|   |                                              | .fill                 |                | {{{ps(fill)}}}                 | ~ f ?       |
+|   |                                              | .stroke               |                | {{{ps(stroke)}}}               | S           |
+|   |                                              | .clip                 |                | {{{ps(clip)}}}                 | ~ W ?       |
+|   | x y                                          | .isPointInPath        | boolean        |                                |             |
+|   | text x y                                     | .fillText1            |                |                                |             |
+|   | text x y maxWidth                            | .fillText2            |                |                                |             |
+|   | text x y                                     | .strokeText1          |                |                                |             |
+|   | text x y maxWidth                            | .strokeText2          |                |                                |             |
+|   | text                                         | .measureText          | textMetrics    |                                |             |
+|   | image dx dy                                  | .drawImage1           |                |                                |             |
+|   | image dx dy dw dh                            | .drawImage2           |                |                                |             |
+|   | image sx sy sw sh dx dy dw dh                | .drawImage3           |                |                                |             |
+|   | imagedata                                    | .createImageData1     | imageData      |                                |             |
+|   | sw sh                                        | .createImageData1     | imageData      |                                |             |
+|   | sx sy sw sh                                  | .getImageData         | imageData      |                                |             |
+|   | imagedata dx dy                              | .putImageData1        |                |                                |             |
+|   | imagedata dx dy dirtyX dirtyY dirtyW dirtyH  | .putImageData2        |                |                                |             |
+
+*** Canvas attributes
+
+|   | type | attribute                 | values                                             | ps                      | pdf   |
+|---+------+---------------------------+----------------------------------------------------+-------------------------+-------|
+| / |      | <                         |                                                    | <                       | <     |
+|   | num  | .globalAlpha              | (1.0)                                              |                         |       |
+|   | str  | .globalCompositeOperation | (source-over)                                      |                         |       |
+|   | any  | .strokeStyle              | (black)                                            | ~ {{{ps(setdash)}}} ?   | ~ d ? |
+|   | any  | .fillStyle                | (black)                                            |                         |       |
+|   | num  | .lineWidth                | (1)                                                | {{{ps(setlinewidth)}}}  | w     |
+|   | str  | .lineCap                  | (butt) round square                                | ~ {{{ps(setlinecap)}}}  | J     |
+|   | str  | .lineJoin                 | round bevel (miter)                                | ~ {{{ps(setlinejoin)}}} | j     |
+|   | num  | .miterLimit               | (10)                                               | {{{ps(setmiterlimit)}}} | M     |
+|   | num  | .shadowOffsetX            | (0)                                                |                         |       |
+|   | num  | .shadowOffsetY            | (0)                                                |                         |       |
+|   | num  | .shadowBlur               | (0)                                                |                         |       |
+|   | str  | .shadowColor              | (transparent black)                                |                         |       |
+|   | str  | .font                     | (10px sans-serif)                                  |                         |       |
+|   | str  | .textAlign                | (start) end left right center                      |                         |       |
+|   | str  | .textBaseline             | top hanging middle (alphabetic) ideographic bottom |                         |       |
+
+*** Other operators
+
+|   | in                          | canvas        | out | ps | pdf |
+|---+-----------------------------+---------------+-----+----+-----|
+| / |                             | <             |     |    |     |
+|   | canvasGradient offset color | .addColorStop |     |    |     |
+
+*** Other attributes
 
 |   | dict             | type             | attribute | values | ps | pdf |
 |---+------------------+------------------+-----------+--------+----+-----|
@@ -871,15 +799,21 @@ octal chars in string \234
 |   | imageData        | canvasPixelArray | data      |        |    |     |
 |   | canvasPixelArray | cnt              | length    |        |    |     |
 
-[IndexGetter, IndexSetter] CanvasPixelArray
+TODO [IndexGetter, IndexSetter] CanvasPixelArray
 
 ** PostScript operators
 
-|   | category | in      | operator   | out |
-|---+----------+---------+------------+-----|
-| / |          | <       | <          | <   |
-|   |          | x y [m] | transform  | x y |
-|   |          | x y [m] | itransform | x y |
+|   | category | in      | operator              | out |
+|---+----------+---------+-----------------------+-----|
+| / |          | <       | <                     | <   |
+|   |          | x y [m] | {{{ps(transform)}}}   | x y |
+|   |          | x y [m] | {{{ps(itransform)}}}  | x y |
+|   |          | gray    | {{{ps(setgray)}}}     |     |
+|   |          | r g b   | {{{ps(setrgbcolor)}}} |     |
+|   |          | ???     | {{{ps(setfont)}}} ?   |     |
+|   |          |         | {{{ps(clippath)}}} ?  |     |
+|   |          | text    | {{{ps(show)}}} ?      |     |
+|   |          | x y     | {{{ps(rlineto)}}}     |     |
 
 ** PDF operators
 
@@ -891,33 +825,33 @@ octal chars in string \234
 |   |                        | j        | ~ setlinejoin                                             |
 |   |                        | M        | setmiterlimit                                             |
 |   |                        | d        | ~ setdash ?                                               |
-|   |                        | ri       | {SetColorRenderingIntent}                                 |
-|   |                        | i        | {1 .min setflat}                                          |
-|   |                        | gs       | {SetExtGState}                                            |
+|   |                        | ri       |                                                           |
+|   |                        | i        | ~ {1 .min setflat}                                        |
+|   |                        | gs       |                                                           |
 |   | Special graphics state | q        | gsave                                                     |
 |   |                        | Q        | grestore                                                  |
-|   |                        | cm       | .transform   { //TempMatrix astore concat }               |
+|   |                        | cm       | .transform                                                |
 |   | Path construction      | m        | moveto                                                    |
 |   |                        | l        | lineto                                                    |
 |   |                        | c        | .bezierCurveTo (curveto)                                  |
-|   |                        | v        | ! currentpoint cp2 p3 c { currentpoint 6 2 roll curveto } |
-|   |                        | y        | ! cp1 p3 p3 c { 2 copy curveto }                          |
+|   |                        | v        | ! currentpoint cp2 p3 c {currentpoint 6 2 roll curveto}   |
+|   |                        | y        | ! cp1 p3 p3 c {2 copy curveto}                            |
 |   |                        | h        | closepath                                                 |
 |   |                        | re       | ! x y m , x+w y l , x+w y+h l , x y+h l , h               |
 |   | Path painting          | S        | stroke                                                    |
 |   |                        | s        | h S                                                       |
 |   |                        | f        | (fill)                                                    |
 |   |                        | F        | f                                                         |
-|   |                        | f*       | {eofill}                                                  |
-|   |                        | B        | f S { gsave fill grestore stroke }                        |
-|   |                        | B*       | f* S { gsave eofill grestore stroke }                     |
-|   |                        | b        | h b { closepath gsave fill grestore stroke }              |
-|   |                        | b*       | h B* { closepath gsave eofill grestore stroke }           |
-|   |                        | n        | {newpath}                                                 |
+|   |                        | f*       | eofill                                                    |
+|   |                        | B        | f S ! {gsave fill grestore stroke}                        |
+|   |                        | B*       | f* S ! {gsave eofill grestore stroke}                     |
+|   |                        | b        | h b ! {closepath gsave fill grestore stroke}              |
+|   |                        | b*       | h B* ! {closepath gsave eofill grestore stroke}           |
+|   |                        | n        | ~ newpath                                                 |
 |   | Clipping paths         | W        | clip                                                      |
-|   |                        | W*       | {eoclip}                                                  |
+|   |                        | W*       | eoclip                                                    |
 |   | Text objects           | BT       |                                                           |
-|   |                        | ET       | {grestore}                                                |
+|   |                        | ET       | ~ grestore                                                |
 |   | Text state             | Tc       |                                                           |
 |   |                        | Tw       |                                                           |
 |   |                        | Tz       |                                                           |
@@ -933,8 +867,8 @@ octal chars in string \234
 |   |                        | TJ       |                                                           |
 |   |                        | '        |                                                           |
 |   |                        | "        |                                                           |
-|   | Type 3 fonts           | d0       | setcharwidth                                                          |
-|   |                        | d1       | setcachedevice                                                          |
+|   | Type 3 fonts           | d0       | setcharwidth                                              |
+|   |                        | d1       | setcachedevice                                            |
 |   | Color                  | CS       |                                                           |
 |   |                        | cs       |                                                           |
 |   |                        | SC       |                                                           |
@@ -946,7 +880,7 @@ octal chars in string \234
 |   |                        | RG       | rg                                                        |
 |   |                        | rg       | setrgbcolor                                               |
 |   |                        | K        | k                                                         |
-|   |                        | k        | setcmykcolor                                                          |
+|   |                        | k        | setcmykcolor                                              |
 |   | Shading patterns       | sh       |                                                           |
 |   | Inline images          | BI       |                                                           |
 |   |                        | ID       |                                                           |
@@ -960,94 +894,39 @@ octal chars in string \234
 |   | Compatibility          | BX       |                                                           |
 |   |                        | EX       |                                                           |
 
-* References
-
-** PostScript in JavaScript by others
-
-Somebody before: http://www.feiri.de/pcan/ RPN calculator with many
-PostScript operators implemented directly in JavaScript.  Fast drawing
-but not "real" PostScript.
-
-Wish list: http://svgkit.sourceforge.net/
-
-** Interesting PostScript applications
-
-http://starynkevitch.net/Basile/NeWS_descr_oct_1993.html \\
-http://www.art.net/studios/Hackers/Hopkins/Don/hyperlook/index.html \\
-http://books.google.com/books?id=xHSoK66z34YC&pg=PA193 \\
-http://www.art.net/studios/Hackers/Hopkins/Don/lang/NeWS.html \\
-http://en.wikipedia.org/wiki/Display_PostScript
-
-** Mess
-
-http://canvaspaint.org/blog/2006/12/rendering-text/ \\
-http://dev.opera.com/articles/view/html-5-canvas-the-basics/ \\
-http://www.benjoffe.com/code/ \\
-http://arapehlivanian.com/wp-content/uploads/2007/02/canvas.html
-
-http://two.pairlist.net/pipermail/reportlab-users/2002-September/000599.html
-
-http://www.capcode.de/help/put ref \\
-http://www.math.ubc.ca/~cass/courses/ps.html ps operator summary \\
-http://atrey.karlin.mff.cuni.cz/~milanek/PostScript/Reference/PSL2d.html \\
-http://gauss.upf.es/www-storage/PS_Guide-1.0/operators.html \\
-http://web.mit.edu/ghostscript/www/Language.htm \\
-http://pages.cs.wisc.edu/~ghost/doc/gnu/7.05/Ps-style.htm \\
-http://www.tailrecursive.org/postscript/operators.html \\
-
-http://www.math.ubc.ca/~cass/courses/m308-7b/ch5.pdf
-
-http://en.wikibooks.org/wiki/PostScript_FAQ
-
-: http://www.nihilogic.dk/labs/canvas_sheet/HTML5_Canvas_Cheat_Sheet.png
+* Supported Browsers
 
-http://dev.opera.com/articles/view/creating-pseudo-3d-games-with-html-5-can-1/
+I have tried the following browsers so far:
 
-http://dev.opera.com/articles/view/html-5-canvas-the-basics/
+|   | Browser | Version    | Note                               |
+|---+---------+------------+------------------------------------|
+| / |         | <          |                                    |
+|   | Firefox | 3.0.11     | no text drawing, linecap, linejoin |
+|   | Firefox | 3.5b4pre   | no text drawing, linecap, linejoin |
+|   | Opera   | 10.00 Beta | no text drawing, ugly aliasing     |
+|   | Chrome  | 3.0.189.0  | arc drawing looks partially broken |
 
-js closure over value, not var!
+If you are using a different browser, please [[http://logand.com/contact.html][let me know]] if it works
+for you.
 
-http://www.pvv.ntnu.no/~andersr/fractal/PostScript.html
-http://www.cs.unh.edu/~charpov/Programming/L-systems/
-http://www.cs.unh.edu/~charpov/Programming/PostScript-primes/
+* Limitations and Known Issues 
 
-http://www.adobe.com/print/features/psvspdf/index.html
+- many PostScript operators are still to be implemented
+- only small fraction of PDF operators has been implemented
+- text drawing and font related functionality has not been implemented
 
-   A PDF file is actually a PostScript file which has already been
-   interpreted by a RIP and made into clearly defined objects.
+* Changes
 
-http://c2.com/cgi/wiki?PortableDocumentFormat
+2009-06-30 v0.1
 
-   Unfortunately, Adobe have a history of making needlessly
-   incompatible changes to the PDF format. It's not quite as bad as
-   PostScript in terms of the number of broken documents out there,
-   but it's definitely not a stable format.
+- Initial version
 
-hate pdf http://www.useit.com/alertbox/20030714.html
-
-* Ideas
-
-do better than http://processingjs.org/ http://processingjs.org/reference
-
-try 3d interface in chrome
-
-reactive programming http://conal.net/fran/tutorial.htm
-
-
-* LINKS
-
-http://www.math.ubc.ca/~cass/graphics/manual/
-
-http://www.anastigmatix.net/postscript/MetaPre.html
-
-http://svn.ghostscript.com/ghostscript/branches/icc_work/psi/estack.h
-
-https://dev.mobileread.com/trac/iliados/browser/upstream/poppler/test/pdf-operators.c
-
-http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_paths/dq_paths.html#//apple_ref/doc/uid/TP30001066-CH211-DontLinkElementID_10
+* References
 
-http://easyweb.easynet.co.uk/~mrmeanie/matrix/matrices.htm
+[[http://www.feiri.de/pcan/][PostCanvas]] is a RPN interpreter with many PostScript operators
+implemented directly in JavaScript.  It is faster than WPS but not a
+"real" PostScript.
 
-https://developer.mozilla.org/samples/canvas-tutorial/6_2_canvas_clipping.html
+[[http://svgkit.sourceforge.net/][SVGKit]] has a PostScript interpreter on the wish list.
 
-https://developer.mozilla.org/samples/canvas-tutorial/5_2_canvas_translate.html
+Postscript is a registered trademark of [[http://www.adobe.com][Adobe Systems Incorporated]].