wps

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

index.org (51539B)


      1 #+title: WPS: PostScript for the Web
      2 #+description: PostScript and PDF interpreter for HTML 5 canvas
      3 #+keywords: PostScript, PDF, interpreter, HTML 5, canvas, JavaScript
      4 #+options: num:nil toc:t
      5 #+macro: ps [[http://www.capcode.de/help/$1][$1]]
      6 
      7 #+begin_html
      8 <p class="h0">WPS: PostScript for the Web</p>
      9 #+end_html
     10 
     11 Welcome to WPS, a PostScript and PDF interpreter for HTML 5 canvas.
     12 
     13 Note that to see and run the examples, JavaScript must be enabled and
     14 your browser must support HTML 5 canvas (latest Firefox, Opera and
     15 Chrome should work).
     16 
     17 This document allows you to try simple PostScript programs in the WPS
     18 sandbox.  A few examples are presented here accompanied by a brief
     19 description of the interpreter and listing some implementation notes
     20 for my future reference.
     21 
     22 #+begin_html
     23 <style>
     24 tt {background-color:#fdf}
     25 canvas {width:12em;height:12em;border:1px dashed black}
     26 </style>
     27 #+end_html
     28 
     29 #+html: <div id="wps" style="display:none">
     30 #+include "wps.wps" src text
     31 #+html: </div>
     32 
     33 #+begin_html
     34 <script type="text/javascript" src="wps.js"></script>
     35 <script>
     36 function $(Id) {return document.getElementById(Id);}
     37 function $$(Id) {return $(Id).textContent;}
     38 wps = new Wps;
     39 wps.parse($$("wps"));
     40 </script>
     41 #+end_html
     42 
     43 * WPS sandbox
     44 
     45 #+begin_html
     46 <canvas id="xsandbox"></canvas>
     47 <p>Sandbox:</p>
     48 <p>
     49 <textarea id="sandbox" style="width:100%" rows="18">
     50 /n 10 def
     51 /w 25 def
     52 
     53 0 0 n w mul dup .gbox
     54 
     55 4 dict begin
     56   0 1 n 1 sub {
     57     /i exch def
     58     /ii 1 1 n div i mul sub def
     59     0 1 n 1 sub {
     60       /j exch def
     61       /jj 1 1 n div j mul sub def
     62       ii jj 0 setrgbcolor
     63       w j mul w i mul w w rectfill
     64     } for
     65   } for
     66 end
     67 </textarea>
     68 </p>
     69 <script>
     70 function sandbox() {(new Wps).parse($$("wps"), "(xsandbox) .setGc", $("sandbox").value);}
     71 </script>
     72 <button onclick="javascript:sandbox();">Run</button> code from sandbox.
     73 #+end_html
     74 
     75 * PostScript interpreter
     76 
     77 A few initial ideas and questions:
     78 
     79 - Learn and implement a Forth like language.  PostScript seems like a
     80   good choice:
     81   - It has the right syntax and stack based evaluation.
     82   - It is practical and widely used.
     83   - It has long sucessful history in print and publishing (and more).
     84   - It is a predecessor of PDF.
     85   - Almost everything (e.g. editors, pictures, documentation) can be
     86     reused to a great extent.
     87   - It is ideal for HTML 5 canvas experiments because from the
     88     PostScript point of view, canvas is just another low level device.
     89 - Flexibility and simplicity first.
     90   - Optimize for fast code change, not for raw running speed.  Keep
     91     the code small and regular if possible.
     92   - Can JavaScript be used as a portable assembler for the Web?  Is
     93     building scripting languages on top of JavaScript feasible and
     94     efficient enough for real-world use?  If not, why?  Find the
     95     limits.
     96 - Keep the language/environment specific core as small as possible.
     97   - Allow to port the interpreter to other languages on both
     98     client and server side.
     99   - Be open for the possibility of running "the same code" on both the
    100     client and server side.
    101 - Can PDF documents be displayed in web browsers without server-side
    102   image rendering?
    103   - Implement a canvas based version of PDF page contents in [[../ondoc/index.org][OnDoc]].
    104 - It might be possible to implement different backend devices to be
    105   used instead of HTML 5 canvas, for example an SVG device.
    106 - Investigate the possibility of implementing a Lisp interpreter
    107   suitable for production use in web applications.
    108 
    109 There are several things WPS is about:
    110 
    111 - stack(s)
    112 - function (operator) dictionary
    113 - reader
    114 - interpreter/evaluator
    115 - data types
    116 - native bindings (JavaScript FFI)
    117 - PostScript and PDF API
    118 
    119 [[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]]
    120 programming languages.  It is (roughly) a programming language with
    121 [[http://en.wikipedia.org/wiki/Reverse_Polish_notation][RPN]], complex data types, garbage collection and specialized
    122 drawing operators.
    123 
    124 ** Trivial example
    125 
    126 The core essence of a RPN calculator is captured in the JavaScript
    127 code bellow.
    128 
    129 #+html: <div id="example1">
    130 #+begin_src js2
    131 function example1() {
    132    // define stack and operators
    133    var Os = [];
    134    var Sd = {};
    135    Sd["+"] = function() {Os.push(Os.pop() + Os.pop());};
    136    Sd["="] = function() {alert(Os.pop());};
    137    // compute 1 2 = 3 + =
    138    Os.push(1);
    139    Os.push(2);
    140    Sd["="]();
    141    Os.push(3);
    142    Sd["+"]();
    143    Sd["="]();
    144 }
    145 #+end_src
    146 #+html: </div>
    147 #+begin_html
    148 <script>
    149 function ex1() {
    150    eval($$("example1"));
    151    example1();
    152 }
    153 </script>
    154 <button onclick="javascript:ex1()">Eval</button>
    155 "<tt>1 2 = 3 + =</tt>"
    156 #+end_html
    157 
    158 =Os= stands for Operand Stack, which holds arguments for operators.
    159 =Sd= is a System Dictionary which contains definitions of operators
    160 (JavaScript functions in this case).
    161 
    162 ** Example with PostScript reader
    163 
    164 PostScript has simple but non-trivial syntax so a reader which reads
    165 text and creates internal PostScript objects is necessary.  The reader
    166 and evaluator is called =Ps0= (an empty PostScript interpreter) in the
    167 JavaScript code bellow.
    168 
    169 #+html: <div id="example2">
    170 #+begin_src js2
    171 function example2(T) {
    172    var Os = [];
    173    var Sd = {};
    174    var Ds = [Sd];
    175    var Es = [];
    176    Sd[new Symbol("+")] = function() {Os.push(Os.pop() + Os.pop());};
    177    Sd[new Symbol("dup")] = function() {var X = Os.pop(); Os.push(X); Os.push(X);};
    178    Sd[new Symbol("=")] = function() {alert(Os.pop());};
    179    (new Ps0(Os, Ds, Es)).parse(T); // read and interpret code T
    180 }
    181 #+end_src
    182 #+html: </div>
    183 #+begin_html
    184 <script>
    185 function ex2() {
    186    eval($$("example2"));
    187    example2($$("ex2"));
    188 }
    189 </script>
    190 <button onclick="javascript:ex2()">Eval</button>
    191 "<tt id="ex2">12 34 + dup = 56 + =</tt>"
    192 #+end_html
    193 
    194 =Ds= is a Dictionary Stack allowing users to redefine existing
    195 operators and revert back to the original ones.  =Es= is an Execution
    196 Stack which is used to implement a tail recursive evaluator.
    197 
    198 ** Example with recursion
    199 
    200 It is possible to write recursive code in PostScript.  The following
    201 PostScript code is from the [[http://www.math.ubc.ca/~cass/graphics/manual/pdf/ch9.pdf][Recursion in PostScript PDF document]].
    202 
    203 #+html: <div id="example3">
    204 #+begin_src ps
    205 /factorial1 {
    206   1 dict begin
    207     /n exch def
    208     n 0 eq {1}{n n 1 sub factorial1 mul} ifelse
    209   end
    210 } def
    211 
    212 5 factorial1 =
    213 
    214 /factorial2 {
    215   dup 0 eq {pop 1}{dup 1 sub factorial2 mul} ifelse
    216 } def
    217 
    218 5 factorial2 =
    219 
    220 % based on the PostScript example from
    221 % http://partners.adobe.com/public/developer/en/ps/sdk/sample/BlueBook.zip
    222 
    223 /factorial3 {
    224   dup 1 gt {dup 1 sub factorial3 mul} if
    225 } def
    226 
    227 5 factorial3 =
    228 #+end_src
    229 #+html: </div>
    230 #+begin_html
    231 <script>
    232 function ex3() {(new Wps).parse($$("wps"), $$("example3"));}
    233 </script>
    234 <button onclick="javascript:ex3();">Run</button> the example.
    235 #+end_html
    236 
    237 ** Execution stack
    238 
    239 The interpreter manages its Execution Stack explicitly.
    240 
    241 Most operators simply:
    242 
    243 1. get their arguments from the Operand Stack
    244 2. perform some computation and/or side effects
    245 3. push results to the Operand Stack
    246 
    247 Some operators are more complex and involve some kind of control flow,
    248 e.g. {{{ps(if)}}}, {{{ps(repeat)}}}, {{{ps(for)}}}, {{{ps(loop)}}}
    249 operators.  Such operators:
    250 
    251 1. get their arguments from the Operand Stack
    252 2. perform single step of some computation and/or side effects
    253 3. push the continuation (code and arguments to be executed next) to
    254    the Execution Stack
    255 
    256 [[http://en.wikipedia.org/wiki/Tail_call][Tail Call Optimisation]] is implemented using [[http://logand.com/picoWiki/trampoline][trampoline]].  The evaluator
    257 runs in a loop getting the next [[http://en.wikipedia.org/wiki/Continuation][continuation]] from the Execution Stack.
    258 Operators that want to "continue" their execution (i.e. use the
    259 interpreter to run other operators, including themselves) must perform
    260 only one step at a time and save the remaining steps (continuation) on
    261 the Execution Stack.
    262 
    263 For example, the {{{ps(if)}}} operator saves the "then" or "else" code
    264 branch to the Execution Stack depending on the value of the "test"
    265 argument.  It does not "evaluate" the chosen branch directly
    266 (recursively) but leaves the control to the evaluator loop.
    267 
    268 The whole process of interpreting is fed from JavaScript strings which
    269 are obtained from the content of HTML elements (sometimes hidden from
    270 this document).
    271 
    272 ** PostScript data types
    273 
    274 PostScript has quite rich set of data types.
    275 See [[http://www.adobe.com/devnet/postscript/pdfs/PLRM.pdf][PostScript Language Reference PDF document]] for more details.
    276 
    277 | category  | type        | executable | example                | spec               |
    278 |-----------+-------------+------------+------------------------+--------------------|
    279 | simple    | boolean     |            | true false             |                    |
    280 |           | fontID      |            |                        |                    |
    281 |           | integer     |            | 42 -123 0              |                    |
    282 |           | mark        |            |                        |                    |
    283 |           | name        | Y          | draw /draw             |                    |
    284 |           | null        |            | null                   |                    |
    285 |           | operator    | Y          |                        |                    |
    286 |           | real        |            | 3.14 1e-10             |                    |
    287 |           | save        |            |                        |                    |
    288 | composite | array       | Y          | [1 /hi 3.14] {1 2 add} |                    |
    289 |           | condition   |            |                        | Display PostScript |
    290 |           | dictionary  |            | <</a 1/b 2>>           |                    |
    291 |           | file        |            |                        |                    |
    292 |           | gstate      |            |                        | Level 2            |
    293 |           | lock        |            |                        | Display PostScript |
    294 |           | packedarray |            |                        | Level 2            |
    295 |           | string      | Y          | (hi) <a33f>            |                    |
    296 
    297 The following data types are implemented in WPS:
    298 
    299 | category  | type       | direct | literal | executable |
    300 |-----------+------------+--------+---------+------------|
    301 | simple    | boolean    | Y      | Y       | -          |
    302 |           | number     | Y      | Y       | -          |
    303 |           | mark       | -      | Y       | -          |
    304 |           | name       | -      | Y       | Y          |
    305 |           | null       | Y      | Y       | -          |
    306 |           | operator   | Y      | -       | Y          |
    307 | composite | array      | Y      | Y       | -          |
    308 |           | proc       | -      | -       | Y          |
    309 |           | dictionary | Y      | Y       | -          |
    310 |           | string     | Y      | Y       | -          |
    311 
    312 All the above types are represented directly in JavaScript except:
    313 
    314 | type            | representation  |
    315 |-----------------+-----------------|
    316 | mark            | unique object   |
    317 | literal name    | quoted symbol   |
    318 | executable name | unquoted symbol |
    319 | operator        | function        |
    320 | proc            | quoted array    |
    321 
    322 The interpreter needs to understand when to evaluate an argument.  The
    323 distinction between a "literal" and "executable" is the key.  For the
    324 "proc" type, its origin from the Execution Stack is also important.
    325 
    326 ** Quoting and execution
    327 
    328 There are two important operators to control evaluation at the
    329 PostScript language level.
    330 
    331 The {{{ps(exec)}}} operator usually leaves the argument as is except:
    332 
    333 | type            | result            |
    334 |-----------------+-------------------|
    335 | executable name | exec value        |
    336 | operator        | apply operator    |
    337 | proc            | exec each element |
    338 
    339 The {{{ps(cvx)}}} operator makes the argument "executable".  Usually
    340 leaves the argument as is except:
    341 
    342 | from         | to              | how     |
    343 |--------------+-----------------+---------|
    344 | literal name | executable name | unquote |
    345 | array        | proc            | quote   |
    346 | string       | proc            | ~ parse |
    347 
    348 The ~ (tilde) character in the above table means that the
    349 functionality has not been implemented yet.
    350 
    351 * Drawing with PostScript
    352 
    353 As a convention, operators beginning with dot are non-standard, low
    354 level operators which are subject to change.
    355 
    356 There is a difference in how HTML 5 canvas, PostScript and PDF measure
    357 angles:
    358 
    359 | language/device | unit |
    360 |-----------------+------|
    361 | canvas          | rad  |
    362 | PostScript      | deg  |
    363 | PDF             | rad  |
    364 
    365 Many of the examples below set up their bounding box using the
    366 =.gbox= operator, e.g.
    367 
    368 #+begin_src ps
    369 0 0 180 180 .gbox
    370 #+end_src
    371 
    372 Only the width and height of the canvas clipping rectangle are taken
    373 into account so far.  The width and height is related to the drawing
    374 units rather than to the size of the canvas element.
    375 
    376 Both PostScript and PDF documents have the origin of the coordinate
    377 system in the bottom left corner while HTML 5 canvas in the top left
    378 corner.  Thus, some of the following pictures are displayed upside
    379 down unless an explicit coordinate transformation was added.  This
    380 discrepancy between the origin of the coordinate system is a problem
    381 when drawing text because a simple coordinate transformation on its
    382 own would draw the text upside-down.
    383 
    384 ** Bowtie example
    385 
    386 See the [[https://developer.mozilla.org/en/drawing_graphics_with_canvas#section_6][original example]] in JavaScript.
    387 
    388 #+html: <canvas id="xbowtie"></canvas>
    389 #+html: <div id="bowtie">
    390 #+include "bowtie.wps" src ps
    391 #+html: </div>
    392 #+html: <script>wps.parse("save (xbowtie) .setGc", $$("bowtie"), "restore");</script>
    393 
    394 ** Analog clock example
    395 
    396 See the [[http://oreilly.com/openbook/cgi/ch06_02.html][original example]].
    397 
    398 Click on the clock to start/stop it.  (If using Chrome, you might need
    399 to reload the page for this to work.  Not sure why?)
    400 
    401 #+html: <canvas id="xclock2"></canvas>
    402 
    403 #+html: <div id="clock2">
    404 #+include "clock2.wps" src ps
    405 #+html: </div>
    406 #+html: <script>(new Wps).parse($$("wps"), "(xclock2) .setGc", $$("clock2"));</script>
    407 
    408 Running the clock keeps the CPU noticeably busy.  Chrome is best with
    409 very little overhead.  Firefox and Opera perform significantly worse.
    410 WPS seems to be fast enough for one-off drawings, but its usability
    411 depends on the efficiency of the host JavaScript interpreter when
    412 running the interpreter in a tight loop.
    413 
    414 ** Fill example
    415 
    416 See the [[https://developer.mozilla.org/samples/canvas-tutorial/4_1_canvas_fillstyle.html][original example]] in JavaScript.
    417 
    418 #+html: <canvas id="xfill"></canvas>
    419 #+html: <div id="fill">
    420 #+include "fill.wps" src ps
    421 #+html: </div>
    422 #+html: <script>wps.parse("save (xfill) .setGc", $$("fill"), "restore");</script>
    423 
    424 ** Tiger example
    425 
    426 The [[http://svn.ghostscript.com/viewvc/trunk/gs/examples/tiger.eps?view=co][original example]] is included with [[http://ghostscript.com/][Ghostscript]].
    427 
    428 #+begin_html
    429 <canvas id="xtiger" style="width:283pt;height:369pt">
    430 </canvas>
    431 <p>Drawing took <span id="msg">--</span> seconds.</p>
    432 #+end_html
    433 
    434 #+html: <div id="tiger" style="display:none">
    435 #+include "tiger.eps" src text
    436 #+html: </div>
    437 
    438 #+begin_html
    439 <div id="tiger1" style="display:none">
    440 (xtiger) .setGc
    441 0 0 567 739 .gbox
    442 1 0 0 -1 0 739 .transform
    443 /time1 .date (getTime) 0 .call def
    444 </div>
    445 
    446 <div id="tiger2" style="display:none">
    447 /time2 .date (getTime) 0 .call def
    448 (msg) .getElementById (textContent) time2 time1 sub 1000 div put
    449 </div>
    450 
    451 <script>
    452 function tiger() {(new Wps).parse($$("wps"), $$("tiger1"), $$("tiger"), $$("tiger2"));}
    453 </script>
    454 <button onclick="javascript:tiger();">Draw</button> the tiger (be patient).
    455 #+end_html
    456 
    457 Is this an interesting JavaScript and canvas benchmark?
    458 
    459 #+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"
    460 | browser     | WPS time [s] | WPS time (no bind) [s] | PostCanvas time [s] |
    461 |-------------+--------------+------------------------+---------------------|
    462 | Chrome      |          2.7 |                    4.1 |                 1.6 |
    463 | Opera       |         17.9 |                   12.3 |                   0 |
    464 | Firefox 3.0 |         21.0 |                   19.0 |                 7.0 |
    465 | Firefox 3.5 |         13.0 |                    9.5 |                 3.3 |
    466 | Safari      |          2.9 |                      0 |                   0 |
    467 
    468 The above times were on Vaio T7200 Core 2 2GHz 2GB running Ubuntu.
    469 
    470 [[http://www.feiri.de/pcan/][PostCanvas]] runs this [[http://www.feiri.de/pcan/example1.html][example]] about 1.5 times (Chrome) to 3 times
    471 (Firefox) faster.  I am actually surprised that WPS runs only about
    472 1.5 times slower in Chrome even though it interprets almost everything
    473 with minimal number of operators coded directly in JavaScript
    474 (compared to PostCanvas which implements all operators directly in
    475 JavaScript).  Time for Safari was reported by Will King and even
    476 though it was not run on the same machine as the other tests, it shows
    477 that the speed is comparable to Chrome.
    478 
    479 Another surprise to me is that I expected more significant speed up
    480 after implementing the {{{ps(bind)}}} operator.  Why does Opera and
    481 Firefox get slower in this case?
    482 
    483 It should be fairly easy to speed WPS up by coding more operators
    484 directly in JavaScript.  The speed of PostCanvas could probably be
    485 taken as the best case that could be achieved by optimizing WPS.
    486 
    487 file:tiger.png
    488 
    489 Note by Dave Chapman:
    490 
    491 #+begin_quote
    492 
    493 I've found that reducing the number of function calls in complex
    494 scripts has by far the biggest gains in speed - but I guess you
    495 already know this. For instance, when I run the Tiger demo it takes
    496 about 19sec on my machine (FF3.0, dual core, 4gb ram) but according to
    497 the firebug profiler it's making nearly 4 million function calls (as a
    498 comparison PostCanvas is *only* making about 220,000 calls).
    499 
    500 #+end_quote
    501 
    502 Note by Ray Johnson:
    503 
    504 #+begin_quote
    505 
    506 Tested Safari 4.0.4 (Win) and Firefox 3.5.5 (Win):
    507 
    508 - Safari 4.0.4 Tiger drawing time = 1.76
    509 - Firefox 3.5.5 Tiger drawing time = 6.945
    510 
    511 I’m on a Dell T7400 Xeon Quad Core 3.0 GHz with 4GB Ram and Vista SP2 32 Bit-and
    512 
    513 #+end_quote
    514 
    515 Firefox throws error about linecap and linejoin not being supported so
    516 these were not used here.  Opera throws an error when running the
    517 PostCanvas example.  The tiger does not look the same as rendered by
    518 [[http://projects.gnome.org/evince/][Evince]] ([[http://poppler.freedesktop.org/][poppler]]/[[http://cairographics.org/][cairo]]) so maybe the linecap and linejoin are
    519 really needed to get proper image as intended.
    520 
    521 It is also interesting to observe that PDF operators and their names
    522 probably came up from shortening/compressing common "user-space"
    523 PostScript operators in PostScript files.  The tiger.eps file was
    524 created in 1990 and contains some "shortcuts" that match PDF operators
    525 standardised later.
    526 
    527 * Drawing with PDF
    528 
    529 PDF is rather complex format.  WPS aims to implement only drawing
    530 operators that can be present in PDF content streams.  The number of
    531 these operators is fixed and limited.  Even though the full PostScript
    532 language is not required, it can be convenient to implement them in
    533 PostScript.
    534 
    535 However, some aspects (e.g. colors) are handled differently in PDF
    536 compared to PostScript and these differences are not addressed by WPS.
    537 I imagine that a supporting server-side solution like [[../ondoc/index.org][OnDoc]] would
    538 provide necessary data (e.g. decomposing PDF into pages and objects,
    539 providing HTML 5 web fonts and font metrics) and WPS would only draw
    540 preprocessed page content.
    541 
    542 Quoting from [[http://www.adobe.com/print/features/psvspdf/index.html][Adobe]]:
    543 
    544 #+begin_quote
    545 A PDF file is actually a PostScript file which has already been
    546 interpreted by a RIP and made into clearly defined objects.
    547 #+end_quote
    548 
    549 ** Heart example
    550 
    551 See also the [[https://developer.mozilla.org/samples/canvas-tutorial/2_6_canvas_beziercurveto.html][original example]] in JavaScript.
    552 
    553 #+html: <canvas id="xheart"></canvas>
    554 #+html: <div id="heart">
    555 #+include "heart.wps" src ps
    556 #+html: </div>
    557 #+html: <script>wps.parse("save (xheart) .setGc", $$("heart"), "restore");</script>
    558 
    559 ** Rectangle example
    560 
    561 TODO find the original example
    562 
    563 #+html: <canvas id="xrect"></canvas>
    564 #+html: <div id="rect">
    565 #+include "rect.wps" src ps
    566 #+html: </div>
    567 #+html: <script>wps.parse("save (xrect) .setGc", $$("rect"), "restore");</script>
    568 
    569 ** Triangles example
    570 
    571 See also the [[https://developer.mozilla.org/samples/canvas-tutorial/2_3_canvas_lineto.html][original example]] in JavaScript.
    572 
    573 #+html: <canvas id="xtriangles"></canvas>
    574 #+html: <div id="triangles">
    575 #+include "triangles.wps" src ps
    576 #+html: </div>
    577 #+html: <script>wps.parse("save (xtriangles) .setGc", $$("triangles"), "restore");</script>
    578 
    579 ** Smile example
    580 
    581 See also the [[http://developer.mozilla.org/samples/canvas-tutorial/2_2_canvas_moveto.html][original example]] in JavaScript.
    582 
    583 #+html: <canvas id="xsmile"></canvas>
    584 #+html: <div id="smile">
    585 #+include "smile.wps" src ps
    586 #+html: </div>
    587 #+html: <script>wps.parse("save (xsmile) .setGc", $$("smile"), "restore");</script>
    588 
    589 ** Star example
    590 
    591 See also the [[http://www.adobe.com/technology/pdfs/presentations/KingPDFTutorial.pdf][original PDF document]] where this example is presented.
    592 
    593 #+html: <canvas id="xstar"></canvas>
    594 #+html: <div id="star">
    595 #+include "star.wps" src ps
    596 #+html: </div>
    597 #+html: <script>wps.parse("save (xstar) .setGc", $$("star"), "restore");</script>
    598 
    599 ** Squares example
    600 
    601 See also the [[https://developer.mozilla.org/samples/canvas-tutorial/5_1_canvas_savestate.html][original example]] in JavaScript.
    602 
    603 #+html: <canvas id="xsquares"></canvas>
    604 #+html: <div id="squares">
    605 #+include "squares.wps" src ps
    606 #+html: </div>
    607 #+html: <script>wps.parse("save (xsquares) .setGc", $$("squares"), "restore");</script>
    608 
    609 ** Two squares example
    610 
    611 See also the [[https://developer.mozilla.org/en/drawing_graphics_with_canvas][original example]] in JavaScript.
    612 
    613 #+html: <canvas id="xsquares2"></canvas>
    614 #+html: <div id="squares2">
    615 #+include "squares2.wps" src ps
    616 #+html: </div>
    617 #+html: <script>wps.parse("save (xsquares2) .setGc", $$("squares2"), "restore");</script>
    618 
    619 * Operators and JavaScript bindings
    620 
    621 WPS implements a minimum core in JavaScript and the rest is
    622 implemented in PostScript itself.
    623 
    624 Many JavaScript data types map quite easily to PostScript data types
    625 so native bindings can be implemented mostly in PostScript via
    626 PostScript dictionaries (JavaScript objects).  [[http://www.whatwg.org/specs/web-apps/current-work/#the-canvas-element][HTML 5 canvas API]]
    627 bindings are quite straightforward.
    628 
    629 ** Native operators
    630 
    631 | category       | in                     | operator              | out                                                 |
    632 |----------------+------------------------+-----------------------+-----------------------------------------------------|
    633 | Trivial        |                        | {{{ps(true)}}}        | true                                                |
    634 |                |                        | {{{ps(false)}}}       | false                                               |
    635 |                |                        | {{{ps(null)}}}        | null                                                |
    636 | Math           | x y                    | {{{ps(sub)}}}         | x-y                                                 |
    637 |                | x y                    | {{{ps(mul)}}}         | x*y                                                 |
    638 |                | x y                    | {{{ps(div)}}}         | x/y                                                 |
    639 |                | x y                    | {{{ps(mod)}}}         | x%y                                                 |
    640 | Stack          |                        | {{{ps(mark)}}}        | mark                                                |
    641 |                |                        | {{{ps(counttomark)}}} | n                                                   |
    642 |                | x y                    | {{{ps(exch)}}}        | y x                                                 |
    643 |                | ...                    | {{{ps(clear)}}}       |                                                     |
    644 |                | x                      | {{{ps(pop)}}}         |                                                     |
    645 |                | x_n ... x_0 n          | {{{ps(index)}}}       | x_n ... x_0 x_n                                     |
    646 |                | x_(n-1) ... x_0 n j    | {{{ps(roll)}}}        | x_((j-1) mod n) ... x_0 ... x_(n-1) ... x_(j mod n) |
    647 |                | x_1 ... x_n n          | {{{ps(copy)}}}        | x_1 ... x_n x_1 ... x_n                             |
    648 | Array          | array                  | {{{ps(length)}}}      | n                                                   |
    649 |                | x_n ... x_0 array      | {{{ps(astore)}}}      | array                                               |
    650 |                | n                      | {{{ps(array)}}}       | array                                               |
    651 | Conditionals   | x y                    | {{{ps(eq)}}}          | bool                                                |
    652 |                | x y                    | {{{ps(lt)}}}          | bool                                                |
    653 | Control        | bool then else         | {{{ps(ifelse)}}}      |                                                     |
    654 |                | n proc                 | {{{ps(repeat)}}}      |                                                     |
    655 |                | i j k proc             | {{{ps(for)}}}         |                                                     |
    656 |                | array/dict/string proc | {{{ps(forall)}}}      |                                                     |
    657 |                | any                    | {{{ps(exec)}}}        |                                                     |
    658 |                | any                    | {{{ps(cvx)}}}         | any                                                 |
    659 |                | any                    | {{{ps(cvlit)}}}       | any                                                 |
    660 | Dictionary     | n                      | {{{ps(dict)}}}        | dict                                                |
    661 |                | dict key               | {{{ps(get)}}}         | any                                                 |
    662 |                | dict key any           | {{{ps(put)}}}         |                                                     |
    663 |                | dict                   | {{{ps(begin)}}}       |                                                     |
    664 |                |                        | {{{ps(end)}}}         |                                                     |
    665 |                |                        | {{{ps(currentdict)}}} | dict                                                |
    666 |                | sym                    | {{{ps(where)}}}       | false / dict true                                   |
    667 | Miscellaneous  |                        | {{{ps(save)}}}        | dstack                                              |
    668 |                | dstack                 | {{{ps(restore)}}}     |                                                     |
    669 |                | any                    | {{{ps(type)}}}        | name                                                |
    670 |                | bool                   | .strictBind           |                                                     |
    671 |                | any                    | {{{ps(bind)}}}        | any                                                 |
    672 | Debugging      | x                      | {{{ps(=)}}}           |                                                     |
    673 |                | x                      | {{{ps(==)}}}          |                                                     |
    674 |                |                        | {{{ps(stack)}}}       |                                                     |
    675 |                |                        | {{{ps(pstack)}}}      |                                                     |
    676 | JavaScript FFI | x_1 ... x_n dict key n | .call                 | any                                                 |
    677 |                |                        | .math                 | Math                                                |
    678 |                |                        | .date                 | (new Date)                                          |
    679 |                |                        | .window               | window                                              |
    680 |                | proc                   | .callback             | callback                                            |
    681 | HTML           | m                      | .minv                 | m^-1                                                |
    682 |                | m_1 m_2                | .mmul                 | (m_1 x m_2)                                         |
    683 |                | x y m                  | .xy                   | x' y'                                               |
    684 |                | r g b                  | .rgb                  | text                                                |
    685 |                | r g b a                | .rgba                 | text                                                |
    686 
    687 Some of the above operators could still be implemented in PostScript
    688 instead of directly in JavaScript.
    689 
    690 ** Core operators
    691 
    692 TODO update
    693 
    694 | category     | in          | operator           | out    |   |
    695 |--------------+-------------+--------------------+--------+---|
    696 | Math         |             | {{{ps(abs)}}}      |        |   |
    697 |              |             | .acos              |        |   |
    698 |              |             | .asin              |        |   |
    699 |              |             | {{{ps(atan)}}}     |        |   |
    700 |              |             | .atan2             |        |   |
    701 |              |             | {{{ps(ceiling)}}}  |        |   |
    702 |              |             | {{{ps(cos)}}}      |        |   |
    703 |              |             | .exp               |        |   |
    704 |              |             | {{{ps(floor)}}}    |        |   |
    705 |              |             | {{{ps(log)}}}      |        |   |
    706 |              |             | .max               |        |   |
    707 |              |             | .min               |        |   |
    708 |              |             | .pow               |        |   |
    709 |              |             | .random            |        |   |
    710 |              |             | {{{ps(rand)}}}     |        |   |
    711 |              |             | {{{ps(round)}}}    |        |   |
    712 |              |             | {{{ps(sin)}}}      |        |   |
    713 |              |             | {{{ps(sqrt)}}}     |        |   |
    714 |              |             | .tan               |        |   |
    715 |              |             | {{{ps(truncate)}}} |        |   |
    716 |              |             | .e                 |        |   |
    717 |              |             | .ln2               |        |   |
    718 |              |             | .ln10              |        |   |
    719 |              |             | .log2e             |        |   |
    720 |              |             | .log10e            |        |   |
    721 |              |             | .pi                |        |   |
    722 |              |             | .sqrt1_2           |        |   |
    723 |              |             | .sqrt2             |        |   |
    724 |              |             | {{{ps(sub)}}}      |        |   |
    725 |              |             | {{{ps(idiv)}}}     |        |   |
    726 |              | num/string  | {{{ps(cvr)}}}      | real   |   |
    727 |              | num/string  | {{{ps(cvi)}}}      | int    |   |
    728 | Stack        | x           | {{{ps(dup)}}}      | x x    |   |
    729 | Conditionals | x y         | {{{ps(ne)}}}       | bool   |   |
    730 |              | x y         | {{{ps(ge)}}}       | bool   |   |
    731 |              | x y         | {{{ps(le)}}}       | bool   |   |
    732 |              | x y         | {{{ps(gt)}}}       | bool   |   |
    733 |              | bool proc   | {{{ps(if)}}}       |        |   |
    734 | HTML 5       | key         | .gget              |        |   |
    735 |              | any key     | .gput              |        |   |
    736 |              | key nargs   | .gcall0            |        |   |
    737 |              | key nargs   | .gcall1            |        |   |
    738 |              |             | .gcanvas           | canvas |   |
    739 |              | w h         | .gdim              |        |   |
    740 |              | x0 y0 x1 y1 | .gbox              |        |   |
    741 
    742 ** HTML 5 canvas methods and attributes
    743 
    744 *** Canvas methods
    745 
    746 |   | in                                           | canvas                | out            | ps                             | pdf         |
    747 |---+----------------------------------------------+-----------------------+----------------+--------------------------------+-------------|
    748 | / |                                              |                       |                | <                              | <           |
    749 |   |                                              | .save                 |                | {{{ps(gsave)}}}                | q           |
    750 |   |                                              | .restore              |                | {{{ps(grestore)}}}             | Q           |
    751 |   | x y                                          | .scale                |                | {{{ps(scale)}}}                | -           |
    752 |   | angle                                        | .rotate               |                | {{{ps(rotate)}}}               | -           |
    753 |   | x y                                          | .translate            |                | {{{ps(translate)}}}            | -           |
    754 |   | m11 m12 m21 m22 dx dy                        | .transform            |                | -                              | cm          |
    755 |   | m11 m12 m21 m22 dx dy                        | .setTransform         |                | -                              | -           |
    756 |   | x0 y0 x1 y1                                  | .createLinearGradient | canvasGradient |                                |             |
    757 |   | x0 y0 r0 x1 y1 r1                            | .createRadialGradient | canvasGradient |                                |             |
    758 |   | image repetition                             | .createPattern        | canvasPattern  |                                |             |
    759 |   | x y w h                                      | .clearRect            |                | {{{ps(rectclip)}}}             |             |
    760 |   | x y w h                                      | .fillRect             |                | {{{ps(rectfill)}}}             |             |
    761 |   | x y w h                                      | .strokeRect           |                | {{{ps(rectstroke)}}}           |             |
    762 |   |                                              | .beginPath            |                | {{{ps(newpath)}}}              | m ?         |
    763 |   |                                              | .closePath            |                | {{{ps(closepath)}}}            | ~ h ? ~ n ? |
    764 |   | x y                                          | .moveTo               |                | {{{ps(moveto)}}}               | m ?         |
    765 |   | x y                                          | .lineTo               |                | {{{ps(lineto)}}}               | l           |
    766 |   | cpx cpy x y                                  | .quadraticCurveTo     |                |                                |             |
    767 |   | cp1x cp1y cp2x cp2y x y                      | .bezierCurveTo        |                |                                | c           |
    768 |   | x1 y1 x2 y2 radius                           | .arcTo                |                | {{{ps(arcto)}}}                |             |
    769 |   | x y w h                                      | .rect                 |                | -                              | ~ re        |
    770 |   | x y radius startAngle endAngle anticlockwise | .arc                  |                | ~ {{{ps(arc)}}} {{{ps(arcn)}}} |             |
    771 |   |                                              | .fill                 |                | {{{ps(fill)}}}                 | ~ f ?       |
    772 |   |                                              | .stroke               |                | {{{ps(stroke)}}}               | S           |
    773 |   |                                              | .clip                 |                | {{{ps(clip)}}}                 | ~ W ?       |
    774 |   | x y                                          | .isPointInPath        | boolean        |                                |             |
    775 |   | text x y                                     | .fillText1            |                |                                |             |
    776 |   | text x y maxWidth                            | .fillText2            |                |                                |             |
    777 |   | text x y                                     | .strokeText1          |                |                                |             |
    778 |   | text x y maxWidth                            | .strokeText2          |                |                                |             |
    779 |   | text                                         | .measureText          | textMetrics    |                                |             |
    780 |   | image dx dy                                  | .drawImage1           |                |                                |             |
    781 |   | image dx dy dw dh                            | .drawImage2           |                |                                |             |
    782 |   | image sx sy sw sh dx dy dw dh                | .drawImage3           |                |                                |             |
    783 |   | imagedata                                    | .createImageData1     | imageData      |                                |             |
    784 |   | sw sh                                        | .createImageData1     | imageData      |                                |             |
    785 |   | sx sy sw sh                                  | .getImageData         | imageData      |                                |             |
    786 |   | imagedata dx dy                              | .putImageData1        |                |                                |             |
    787 |   | imagedata dx dy dirtyX dirtyY dirtyW dirtyH  | .putImageData2        |                |                                |             |
    788 
    789 *** Canvas attributes
    790 
    791 |   | type | attribute                 | values                                             | ps                      | pdf   |
    792 |---+------+---------------------------+----------------------------------------------------+-------------------------+-------|
    793 | / |      | <                         |                                                    | <                       | <     |
    794 |   | num  | .globalAlpha              | (1.0)                                              |                         |       |
    795 |   | str  | .globalCompositeOperation | (source-over)                                      |                         |       |
    796 |   | any  | .strokeStyle              | (black)                                            | ~ {{{ps(setdash)}}} ?   | ~ d ? |
    797 |   | any  | .fillStyle                | (black)                                            |                         |       |
    798 |   | num  | .lineWidth                | (1)                                                | {{{ps(setlinewidth)}}}  | w     |
    799 |   | str  | .lineCap                  | (butt) round square                                | ~ {{{ps(setlinecap)}}}  | J     |
    800 |   | str  | .lineJoin                 | round bevel (miter)                                | ~ {{{ps(setlinejoin)}}} | j     |
    801 |   | num  | .miterLimit               | (10)                                               | {{{ps(setmiterlimit)}}} | M     |
    802 |   | num  | .shadowOffsetX            | (0)                                                |                         |       |
    803 |   | num  | .shadowOffsetY            | (0)                                                |                         |       |
    804 |   | num  | .shadowBlur               | (0)                                                |                         |       |
    805 |   | str  | .shadowColor              | (transparent black)                                |                         |       |
    806 |   | str  | .font                     | (10px sans-serif)                                  |                         |       |
    807 |   | str  | .textAlign                | (start) end left right center                      |                         |       |
    808 |   | str  | .textBaseline             | top hanging middle (alphabetic) ideographic bottom |                         |       |
    809 
    810 *** Other operators
    811 
    812 |   | in                          | canvas        | out | ps | pdf |
    813 |---+-----------------------------+---------------+-----+----+-----|
    814 | / |                             | <             |     |    |     |
    815 |   | canvasGradient offset color | .addColorStop |     |    |     |
    816 
    817 *** Other attributes
    818 
    819 |   | dict             | type             | attribute | values | ps | pdf |
    820 |---+------------------+------------------+-----------+--------+----+-----|
    821 | / |                  |                  | <         |        | <  | <   |
    822 |   | textMetrics      | num              | width     |        |    |     |
    823 |   | imageData        | cnt              | width     |        |    |     |
    824 |   | imageData        | cnt              | heigth    |        |    |     |
    825 |   | imageData        | canvasPixelArray | data      |        |    |     |
    826 |   | canvasPixelArray | cnt              | length    |        |    |     |
    827 
    828 TODO [IndexGetter, IndexSetter] CanvasPixelArray
    829 
    830 ** PostScript operators
    831 
    832 TODO update
    833 
    834 |   | category | in      | operator              | out |
    835 |---+----------+---------+-----------------------+-----|
    836 | / |          | <       | <                     | <   |
    837 |   |          | x y [m] | {{{ps(transform)}}}   | x y |
    838 |   |          | x y [m] | {{{ps(itransform)}}}  | x y |
    839 |   |          | gray    | {{{ps(setgray)}}}     |     |
    840 |   |          | r g b   | {{{ps(setrgbcolor)}}} |     |
    841 |   |          | ???     | {{{ps(setfont)}}} ?   |     |
    842 |   |          |         | {{{ps(clippath)}}} ?  |     |
    843 |   |          | text    | {{{ps(show)}}} ?      |     |
    844 |   |          | x y     | {{{ps(rlineto)}}}     |     |
    845 
    846 ** PDF operators
    847 
    848 |   | category               | operator | meaning                                     |
    849 |---+------------------------+----------+---------------------------------------------|
    850 | / |                        | <        |                                             |
    851 |   | General graphics state | w        | setlinewidth                                |
    852 |   |                        | J        | ~ setlinecap                                |
    853 |   |                        | j        | ~ setlinejoin                               |
    854 |   |                        | M        | setmiterlimit                               |
    855 |   |                        | d        | ~ setdash ?                                 |
    856 |   |                        | ri       |                                             |
    857 |   |                        | i        | 1 .min setflat                              |
    858 |   |                        | gs       |                                             |
    859 |   | Special graphics state | q        | gsave                                       |
    860 |   |                        | Q        | grestore                                    |
    861 |   |                        | cm       | .transform                                  |
    862 |   | Path construction      | m        | moveto                                      |
    863 |   |                        | l        | lineto                                      |
    864 |   |                        | c        | .bezierCurveTo (~ curveto)                  |
    865 |   |                        | v        | currentpoint 6 2 roll c                     |
    866 |   |                        | y        | 2 copy c                                    |
    867 |   |                        | h        | closepath                                   |
    868 |   |                        | re       | ! x y m , x+w y l , x+w y+h l , x y+h l , h |
    869 |   | Path painting          | S        | stroke                                      |
    870 |   |                        | s        | h S                                         |
    871 |   |                        | f        | ~ fill                                      |
    872 |   |                        | F        | f                                           |
    873 |   |                        | f*       | ~ eofill                                    |
    874 |   |                        | B        | f S ! q f Q S                               |
    875 |   |                        | B*       | f* S ! q f* Q S                             |
    876 |   |                        | b        | h B                                         |
    877 |   |                        | b*       | h B*                                        |
    878 |   |                        | n        | ~ newpath                                   |
    879 |   | Clipping paths         | W        | clip                                        |
    880 |   |                        | W*       | eoclip                                      |
    881 |   | Text objects           | BT       | ~ q                                         |
    882 |   |                        | ET       | ~ Q                                         |
    883 |   | Text state             | Tc       |                                             |
    884 |   |                        | Tw       |                                             |
    885 |   |                        | Tz       |                                             |
    886 |   |                        | TL       |                                             |
    887 |   |                        | Tf       |                                             |
    888 |   |                        | Tr       |                                             |
    889 |   |                        | Ts       |                                             |
    890 |   | Text positioning       | Td       |                                             |
    891 |   |                        | TD       |                                             |
    892 |   |                        | Tm       |                                             |
    893 |   |                        | T*       |                                             |
    894 |   | Text showing           | Tj       | ~ show                                      |
    895 |   |                        | TJ       |                                             |
    896 |   |                        | '        |                                             |
    897 |   |                        | "        |                                             |
    898 |   | Type 3 fonts           | d0       | setcharwidth                                |
    899 |   |                        | d1       | setcachedevice                              |
    900 |   | Color                  | CS       |                                             |
    901 |   |                        | cs       |                                             |
    902 |   |                        | SC       |                                             |
    903 |   |                        | SCN      |                                             |
    904 |   |                        | sc       |                                             |
    905 |   |                        | scn      |                                             |
    906 |   |                        | G        | g                                           |
    907 |   |                        | g        | setgray                                     |
    908 |   |                        | RG       | rg                                          |
    909 |   |                        | rg       | setrgbcolor                                 |
    910 |   |                        | K        | k                                           |
    911 |   |                        | k        | setcmykcolor                                |
    912 |   | Shading patterns       | sh       |                                             |
    913 |   | Inline images          | BI       |                                             |
    914 |   |                        | ID       |                                             |
    915 |   |                        | EI       |                                             |
    916 |   | XObjects               | Do       |                                             |
    917 |   | Marked content         | MP       |                                             |
    918 |   |                        | DP       |                                             |
    919 |   |                        | BMC      |                                             |
    920 |   |                        | BDC      |                                             |
    921 |   |                        | EMC      |                                             |
    922 |   | Compatibility          | BX       |                                             |
    923 |   |                        | EX       |                                             |
    924 
    925 * Supported Browsers
    926 
    927 I have tried the following browsers so far:
    928 
    929 |   | Browser | Version                         | Note                               |
    930 |---+---------+---------------------------------+------------------------------------|
    931 | / |         | <                               |                                    |
    932 |   | Firefox | 3.0.11                          | no text drawing, linecap, linejoin |
    933 |   |         | 3.5b4pre                        | ~ same as Firefox 3.0.11?          |
    934 |   |         | 3.5.5 Win                       | reported by Ray Johnson            |
    935 |   | Opera   | 10.00 Beta                      | no text drawing, ugly aliasing     |
    936 |   | Chrome  | 3.0.189.0                       | lines not joined properly          |
    937 |   | Safari  | for Mac Version 4.0.2 (5530.19) | reported by Will King              |
    938 |   |         | 4.0.4 Win                       | reported by Ray Johnson            |
    939 
    940 If you are using a different browser, please [[http://logand.com/contact.html][let me know]] if it works
    941 for you.
    942 
    943 * Limitations and Known Issues
    944 
    945 - many PostScript operators are still to be implemented
    946 - only small fraction of PDF operators has been implemented
    947 - text drawing and font related functionality has not been implemented
    948 
    949 * Changes
    950 
    951 2009-07-15 v0.2
    952 
    953 - Capable of drawing tiger.eps
    954 - JavaScript callbacks and timer added
    955 - bind operator implemented
    956 - Refactored JavaScript code: parser, evaluator and PostScript
    957   interpreter separated
    958 - Improved documentation
    959 
    960 2009-06-30 v0.1
    961 
    962 - Initial version
    963 
    964 * Links
    965 
    966 Discussions about WPS on [[http://www.reddit.com/r/programming/comments/95xll/wps_postscript_and_pdf_interpreter_for_html_5/][reddit]] and [[http://ajaxian.com/archives/wps-postscript-and-pdf-interpreter-for-html-5-canvas][ajaxian]].
    967 
    968 [[http://www.feiri.de/pcan/][PostCanvas]] is a RPN interpreter with many PostScript operators
    969 implemented directly in JavaScript.  It is faster than WPS but not a
    970 "real" PostScript.
    971 
    972 [[http://svgkit.sourceforge.net/][SVGKit]] has a PostScript interpreter on the wish list.
    973 
    974 PostScript is a registered trademark of [[http://www.adobe.com][Adobe Systems Incorporated]].