HTML Renderer in Pure PostScript - Source Code
html.ps
%!PS-Adobe-2.0
%%Creator: Terry Burton
%%DocumentPaperSizes: a4
%%EndComments
%%EndProlog
% HTML Renderer in Pure PostScript - Version 2006-01-05
% http://www.terryburton.co.uk/htmlpostscript/
%
% Copyright (c) 2006 Terry Burton - tez@terryburton.co.uk
%
% All commercial rights reserved in full.
%
% TODO:
%
% Ignore DTD <!DOCTYPE asdf>
% Ignore comments <!-- asdf -->
% <ol> - for item number > 9
% <u> & <strike> - Underline and strike
% <pre> - preformatted contents
% Table elements
% Input elements
% Center and right alignment
/state {states states length 1 sub get} bind def
/laststate {states states length 2 sub get} bind def
/newline {
state (marginleft) get
currentpoint exch pop state (fontsize) get 1.1 mul sub
moveto
} bind def
/softnewline {
currentpoint pop state (marginleft) get ne {newline} if
} bind def
/taga {tagu} bind def
/tag_a {tag_u} bind def
/tagaddress {tagi} bind def
/tag_address {tag_i} bind def
/tagb {
state (fontstyle) get dup (-Italic) eq exch (-BoldItalic) eq or {
state (fontstyle) (-BoldItalic) put
} {
state (fontstyle) (-Bold) put
} ifelse
} bind def
/tag_b {} bind def
/tagbig {
state (fontsize) state (fontsize) get 1.5 mul put
} bind def
/tag_big {} bind def
/tagblockquote {
softnewline
state (marginleft) state (marginleft) get state (fontsize) get 1.5 mul add put
currentpoint exch pop state (marginleft) get exch moveto
state (width) state (width) get state (fontsize) get 3 mul sub put
} bind def
/tag_blockquote {
newline
currentpoint exch pop state (marginleft) get exch moveto
} bind def
/tagbr {
newline
} bind def
/tag_br {} bind def
/tagbutton {
% TODO
} bind def
/tag_button {} bind def
/tagcaption {} bind def
% TODO
/tag_caption {} bind def
/tagcenter {
% TODO
} bind def
/tag_center {} bind def
/tagcite {tagi} bind def
/tag_cite {tag_i} bind def
/tagcode {
state (fontface) (Courier) put
} bind def
/tag_code {} bind def
/tagdd {
state (marginleft) state (marginleft) get state (fontsize) get 3 mul add put
currentpoint exch pop state (marginleft) get exch moveto
} bind def
/tag_dd {
newline
currentpoint exch pop laststate (marginleft) get exch moveto
} bind def
/tagdel {tagstrike} bind def
/tag_del {tag_strike} bind def
/tagdir {tagul} bind def
/tag_dir {tag_ul} bind def
/tagdfn {tagi} bind def
/tag_dfn {tag_i} bind def
/tagdl {
softnewline
state (marginleft) state (marginleft) get state (fontsize) get add put
currentpoint exch pop state (marginleft) get exch moveto
} bind def
/tag_dl {
currentpoint exch pop laststate (marginleft) get exch moveto
} bind def
/tagdt {
} bind def
/tag_dt {
newline
} bind def
/tagdiv {tagp} bind def
/tag_div {tag_p} bind def
/tagem {tagi} bind def
/tag_em {tag_i} bind def
/tagfont {
/attribs exch def
attribs (size) known {
state (fontsize) attribs (size) get cvr put
} if
attribs (face) known {
state (fontface) attribs (face) get put
} if
} bind def
/tag_font {} bind def
/tagform {tagp} bind def
/tag_form {tag_p} bind def
/tagh1 {
softnewline
state (fontface) (Times-Roman) put
state (fontsize) 22 put
state (fontstyle) (Bold) put
} bind def
/tag_h1 {
newline
} bind def
/tagh2 {
softnewline
state (fontface) (Times-Roman) put
state (fontsize) 16 put
state (fontstyle) (Bold) put
} bind def
/tag_h2 {
newline
} bind def
/tagh3 {
softnewline
state (fontface) (Times-Roman) put
state (fontsize) 12 put
state (fontstyle) (Bold) put
} bind def
/tag_h3 {
newline
} bind def
/tagh4 {
softnewline
state (fontface) (Times-Roman) put
state (fontsize) 10 put
state (fontstyle) (Bold) put
} bind def
/tag_h4 {
newline
} bind def
/tagh5 {
softnewline
state (fontface) (Times-Roman) put
state (fontsize) 9 put
state (fontstyle) (Bold) put
} bind def
/tag_h5 {
newline
} bind def
/tagh6 {
softnewline
state (fontface) (Times-Roman) put
state (fontsize) 8 put
state (fontstyle) (Bold) put
} bind def
/tag_h6 {
newline
} bind def
/taghr {
softnewline
gsave
currentpoint state (fontsize) get add exch pop state (marginleft) get exch
moveto
1 setlinewidth
state (width) get state (marginleft) get sub 0 rlineto
stroke
grestore
} bind def
/tag_hr {} bind def
/tagi {
state (fontstyle) get dup (-Bold) eq exch (-BoldItalic) eq or {
state (fontstyle) (-BoldItalic) put
} {
state (fontstyle) (-Italic) put
} ifelse
} bind def
/tag_i {} bind def
/tagimg {
% TODO
} bind def
/tag_img {} bind def
/taginput {
% TODO
} bind def
/tag_input {} bind def
/tagins {tagu} bind def
/tag_ins {tag_u} bind def
/tagkbd {tagcode} bind def
/tag_kbd {tag_kbd} bind def
/tagli {
softnewline
laststate (tag) get (ol) eq {
gsave
state (fontsize) get neg 0 rmoveto
state (itemnumber) get ( ) cvs show
grestore
} { % <ul>
gsave
currentpoint state (fontsize) get 3 div add exch state (fontsize) get sub state (fontsize) get 3 div add exch translate
newpath 0 0 state (fontsize) get 6 div 0 360 arc fill
grestore
} ifelse
} bind def
/tag_li {
softnewline
laststate (tag) get (ol) eq {
laststate (itemnumber) laststate (itemnumber) get 1 add put
} if
} bind def
/tagol {
state (itemnumber) 1 put
state (marginleft) state (marginleft) get state (fontsize) get add put
currentpoint exch pop state (marginleft) get exch moveto
} bind def
/tag_ol {
currentpoint exch pop laststate (marginleft) get exch moveto
} bind def
/tagp {
softnewline newline
} bind def
/tag_p {
newline
} bind def
/tagpre {
% TODO
} bind def
/tag_pre {} bind def
/tags {tagstrike} bind def
/tag_s {tag_strike} bind def
/tagsamp {tagcode} bind def
/tag_samp {tag_code} bind def
/tagsmall {
state (fontsize) state (fontsize) get 1.5 div put
} bind def
/tag_small {} bind def
/tagstrike {
% TODO
4} bind def
/tag_strike {} bind def
/tagstrong {tagb} bind def
/tag_strong {tag_b} bind def
/tagsub {
0 state (fontsize) get 5 div neg rmoveto
state (fontsize) state (fontsize) get 1.5 div put
} bind def
/tag_sub {
0 laststate (fontsize) get 5 div rmoveto
} bind def
/tagsup {
0 state (fontsize) get 3 div rmoveto
state (fontsize) state (fontsize) get 1.5 div put
} bind def
/tag_sup {
0 laststate (fontsize) get 3 div neg rmoveto
} bind def
/tagtable {
% TODO
} bind def
/tag_table {} bind def
/tagtbody {
% TODO
} bind def
/tag_tbody {} bind def
/tagtd {
% TODO
} bind def
/tag_td {} bind def
/tagtextarea {
% TODO
} bind def
/tag_textarea {} bind def
/tagtfoot {
% TODO
} bind def
/tag_tfoot {} bind def
/tagth {tagthead} bind def
/tag_th {tag_thead} bind def
/tagthead {
% TODO
} bind def
/tag_thead {} bind def
/tagtr {
% TODO
} bind def
/tag_tr {} bind def
/tagtt {tagcode} bind def
/tag_tt {tag_code} bind def
/tagu {
% TODO
} bind def
/tag_u {} bind def
/tagul {
state (marginleft) state (marginleft) get state (fontsize) get add put
currentpoint exch pop state (marginleft) get exch moveto
} bind def
/tag_ul {
currentpoint exch pop laststate (marginleft) get exch moveto
} bind def
/tagvar {tagi} bind def
/tag_var {tag_i} bind def
/tagxmp {tagpre} bind def
/tag_xmp {tag_pre} bind def
/showword {
/word exch def
% Substitutions
word (<) eq {/word (<) def} if
word (>) eq {/word (>) def} if
word (&) eq {/word (&) def} if
word stringwidth pop state (width) get currentpoint pop sub lt
word ( ) eq or
currentpoint pop state (marginleft) get eq or {
word ( ) ne currentpoint pop state (marginleft) get ne or {
word show
} if
} {
newline word show
} ifelse
} bind def
/render {
/contents exch def
% Set up the font
/thisfont state (fontface) get def
/thisstyle state (fontstyle) get def
/thissize state (fontsize) get def
/styledfont thisfont length thisstyle length add string def
styledfont 0 thisfont putinterval
styledfont thisfont length thisstyle putinterval
styledfont findfont thissize scalefont setfont
{ %loop
contents () eq {exit} if
contents ( ) search {
showword ( ) showword
pop
/contents exch def
} {
showword
/contents () def
} ifelse
} loop
} bind def
/html {
/options exch def
/txt exch def
gsave
currentpoint translate
/states [options] def
state (marginleft) 0 put
% Replace end of lines with spaces
{ % loop
txt (\n) search {
length ( ) txt 3 1 roll putinterval
pop pop
} {
pop exit
} ifelse
} loop
{ % loop
txt (<) search {
% Read up to and including next tag
/content exch def
pop (>) search pop
/tag exch def
pop
/txt exch def
% Plot content
content () ne {content render} if
% Call handler for tag and pass attributes
tag 0 1 getinterval (/) ne { % Open tag
% If tag ends " /" then inject close tag
tag ( /) search {
/tag exch def pop pop
tag ( ) search {
/tagname exch def pop pop
} {
/tagname tag def pop
} ifelse
/pad txt length tagname length add 3 add string def
pad 0 (</) putinterval
pad 2 tagname putinterval
pad tagname length 2 add (>) putinterval
pad tagname length 3 add txt putinterval
/txt pad def
} {
pop
} ifelse
% Create a dictionary from the attributes
/attribs 0 dict def
tag ( ) search {
/tag exch def
pop
{ % loop
(=") search {
/atname exch def
pop
(") search pop
/atvalue exch def
pop
attribs atname atvalue put
dup () eq {pop exit} if
dup length 1 sub 1 exch getinterval
} {
pop exit
} ifelse
} loop
} {
pop
} ifelse
% Extend states by duplicating the last state
/states states length 1 add array dup states exch copy pop def
states dup length 1 sub states dup length 2 sub get dup length dict copy put
state (tag) tag put
/tagfn tag length 3 add string def
tagfn 0 (tag) putinterval
tagfn 3 tag putinterval
currentdict tagfn known {
mark
attribs tagfn cvx exec
cleartomark
} if
} { % Close tag
tag 0 (_) putinterval
/tagfn tag length 3 add string def
tagfn 0 (tag) putinterval
tagfn 3 tag putinterval
currentdict tagfn known {
tagfn cvx exec
} if
% Reduce states by dropping the last state
/states states 0 states length 1 sub getinterval def
} ifelse
} {
render exit
} ifelse
} loop
grestore
} bind def
Back to Project Home