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 (&lt;) eq {/word (<) def} if
    word (&gt;) eq {/word (>) def} if
    word (&amp;) 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