plmarg => 50, # Page number position relative to left margin
prmarg => 0, # Page number position relative to right margin
pymarg => 50, # Page number position relative to bot margin
- startcopyright => 100, # How much above the bottom margin is the
- # copyright notice stuff
+ startcopyright => 75, # How much above the bottom margin is the
+ # copyright notice stuff
bulladj => 12, # How much to indent a bullet paragraph
tocind => 12, # TOC indentation per level
tocpnz => 24, # Width of TOC page number only zone
tocdots => 8, # Spacing between TOC dots
idxspace => 24, # Minimum space between index title and pg#
- idxindent => 32, # How much to indent a subindex entry
+ idxindent => 24, # How much to indent a subindex entry
idxgutter => 24, # Space between index columns
idxcolumns => 2, # Number of index columns
);
# Known paper sizes
%papersizes = (
+ 'a5' => [421, 595], # ISO half paper size
+ 'b5' => [501, 709], # ISO small paper size
'a4' => [595, 842], # ISO standard paper size
'letter' => [612, 792], # US common paper size
'pa4' => [595, 792], # Compromise ("portable a4")
$psbool{$parm} = $true;
} elsif ( $true && defined($psconf{$parm}) ) {
$psconf{$parm} = shift(@ARGV);
+ } elsif ( $parm =~ /^(title|subtitle|year|author|license)$/ ) {
+ $metadata{$parm} = shift(@ARGV);
} else {
die "$0: Unknown option: $arg\n";
}
'code' => $paraskip, 'toc0' => $tocskip,
'toc1' => $tocskip, 'toc2' => $tocskip);
-#
# Custom encoding vector. This is basically the same as
# ISOLatin1Encoding (a level 2 feature, so we dont want to use it),
-# but with a few extra characters thrown in. It is basically a
-# modified Windows 1252 codepage, minus, for now, the euro sign (\200
-# is reserved for euro.)
-#
+# but with the "naked" accents at \200-\237 moved to the \000-\037
+# range (ASCII control characters), and a few extra characters thrown
+# in. It is basically a modified Windows 1252 codepage, minus, for
+# now, the euro sign (\200 is reserved for euro.)
+
@NASMEncoding =
(
- (undef)x32,
- 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
- 'ampersand', 'quoteright', 'parenleft',
- 'parenright', 'asterisk', 'plus', 'comma', 'minus',
- 'period', 'slash', 'zero', 'one', 'two', 'three',
- 'four', 'five', 'six', 'seven', 'eight', 'nine',
- 'colon', 'semicolon', 'less', 'equal', 'greater',
- 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
- 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
- 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
- 'bracketleft', 'backslash', 'bracketright',
- 'asciicircum', 'underscore', 'quoteleft', 'a', 'b',
- 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
- 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
- 'w', 'x', 'y', 'z', 'braceleft', 'bar',
- 'braceright', 'asciitilde', undef,
- undef, 'macron', 'quotesinglbase', 'florin',
- 'quotedblbase', 'ellipsis', 'dagger', 'dbldagger',
- 'circumflex', 'perthousand', 'Scaron', 'guilsinglleft',
- 'OE', 'hungarumlaut', 'Zcaron', 'caron',
- 'ogonek', 'grave', 'quotesingle', 'quotedblleft',
- 'quotedblright', 'bullet', 'endash', 'emdash',
- 'tilde', 'trademark', 'scaron', 'guilsignlright',
- 'oe', 'ring', 'zcaron', 'Ydieresis',
- 'space', 'exclamdown', 'cent', 'sterling',
- 'currency', 'yen', 'brokenbar', 'section',
- 'dieresis', 'copyright', 'ordfeminine',
- 'guillemotleft', 'logicalnot', 'hyphen',
- 'registered', 'macron', 'degree', 'plusminus',
- 'twosuperior', 'threesuperior', 'acute', 'mu',
- 'paragraph', 'periodcentered', 'cedilla',
- 'onesuperior', 'ordmasculine', 'guillemotright',
- 'onequarter', 'onehalf', 'threequarters',
- 'questiondown', 'Agrave', 'Aacute', 'Acircumflex',
- 'Atilde', 'Adieresis', 'Aring', 'AE', 'Ccedilla',
- 'Egrave', 'Eacute', 'Ecircumflex', 'Edieresis',
- 'Igrave', 'Iacute', 'Icircumflex', 'Idieresis',
- 'Eth', 'Ntilde', 'Ograve', 'Oacute', 'Ocircumflex',
- 'Otilde', 'Odieresis', 'multiply', 'Oslash',
- 'Ugrave', 'Uacute', 'Ucircumflex', 'Udieresis',
- 'Yacute', 'Thorn', 'germandbls', 'agrave', 'aacute',
- 'acircumflex', 'atilde', 'adieresis', 'aring', 'ae',
- 'ccedilla', 'egrave', 'eacute', 'ecircumflex',
- 'edieresis', 'igrave', 'iacute', 'icircumflex',
- 'idieresis', 'eth', 'ntilde', 'ograve', 'oacute',
- 'ocircumflex', 'otilde', 'odieresis', 'divide',
- 'oslash', 'ugrave', 'uacute', 'ucircumflex',
- 'udieresis', 'yacute', 'thorn', 'ydieresis'
+ undef, undef, undef, undef, undef, undef, undef, undef, undef, undef,
+ undef, undef, undef, undef, undef, undef, 'dotlessi', 'grave',
+ 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent',
+ 'dieresis', undef, 'ring', 'cedilla', undef, 'hungarumlaut',
+ 'ogonek', 'caron', 'space', 'exclam', 'quotedbl', 'numbersign',
+ 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft',
+ 'parenright', 'asterisk', 'plus', 'comma', 'minus', 'period',
+ 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six',
+ 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal',
+ 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright',
+ 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e',
+ 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
+ 'asciitilde', undef, undef, undef, 'quotesinglbase', 'florin',
+ 'quotedblbase', 'ellipsis', 'dagger', 'dbldagger', 'circumflex',
+ 'perthousand', 'Scaron', 'guilsinglleft', 'OE', undef, 'Zcaron',
+ undef, undef, 'grave', 'quotesingle', 'quotedblleft',
+ 'quotedblright', 'bullet', 'endash', 'emdash', 'tilde', 'trademark',
+ 'scaron', 'guilsignlright', 'oe', undef, 'zcaron', 'Ydieresis',
+ 'space', 'exclamdown', 'cent', 'sterling', 'currency', 'yen',
+ 'brokenbar', 'section', 'dieresis', 'copyright', 'ordfeminine',
+ 'guillemotleft', 'logicalnot', 'hyphen', 'registered', 'macron',
+ 'degree', 'plusminus', 'twosuperior', 'threesuperior', 'acute', 'mu',
+ 'paragraph', 'periodcentered', 'cedilla', 'onesuperior',
+ 'ordmasculine', 'guillemotright', 'onequarter', 'onehalf',
+ 'threequarters', 'questiondown', 'Agrave', 'Aacute', 'Acircumflex',
+ 'Atilde', 'Adieresis', 'Aring', 'AE', 'Ccedilla', 'Egrave', 'Eacute',
+ 'Ecircumflex', 'Edieresis', 'Igrave', 'Iacute', 'Icircumflex',
+ 'Idieresis', 'Eth', 'Ntilde', 'Ograve', 'Oacute', 'Ocircumflex',
+ 'Otilde', 'Odieresis', 'multiply', 'Oslash', 'Ugrave', 'Uacute',
+ 'Ucircumflex', 'Udieresis', 'Yacute', 'Thorn', 'germandbls',
+ 'agrave', 'aacute', 'acircumflex', 'atilde', 'adieresis', 'aring',
+ 'ae', 'ccedilla', 'egrave', 'eacute', 'ecircumflex', 'edieresis',
+ 'igrave', 'iacute', 'icircumflex', 'idieresis', 'eth', 'ntilde',
+ 'ograve', 'oacute', 'ocircumflex', 'otilde', 'odieresis', 'divide',
+ 'oslash', 'ugrave', 'uacute', 'ucircumflex', 'udieresis', 'yacute',
+ 'thorn', 'ydieresis'
);
-$emdash = "\227";
-$endash = "\226";
-$bullet = "\225";
+# Name-to-byte lookup hash
+%charcode = ();
+for ( $i = 0 ; $i < 256 ; $i++ ) {
+ $charcode{$NASMEncoding[$i]} = chr($i);
+}
#
# First, format the stuff coming from the front end into
$ixterms{$ixentry} = [split(/\037/, $data)];
# Look for commas. This is easier done on the string
# representation, so do it now.
- if ( $line =~ /^(.*\,)\037sp\037/ ) {
+ if ( $data =~ /^(.*)\,\037sp\037/ ) {
$ixprefix = $1;
+ $ixprefix =~ s/\037n $//; # Discard possible font change at end
$ixhasprefix{$ixentry} = $ixprefix;
if ( !$ixprefixes{$ixprefix} ) {
$ixcommafirst{$ixentry}++;
}
$ixprefixes{$ixprefix}++;
+ } else {
+ # A complete term can also be used as a prefix
+ $ixprefixes{$data}++;
}
} else {
push(@ptypes, $line);
my($s) = @_;
my(@a) = ();
+ $s =~ s/ \- / $charcode{'endash'} /g; # Replace " - " with en dash
+
while ( $s =~ /^(\s+|\S+)(.*)$/ ) {
push(@a, [0,$1]);
$s = $2;
}
}
}
- push(@l,@wd);
+ push(@l,@xd);
if ( scalar(@l) ) {
push(@ls, [[$type,0,undef,$fontset,0,0],[@l]]); # Final line
}
if ( $type eq 'sp' ) {
push(@para, [$in_e?1:0, ' ']);
} elsif ( $type eq 'da' ) {
- push(@para, [$in_e?1:0, $endash]);
+ push(@para, [$in_e?1:0, $charcode{'endash'}]);
} elsif ( $type eq 'n ' ) {
push(@para, [0, $text]);
$in_e = 0;
}
#
+# This generates a duplicate of a paragraph, stripping anchor
+# tags (-4 and -5)
+#
+sub ps_dup_para_noanchor(@) {
+ my(@i) = @_;
+ my(@o) = ();
+ my($c);
+
+ foreach $c ( @i ) {
+ my @cc = @{$c};
+ push(@o, [@cc]) unless ( $cc[0] == -4 || $cc[0] == -5 );
+ }
+ return @o;
+}
+
+#
# Scan for header paragraphs and fix up their contents;
# also generate table of contents and PDF bookmarks.
#
@tocparas = ([[-5, 'contents'], [0,'Contents']]);
@tocptypes = ('chap');
-@bookmarks = (['title', 0, 'Title Page'], ['contents', 0, 'Contents']);
+@bookmarks = (['title', 0, 'Title'], ['contents', 0, 'Contents']);
%bookref = ();
for ( $i = 0 ; $i < $npara ; $i++ ) {
my $xtype = $ptypes[$i];
push(@bookmarks, $book);
$bookref{$secn} = $book;
- push(@tocparas, [ps_dup_para(@{$paras[$i]})]);
+ push(@tocparas, [ps_dup_para_noanchor(@{$paras[$i]})]);
push(@tocptypes, 'toc0'.' :'.$sech.':'.$chap.' '.$secn.':');
unshift(@{$paras[$i]},
$bookref{$secn} = $book;
$bookref{$pref}->[1]--; # Adjust count for parent node
- push(@tocparas, [ps_dup_para(@{$paras[$i]})]);
+ push(@tocparas, [ps_dup_para_noanchor(@{$paras[$i]})]);
push(@tocptypes,
(($ptype eq 'subh') ? 'toc2':'toc1').' :'.$sech.':'.$secn);
#
# Add copyright notice to the beginning
#
-unshift(@paras, [[0, "\251"], [0, ' '], [0,$metadata{'year'}],
- [0, ' '], string2array($metadata{'author'})],
- [[0, ' ']], [string2array($metadata{'license'})]);
-unshift(@ptypes, 'norm', 'norm', 'norm');
+unshift(@paras,
+ [[0, $charcode{'copyright'}], [0, ' '], [0,$metadata{'year'}],
+ [0, ' '], string2array($metadata{'author'})],
+ [string2array($metadata{'license'})]);
+unshift(@ptypes, 'norm', 'norm');
$npara = scalar(@paras);
my $p;
# Code paragraph; each chunk is a line
foreach $p ( @data ) {
- push(@ls, [[$ptype,0,undef,\%TextFont,0,0],[$p]]);
+ push(@ls, [[$ptype,0,undef,\%BodyFont,0,0],[$p]]);
}
$ls[0]->[0]->[1] |= 1; # First in para
$ls[-1]->[0]->[1] |= 2; # Last in para
# 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);
+ @ls = ps_flow_lines($linewidth, \%BodyFont, $ptype, @data);
} elsif ( $ptype eq 'bull' ) {
- @ls = ps_flow_lines($bullwidth, \%TextFont, $ptype, @data);
+ @ls = ps_flow_lines($bullwidth, \%BodyFont, $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],
+ my $refwidth = ps_width($refname, $BodyFont{fonts}->[0][1],
\@NASMEncoding) *
- ($TextFont{fonts}->[0][0]/1000);
+ ($BodyFont{fonts}->[0][0]/1000);
@ls = ps_flow_lines($linewidth-$ntoc*$psconf{tocind}-
$psconf{tocpnz}-$refwidth,
- \%TextFont, $ptype, @data);
+ \%BodyFont, $ptype, @data);
# Auxilliary data: for the first line, the cross reference symbol
# and the reference name; for all lines but the first, the
my $lvl = substr($ptype,3,1)+0;
@ls = ps_flow_lines($indxwidth-$lvl*$psconf{idxindent},
- \%TextFont, $ptype, @data);
+ \%BodyFont, $ptype, @data);
} else {
die "Unknown para type: $ptype";
}
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)\$";
+ my $nobreakafter = "^(chap|appn|head|subh)\$";
+ # Paragraph types which should never be broken *before*
+ my $nobreakbefore = "^idx[1-9]\$";
# Paragraph types which are set in columnar format
my $columnregexp = "^idx.\$";
} 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 );
+ $broken = 1 if ( $$pinfo[0] !~ /$nobreakafter/o &&
+ $$linfo[0] !~ /$nobreakbefore/o );
} else {
# Middle of paragraph. Break unless we're in a
# no-break paragraph, or the previous line would
foreach $k ( @ixentries ) {
my $n,$i;
my $ixptype = 'idx0';
- my @ixpara = mkparaarray('idx0',@{$ixterms{$k}});
+ my $prefix = $ixhasprefix{$k};
+ my @ixpara = mkparaarray($ixptype,@{$ixterms{$k}});
+ my $commapos = undef;
+
+ if ( defined($prefix) && $ixprefixes{$prefix} > 1 ) {
+ # This entry has a "hanging comma"
+ for ( $i = 0 ; $i < scalar(@ixpara)-1 ; $i++ ) {
+ if ( substr($ixpara[$i]->[1],-1,1) eq ',' &&
+ $ixpara[$i+1]->[1] eq ' ' ) {
+ $commapos = $i;
+ last;
+ }
+ }
+ }
+ if ( defined($commapos) ) {
+ if ( $ixcommafirst{$k} ) {
+ # This is the first entry; generate the
+ # "hanging comma" entry
+ my @precomma = splice(@ixpara,0,$commapos);
+ if ( $ixpara[0]->[1] eq ',' ) {
+ shift(@ixpara); # Discard lone comma
+ } else {
+ # Discard attached comma
+ $ixpara[0]->[1] =~ s/\,$//;
+ push(@precomma,shift(@ixpara));
+ }
+ push(@precomma, [-6,undef]);
+ push(@ixparas, [@precomma]);
+ push(@ixptypes, $ixptype);
+ shift(@ixpara); # Remove space
+ } else {
+ splice(@ixpara,0,$commapos+2);
+ }
+ $ixptype = 'idx1';
+ }
push(@ixpara, [-6,undef]); # Left/right marker
$i = 1; $n = scalar(@{$ps_index_pages{$k}});
print "%!PS-Adobe-3.0\n";
print "%%Pages: $curpage\n";
print "%%BoundingBox: 0 0 ", $psconf{pagewidth}, ' ', $psconf{pageheight}, "\n";
-print "%%Creator: NASM psflow.pl\n";
+print "%%Creator: (NASM psflow.pl)\n";
print "%%DocumentData: Clean7Bit\n";
print "%%DocumentFonts: ", join(' ', keys(%ps_all_fonts)), "\n";
print "%%DocumentNeededFonts: ", join(' ', keys(%ps_all_fonts)), "\n";
print "} def\n";
# Emit fontset definitions
+foreach $font ( keys(%ps_all_fonts) ) {
+ print '/',$font,'-NASM /',$font," nasmenc\n";
+}
+
foreach $fset ( @AllFonts ) {
my $i = 0;
my @zfonts = ();
- my %allfonts = ();
- foreach $font ( @{$fset->{fonts}} ) {
- $allfonts{$font->[1]->{name}}++;
- }
- foreach $font ( keys(%allfonts) ) {
- print '/',$font,'-NASM /',$font," nasmenc\n";
- }
foreach $font ( @{$fset->{fonts}} ) {
print '/', $fset->{name}, $i, ' ',
'/', $font->[1]->{name}, '-NASM findfont ',
print '/', $fset->{name}, ' [', join(' ',@zfonts), "] def\n";
}
-# Emit the result as PostScript. This is *NOT* correct code yet!
+# This is used by the bullet-paragraph PostScript methods
+print "/bullet [",ps_string($charcode{'bullet'}),"] def\n";
+
+# Emit the canned PostScript prologue
open(PSHEAD, "< head.ps");
while ( defined($line = <PSHEAD>) ) {
print $line;
$ps_page = 0;
-# Title page and inner cover
+# Title page
ps_start_page();
-$title = $metadata{'title'};
+$title = $metadata{'title'} || '';
$title =~ s/ \- / $emdash /;
-$pstitle = ps_string($title);
-
-# FIX THIS: This shouldn't be hard-coded like this
-print <<EOF;
-lmarg pageheight 2 mul 3 div moveto
-/Helvetica-Bold-NASM findfont 20 scalefont setfont
-/title linkdest ${pstitle} show
-lmarg pageheight 2 mul 3 div 10 sub moveto
-0 setlinecap 3 setlinewidth
-pagewidth lmarg sub rmarg sub 0 rlineto stroke
-/nasmlogo {
-gsave 1 dict begin
-/sz exch def
-/Courier-Bold findfont sz scalefont setfont
-moveto
-0.85 1.22 scale
-[(-~~..~:\#;L .-:\#;L,.- .~:\#:;.T -~~.~:;. .~:;. )
-( E8+U *T +U\' *T\# .97 *L E8+\' *;T\' *;, )
-( D97 \`*L .97 \'*L \"T;E+:, D9 *L *L )
-( H7 I\# T7 I\# \"*:. H7 I\# I\# )
-( U: :8 *\#+ , :8 T, 79 U: :8 :8 )
-(,\#B. .IE, \"T;E* .IE, J *+;\#:T*\" ,\#B. .IE, .IE,)] {
-currentpoint 3 -1 roll
-sz -0.10 mul 0 3 -1 roll ashow
-sz 0.72 mul sub moveto
-} forall
-end grestore
-} def
-0.6 setgray
-pagewidth 2 div 143 sub
-pageheight 2 div 33 add
-12 nasmlogo
-EOF
+
+$subtitle = $metadata{'subtitle'} || '';
+$subtitle =~ s/ \- / $emdash /;
+
+# Print title
+print "/ti ", ps_string($title), " def\n";
+print "/sti ", ps_string($subtitle), " def\n";
+print "lmarg pageheight 2 mul 3 div moveto\n";
+print "tfont0 setfont\n";
+print "/title linkdest ti show\n";
+print "lmarg pageheight 2 mul 3 div 10 sub moveto\n";
+print "0 setlinecap 3 setlinewidth\n";
+print "pagewidth lmarg sub rmarg sub 0 rlineto currentpoint stroke moveto\n";
+print "hfont1 setfont sti stringwidth pop neg ",
+ -$HeadFont{leading}, " rmoveto\n";
+print "sti show\n";
+
+# Print logo, if there is one
+# FIX: To be 100% correct, this should look for DocumentNeeded*
+# and DocumentFonts in the header of the EPSF and add those to the
+# global header.
+if ( defined($metadata{epslogo}) &&
+ sysopen(EPS, $metadata{epslogo}, O_RDONLY) ) {
+ my @eps = ();
+ my ($bbllx,$bblly,$bburx,$bbury) = (undef,undef,undef,undef);
+ my $line;
+ my $scale = 1;
+ my $maxwidth = $psconf{pagewidth}-$psconf{lmarg}-$psconf{rmarg};
+ my $maxheight = $psconf{pageheight}/3-40;
+ my $width, $height;
+ my $x, $y;
+
+ while ( defined($line = <EPS>) ) {
+ last if ( $line =~ /^%%EOF/ );
+ if ( !defined($bbllx) &&
+ $line =~ /^\%\%BoundingBox\:\s*([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)/i ) {
+ $bbllx = $1+0; $bblly = $2+0;
+ $bburx = $3+0; $bbury = $4+0;
+ }
+ push(@eps,$line);
+ }
+ close(EPS);
+
+ if ( defined($bbllx) ) {
+ $width = $bburx-$bbllx;
+ $height = $bbury-$bblly;
+
+ if ( $width > $maxwidth ) {
+ $scale = $maxwidth/$width;
+ }
+ if ( $height*$scale > $maxheight ) {
+ $scale = $maxheight/$height;
+ }
+
+ $x = ($psconf{pagewidth}-$width*$scale)/2;
+ $y = ($psconf{pageheight}-$height*$scale)/2;
+
+ print "BeginEPSF\n";
+ print $x, ' ', $y, " translate\n";
+ print $scale, " dup scale\n" unless ( $scale == 1 );
+ print -$bbllx, ' ', -$bblly, " translate\n";
+ print "$bbllx $bblly moveto\n";
+ print "$bburx $bblly lineto\n";
+ print "$bburx $bbury lineto\n";
+ print "$bbllx $bbury lineto\n";
+ print "$bbllx $bblly lineto clip newpath\n";
+ print "%%BeginDocument: ",ps_string($metadata{epslogo}),"\n";
+ print @eps;
+ print "%%EndDocument\n";
+ print "EndEPSF\n";
+ }
+}
ps_end_page(0);
+# Emit the rest of the document (page 2 and on)
$curpage = 2;
ps_start_page();
foreach $line ( @pslines ) {