3 # Format the documentation as PostScript
6 require 'psfonts.ph'; # The fonts we want to use
7 require 'pswidth.ph'; # PostScript string width
10 # PostScript configurables; these values are also available to the
11 # PostScript code itself
14 pagewidth => 595, # Page width in PostScript points
15 pageheight => 792, # Page height in PostScript points
16 lmarg => 100, # Left margin in PostScript points
17 rmarg => 50, # Right margin in PostScript points
18 topmarg => 100, # Top margin in PostScript points
19 botmarg => 100, # Bottom margin in PostScript points
20 plmarg => 50, # Page number position relative to left margin
21 prmarg => 0, # Page number position relative to right margin
22 pymarg => 50, # Page number position relative to bot margin
23 bulladj => 12, # How much to indent a bullet paragraph
24 tocind => 12, # TOC indentation per level
25 tocpnz => 24, # Width of TOC page number only zone
26 tocdots => 8, # Spacing between TOC dots
27 idxspace => 24, # Minimum space between index title and pg#
28 idxindent => 32, # How much to indent a subindex entry
29 idxgutter => 24, # Space between index columns
30 idxcolumns => 2, # Number of index columns
34 # $psconf{pagewidth} = 612; $psconf{pageheight} = 792;
36 # $psconf{pagewidth} = 595; $psconf{pageheight} = 842;
38 $paraskip = 6; # Space between paragraphs
39 $chapstart = 30; # Space before a chapter heading
40 $chapskip = 24; # Space after a chapter heading
41 $tocskip = 6; # Space between TOC entries
43 # Configure post-paragraph skips for each kind of paragraph
44 %skiparray = ('chap' => $chapskip, 'appn' => $chapstart,
45 'head' => $paraskip, 'subh' => $paraskip,
46 'norm' => $paraskip, 'bull' => $paraskip,
47 'code' => $paraskip, 'toc0' => $tocskip,
48 'toc1' => $tocskip, 'toc2' => $tocskip);
51 # First, format the stuff coming from the front end into
52 # a cleaner representation
54 open(PARAS, '< nasmdoc.dip');
55 while ( defined($line = <PARAS>) ) {
59 if ( $line =~ /^meta :/ ) {
61 $metadata{$metakey} = $data;
62 } elsif ( $line =~ /^indx :/ ) {
64 push(@ixentries, $ixentry);
65 $ixterms{$ixentry} = [split(/\037/, $data)];
66 # Look for commas. This is easier done on the string
67 # representation, so do it now.
68 if ( $line =~ /^(.*\,)\037sp\037/ ) {
70 $ixhasprefix{$ixentry} = $ixprefix;
71 if ( !$ixprefixes{$ixprefix} ) {
72 $ixcommafirst{$ixentry}++;
74 $ixprefixes{$ixprefix}++;
78 push(@paras, [split(/\037/, $data)]);
84 # Convert an integer to a chosen base
90 my($z) = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
91 return '0' if ($i == 0);
92 if ( $i < 0 ) { $n = '-'; $i = -$i; }
94 $s = substr($z,$i%$b,1) . $s;
101 # Take a crossreference name and generate the PostScript name for it.
103 # This hack produces a somewhat smaller PDF...
108 # my $q = $ps_xref_list{$s};
109 # return $q if ( defined($ps_xref_list{$s}) );
110 # $q = 'X'.int2base($ps_xref_next++, 52);
111 # $ps_xref_list{$s} = $q;
115 # Somewhat bigger PDF, but one which obeys # URLs
121 # Flow lines according to a particular font set and width
123 # A "font set" is represented as an array containing
124 # arrays of pairs: [<size>, <metricref>]
126 # Each line is represented as:
127 # [ [type,first|last,aux,fontset,page,ypos,optional col],
128 # [rendering array] ]
130 # A space character may be "squeezed" by up to this much
131 # (as a fraction of the normal width of a space.)
133 $ps_space_squeeze = 0.00; # Min space width 100%
134 sub ps_flow_lines($$$@) {
135 my($wid, $fontset, $type, @data) = @_;
136 my($fonts) = $$fontset{fonts};
138 my($w) = 0; # Width of current line
139 my($sw) = 0; # Width of current line due to spaces
140 my(@l) = (); # Current line
141 my(@ls) = (); # Accumulated output lines
142 my(@xd) = (); # Metadata that goes with subsequent text
143 my $hasmarker = 0; # Line has -6 marker
144 my $pastmarker = 0; # -6 marker found
146 # If there is a -6 marker anywhere in the paragraph,
147 # *each line* output needs to have a -6 marker
148 foreach $e ( @data ) {
149 $hasmarker = 1 if ( $$e[0] == -6 );
153 foreach $e ( @data ) {
155 # Type is metadata. Zero width.
156 if ( $$e[0] == -6 ) {
159 if ( $$e[0] == -1 || $$e[0] == -6 ) {
160 # -1 (end anchor) or -6 (marker) goes with the preceeding
161 # text, otherwise with the subsequent text
167 my $ew = ps_width($$e[1], $fontset->{fonts}->[$$e[0]][1]) *
168 ($fontset->{fonts}->[$$e[0]][0]/1000);
170 $sp =~ tr/[^ ]//d; # Delete nonspaces
171 my $esw = ps_width($sp, $fontset->{fonts}->[$$e[0]][1]) *
172 ($fontset->{fonts}->[$$e[0]][0]/1000);
174 if ( ($w+$ew) - $ps_space_squeeze*($sw+$esw) > $wid ) {
176 # Search backwards for previous space chunk
177 my $lx = scalar(@l)-1;
180 while ( $lx >= 0 && $l[$lx]->[0] < 0 ) {
182 $pastmarker = 0 if ( $l[$lx]->[0] == -6 );
186 if ( $l[$lx]->[1] eq ' ' ) {
188 @rm = splice(@l, $lx);
189 last; # Found place to break
196 # Now @l contains the stuff to remain on the old line
197 # If we broke the line inside a link, then split the link
200 foreach my $lc ( @l ) {
201 if ( $$lc[0] == -2 || $$lc[0] == -3 || $lc[0] == -7 ) {
203 } elsif ( $$lc[0] == -1 ) {
208 if ( defined($lkref) ) {
209 push(@l, [-1,undef]); # Terminate old reference
210 unshift(@rm, $lkref); # Duplicate reference on new line
215 unshift(@rm,[-6,undef]); # New line starts with marker
217 push(@l,[-6,undef]); # Old line ends with marker
221 push(@ls, [[$type,0,undef,$fontset,0,0],[@l]]);
225 # Compute the width of the remainder array
227 if ( $$le[0] >= 0 ) {
228 my $xew = ps_width($$le[1], $fontset->{fonts}->[$$le[0]][1]) *
229 ($fontset->{fonts}->[$$le[0]][0]/1000);
231 $xsp =~ tr/[^ ]//d; # Delete nonspaces
232 my $xsw = ps_width($xsp, $fontset->{fonts}->[$$le[0]][1]) *
233 ($fontset->{fonts}->[$$le[0]][0]/1000);
234 $w += $xew; $sw += $xsw;
238 push(@l, @xd); # Accumulated metadata
240 if ( $$e[1] ne '' ) {
242 $w += $ew; $sw += $esw;
248 push(@ls, [[$type,0,undef,$fontset,0,0],[@l]]); # Final line
251 # Mark the first line as first and the last line as last
253 $ls[0]->[0]->[1] |= 1; # First in para
254 $ls[-1]->[0]->[1] |= 2; # Last in para
260 # Once we have broken things into lines, having multiple chunks
261 # with the same font index is no longer meaningful. Merge
262 # adjacent chunks to keep down the size of the whole file.
264 sub ps_merge_chunks(@) {
271 $eco = -1; # Index of the last entry in @co
273 if ( defined($lc) && $$c[0] == $lc && $$c[0] >= 0 ) {
274 $co[$eco]->[1] .= $$c[1];
276 push(@co, $c); $eco++;
284 # Convert paragraphs to rendering arrays. Each
285 # element in the array contains (font, string),
286 # where font can be one of:
290 # -4 index item anchor
292 # -6 left/right marker (used in the index)
293 # -7 page link (used in the index)
296 # 2 code (fixed spacing)
299 sub mkparaarray($@) {
300 my($ptype, @chunks) = @_;
306 if ( $ptype =~ /^code/ ) {
307 foreach $chunk ( @chunks ) {
308 push(@para, [2, $chunk]);
311 foreach $chunk ( @chunks ) {
312 my $type = substr($chunk,0,2);
313 my $text = substr($chunk,2);
315 if ( $type eq 'sp' ) {
316 push(@para, [$in_e?1:0, ' ']);
317 } elsif ( $type eq 'da' ) {
318 # \261 is en dash in Adobe StandardEncoding
319 push(@para, [$in_e?1:0, "\261"]);
320 } elsif ( $type eq 'n ' ) {
321 push(@para, [0, $text]);
323 } elsif ( $type =~ '^e' ) {
324 push(@para, [1, $text]);
325 $in_e = ($type eq 'es' || $type eq 'e ');
326 } elsif ( $type eq 'c ' ) {
327 push(@para, [2, $text]);
329 } elsif ( $type eq 'x ' ) {
330 push(@para, [-2, ps_xref($text)]);
331 } elsif ( $type eq 'xe' ) {
332 push(@para, [-1, undef]);
333 } elsif ( $type eq 'wc' || $type eq 'w ' ) {
334 $text =~ /\<(.*)\>(.*)$/;
335 my $link = $1; $text = $2;
336 push(@para, [-3, $link]);
337 push(@para, [($type eq 'wc') ? 2:0, $text]);
338 push(@para, [-1, undef]);
340 } elsif ( $type eq 'i ' ) {
341 push(@para, [-4, $text]);
343 die "Unexpected paragraph chunk: $chunk";
350 $npara = scalar(@paras);
351 for ( $i = 0 ; $i < $npara ; $i++ ) {
352 $paras[$i] = [mkparaarray($ptypes[$i], @{$paras[$i]})];
356 # This converts a rendering array to a simple string
358 sub ps_arraytostr(@) {
362 $s .= $$c[1] if ( $$c[0] >= 0 );
368 # This generates a duplicate of a paragraph
383 # Scan for header paragraphs and fix up their contents;
384 # also generate table of contents and PDF bookmarks.
386 @tocparas = ([[-5, 'contents'], [0,'Contents']]);
387 @tocptypes = ('chap');
388 @bookmarks = (['title', 0, 'Title Page'], ['contents', 0, 'Contents']);
390 for ( $i = 0 ; $i < $npara ; $i++ ) {
391 my $xtype = $ptypes[$i];
392 my $ptype = substr($xtype,0,4);
396 if ( $ptype eq 'chap' || $ptype eq 'appn' ) {
397 unless ( $xtype =~ /^\S+ (\S+) :(.*)$/ ) {
402 my $xref = ps_xref($sech);
403 my $chap = ($ptype eq 'chap')?'Chapter':'Appendix';
405 $book = [$xref, 0, ps_arraytostr(@{$paras[$i]})];
406 push(@bookmarks, $book);
407 $bookref{$secn} = $book;
409 push(@tocparas, [ps_dup_para(@{$paras[$i]})]);
410 push(@tocptypes, 'toc0'.' :'.$sech.':'.$chap.' '.$secn.':');
412 unshift(@{$paras[$i]},
413 [-5, $xref], [0,$chap.' '.$secn.':'], [0, ' ']);
414 } elsif ( $ptype eq 'head' || $ptype eq 'subh' ) {
415 unless ( $xtype =~ /^\S+ (\S+) :(.*)$/ ) {
420 my $xref = ps_xref($sech);
422 $pref = $secn; $pref =~ s/\.[^\.]+$//; # Find parent node
424 $book = [$xref, 0, ps_arraytostr(@{$paras[$i]})];
425 push(@bookmarks, $book);
426 $bookref{$secn} = $book;
427 $bookref{$pref}->[1]--; # Adjust count for parent node
429 push(@tocparas, [ps_dup_para(@{$paras[$i]})]);
431 (($ptype eq 'subh') ? 'toc2':'toc1').' :'.$sech.':'.$secn);
433 unshift(@{$paras[$i]}, [-5, $xref]);
438 # Add TOC to beginning of paragraph list
440 unshift(@paras, @tocparas); undef @tocparas;
441 unshift(@ptypes, @tocptypes); undef @tocptypes;
442 $npara = scalar(@paras);
445 # No lines generated, yet.
450 # Line Auxilliary Information Types
452 $AuxStr = 1; # String
453 $AuxPage = 2; # Page number (from xref)
454 $AuxPageStr = 3; # Page number as a PostScript string
455 $AuxXRef = 4; # Cross reference as a name
456 $AuxNum = 5; # Number
459 # Break or convert paragraphs into lines, and push them
460 # onto the @pslines array.
462 sub ps_break_lines($$) {
463 my ($paras,$ptypes) = @_;
465 my $linewidth = $psconf{pagewidth}-$psconf{lmarg}-$psconf{rmarg};
466 my $bullwidth = $linewidth-$psconf{bulladj};
467 my $indxwidth = ($linewidth-$psconf{idxgutter})/$psconf{idxcolumns}
470 my $npara = scalar(@{$paras});
473 for ( $i = 0 ; $i < $npara ; $i++ ) {
474 my $xtype = $ptypes->[$i];
475 my $ptype = substr($xtype,0,4);
476 my @data = @{$paras->[$i]};
478 if ( $ptype eq 'code' ) {
480 # Code paragraph; each chunk is a line
481 foreach $p ( @data ) {
482 push(@ls, [[$ptype,0,undef,\%TextFont,0,0],[$p]]);
484 $ls[0]->[0]->[1] |= 1; # First in para
485 $ls[-1]->[0]->[1] |= 2; # Last in para
486 } elsif ( $ptype eq 'chap' || $ptype eq 'appn' ) {
487 # Chapters are flowed normally, but in an unusual font
488 @ls = ps_flow_lines($linewidth, \%ChapFont, $ptype, @data);
489 } elsif ( $ptype eq 'head' || $ptype eq 'subh' ) {
490 unless ( $xtype =~ /^\S+ (\S+) :(.*)$/ ) {
495 my $font = ($ptype eq 'head') ? \%HeadFont : \%SubhFont;
496 @ls = ps_flow_lines($linewidth, $font, $ptype, @data);
497 # We need the heading number as auxillary data
498 $ls[0]->[0]->[2] = [[$AuxStr,$secn]];
499 } elsif ( $ptype eq 'norm' ) {
500 @ls = ps_flow_lines($linewidth, \%TextFont, $ptype, @data);
501 } elsif ( $ptype eq 'bull' ) {
502 @ls = ps_flow_lines($bullwidth, \%TextFont, $ptype, @data);
503 } elsif ( $ptype =~ /^toc/ ) {
504 unless ( $xtype =~/^\S+ :([^:]*):(.*)$/ ) {
508 my $refname = $2.' ';
509 my $ntoc = substr($ptype,3,1)+0;
510 my $refwidth = ps_width($refname, $TextFont{fonts}->[0][1]) *
511 ($TextFont{fonts}->[0][0]/1000);
513 @ls = ps_flow_lines($linewidth-$ntoc*$psconf{tocind}-
514 $psconf{tocpnz}-$refwidth,
515 \%TextFont, $ptype, @data);
517 # Auxilliary data: for the first line, the cross reference symbol
518 # and the reference name; for all lines but the first, the
519 # reference width; and for the last line, the page number
521 my $nl = scalar(@ls);
522 $ls[0]->[0]->[2] = [[$AuxStr,$refname], [$AuxXRef,$xref]];
523 for ( $j = 1 ; $j < $nl ; $j++ ) {
524 $ls[$j]->[0]->[2] = [[$AuxNum,$refwidth]];
526 push(@{$ls[$nl-1]->[0]->[2]}, [$AuxPageStr,$xref]);
527 } elsif ( $ptype =~ /^idx/ ) {
528 my $lvl = substr($ptype,3,1)+0;
530 @ls = ps_flow_lines($indxwidth-$lvl*$psconf{idxindent},
531 \%TextFont, $ptype, @data);
533 die "Unknown para type: $ptype";
535 # Merge adjacent identical chunks
537 @{$$l[1]} = ps_merge_chunks(@{$$l[1]});
543 # Break the main body text into lines.
544 ps_break_lines(\@paras, \@ptypes);
547 # Break lines in to pages
550 $curpage = 3; # First text page is page 3
551 $curypos = 0; # Space used on this page
552 undef $columnstart; # Not outputting columnar text
553 undef $curcolumn; # Current column
554 $nlines = scalar(@pslines);
557 # This formats lines inside the global @pslines array into pages,
558 # updating the page and y-coordinate entries. Start at the
559 # $startline position in @pslines and go to but not including
560 # $endline. The global variables $curpage, $curypos, $columnstart
561 # and $curcolumn are updated appropriately.
563 sub ps_break_pages($$) {
564 my($startline, $endline) = @_;
566 # Paragraph types which should never be broken
567 my $nobreakregexp = "^(chap|appn|head|subh|toc.|idx.)\$";
568 # Paragraph types which are heading (meaning they should not be broken
570 my $headingregexp = "^(chap|appn|head|subh)\$";
571 # Paragraph types which are set in columnar format
572 my $columnregexp = "^idx.\$";
574 my $upageheight = $psconf{pageheight}-$psconf{topmarg}-$psconf{botmarg};
578 for ( $i = $startline ; $i < $endline ; $i++ ) {
579 my $linfo = $pslines[$i]->[0];
580 if ( ($$linfo[0] eq 'chap' || $$linfo[0] eq 'appn' )
581 && ($$linfo[1] & 1) ) {
582 # First line of a new chapter heading. Start a new page.
584 $curpage++ if ( $curypos > 0 || defined($columnstart) );
585 $curypos = $chapstart;
586 } elsif ( defined($columnstart) && $$linfo[0] !~ /$columnregexp/o ) {
592 if ( $$linfo[0] =~ /$columnregexp/o && !defined($columnstart) ) {
593 $columnstart = $curypos;
597 # Adjust position by the appropriate leading
598 $curypos += $$linfo[3]->{leading};
600 # Record the page and y-position
601 $$linfo[4] = $curpage;
602 $$linfo[5] = $curypos;
603 $$linfo[6] = $curcolumn if ( defined($columnstart) );
605 if ( $curypos > $upageheight ) {
606 # We need to break the page before this line.
607 my $broken = 0; # No place found yet
608 while ( !$broken && $pslines[$i]->[0]->[4] == $curpage ) {
609 my $linfo = $pslines[$i]->[0];
610 my $pinfo = $pslines[$i-1]->[0];
612 if ( $$linfo[1] == 2 ) {
613 # This would be an orphan, don't break.
614 } elsif ( $$linfo[1] & 1 ) {
615 # Sole line or start of paragraph. Break unless
616 # the previous line was part of a heading.
617 $broken = 1 if ( $$pinfo[0] !~ /$headingregexp/o );
619 # Middle of paragraph. Break unless we're in a
620 # no-break paragraph, or the previous line would
621 # end up being a widow.
622 $broken = 1 if ( $$linfo[0] !~ /$nobreakregexp/o &&
627 die "Nowhere to break page $curpage\n" if ( !$broken );
628 # Now $i should point to line immediately before the break, i.e.
629 # the next paragraph should be the first on the new page
630 if ( defined($columnstart) &&
631 ++$curcolumn < $psconf{idxcolumns} ) {
632 # We're actually breaking text into columns, not pages
633 $curypos = $columnstart;
642 # Add end of paragraph skip
643 if ( $$linfo[1] & 2 ) {
644 $curypos += $skiparray{$$linfo[0]};
649 ps_break_pages(0,$nlines); # Break the main text body into pages
652 # Find the page number of all the indices
654 %ps_xref_page = (); # Crossref anchor pages
655 %ps_index_pages = (); # Index item pages
656 $nlines = scalar(@pslines);
657 for ( $i = 0 ; $i < $nlines ; $i++ ) {
658 my $linfo = $pslines[$i]->[0];
659 foreach my $c ( @{$pslines[$i]->[1]} ) {
660 if ( $$c[0] == -4 ) {
661 if ( !defined($ps_index_pages{$$c[1]}) ) {
662 $ps_index_pages{$$c[1]} = [];
663 } elsif ( $ps_index_pages{$$c[1]}->[-1] eq $$linfo[4] ) {
664 # Pages are emitted in order; if this is a duplicated
665 # entry it will be the last one
668 push(@{$ps_index_pages{$$c[1]}}, $$linfo[4]);
669 } elsif ( $$c[0] == -5 ) {
670 $ps_xref_page{$$c[1]} = $$linfo[4];
676 # Emit index paragraphs
678 $startofindex = scalar(@pslines);
679 @ixparas = ([[-5,'index'],[0,'Index']]);
680 @ixptypes = ('chap');
682 foreach $k ( @ixentries ) {
684 my $ixptype = 'idx0';
685 my @ixpara = mkparaarray('idx0',@{$ixterms{$k}});
687 push(@ixpara, [-6,undef]); # Left/right marker
688 $i = 1; $n = scalar(@{$ps_index_pages{$k}});
689 foreach $p ( @{$ps_index_pages{$k}} ) {
691 push(@ixpara,[-7,$p],[0,"$p"],[-1,undef]);
693 push(@ixpara,[-7,$p],[0,"$p,"],[-1,undef],[0,' ']);
697 push(@ixparas, [@ixpara]);
698 push(@ixptypes, $ixptype);
702 # Flow index paragraphs into lines
704 ps_break_lines(\@ixparas, \@ixptypes);
707 # Format index into pages
709 $nlines = scalar(@pslines);
710 ps_break_pages($startofindex, $nlines);
713 # Push index onto bookmark list
715 push(@bookmarks, ['index', 0, 'Index']);
717 # Get the list of fonts used
719 foreach $fset ( @AllFonts ) {
720 foreach $font ( @{$fset->{fonts}} ) {
721 $ps_all_fonts{$font->[1]->{name}}++;
725 # Emit the PostScript DSC header
726 print "%!PS-Adobe-3.0\n";
727 print "%%Pages: $curpage\n";
728 print "%%BoundingBox: 0 0 ", $psconf{pagewidth}, ' ', $psconf{pageheight}, "\n";
729 print "%%Creator: NASM psflow.pl\n";
730 print "%%DocumentData: Clean7Bit\n";
731 print "%%DocumentFonts: ", join(' ', keys(%ps_all_fonts)), "\n";
732 print "%%DocumentNeededFonts: ", join(' ', keys(%ps_all_fonts)), "\n";
733 print "%%Orientation: Portrait\n";
734 print "%%PageOrder: Ascend\n";
735 print "%%EndComments\n";
736 print "%%BeginProlog\n";
738 # Emit the configurables as PostScript tokens
739 for $c ( keys(%psconf) ) {
740 print "/$c ", $psconf{$c}, " def\n";
743 # Emit fontset definitions
744 foreach $fset ( @AllFonts ) {
747 foreach $font ( @{$fset->{fonts}} ) {
748 print '/', $fset->{name}, $i, ' ',
749 '/', $font->[1]->{name}, ' findfont ',
750 $font->[0], " scalefont def\n";
751 push(@zfonts, $fset->{name}.$i);
754 print '/', $fset->{name}, ' [', join(' ',@zfonts), "] def\n";
757 # Emit the result as PostScript. This is *NOT* correct code yet!
758 open(PSHEAD, "< head.ps");
759 while ( defined($line = <PSHEAD>) ) {
763 print "%%EndProlog\n";
765 # Generate a PostScript string
770 my ($l) = length($s);
771 for ( $i = 0 ; $i < $l ; $i++ ) {
772 $c = substr($s,$i,1);
773 if ( ord($c) < 32 || ord($c) > 126 ) {
774 $o .= sprintf("\\%03o", ord($c));
775 } elsif ( $c eq '(' || $c eq ')' || $c eq "\\" ) {
784 # Generate PDF bookmarks
785 print "%%BeginSetup\n";
786 foreach $b ( @bookmarks ) {
787 print '[/Title ', ps_string($b->[2]), "\n";
788 print '/Count ', $b->[1], ' ' if ( $b->[1] );
789 print '/Dest /',$b->[0]," /OUT pdfmark\n";
792 # Ask the PostScript interpreter for the proper size media
793 print "setpagesize\n";
794 print "%%EndSetup\n";
796 # Start a PostScript page
797 sub ps_start_page() {
799 print "%%Page: $ps_page $ps_page\n";
800 print "%%BeginPageSetup\n";
802 print "%%EndPageSetup\n";
803 print '/', $ps_page, " pa\n";
806 # End a PostScript page
810 print "($ps_page)", (($ps_page & 1) ? 'pageodd' : 'pageeven'), "\n";
812 print "restore showpage\n";
817 # Title page and inner cover
819 # FIX THIS: This shouldn't be hard-coded like this
820 $title = $metadata{'title'};
821 $title =~ s/ \- / \320 /; # \320 = em dash
822 $pstitle = ps_string($title);
824 lmarg pageheight 2 mul 3 div moveto
825 /Helvetica-Bold findfont 20 scalefont setfont
826 /title linkdest ${pstitle} show
827 lmarg pageheight 2 mul 3 div 10 sub moveto
828 0 setlinecap 3 setlinewidth
829 pagewidth lmarg sub rmarg sub 0 rlineto stroke
833 /Courier-Bold findfont sz scalefont setfont
836 [(-~~..~:\#;L .-:\#;L,.- .~:\#:;.T -~~.~:;. .~:;. )
837 ( E8+U *T +U\' *T\# .97 *L E8+\' *;T\' *;, )
838 ( D97 \`*L .97 \'*L \"T;E+:, D9 *L *L )
839 ( H7 I\# T7 I\# \"*:. H7 I\# I\# )
840 ( U: :8 *\#+ , :8 T, 79 U: :8 :8 )
841 (,\#B. .IE, \"T;E* .IE, J *+;\#:T*\" ,\#B. .IE, .IE,)] {
842 currentpoint 3 -1 roll
843 sz -0.10 mul 0 3 -1 roll ashow
844 sz 0.72 mul sub moveto
849 pagewidth 2 div 143 sub
850 pageheight 2 div 33 add
855 print "% Inner cover goes here\n";
860 foreach $line ( @pslines ) {
861 my $linfo = $line->[0];
863 if ( $$linfo[4] != $curpage ) {
866 $curpage = $$linfo[4];
871 foreach my $c ( @{$line->[1]} ) {
873 if ( $curfont != $$c[0] ) {
874 print ($curfont = $$c[0]);
876 print ps_string($$c[1]);
877 } elsif ( $$c[0] == -1 ) {
878 print '{el}'; # End link
879 } elsif ( $$c[0] == -2 ) {
880 print '{/',$$c[1],' xl}'; # xref link
881 } elsif ( $$c[0] == -3 ) {
882 print '{',ps_string($$c[1]),'wl}'; # web link
883 } elsif ( $$c[0] == -4 ) {
884 # Index anchor -- ignore
885 } elsif ( $$c[0] == -5 ) {
886 print '{/',$$c[1],' xa}'; #xref anchor
887 } elsif ( $$c[0] == -6 ) {
888 print ']['; # Start a new array
890 } elsif ( $$c[0] == -7 ) {
891 print '{/',$$c[1],' pl}'; # page link
893 die "Unknown annotation";
897 if ( defined($$linfo[2]) ) {
898 foreach my $x ( @{$$linfo[2]} ) {
899 if ( $$x[0] == $AuxStr ) {
900 print ps_string($$x[1]);
901 } elsif ( $$x[0] == $AuxPage ) {
902 print $ps_xref_page{$$x[1]},' ';
903 } elsif ( $$x[0] == $AuxPageStr ) {
904 print ps_string($ps_xref_page{$$x[1]});
905 } elsif ( $$x[0] == $AuxXRef ) {
906 print '/',ps_xref($$x[1]),' ';
907 } elsif ( $$x[0] == $AuxNum ) {
910 die "Unknown auxilliary data type";
914 print ($psconf{pageheight}-$psconf{topmarg}-$$linfo[5]);
915 print ' ', $$linfo[6] if ( defined($$linfo[6]) );
916 print ' ', $$linfo[0].$$linfo[1], "\n";