%!
%%Title:PStab 2.0
%%Creator:Baz
%%CreationDate:14 March 1994
%PStab 2.0 (first full release)
%
%
%   Copyright (C) 1994 Brian Ewins.  All rights reserved.
%
%PStab is distributed in the hope that it will be useful, but
%WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
%to anyone for the consequences of using it or for whether it serves any
%particular purpose or works at all, unless he says so in writing.  Refer
%to the PStab General Public License for full details.
%
%Everyone is granted permission to copy, modify and redistribute
%PStab, but only under the conditions described in the PStab
%General Public License.  A copy of this license is supposed to have been
%given to you along with PStab so you can know your rights and
%responsibilities.  It should be in a file named COPYING.  Among other
%things, the copyright notice and this notice must be preserved on all
%copies.
%
%-------------------------------------------------------------------------------
%Revision History: started Friday, 2 March 1994.
%-------------------------------------------------------------------------------
% 0.1   :Friday:        crap program for lining numbers up in columns
% 0.2   :Sunday:        complete rewrite. chord table program only
% 0.3   :Tuesday:       reorganized into core & styles.
%                       Centred,Columns,ChordTable supplied.
%                       ifcase added to core.
% 0.4   :Wednesday:     rethink on text styles.easier method of entry.
%                       Verses replaced Columns
%                       ChordPro style provides compatability.
%                       ChordTable renamed Chords, now has
%                       huge table of chords you can use.
%                       Handles VM restore properly !!!
%                       added revision history.
% 0.5   :Thursday:      Back to doing tablature (again)
%                       Chords now size-independent
%                       Verbatim made default style.
%                       Verbatim-equivalent defs removed.(shortens prog}
%                       Tablature works! hooray!!!
%                       Many marks allowed in music.
% 0.6   :Friday:        added default case to ifcase
%                       finished Parser. program is still buggy.
% 0.7   ::              Due to a hitch, there was no alpha 7.
% 0.8   :Sunday:        Commented.
%                       fbox made internal size.
%                       Manual doubles as torture test.
%                       manual nearly complete, program debugged
%                       completely except for Tablature.
% 1.0   :Monday:        It all works!!! I'll type last man page & post.
% 1.1   :Tuesday:       the '2nd last page' bug fixed.
%                       fingermark bug fixed. top , bottom, rows set
%                       once only in Tablature.
%                       Parsers now separate part of the program.
%                       Font sizes reduced:I was viewing on A3!!!
%                       Errors in manual fixed.
%                       Only things left to do now are ASCII tab reader,
%                       and more complete manual.
% 1.2:  :Tuesday:       ASCII parser started. buggy. remembered to fix bug
%                       for looooong bars.incidentally, redundant defs were
%                       placed back in at 1.0. ASCII parser works. WOW!
%                       manual to do. ASCII parser now does an excellent
%                       job of it. only two pathogenic cases I can think of.
% 2.0:  :Wednesday:     ASCII parser fixed so it split up 'bars'. this
%                       is truly monumental: ASCII'd tab now looks
%                       _nice_ (!)... almost worth using...
%                       manual finished.
%-------------------------------------------------------------------------------
%-CORE--------------------------------------------------------------------------
%Contains idioms, and other stuff essential for
%the program to break lines, pages, etc.
%-------------------------------------------------------------------------------
%-------Idioms
%-------------------------------------------------------------------------------
/debug {dup 20 string cvs show ( ) show} def
/edef {exch def} def
/max { 2 copy gt {pop} {exch pop} ifelse } def
/min { 2 copy lt {pop} {exch pop} ifelse } def
/inch {72 mul} def
/plus
{ % x y plus is equivalent to x+=y; in C
       1 index cvx exec add def
} def
/minus
{ % x y minus is equivalent to x-=y; in C
       1 index cvx exec exch sub def
} def
/ifcase
{       % syntax: pattern { {match1 {proc1}} {match2 {proc2}} ...} ifcase
       % if match is /default, it will be executed immediately
       % pattern is on the stack at the start of any proc.
       % proc should really remove it.
       exch /pattern edef
       {
               aload pop exch dup
               /default eq {pop pattern exch exec exit} if
               pattern eq {pattern exch exec exit} {pop} ifelse
       } forall
} def
/ToString {3 string cvs} def
/ChooseFont
{       % selects font and sets fbox
       /fbox edef
       findfont fbox scalefont setfont
} def
%-------------------------------------------------------------------------------
%-------Sizes: internal unit is font size.
%------------- also here is code to use real page size.
%-------------------------------------------------------------------------------
/ptof {fbox div exch fbox div exch} def
/ftop {fbox mul exch fbox mul exch} def
/moverel {ftop rmoveto} def
/linerel {ftop rlineto} def
/StringWidth {stringwidth pop fbox div} def
/GetPageMargins
{
       clippath pathbbox       %get page size
       /TopMargin edef
       /RightMargin edef       %put it where I can use it.
       /BottomMargin edef
       /LeftMargin edef
       newpath %stops me strokeing the clipping path
} def
/PageWidth {RightMargin LeftMargin ptof sub} def
/smaller
{
       exch
       {       {/LeftMargin {exch plus}}
               {/RightMargin {exch minus}}
               {/TopMargin {exch minus}}
               {/BottomMargin {exch plus}}
       } ifcase
} def
%-------------------------------------------------------------------------------
%-------Line,Page breaking code.
%-------------------------------------------------------------------------------
/Atom
{
       GetAtom
       currentpoint pop
       hbox fbox mul add
       RightMargin gt
       {NewLine} if
       currentpoint
       vbox fbox mul sub
       BottomMargin lt
       {NewPage} if pop
} def
/NewDoc
{       % begin a document
       % assumes margins have been set
       LeftMargin TopMargin moveto
       Verbatim %default style
       /Home save def
       /StyleDict 128 dict def
       StyleDict begin
} def
/NewStyle
{       % clean up and start again
       Flush end
       currentpoint
       Home restore
       /Home save def
       moveto
       /StyleDict 128 dict def
       StyleDict begin
} def
/EndDoc
{
       Flush end
       showpage        %show what's left.
       Home
       restore
} def
/PageBreak
{       % when the user wants to insert a page break.
       EndDoc NewDoc
} def
%-------------------------------------------------------------------------------
%-------Chord data: a table taken from ChordPro, and put in
%------------------ the simpler format used here. Some mistakes corrected.
%------------------ Chordpro is (c) Mario Dorion & Martin Leclerc
%-------------------------------------------------------------------------------
/Chordata       % this is spinal tap!
[
       [(Ab)[ 4 6 6 5 4 4 ]]           [(Abm)[ 4 6 6 4 4 4 ]]
       [(Ab7)[ -1 -1 1 1 1 2 ]]        [(Abm7)[ -1 -1 4 4 4 4 ]]
       [(Absus)[ -1 -1 1 1 2 4 ]]      [(Ab+)[ -1 -1 2 1 1 0 ]]
       [(Abdim)[ -1 -1 0 1 0 1 ]]      [(Abmaj7)[ -1 -1 1 1 1 3 ]]
       [(A)[ -1 0 2 2 2 0 ]]           [(Am)[ -1 0 2 2 1 0 ]]
       [(A7)[ -1 0 2 0 2 0 ]]          [(Am7)[ -1 0 2 2 1 3 ]]
       [(Asus)[ -1 -1 2 2 3 0 ]]       [(A+)[ -1 0 3 2 2 1 ]]
       [(Adim)[ -1 -1 1 2 1 2 ]]       [(Amaj7)[ -1 0 2 1 2 0 ]]
       [(A#)[ -1 1 3 3 3 1 ]]          [(A#m)[ -1 1 3 3 2 1 ]]
       [(A#7)[ -1 -1 3 3 3 4 ]]        [(A#m7)[ -1 1 3 1 2 1 ]]
       [(A#sus)[ -1 -1 3 3 4 1 ]]      [(A#+)[ -1 -1 0 3 3 2 ]]
       [(A#dim)[ -1 -1 2 3 2 3 ]]      [(A#maj7)[ -1 1 3 2 3 -1 ]]
       [(Bb)[ -1 1 3 3 3 1 ]]          [(Bbm)[ -1 1 3 3 2 1 ]]
       [(Bb7)[ -1 -1 3 3 3 4 ]]        [(Bbm7)[ -1 1 3 1 2 1 ]]
       [(Bbsus)[ -1 -1 3 3 4 1 ]]      [(Bb+)[ -1 -1 0 3 3 2 ]]
       [(Bbdim)[ -1 -1 2 3 2 3 ]]      [(Bbmaj7)[ -1 1 3 2 3 -1 ]]
       [(B)[ -1 2 4 4 4 2 ]]           [(Bm)[ -1 2 4 4 3 2 ]]
       [(B7)[ 0 2 1 2 0 2 ]]           [(Bm7)[ -1 2 4 2 3 2 ]]
       [(Bsus)[ -1 -1 4 4 5 2 ]]       [(B+)[ -1 -1 1 0 0 4 ]]
       [(Bdim)[ -1 -1 0 1 0 1 ]]       [(Bmaj7)[ -1 2 4 3 4 -1 ]]
       [(C)[ 0 3 2 0 1 0 ]]            [(Cm)[ -1 1 5 5 4 3 ]]
       [(C7)[ 0 3 2 3 1 0 ]]           [(Cm7)[ -1 1 5 3 4 3 ]]
       [(Csus)[ -1 -1 3 0 1 3 ]]       [(C+)[ -1 -1 2 1 1 0 ]]
       [(Cdim)[ -1 -1 1 2 1 2 ]]       [(Cmaj7)[ -1 3 2 0 0 0 ]]
       [(C#)[ -1 -1 3 1 2 1 ]]         [(C#m)[ -1 -1 2 1 2 0 ]]
       [(C#7)[ -1 -1 3 4 2 4 ]]        [(C#m7)[ -1 -1 2 4 2 4 ]]
       [(C#sus)[ -1 -1 6 6 7 4 ]]      [(C#+)[ -1 -1 3 2 2 1 ]]
       [(C#dim)[ -1 -1 2 3 2 3 ]]      [(C#maj7)[ -1 4 3 1 1 1 ]]
       [(Db)[ -1 -1 3 1 2 1 ]]         [(Dbm)[ -1 -1 2 1 2 0 ]]
       [(Db7)[ -1 -1 3 4 2 4 ]]        [(Dbm7)[ -1 -1 2 4 2 4 ]]
       [(Dbsus)[ -1 -1 6 6 7 4 ]]      [(Db+)[ -1 -1 3 2 2 1 ]]
       [(Dbdim)[ -1 -1 2 3 2 3 ]]      [(Dbmaj7)[ -1 4 3 1 1 1 ]]
       [(D)[ -1 -1 0 2 3 2 ]]          [(Dm)[ -1 -1 0 2 3 1 ]]
       [(D7)[ -1 -1 0 2 1 2 ]]         [(Dm7)[ -1 -1 0 2 1 1 ]]
       [(Dsus)[ -1 -1 0 2 3 3 ]]       [(D+)[ -1 -1 0 3 3 2 ]]
       [(Ddim)[ -1 -1 0 1 0 1 ]]       [(Dmaj7)[ -1 -1 0 1 1 1 ]]
       [(D#)[ -1 -1 5 3 4 3 ]]         [(D#m)[ -1 -1 4 3 4 2 ]]
       [(D#7)[ -1 -1 1 3 2 3 ]]        [(D#m7)[ -1 -1 1 3 2 2 ]]
       [(D#sus)[ -1 -1 1 3 4 4 ]]      [(D#+)[ -1 -1 1 0 0 4 ]]
       [(D#dim)[ -1 -1 1 2 1 2 ]]      [(D#maj7)[ -1 -1 1 3 3 3 ]]
       [(Eb)[ -1 -1 6 4 5 4 ]]         [(Ebm)[ -1 -1 4 3 4 2 ]]
       [(Eb7)[ -1 -1 1 3 2 3 ]]        [(Ebm7)[ -1 -1 1 3 2 2 ]]
       [(Ebsus)[ -1 -1 1 3 4 4 ]]      [(Eb+)[ -1 -1 1 0 0 4 ]]
       [(Ebdim)[ -1 -1 1 2 1 2 ]]      [(Ebmaj7)[ -1 -1 1 3 3 3 ]]
       [(E)[ 0 2 2 1 0 0 ]]            [(Em)[ 0 2 2 0 0 0 ]]
       [(E7)[ 0 2 2 1 3 0 ]]           [(Em7)[ 0 2 2 0 3 0 ]]
       [(Esus)[ 0 2 2 2 0 0 ]]         [(E+)[ -1 -1 2 1 1 0 ]]
       [(Edim)[ -1 -1 2 3 2 3 ]]       [(Emaj7)[ 0 2 1 1 0 -1 ]]
       [(F)[ 1 3 3 2 1 1 ]]            [(Fm)[ 1 3 3 1 1 1 ]]
       [(F7)[ 1 3 1 2 1 1 ]]           [(Fm7)[ 1 3 1 1 1 1 ]]
       [(Fsus)[ -1 -1 3 3 1 1 ]]       [(F+)[ -1 -1 3 2 2 1 ]]
       [(Fdim)[ -1 -1 0 1 0 1 ]]       [(Fmaj7)[ -1 3 3 2 1 0 ]]
       [(F#)[ 2 4 4 3 2 2 ]]           [(F#m)[ 2 4 4 2 2 2 ]]
       [(F#7)[ -1 -1 4 3 2 0 ]]        [(F#m7)[ -1 -1 2 2 2 2 ]]
       [(F#sus)[ -1 -1 4 4 2 2 ]]      [(F#+)[ -1 -1 4 3 3 2 ]]
       [(F#dim)[ -1 -1 1 2 1 2 ]]      [(F#maj7)[ -1 -1 4 3 2 1 ]]
       [(Gb)[ 2 4 4 3 2 2 ]]           [(Gbm)[ 2 4 4 2 2 2 ]]
       [(Gb7)[ -1 -1 4 3 2 0 ]]        [(Gbm7)[ -1 -1 2 2 2 2 ]]
       [(Gbsus)[ -1 -1 4 4 2 2 ]]      [(Gb+)[ -1 -1 4 3 3 2 ]]
       [(Gbdim)[ -1 -1 1 2 1 2 ]]      [(Gbmaj7)[ -1 -1 4 3 2 1 ]]
       [(G)[ 3 2 0 0 0 3 ]]            [(Gm)[ 3 5 5 3 3 3]]
       [(G7)[ 3 2 0 0 0 1 ]]           [(Gm7)[ 3 5 3 3 3 3 ]]
       [(Gsus)[ -1 -1 0 0 1 3 ]]       [(G+)[ -1 -1 1 0 0 4 ]]
       [(Gdim)[ -1 -1 2 3 2 3 ]]       [(Gmaj7)[ -1 -1 5 4 3 2 ]]
       [(G#)[ 4 6 6 5 4 4 ]]           [(G#m)[ 4 6 6 4 4 4 ]]
       [(G#7)[ -1 -1 1 1 1 2 ]]        [(G#m7)[ -1 -1 4 4 4 4]]
       [(G#sus)[ -1 -1 1 1 2 4 ]]      [(G#+)[ -1 -1 2 1 1 0 ]]
       [(G#dim)[ -1 -1 0 1 0 1 ]]      [(G#maj7)[ -1 -1 1 1 1 3 ]]
] def
%-------------------------------------------------------------------------------
%-PARSERS-----------------------------------------------------------------------
% This new section is to allow different methods of entry in different styles.
% There is a plan to allow the program to parse ASCII tab, and also for
% a possible interface with TabEdit, or other programs.
%-------------------------------------------------------------------------------
/StdParser
{ %the usual parser, for tablature.
       /Parse
       {
               /lastwasname false def
               /barcols 0 def
               { % open ({) loop
                       ({) search exch
                       { % open (,) loop
                               (,) search exch
                               { %loop again
                                       (\n) search exch
                                       { %loop again
                                               ( ) search exch
                                               dup length 0 ne
                                               {TabHandler}
                                               {pop} ifelse
                                               {pop}{exit} ifelse
                                       } loop
                                       {pop}{exit} ifelse
                               } loop
                               {pop WriteColumn}{exit} ifelse
                       } loop
                       {pop}{exit} ifelse
                       (}) search % assumed true
                       pop exch pop
                       (:) search
                       {exch pop TextHandler}
                       {MarkHandler} ifelse
               } loop
               WriteColumn
               0 1 barcols 1 sub
               {
                       BarArray exch get
               } for % get everything back
               barcols array astore
               Atom %  linebreaks do printing.
               /cols barcols plus %more columns...
               /bars 1 plus %and another bar.
       } def
       /WriteColumn
       { %push column to stack; make a fresh column
               lastwasname
               {
                       DefaultColumn DefaultString get
                       dup length array copy
                       CurrentColumn DefaultString
                       3 -1 roll put
               } if
               /lastwasname false def
               BarArray barcols [0 CurrentColumn]  put
               /barcols barcols 1 add def
               /CurrentColumn BlankColumn rows array
               copy def %Do this every time.
       } def
       /IsTextRow
       {       % put text into column.
               CurrentColumn exch 3 -1 roll put
       } def
       /IsRow
       { %if last was row too, fill it in.
               lastwasname
               {
                       DefaultColumn DefaultString get
                       dup length array copy
                       CurrentColumn DefaultString
                       3 -1 roll put
               } if
               /DefaultString edef
               /lastwasname true def
       } def
       /IsFret
       {
               /lastwasname false def
               1 array dup 0 3 index put
               CurrentColumn DefaultString
               2 index put
               DefaultColumn DefaultString
               2 index put
               pop pop
       } def
       /MarkHandler
       {% what to do with special marks.
               CurrentColumn DefaultString get
               0 get % for sensible use, we must set this
               dup length 0 eq % if it's not already set.
               {       pop DefaultColumn DefaultString get
                       0 get /lastwasname false def} if
               exch
               {
                       { (tie) {pop 1 2} }     { (up)  {pop 2 2} }
                       { (down) {pop 3 2} }    { (h)   {pop 4 2} }
                       { (p)   {pop 5 2} }     { (harm){pop 6 2} }
                       { (vib) {pop 8 2} }     { (0)   {pop 7 (0) 3} }
                       { (1)   {pop 7 (1) 3} } { (2)   {pop 7 (2) 3} }
                       { (3)   {pop 7 (3) 3} } { (4)   {pop 7 (4) 3} }
               } ifcase
               array astore CurrentColumn DefaultString 3 -1 roll put
       } def
} def
%-------------------------------------------------------------------------------
%-------ASCIIparser: puts any marks that aren't - into the tab. ignores first
%------------------- and last thing to avoid spurious barlines.
%-------------------------------------------------------------------------------
/ASCIIparser
{
       /WholeBar rows array def
       /WholeLine rows array def
       BlankColumn WholeLine copy /WholeLine def
       BlankColumn WholeBar copy /WholeBar def
       /Parse
       { %parse ascii tab.
               0 1 rows 2 sub
               {
                       exch
                       (\n) search pop %must be true
                       WholeLine exch 5 -1 roll exch put
                       pop
               } for
               %tab now split into rows. now generate columns.
               WholeLine rows 1 sub 3 -1 roll put
               {

                       /barcols 0 def /alldone true def
                       /row 0 def
                       {
                               WholeLine row get (|) search
                               {       /alldone false def
                                       WholeBar row 3 -1 roll put
                                       pop WholeLine row 3 -1 roll put
                               }
                               { WholeBar row 3 -1 roll put
                                       WholeLine row () put
                               } ifelse
                               /row 1 plus
                               row rows eq {exit} if
                       } loop
                       WholeBar
                       {
                               (-) search
                               {       3 1 roll pop pop
                                       dup length 1 sub 0 1 3 -1 roll
                                       {1 index exch (-) 0 get put} for pop
                               }{pop} ifelse
                       } forall
                       {
                               /finished true def
                               /makecolumn false def
                               /row 0 def
                               WholeBar
                               {
                                       (-) search
                                       {
                                               /finished false def
                                               pop 0 ( ) 0 get put
                                               (-) search dup
                                               {4 1 roll exch pop exch pop}
                                               {exch} ifelse
                                               dup dup 4 1 roll length dup 0 ne
                                               {3 -1 roll makecolumn or /makecolumn edef}
                                               {3 -1 roll pop} ifelse
                                               % stick it in the column.
                                               string copy 1 array dup 0 4 -1
                                               roll put
                                               CurrentColumn row
                                               3 -1 roll put
                                               %left with a copy of the string
                                               %fill it with minus signs.
                                               dup length 1 sub 0 1 3 -1 roll
                                               {1 index exch (-) 0 get put} for pop
                                       }
                                       {
                                               pop
                                       } ifelse
                                       /row 1 plus
                               } forall
                               makecolumn
                               { % write a column !
                                       /row 0 def
                                       %needs to blank out later columns.
                                       BarArray barcols [0 CurrentColumn]  put
                                       /barcols barcols 1 add def
                                       /CurrentColumn BlankColumn rows array
                                       copy def %Do this every time.
                               } if
                               finished {exit} if
                       } loop
                       barcols 0 ne
                       {
                               0 1 barcols 1 sub
                               {
                                       BarArray exch get
                               } for % get everything back
                               barcols array astore
                               Atom %  linebreaks do printing.
                               /cols barcols plus %more columns...
                               /bars 1 plus %and another bar.
                       } if
                       alldone {exit} if
               } loop
       } def
} def
%-------------------------------------------------------------------------------
%-STYLES------------------------------------------------------------------------
% In this section I define some styles in which
% the program will print. You can add more, but
% don't change these.
%-------------------------------------------------------------------------------
%-------Tablature: this is an engine for printing tablature
%----------------- in a very general way. It needs some externally
%----------------- supplied information to work.
%-------------------------------------------------------------------------------
/Tablature
{
       /BlankColumn edef
       /rows BlankColumn length def
       /DefaultColumn BlankColumn rows array copy def %One-shot initalise
       /DefaultString 1 def% first string.
       /CurrentColumn BlankColumn rows array copy def %Do this every time.
       /BarArray 128 array def %very generous too!
       % Initialise variables.
       /hbox 0 def /cols 0 def /bars 0 def
       BlankColumn
       0 1 rows 1 sub
       {       dup 2 index exch get type cvlit
               /arraytype eq
               {neg 0.25 add /top edef exit}
               {pop} ifelse
       } for
       rows 1 sub -1 0
       {       dup 2 index exch get type cvlit
               /arraytype eq
               {neg 0.25 add /bottom edef exit}
               {pop} ifelse
       } for
       /bottom top minus
       pop
       /vbox {rows 1 add} def
       /, {Parse} def
       /NewPage {showpage LeftMargin TopMargin moveto} def
       /NewLine
       {
               bars 0 eq
               {       %very long bar. attempts to recover,
                       %but an extra bar line gets drawn.
                       /bars 1 def /cols barcols def
                       /Justify PageWidth hbox sub cols div def
                       Display
                       /bars -1 def /cols barcols neg def /hbox 0 def
               }
               { %/Justify 0 def
                       /hbox barbox minus
                       /Justify PageWidth hbox sub cols div def
                       bars 1 add 1 roll % stick this one on the bottom
                       Display
                       /bars 0 def /cols 0 def /hbox barbox def
               } ifelse
               LeftMargin currentpoint vbox fbox mul sub
               exch pop moveto %usual newline
       } def
       /Flush
       {
               Parse
               /Justify 0 def  % Don't justify
               Display %don't bother cleaning up-we're outta here
               LeftMargin currentpoint vbox fbox mul sub
               exch pop moveto %usual newline
       }       def
       /Display
       {       %Draw bar line, tab all columns, draw bar line..
               gsave
               0 top moverel
               0 bottom linerel
               stroke  grestore
               bars 1 sub -1 0
               {       index % ...not reverse order
                       [2 BlankColumn] TabColumn
                       {       %tab a single column.
                               TabColumn
                       } forall gsave
                       0 top moverel
                       0 bottom linerel
                       stroke grestore
               } for
               bars {pop} repeat
       } def
       /TabColumn
       {       /finger (x) def
               dup 0 get /cbox edef 1 get gsave
               { %forall loop
                       gsave dup type cvlit
                       /arraytype eq
                       {
                               /tie 0 def dup length
                               {{1     {pop}}
                                {2     {pop dup 1 get /tie edef}}
                                {3     {pop dup dup 1 get /tie
                                        edef 2 get /finger edef}}
                               } ifcase
                               0 get dup StringWidth
                               cbox exch sub Justify add /rbox edef
                               show gsave 0 0.25 moverel
                               rbox 0 linerel stroke grestore
                               SpecialMarks
                       }{show} ifelse
                       grestore 0 -1 moverel
               } forall
               grestore cbox Justify add 0 moverel
       } def
       /GetAtom
       {       % needs to set: top,bottom,barcols,barbox,vbox
               dup %leave a copy on the stack.
               dup length 1 add /barcols edef
               /barbox 0 def %1 down!
               /previous [0] def
               /oldsbox 0 def /oldcbox 0 def
               { %forall
                       /cbox 0 def
                       /sbox 0 def
                       0 1 rows 1 sub
                       { % for loop...
                               1 index 1 get% copy of array
                               exch get % get this row
                               dup type cvlit
                               /arraytype eq
                               {
                                       0 get %get the string
                                       StringWidth cbox max /cbox edef
                               }
                               {
                                       StringWidth sbox max /sbox edef
                               } ifelse
                       } for
                       sbox 0 gt
                       {%there is a string
                               oldsbox oldcbox max 2 add
                               dup barbox add /barbox edef
                               previous 0 3 -1 roll put
                               /oldsbox sbox def
                               /oldcbox cbox def
                       }
                       {%there is no string!
                               previous 0 oldcbox 2 add put
                               oldcbox 2 add barbox add /barbox edef
                               /oldsbox oldsbox oldcbox sub 2 sub def
                               /oldcbox cbox def
                       } ifelse
                       /previous edef
               } forall
               previous 0 oldsbox oldcbox max 2 add put
               oldsbox oldcbox max 2 add barbox add /barbox edef
               /hbox barbox plus
       } def
       /SpecialMarks
       {       % produce all the extra bits of notation.
               gsave tie
               {       {1{pop  % code to do ties
                               0.25 0 moverel
                               rbox 0.5 sub 3 div -0.25  ftop
                               rbox 0.5 sub 3 div 2 mul -0.25 ftop
                               rbox 0.5 sub 0 ftop rcurveto stroke}}
                       {2{pop  % code to do slides up
                               0.25 0 moverel
                               rbox 0.5 sub 0.5 linerel stroke}}
                       {3{pop  % code to do slides down
                               0.25 0.5 moverel
                               rbox 0.5 sub -0.5 linerel stroke}}
                       {4{pop  % code to do hammer on
                               0.5 0.5 moverel 0.25 -0.5 linerel
                               0.25 0.5 linerel stroke}}
                       {5{pop  % code to do pull off
                               0.5 0 moverel
                               0.25 0.5 linerel
                               0.25 -0.5 linerel stroke}}
                       {6{pop  % code to do harmonic
                               0.5 0.25 moverel 0.25 0.25 linerel
                               0.25 -0.25 linerel -0.25 -0.25 linerel
                               closepath stroke}}
                       {7{pop  % code to do finger mark (specify fingering)
                               gsave 1 0.25 moverel
                               currentpoint newpath
                               fbox 2 div 0 360 arc
                               gsave 1 setgray fill grestore
                               stroke grestore
                               13 16 div  0 moverel
                               finger ToString show}}
                       {8{pop  % code to do vibrato
                               0.25 0.5 moverel
                               3 {0.125 -0.125 linerel 0.125 0.125 linerel}
                               repeat stroke}}
               } ifcase grestore
       } def
} def
%-------------------------------------------------------------------------------
%-------Verbatim: this is the default style. It's intended for reproducing
%---------------- blocks of text directly, such as an ASCII tab file.
%-------------------------------------------------------------------------------
/Verbatim
{
       /, {Parse} def
       /vbox 1 def
       /Courier 8 ChooseFont
       /Parse
       { % breaks into words.
               {
                       (\n) search exch
                       {
                               ( ) search exch
                               Atom Display
                               {Atom Display}{exit} ifelse
                       } loop
                       {pop} {exit} ifelse
                       NewLine
               } loop
               NewLine
       } def
       /GetAtom {dup StringWidth /hbox edef} def
       /Display {gsave 0 -1 moverel show grestore hbox 0 moverel} def
       /NewLine
       {       LeftMargin currentpoint vbox fbox mul sub
               exch pop moveto
       } def
       /NewPage {showpage LeftMargin TopMargin moveto} def
       /Flush {Parse NewLine} def
} def
%-------------------------------------------------------------------------------
%-------Centred: This style produces centred text. It's intended for
%--------------- making titles.
%-------------------------------------------------------------------------------
/Centred
{
       /, {Parse} def
       /vbox 1 def
       /Helvetica 12 ChooseFont
       /Parse
       { % breaks into words.
               {
                       (\n) search exch
                       {
                               dup
                               ( ) search
                               {
                                       3 1 roll
                                       0 (\267) putinterval
                                       pop
                                       Atom pop dup
                                       (\267) search pop
                                       exch pop
                                       ( ) search
                                       {
                                               pop pop pop
                                               exch pop
                                       }
                                       {
                                               pop pop
                                       } ifelse
                               }
                               {
                                       pop Atom exit
                               } ifelse
                       } loop
                       NewLine pop
                       {pop} {exit} ifelse
               } loop
       } def
       /GetAtom {dup StringWidth /hbox edef} def
       /Display
       {
               dup dup
               {
                       (\267) search
                       {pop 0 ( ) putinterval}
                       {pop exit} ifelse
               } loop
               gsave PageWidth hbox sub 2 div -1 moverel
               show grestore
       }def
       /NewLine
       {
               Display
               LeftMargin currentpoint vbox fbox mul sub
               exch pop moveto
       } def
       /NewPage {showpage LeftMargin TopMargin moveto} def
       /Flush {Parse} def
} def
%-------------------------------------------------------------------------------
%-------Verses: Each block of text is assumed to be a verse, and this
%-------------- style tries to place as many verses horizontally as it can.
%-------------------------------------------------------------------------------
/Verses
{
       /rows 0 def
       /vbox 0 def
       /Times-Roman 8 ChooseFont
       /, {Parse} def
       /Parse
       {       /hbox 0 def
               {
                       (\n) search exch Atom exch
                       {exch pop exch} {exit} ifelse
               } loop
               Display
       } def
       /GetAtom
       {       dup StringWidth
               hbox max /hbox edef
               /rows 1 plus
               rows vbox max /vbox edef
       } def
       /NewLine
       {
               LeftMargin currentpoint vbox 1 add fbox mul sub
               exch pop moveto %usual def ...
               /vbox  rows def % and reset vbox
       } def
       /NewPage
       {
               showpage
               LeftMargin TopMargin moveto %usual def..
               /vbox rows def % and reset vbox
       } def
       /Display
       { % show whole verse
               rows -1 1
               {
                       gsave
                       neg 0 exch moverel
                       show grestore %start popping strings
               } for
               /rows 0 def % reset rows
               hbox 1 add 0  moverel
               /hbox 0 def % reset hbox too.
       } def
       /Flush {Parse NewLine} def
} def
%-------------------------------------------------------------------------------
%-------Chords: This style prints chord diagrams. It 'knows' over
%-------------- 130 chords, but you can use your own.
%-------------------------------------------------------------------------------
/Chords
{
       /, {Parse} def
       /vbox 9 def
       /Times-Roman 8 ChooseFont
       /Parse {Atom Display} def
       /Flush {Parse NewLine} def
       /CentreShow
       {       % centre a string horizontally and vertically
               gsave
               dup StringWidth
               2 div neg -0.3 moverel% horizontal
               show grestore   %back to centre
       } def
       /GetAtom
       {               % first pass, find range, also diagram width.
               dup type cvlit
               /stringtype eq
               {       %find it in Chordata
                       Chordata
                       {
                               dup 0 get 2 index eq
                               {       %found it
                                       1 get
                                       exit % stop search
                               }
                               {pop} ifelse
                       } forall
                       dup type cvlit /stringtype eq
                       { %there was no match
                               [-1 -1 -1 -1 -1 -1] % rescue program
                       } if
               } if % no action otherwise
               dup dup length /cols edef % width of diagram.
               /top -2 def
               /bottom 99 def
               {
                       dup type cvlit
                       /arraytype eq
                       {       0 get   } if
                       %pop % don't want string this time round.
                       dup
                       top max /top edef %highest fret
                       dup
                       0 gt    %ignore 0 fret, unplayed strings
                       {       bottom min /bottom edef }
                       {       pop     }       ifelse
               } forall
               top 5 le
               {       /top 5 def      /bottom 0 def   }
               {       /top bottom 5 add def
                       bottom 1 sub /bottom edef
               } ifelse
               /hbox cols 2 add def
       } def
       /Display
       { %style object.
               gsave
               0.5 -1 moverel
               gsave
               -1 -1 -6
               {
                       1 exch moverel
                       cols 1 sub 0 linerel stroke
                       grestore gsave
               } for
               bottom 0 eq
               {       % gotta draw extra line.
                       1 -0.875 moverel cols 1 sub 0 linerel stroke    % ok that's it.
               } if %label first fret elsewhere...
               grestore gsave 1 -0.5 moverel
               {
                       dup type cvlit
                       /arraytype eq
                       {       aload pop ToString }
                       {       ()      } ifelse
                       exch dup gsave % draw string
                       0 -0.5 moverel 0 -5 linerel stroke
                       grestore gsave 0 le
                       { % something is printed here
                               -1 eq
                               {       (x) CentreShow  }
                               {       (o) CentreShow  }
                               ifelse pop      % if we did that,that's this string done.
                       }
                       { % figure out where to put this string
                               bottom sub neg 0 exch moverel
                               gsave currentpoint newpath
                               0.5 fbox mul 0 360 arc
                               gsave 1 setgray fill grestore
                               stroke grestore CentreShow
                       } ifelse
                       grestore
                       1 0 moverel
               } forall
               bottom 0 ne
               { %   label first fret.
                       0 -0.5 moverel
                       bottom ToString CentreShow
               } if
               grestore
               cols 1 add 2 div -7 moverel
               CentreShow      %write name centred.
               grestore
               cols 2 add 0 moverel
       } def
       /NewLine
       {       LeftMargin currentpoint vbox fbox mul sub
               exch pop moveto
       } def
       /NewPage {showpage LeftMargin TopMargin moveto} def
} def
%-------------------------------------------------------------------------------
%-------ChordPro: This style prints chords above lyrics, a la ChordPro
%-------------------------------------------------------------------------------
/ChordPro
{
       /vbox 3 def
       /Times-Roman 8 ChooseFont
       /, {Parse} def
       /Flush {Parse NewLine} def
       /Parse
       {       %split into atoms
               {
                       (\n) search exch
                       {
                               ([) search
                               {
                                       dup length 0 ne
                                       { Atom Display }
                                       { pop } ifelse
                                       pop
                               }
                               { Atom Display exit } ifelse
                       } loop
                       {pop NewLine} {exit} ifelse
               } loop
       } def
       /GetAtom
       {       % get width attributes of atom
               /hbox 0 def
               dup
               (]) search % contains a chord ?
               {       StringWidth 1 add /hbox edef pop
                       StringWidth hbox max /hbox edef }
               {       StringWidth /hbox edef          } ifelse
       }def
       /Display
       {
               (]) search % contains a chord ?
               {       gsave 0 -1 moverel
                       show grestore pop } if
               gsave 0 -2 moverel
               show grestore hbox 0 moverel
       } def
       /NewLine
       {
               LeftMargin currentpoint vbox fbox mul sub
               exch pop moveto
       } def
       /NewPage {showpage LeftMargin TopMargin moveto} def
} def
%-------------------------------------------------------------------------------
%-------Guitar: an example of a style that uses Tablature. It defines the
%-------------- things that Tablature needs to know, for printing guitar
%-------------- tab with a row of text above & below.
%-------------------------------------------------------------------------------
/Guitar
{       % a tab style. this must come BEFORE 'Tablature'

       /Times-Roman 8 ChooseFont
       StdParser % usual method of entry.
       /TextHandler
       {% what to do with text.depends how many text objects you have.
               {
                       { (chord) {pop 0 IsTextRow} }
                       { (lyric) {pop 7 IsTextRow} }
               } ifcase
       } def

       /TabHandler
       {% what you call the rows that contain strings.
               {
                       { (e) {pop 1 IsRow} }
                       { (B) {pop 2 IsRow} }
                       { (G) {pop 3 IsRow} }
                       { (D) {pop 4 IsRow} }
                       { (A) {pop 5 IsRow} }
                       { (E) {pop 6 IsRow} }
                       { /default {IsFret}}
               } ifcase
       } def
       %template

       [() [()][()][()][()][()][()] ()]
} def
/ASCIItab
{       % a tab style. this must come BEFORE 'Tablature'

       /Times-Roman 8 ChooseFont
       [[()][()][()][()][()][()]]
       Tablature
       ASCIIparser
} def
%-------------------------------------------------------------------------------
%-DOCUMENT SETUP----------------------------------------------------------------
%This section defines the margins of the page, and
%the line thickness used in the program. you may want
%to change some of this.
%-------------------------------------------------------------------------------
GetPageMargins
/LeftMargin 1 inch smaller
/RightMargin 1 inch smaller
/TopMargin 1 inch smaller
/BottomMargin 1 inch smaller
0.1 setlinewidth
/Text
{
       /Times-Roman 8 ChooseFont
} def
%-------------------------------------------------------------------------------
%-DOCUMENT----------------------------------------------------------------------
% Your PStab files are added beyond this point.
%-------------------------------------------------------------------------------