New PS/PDF backend: first attempt at generating the index

This commit is contained in:
H. Peter Anvin 2002-05-15 07:11:21 +00:00
parent f1f1fa7148
commit 916c52e376
3 changed files with 346 additions and 170 deletions

View file

@ -42,7 +42,7 @@ nasmdoc.texi: nasmdoc.dip
new: nasmdocx.ps nasmdocx.pdf
nasmdocx.ps: nasmdoc.dip nasmlogo.eps genps.pl psfonts.ph pswidth.pl head.ps
nasmdocx.ps: nasmdoc.dip nasmlogo.eps genps.pl psfonts.ph pswidth.ph head.ps
$(PERL) $(srcdir)/genps.pl > nasmdocx.ps
nasmdocx.pdf: nasmdocx.ps

View file

@ -27,6 +27,7 @@ require 'pswidth.ph'; # PostScript string width
idxspace => 24, # Minimum space between index title and pg#
idxindent => 32, # How much to indent a subindex entry
idxgutter => 24, # Space between index columns
idxcolumns => 2, # Number of index columns
);
# US-Letter paper
@ -99,9 +100,21 @@ sub int2base($$) {
#
# Take a crossreference name and generate the PostScript name for it.
#
# This hack produces a somewhat smaller PDF...
#%ps_xref_list = ();
#$ps_xref_next = 0;
#sub ps_xref($) {
# my($s) = @_;
# my $q = $ps_xref_list{$s};
# return $q if ( defined($ps_xref_list{$s}) );
# $q = 'X'.int2base($ps_xref_next++, 52);
# $ps_xref_list{$s} = $q;
# return $q;
#}
# Somewhat bigger PDF, but one which obeys # URLs
sub ps_xref($) {
my($s) = @_;
return $s; # Identity transform should be OK for now
return @_[0];
}
#
@ -111,7 +124,8 @@ sub ps_xref($) {
# arrays of pairs: [<size>, <metricref>]
#
# Each line is represented as:
# [ [type,first|last,aux,fontset,page,ypos], [rendering array] ]
# [ [type,first|last,aux,fontset,page,ypos,optional col],
# [rendering array] ]
#
# A space character may be "squeezed" by up to this much
# (as a fraction of the normal width of a space.)
@ -126,17 +140,28 @@ sub ps_flow_lines($$$@) {
my(@l) = (); # Current line
my(@ls) = (); # Accumulated output lines
my(@xd) = (); # Metadata that goes with subsequent text
my $hasmarker = 0; # Line has -6 marker
my $pastmarker = 0; # -6 marker found
# If there is a -6 marker anywhere in the paragraph,
# *each line* output needs to have a -6 marker
foreach $e ( @data ) {
$hasmarker = 1 if ( $$e[0] == -6 );
}
$w = 0;
foreach $e ( @data ) {
if ( $$e[0] < 0 ) {
# Type is metadata. Zero width.
if ( $$e[0] < -1 ) {
# -1 (end anchor) goes with the preceeding text, otherwise
# with the subsequent text
push(@xd, $e);
} else {
if ( $$e[0] == -6 ) {
$pastmarker = 1;
}
if ( $$e[0] == -1 || $$e[0] == -6 ) {
# -1 (end anchor) or -6 (marker) goes with the preceeding
# text, otherwise with the subsequent text
push(@l, $e);
} else {
push(@xd, $e);
}
} else {
my $ew = ps_width($$e[1], $fontset->{fonts}->[$$e[0]][1]) *
@ -152,7 +177,11 @@ sub ps_flow_lines($$$@) {
my $lx = scalar(@l)-1;
my @rm = ();
while ( $lx >= 0 ) {
while ( $lx >= 0 && $l[$lx]->[0] < 0 ) { $lx-- }; # Skip metadata
while ( $lx >= 0 && $l[$lx]->[0] < 0 ) {
# Skip metadata
$pastmarker = 0 if ( $l[$lx]->[0] == -6 );
$lx--;
};
if ( $lx >= 0 ) {
if ( $l[$lx]->[1] eq ' ' ) {
splice(@l, $lx, 1);
@ -165,20 +194,33 @@ sub ps_flow_lines($$$@) {
}
# Now @l contains the stuff to remain on the old line
# If we broke the line inside a link of type -2 or -3,
# then split the link into two.
# If we broke the line inside a link, then split the link
# into two.
my $lkref = undef;
foreach my $lc ( @l ) {
if ( $$lc[0] == -2 || $$lc[0] == -3 ) {
if ( $$lc[0] == -2 || $$lc[0] == -3 || $lc[0] == -7 ) {
$lkref = $lc;
} elsif ( $$lc[0] == -1 ) {
undef $lkref;
}
}
push(@l, [-1,undef]) if ( defined($lkref) );
if ( defined($lkref) ) {
push(@l, [-1,undef]); # Terminate old reference
unshift(@rm, $lkref); # Duplicate reference on new line
}
if ( $hasmarker ) {
if ( $pastmarker ) {
unshift(@rm,[-6,undef]); # New line starts with marker
} else {
push(@l,[-6,undef]); # Old line ends with marker
}
}
push(@ls, [[$type,0,undef,$fontset,0,0],[@l]]);
@l = @rm;
unshift(@l, $lkref) if ( defined($lkref) );
$w = $sw = 0;
# Compute the width of the remainder array
for my $le ( @l ) {
@ -247,6 +289,8 @@ sub ps_merge_chunks(@) {
# -3 begin weblink
# -4 index item anchor
# -5 crossref anchor
# -6 left/right marker (used in the index)
# -7 page link (used in the index)
# 0 normal
# 1 empatic (italic)
# 2 code (fixed spacing)
@ -260,11 +304,11 @@ sub mkparaarray($@) {
my $chunk;
if ( $ptype =~ /^code/ ) {
foreach $chunk ( @{$paras[$i]} ) {
foreach $chunk ( @chunks ) {
push(@para, [2, $chunk]);
}
} else {
foreach $chunk ( @{$paras[$i]} ) {
foreach $chunk ( @chunks ) {
my $type = substr($chunk,0,2);
my $text = substr($chunk,2);
@ -393,10 +437,15 @@ for ( $i = 0 ; $i < $npara ; $i++ ) {
#
# Add TOC to beginning of paragraph list
#
unshift(@paras, @tocparas);
unshift(@ptypes, @tocptypes);
unshift(@paras, @tocparas); undef @tocparas;
unshift(@ptypes, @tocptypes); undef @tocptypes;
$npara = scalar(@paras);
#
# No lines generated, yet.
#
@pslines = ();
#
# Line Auxilliary Information Types
#
@ -407,150 +456,204 @@ $AuxXRef = 4; # Cross reference as a name
$AuxNum = 5; # Number
#
# Break or convert paragraphs into lines.
# Break or convert paragraphs into lines, and push them
# onto the @pslines array.
#
@pslines = ();
@pslinedata = ();
$linewidth = $psconf{pagewidth}-$psconf{lmarg}-$psconf{rmarg};
$bullwidth = $linewidth-$psconf{bulladj};
sub ps_break_lines($$) {
my ($paras,$ptypes) = @_;
for ( $i = 0 ; $i < $npara ; $i++ ) {
my $xtype = $ptypes[$i];
my $ptype = substr($xtype,0,4);
my @data = @{$paras[$i]};
my @ls = ();
if ( $ptype eq 'code' ) {
my $p;
# Code paragraph; each chunk is a line
foreach $p ( @data ) {
push(@ls, [[$ptype,0,undef,\%TextFont,0,0],[$p]]);
}
$ls[0]->[0]->[1] |= 1; # First in para
$ls[-1]->[0]->[1] |= 2; # Last in para
} elsif ( $ptype eq 'chap' || $ptype eq 'appn' ) {
# Chapters are flowed normally, but in an unusual font
@ls = ps_flow_lines($linewidth, \%ChapFont, $ptype, @data);
} elsif ( $ptype eq 'head' || $ptype eq 'subh' ) {
unless ( $xtype =~ /^\S+ (\S+) :(.*)$/ ) {
die "Bad para";
}
my $secn = $1;
my $sech = $2;
my $font = ($ptype eq 'head') ? \%HeadFont : \%SubhFont;
@ls = ps_flow_lines($linewidth, $font, $ptype, @data);
# We need the heading number as auxillary data
$ls[0]->[0]->[2] = [[$AuxStr,$secn]];
} elsif ( $ptype eq 'norm' ) {
@ls = ps_flow_lines($linewidth, \%TextFont, $ptype, @data);
} elsif ( $ptype eq 'bull' ) {
@ls = ps_flow_lines($bullwidth, \%TextFont, $ptype, @data);
} elsif ( $ptype =~ /^toc/ ) {
unless ( $xtype =~/^\S+ :([^:]*):(.*)$/ ) {
die "Bad para";
}
my $xref = $1;
my $refname = $2.' ';
my $ntoc = substr($ptype,3,1)+0;
my $refwidth = ps_width($refname, $TextFont{fonts}->[0][1]) *
($TextFont{fonts}->[0][0]/1000);
@ls = ps_flow_lines($linewidth-$ntoc*$psconf{tocind}-
$psconf{tocpnz}-$refwidth,
\%TextFont, $ptype, @data);
my $linewidth = $psconf{pagewidth}-$psconf{lmarg}-$psconf{rmarg};
my $bullwidth = $linewidth-$psconf{bulladj};
my $indxwidth = ($linewidth-$psconf{idxgutter})/$psconf{idxcolumns}
-$psconf{idxspace};
# Auxilliary data: for the first line, the cross reference symbol
# and the reference name; for all lines but the first, the
# reference width; and for the last line, the page number
# as a string.
my $nl = scalar(@ls);
$ls[0]->[0]->[2] = [[$AuxStr,$refname], [$AuxXRef,$xref]];
for ( $j = 1 ; $j < $nl ; $j++ ) {
$ls[$j]->[0]->[2] = [[$AuxNum,$refwidth]];
my $npara = scalar(@{$paras});
my $i;
for ( $i = 0 ; $i < $npara ; $i++ ) {
my $xtype = $ptypes->[$i];
my $ptype = substr($xtype,0,4);
my @data = @{$paras->[$i]};
my @ls = ();
if ( $ptype eq 'code' ) {
my $p;
# Code paragraph; each chunk is a line
foreach $p ( @data ) {
push(@ls, [[$ptype,0,undef,\%TextFont,0,0],[$p]]);
}
$ls[0]->[0]->[1] |= 1; # First in para
$ls[-1]->[0]->[1] |= 2; # Last in para
} elsif ( $ptype eq 'chap' || $ptype eq 'appn' ) {
# Chapters are flowed normally, but in an unusual font
@ls = ps_flow_lines($linewidth, \%ChapFont, $ptype, @data);
} elsif ( $ptype eq 'head' || $ptype eq 'subh' ) {
unless ( $xtype =~ /^\S+ (\S+) :(.*)$/ ) {
die "Bad para";
}
my $secn = $1;
my $sech = $2;
my $font = ($ptype eq 'head') ? \%HeadFont : \%SubhFont;
@ls = ps_flow_lines($linewidth, $font, $ptype, @data);
# We need the heading number as auxillary data
$ls[0]->[0]->[2] = [[$AuxStr,$secn]];
} elsif ( $ptype eq 'norm' ) {
@ls = ps_flow_lines($linewidth, \%TextFont, $ptype, @data);
} elsif ( $ptype eq 'bull' ) {
@ls = ps_flow_lines($bullwidth, \%TextFont, $ptype, @data);
} elsif ( $ptype =~ /^toc/ ) {
unless ( $xtype =~/^\S+ :([^:]*):(.*)$/ ) {
die "Bad para";
}
my $xref = $1;
my $refname = $2.' ';
my $ntoc = substr($ptype,3,1)+0;
my $refwidth = ps_width($refname, $TextFont{fonts}->[0][1]) *
($TextFont{fonts}->[0][0]/1000);
@ls = ps_flow_lines($linewidth-$ntoc*$psconf{tocind}-
$psconf{tocpnz}-$refwidth,
\%TextFont, $ptype, @data);
# Auxilliary data: for the first line, the cross reference symbol
# and the reference name; for all lines but the first, the
# reference width; and for the last line, the page number
# as a string.
my $nl = scalar(@ls);
$ls[0]->[0]->[2] = [[$AuxStr,$refname], [$AuxXRef,$xref]];
for ( $j = 1 ; $j < $nl ; $j++ ) {
$ls[$j]->[0]->[2] = [[$AuxNum,$refwidth]];
}
push(@{$ls[$nl-1]->[0]->[2]}, [$AuxPageStr,$xref]);
} elsif ( $ptype =~ /^idx/ ) {
my $lvl = substr($ptype,3,1)+0;
@ls = ps_flow_lines($indxwidth-$lvl*$psconf{idxindent},
\%TextFont, $ptype, @data);
} else {
die "Unknown para type: $ptype";
}
push(@{$ls[$nl-1]->[0]->[2]}, [$AuxPageStr,$xref]);
} else {
die "Unknown para type: $ptype";
# Merge adjacent identical chunks
foreach $l ( @ls ) {
@{$$l[1]} = ps_merge_chunks(@{$$l[1]});
}
push(@pslines,@ls);
}
# Merge adjacent identical chunks
foreach $l ( @ls ) {
@{$$l[1]} = ps_merge_chunks(@{$$l[1]});
}
push(@pslines,@ls);
}
# Break the main body text into lines.
ps_break_lines(\@paras, \@ptypes);
#
# Break lines in to pages
#
# Paragraph types which should never be broken
$nobreakregexp = "^(chap|appn|head|subh|toc.)\$";
# Paragraph types which are heading (meaning they should not be broken
# immediately after)
$headingregexp = "^(chap|appn|head|subh)\$";
$curpage = 3; # First text page is page 3
$curypos = 0; # Space used on this page
undef $columnstart; # Not outputting columnar text
undef $curcolumn; # Current column
$nlines = scalar(@pslines);
$upageheight = $psconf{pageheight}-$psconf{topmarg}-$psconf{botmarg};
for ( $i = 0 ; $i < $nlines ; $i++ ) {
my $linfo = $pslines[$i]->[0];
if ( ($$linfo[0] eq 'chap' || $$linfo[0] eq 'appn')
&& ($$linfo[1] & 1) ) {
# First line of a new chapter heading. Start a new line.
$curpage++ if ( $curypos > 0 );
$curypos = $chapstart;
}
#
# This formats lines inside the global @pslines array into pages,
# updating the page and y-coordinate entries. Start at the
# $startline position in @pslines and go to but not including
# $endline. The global variables $curpage, $curypos, $columnstart
# and $curcolumn are updated appropriately.
#
sub ps_break_pages($$) {
my($startline, $endline) = @_;
# Adjust position by the appropriate leading
$curypos += $$linfo[3]->{leading};
# Paragraph types which should never be broken
my $nobreakregexp = "^(chap|appn|head|subh|toc.|idx.)\$";
# Paragraph types which are heading (meaning they should not be broken
# immediately after)
my $headingregexp = "^(chap|appn|head|subh)\$";
# Paragraph types which are set in columnar format
my $columnregexp = "^idx.\$";
# Record the page and y-position
$$linfo[4] = $curpage;
$$linfo[5] = $curypos;
my $upageheight = $psconf{pageheight}-$psconf{topmarg}-$psconf{botmarg};
if ( $curypos > $upageheight ) {
# We need to break the page before this line.
my $broken = 0; # No place found yet
while ( !$broken && $pslines[$i]->[0]->[4] == $curpage ) {
my $linfo = $pslines[$i]->[0];
my $pinfo = $pslines[$i-1]->[0];
my $i;
if ( $$linfo[1] == 2 ) {
# This would be an orphan, don't break.
} elsif ( $$linfo[1] & 1 ) {
# Sole line or start of paragraph. Break unless
# the previous line was part of a heading.
$broken = 1 if ( $$pinfo[0] !~ /$headingregexp/o );
} else {
# Middle of paragraph. Break unless we're in a
# no-break paragraph, or the previous line would
# end up being a widow.
$broken = 1 if ( $$linfo[0] !~ /$nobreakregexp/o &&
$$pinfo[1] != 1 );
}
$i--;
for ( $i = $startline ; $i < $endline ; $i++ ) {
my $linfo = $pslines[$i]->[0];
if ( ($$linfo[0] eq 'chap' || $$linfo[0] eq 'appn' )
&& ($$linfo[1] & 1) ) {
# First line of a new chapter heading. Start a new page.
undef $columnstart;
$curpage++ if ( $curypos > 0 || defined($columnstart) );
$curypos = $chapstart;
} elsif ( defined($columnstart) && $$linfo[0] !~ /$columnregexp/o ) {
undef $columnstart;
$curpage++;
$curypos = 0;
}
die "Nowhere to break page $curpage\n" if ( !$broken );
# Now $i should point to line immediately before the break, i.e.
# the next paragraph should be the first on the new page
$curpage++;
$curypos = 0;
next;
}
# Add end of paragraph skip
if ( $$linfo[1] & 2 ) {
$curypos += $skiparray{$$linfo[0]};
if ( $$linfo[0] =~ /$columnregexp/o && !defined($columnstart) ) {
$columnstart = $curypos;
$curcolumn = 0;
}
# Adjust position by the appropriate leading
$curypos += $$linfo[3]->{leading};
# Record the page and y-position
$$linfo[4] = $curpage;
$$linfo[5] = $curypos;
$$linfo[6] = $curcolumn if ( defined($columnstart) );
if ( $curypos > $upageheight ) {
# We need to break the page before this line.
my $broken = 0; # No place found yet
while ( !$broken && $pslines[$i]->[0]->[4] == $curpage ) {
my $linfo = $pslines[$i]->[0];
my $pinfo = $pslines[$i-1]->[0];
if ( $$linfo[1] == 2 ) {
# This would be an orphan, don't break.
} elsif ( $$linfo[1] & 1 ) {
# Sole line or start of paragraph. Break unless
# the previous line was part of a heading.
$broken = 1 if ( $$pinfo[0] !~ /$headingregexp/o );
} else {
# Middle of paragraph. Break unless we're in a
# no-break paragraph, or the previous line would
# end up being a widow.
$broken = 1 if ( $$linfo[0] !~ /$nobreakregexp/o &&
$$pinfo[1] != 1 );
}
$i--;
}
die "Nowhere to break page $curpage\n" if ( !$broken );
# Now $i should point to line immediately before the break, i.e.
# the next paragraph should be the first on the new page
if ( defined($columnstart) &&
++$curcolumn < $psconf{idxcolumns} ) {
# We're actually breaking text into columns, not pages
$curypos = $columnstart;
} else {
undef $columnstart;
$curpage++;
$curypos = 0;
}
next;
}
# Add end of paragraph skip
if ( $$linfo[1] & 2 ) {
$curypos += $skiparray{$$linfo[0]};
}
}
}
ps_break_pages(0,$nlines); # Break the main text body into pages
#
# Find the page number of all the indices
#
%ps_xref_page = (); # Crossref anchor pages
%ps_index_pages = (); # Index item pages
$nlines = scalar(@pslines);
for ( $i = 0 ; $i < $nlines ; $i++ ) {
my $linfo = $pslines[$i]->[0];
foreach my $c ( @{$pslines[$i]->[1]} ) {
@ -569,6 +672,48 @@ for ( $i = 0 ; $i < $nlines ; $i++ ) {
}
}
#
# Emit index paragraphs
#
$startofindex = scalar(@pslines);
@ixparas = ([[-5,'index'],[0,'Index']]);
@ixptypes = ('chap');
foreach $k ( @ixentries ) {
my $n,$i;
my $ixptype = 'idx0';
my @ixpara = mkparaarray('idx0',@{$ixterms{$k}});
push(@ixpara, [-6,undef]); # Left/right marker
$i = 1; $n = scalar(@{$ps_index_pages{$k}});
foreach $p ( @{$ps_index_pages{$k}} ) {
if ( $i++ == $n ) {
push(@ixpara,[-7,$p],[0,"$p"],[-1,undef]);
} else {
push(@ixpara,[-7,$p],[0,"$p,"],[-1,undef],[0,' ']);
}
}
push(@ixparas, [@ixpara]);
push(@ixptypes, $ixptype);
}
#
# Flow index paragraphs into lines
#
ps_break_lines(\@ixparas, \@ixptypes);
#
# Format index into pages
#
$nlines = scalar(@pslines);
ps_break_pages($startofindex, $nlines);
#
# Push index onto bookmark list
#
push(@bookmarks, ['index', 0, 'Index']);
# Get the list of fonts used
%ps_all_fonts = ();
foreach $fset ( @AllFonts ) {
@ -655,6 +800,7 @@ sub ps_start_page() {
print "%%BeginPageSetup\n";
print "save\n";
print "%%EndPageSetup\n";
print '/', $ps_page, " pa\n";
}
# End a PostScript page
@ -711,19 +857,19 @@ ps_end_page(0);
$curpage = 3;
ps_start_page();
for ( $i = 0 ; $i < $nlines ; $i++ ) {
my $linfo = $pslines[$i]->[0];
foreach $line ( @pslines ) {
my $linfo = $line->[0];
if ( $$linfo[4] != $curpage ) {
ps_end_page(1);
ps_start_page();
$curpage = $$linfo[4];
ps_end_page(1);
ps_start_page();
$curpage = $$linfo[4];
}
print '[';
my $curfont = 0;
foreach my $c ( @{$pslines[$i]->[1]} ) {
if ( $$c[0] >= 0 ) {
foreach my $c ( @{$line->[1]} ) {
if ( $$c[0] >= 0 ) {
if ( $curfont != $$c[0] ) {
print ($curfont = $$c[0]);
}
@ -738,6 +884,11 @@ for ( $i = 0 ; $i < $nlines ; $i++ ) {
# Index anchor -- ignore
} elsif ( $$c[0] == -5 ) {
print '{/',$$c[1],' xa}'; #xref anchor
} elsif ( $$c[0] == -6 ) {
print ']['; # Start a new array
$curfont = 0;
} elsif ( $$c[0] == -7 ) {
print '{/',$$c[1],' pl}'; # page link
} else {
die "Unknown annotation";
}
@ -761,16 +912,9 @@ for ( $i = 0 ; $i < $nlines ; $i++ ) {
}
}
print ($psconf{pageheight}-$psconf{topmarg}-$$linfo[5]);
print ' ', $$linfo[6] if ( defined($$linfo[6]) );
print ' ', $$linfo[0].$$linfo[1], "\n";
}
ps_end_page(1);
print "%%EOF\n";
# Emit index as comments for now
foreach $k ( sort(keys(%ps_index_pages)) ) {
print "% ",$k, ' ', join(',', @{$ps_index_pages{$k}});
print ' [prefix]' if ( defined($ixhasprefix{$k}) );
print ' [first]' if ( defined($ixcommafirst{$k}) );
print "\n";
}

View file

@ -26,7 +26,7 @@
/max { 2 copy lt { exch } if pop } def
/lkbegun 0 def
/lkisuri false def
/lktype null def
/lkury 0 def
/lkurx 0 def
/lklly 0 def
@ -35,22 +35,28 @@
/lkymarg 1 def % Extra space for link in y dir
/lktarget () def
% target --
% target type --
/linkbegin {
userdict begin
/lkbegun 1 def
/lkisuri false def
/lktype exch def
/lktarget exch def
end
} def
% target --
/linkbegindest {
/Dest linkbegin
} def
% uristring --
/linkbeginuri {
userdict begin
/lkbegun 1 def
/lkisuri true def
/lktarget exch def
end
/URI linkbegin
} def
% pageno --
/linkbeginpage {
/Page linkbegin
} def
% string spacepadding --
@ -79,12 +85,17 @@
% --
/linkend {
userdict begin
[ lkisuri {
/Action
% << .. >> would be languagelevel 2 :(
2 dict dup /Subtype /URI put dup /URI lktarget put
[ lktype /Dest eq {
/Dest lktarget
} {
/Dest lktarget
lktype /URI eq {
/Action
% << .. >> would be languagelevel 2 :(
2 dict dup /Subtype /URI put dup /URI lktarget put
} {
/Dest lktarget
% /Page lktarget /View [ /XYZ 0 pageheight null ]
} ifelse
} ifelse
/Border [0 0 0]
/Rect [ lkllx lkxmarg sub
@ -278,27 +289,46 @@
/toc00 { tocx0 exch moveto 0 rmoveto tfont showstream } def
/toc01 { tocx0 exch moveto
linkbegin tfont0 setfont 0 linkshow tfont showstream } def
linkbegindest tfont0 setfont 0 linkshow tfont showstream } def
/toc02 { tocx0 exch moveto 3 1 roll
0 rmoveto tfont showstream tocpn } def
/toc03 { tocx0 exch moveto 4 1 roll
linkbegin tfont0 setfont 0 linkshow tfont showstream tocpn } def
linkbegindest tfont0 setfont 0 linkshow tfont showstream tocpn } def
/toc10 { tocx1 exch moveto 0 rmoveto tfont showstream } def
/toc11 { tocx1 exch moveto
linkbegin tfont0 setfont 0 linkshow tfont showstream } def
linkbegindest tfont0 setfont 0 linkshow tfont showstream } def
/toc12 { tocx1 exch moveto 3 1 roll
0 rmoveto tfont showstream tocpn } def
/toc13 { tocx1 exch moveto 4 1 roll
linkbegin tfont0 setfont 0 linkshow tfont showstream tocpn } def
linkbegindest tfont0 setfont 0 linkshow tfont showstream tocpn } def
/toc20 { tocx2 exch moveto 0 rmoveto tfont showstream } def
/toc21 { tocx2 exch moveto
linkbegin tfont0 setfont 0 linkshow tfont showstream } def
linkbegindest tfont0 setfont 0 linkshow tfont showstream } def
/toc22 { tocx2 exch moveto 3 1 roll
0 rmoveto tfont showstream tocpn } def
/toc23 { tocx2 exch moveto 4 1 roll
linkbegin tfont0 setfont 0 linkshow tfont showstream tocpn } def
linkbegindest tfont0 setfont 0 linkshow tfont showstream tocpn } def
% Spacing between index columns
/indexcolumn pagewidth lmarg sub rmarg sub idxgutter add idxcolumns div def
% Width of an individual index column
/indexcolwid indexcolumn idxgutter sub def
/idx03 {
2 dict begin
indexcolumn mul lmarg add
/x exch def /y exch def x y moveto
exch tfont showstream
dup tfont streamwidth
x indexcolwid add exch sub exch pop y moveto
tfont showstream
end
} def
/idx00 {idx03} def
/idx01 {idx03} def
/idx02 {idx03} def
%
% Page numbers
@ -315,8 +345,10 @@
% Functions invoked during parsing
%
/xa { linkdest } def
/xl { linkbegin } def
/pa { 0 pageheight moveto linkdest } def
/xl { linkbegindest } def
/wl { linkbeginuri } def
/pl { linkbeginpage } def
/el { linkend } def
%