[alt.sources] Yet another text to postscript filter

aas@boeygen.nr.no (Gisle Aas) (06/28/90)

The unusual thing about this one, is that it is written in perl.
Comments and bug fixes are welcome.

# This is a shell archive.
# Remove everything above and including the cut line.
# Then run the rest of the file through /bin/sh (not csh).
#--cut here-----cut here-----cut here-----cut here-----cut here-----cut here--#
#!/bin/sh
# shar: Shell Archiver
# Execute the following text with /bin/sh to create the file(s):
#	a2ps.1
#	a2ps
# This archive created: Thu Jun 28 14:40:28 1990
# Wrapped by: Gisle Aas (Norwegain Computing Centre, Oslo, Norway)
sed 's/^X//' << \SHAR_EOF > a2ps.1
X.TH A2PS 1 "June 1990"
X.SH NAME
Xa2ps \- convert ISO Latin1 text to PostScript
X.SH SYNOPSIS
X.B a2ps
X[
X.B \-nlrth23
X] [
X.B \-b
X.I "text"
X] [
X.B \-s
X.I size
X] [
X.I filename
X\&.\|.\|.
X]
X.SH DESCRIPTION
XThe
X.B a2ps
Xprogram is used to print simple
Xtext files (e.g. program listings) on a PostScript\*R device. 
XThe name
X.B a2ps
Xused to stand for ascii-to-postscript, but the program
Xis designed to handle the whole
XISO Latin1 character set.
XEfforts have been made to ensure that 
X.B a2ps
Xproduces good looking and effective PostScript code.
XThe output conforms to Adobe's
Xdocument structuring conventions (version 2.1). The 
X.B a2ps 
Xprogram
Xassumes that the page format on the output device is A4.
XThe meaning of form feed (^L) characters in the input stream is understood.
X.PP
XThe
X.B a2ps
Xprogram is written in
X.I perl.
XSo if there is something you don't like about how
X.B a2ps
Xworks, you can easily fix it youself.
X.SH OPTIONS
X.TP 5
X.B \-n
Xuse the norwegian version of ISO 646 (7-bit ascii) to encode the text.
XNorwegian letters will replace the left/right braces, left/right
Xbrackets, backslash and horisontal bar characters.
X.TP 5
X.B \-l
Xproduces linenumbers on each 5'th line. Nice for program listings.
X.TP 5
X.B \-t
Xtruncate lines that are to long. The default is to wrap
Xlong lines so that they continue on the next line on the printed output.
X.TP 5
X.B \-r
Xrotate the output page 90 degrees. This is also called
X.I landscape
Xmode.
X.TP 5
X.B \-h
Xsuppress generation of page headers.
X.TP 5
X.B \-2
Xuse two column format.
X.TP 5
X.B \-3
Xuse three column format (not supported).
X.TP 5
X.BI \-b " text"
Xthe 
X.I text
Xparameter replaces the default text in the header of the pages.
XThe default text is the filename of the file to be printed. There is no
Xdefault if the filtered text comes from standard input.
X.TP 5
X.BI \-s " size"
Xspecifies a new font size for the body text. The default is 10 point.
X.SH EXAMPLES
XTo print files on a PostScript printer:
X.nf
X    a2ps -l *.[ch] | lpr
X.fi
X.PP
XTo find out how many pages a2ps will produce for a given file:
X.nf
X    a2ps file.txt | tail -1
X.fi
X.PP
XOur printer stacks the pages the wrong way. To fix it try:
X.nf
X    cat file.txt | a2ps -b "some text" | psrev | lpr
X.fi
X.SH SEE ALSO
X.BR perl (1),
X.BR enscript (1),
X.BR psrev (1),
X.BR ISO8859 (7),
X.BR PostScript (7)
X.SH BUGS
XIf some of the specified
X.I files
Xdoes not exist,
X.B a2ps
Xget confused about what to put in the header.
XThere is no easy way to change the margins or the fonts that
X.B a2ps
Xinsist on using.
XThe
X.B a2ps
Xfilter ought to understand the meaning of the backspace character. It is used
Xto produce boldfaced and underlined text by programs like 
X.BR nroff (1).
XPerhaps
X.B a2ps
Xshould be made to reject input which looks like garbage (binary files).
X.SH AUTHOR
XGisle Aas, Norwegain Computing Centre (NR), Oslo, Norway.
X<Gisle.Aas@nr.no>
SHAR_EOF
if test 2839 -ne "`wc -c a2ps.1`"
then
echo shar: error transmitting a2ps.1 '(should have been 2839 characters)'
fi
sed 's/^X//' << \SHAR_EOF > a2ps
X#!/local/boeygen/bin/perl
X# "a2ps" text to PostScript filter written in perl by Gisle Aas, NCC 1990
X# version 1.1
X#
X# Efforts have been made to produce nice and effective PostScript. The
X# output conforms to Adobe's document structuring conventions version-2.1.
X# 
X# Whish list:  (this may become a feature some time)
X#     Line number on the last line (in addition to each 5th line)
X#     Marking (by some funny char) truncation and wrapping of lines
X#     Faster execution (rewrite the hole thing in C?)
X#     Parsing of backspace to produce bold and underlined fonts.
X
X# Some configuration constants, meassured in points (1/72 inch)
Xsub a4_top        { 841; }
Xsub a4_right_edge { 595; }
X
X# The next few entries are from the AFM file for Adobe's font Courier
Xsub cour_char_width     { 600; }   # The width of each char in 1000x1000 square
X#sub underline_position  { -82; }   # Where underline goes relative to baseline
X#sub underline_thickness {  40; }   # and it's thickness
X
X# Parse command line for options and flags
Xdo 'getopts.pl';
Xunless (&Getopts('nrth12s:b:l')) {
X   print STDERR "Usage: a2ps [-<options>] [file]...\n";
X   print STDERR "Options: -n       norwegian 7bit-ascii encoding\n";
X   print STDERR "         -l       print with line numbers\n";
X   print STDERR "         -r       rotated, landscape orientation\n";
X   print STDERR "         -t       truncate long lines, " . 
X                                  "default is to wrap lines\n";
X   print STDERR "         -b\"text\" replaces the text in the page header\n";
X   print STDERR "         -h       no page headers\n";
X   print STDERR "         -2       set text in two columns\n";
X   print STDERR "         -s<size> select new text fontsize, default 10pt\n";
X   exit(1);
X}
X
X# Set default values, some based on command line options
X$left_margin  = 80;
X$right_margin = 40;
X$tb_margin    = 45;
X$font         = "Courier";
X$font_size    = 10;		$font_size = $opt_s if ($opt_s > 0);
X$header_font  = "Helvetica-Bold";
X$header_font_size = 12;
X$line_number_font = "Helvetica";
X$line_number_size = 5;
X
X$line_height = $font_size * 1.08;
X$no_columns = defined($opt_2) ? 2 : 1;
X$col_separation = 30;
X$sep_bars = '';                 # false
X$landscape = defined($opt_r);
X$header_height = 30;
X$show_header = !defined($opt_h);
X$wrap_lines = !defined($opt_t);
X$truncate_lines = !$wrap_lines; # don't change this
X$norsk_ascii = defined($opt_n);
X
X# Some initial values
X$opt_b = &ps_string($opt_b) if ($opt_b);
X$form_feed = ''; # false;
X$page_no  = 0;
X$line_no = 0;
Xif ($landscape) {
X    $top = &a4_right_edge;
X    $right_edge = &a4_top;
X    $left_margin = $right_margin; # this is a dirty one
X} else {
X    $top = &a4_top;
X    $right_edge = &a4_right_edge;
X}
X$home_pos = $top - $tb_margin - ($show_header ? $header_height : 0);
X$col_width = ($right_edge - $left_margin - $right_margin
X              - ($no_columns - 1) * $col_separation) / $no_columns;
X$char_width = &cour_char_width * $font_size / 1000;
X$chars_per_line = int ($col_width / $char_width + 1);
X
X&prolog;
X
X$cur_pos = -1;
X$cur_col = 100;
X$file_name = &ps_string($ARGV[0]);
XLINES:
Xwhile (<>) {
X   chop;
X   $line_no++;
X   if (ord == 014) {		# form feed
X       s/.//;	# chop off first char
X       $cur_pos = -1; 
X       next LINES if (length == 0);
X   }
X   while (s/\t/' ' x (8 - length($`) % 8)/e) {}   # expand tabs
X   do {
X      if ($cur_pos < $tb_margin) {
X          $cur_pos = $home_pos;
X          if ($cur_col < $no_columns) {
X              $cur_col++;
X          } else {
X              $cur_col = 1;
X              &new_page;
X          }
X      }
X      $text = substr($_,0,$chars_per_line);
X      $_ = $truncate_lines ? '' : substr($_,$chars_per_line,10000);
X      if ($text =~ s/^ +//) {			# suppress leading blanks
X          $indent = $char_width * length($&);
X      } else {
X          $indent = 0;
X      }
X      # Make suitable as a postscript string
X      $text =~ s/[\\\(\)]/\\$&/g;
X      $text =~ s/[\000-\037\177-\377]/sprintf("\\%03o",ord($&))/ge;
X      # Calculate position
X      $x = $left_margin + ($cur_col - 1) * ($col_width + $col_separation);
X      $cur_pos -= $line_height;
X      printf "(%s)%.1f %.1f S\n", $text, $x + $indent, $cur_pos 
X             if (length($text));
X      if ($opt_l && ($line_no % 5) == 0) { # print line numbers
X          print "F2 SF ";
X          printf "($line_no) dup stringwidth pop neg %.1f add %.1f M show ",
X                  $x - 10, $cur_pos;
X          print "F1 SF\n";
X      }
X      if (eof) {
X         $file_name = &ps_string($ARGV[0]);
X         $cur_pos = -1;  # will force a new column next time
X         $cur_col = 100; # will force a new page next time
X         $line_no = 0;
X      }
X   } while (length($_));
X}
X&end_page;
Xprint "%%Trailer\n";
Xprint "%%Pages: $page_no\n";
X
X#--end of main-------------------------------------------------------
X
X
Xsub prolog {
X   local($user) = getlogin || "(unknown)";
X   local($sec,$min,$hour,$mday,$mon,$year) = localtime;
X   $date = sprintf("(%d. %s %d) (%2d:%02d)",$mday,
X                    ('Januar','Februar','Mars','April','Mai','Juni',
X                     'Juli','August','Oktober','November','Desember')[$mon],
X                     $year+1900, $hour,$min);
X#   open(LOG,">>/home/boeygen/aas/.a2ps-log");
X#   print LOG "$user, $date\n";
X#   close(LOG);
X
X   print "%!PS-Adobe-2.0\n";
X   print "%%Title: @ARGV\n" if (@ARGV);
X   print <<"EOT";
X%%Creator: a2ps, Text to PostScript filter in perl, (C) 1990 Gisle Aas, NCC
X%%CreationDate: $date
X%%For: $user
X%%Pages: (atend)
X%%DocumentFonts: $font
XEOT
X   print "%%+ $line_number_font\n" if ($opt_l);
X   print "%%+ $header_font\n" if ($show_header);
X   print <<"EOT";
X%%EndComments
X/S{moveto show}bind def
X/M{moveto}bind def
X/L{lineto}bind def
X/SF{setfont}bind def
X%%BeginProcSet: reencode 1.0 0
X/RE { %def
X   findfont begin
X   currentdict dup length dict begin
X        { %forall
X             1 index/FID ne {def} {pop pop} ifelse
X         } forall
X         /FontName exch def dup length 0 ne { %if
X            /Encoding Encoding 256 array copy def
X            0 exch { %forall
X                dup type /nametype eq { %ifelse
X                    Encoding 2 index 2 index put
X                    pop 1 add
X                 }{%else
X                    exch pop
X                 } ifelse
X          } forall
X       } if pop
X    currentdict dup end end
X    /FontName get exch definefont pop
X} bind def
X%%EndProcSet: reencode 1.0 0
X%%EndProlog
X%%BeginSetup
X0.15 setlinewidth
XEOT
X   if ($norsk_ascii) {
X      print "[8#133 /AE/Oslash/Aring 8#173 /ae/oslash/aring] dup\n";
X      print "/Body-Font/$font RE\n";
X      print "/Header-Font/$header_font RE\n" if ($show_header);
X   } else {
X      print "ISOLatin1Encoding /Body-Font/$font RE\n";
X      print "ISOLatin1Encoding /Header-Font/$header_font RE\n"
X         if ($show_header);
X   }
X   print "/F1/Body-Font findfont $font_size scalefont def\n";
X   print "/F2/$line_number_font findfont $line_number_size scalefont def\n"
X        if ($opt_l);
X   print "/F3/Header-Font findfont $header_font_size scalefont def\n"
X        if ($show_header);
X   print "F1 SF\n";
X   if ($landscape) {
X      printf "90 rotate 0 -%d translate %% landscape mode\n",&a4_right_edge;
X   }
X   print "%%EndSetup\n";
X}
X
X
X
Xsub new_page {
X   &end_page unless ($page_no == 0);
X   $page_no++;
X   print "%%Page: $page_no $page_no\n";
X   print "/my_save save def\n";
X   if ($show_header) {
X      # First print a box
X      local($llx,$lly,$urx,$ury) = ($left_margin - 10,
X            $top - $tb_margin - $header_font_size * 1.3,
X            $right_edge - $right_margin + 10, $top - $tb_margin);
X      printf "%.1f %.1f M %.1f %.1f L %.1f %.1f L ",
X             $llx,$lly, $urx,$lly, $urx, $ury;
X      printf "%.1f %.1f L closepath \n",$llx,$ury;
X      print  "gsave .95 setgray fill grestore stroke\n";
X      # Then the banner or the filename
X      print "F3 SF\n";
X      if ($opt_b) {
X         printf "($opt_b)%.1f %.1f S\n",
X                $left_margin,$top - $tb_margin - $header_font_size;
X      }
X      elsif ($file_name) {
X         printf "(%s)%.1f %.1f S\n", $file_name, 
X                      $left_margin,
X                      $top - $tb_margin - $header_font_size;
X      }
X      # Then print page number
X      printf "%.1f %.1f M($page_no)dup stringwidth pop neg 0 rmoveto show\n",
X                 $right_edge - $right_margin, 
X                 $top - $tb_margin - $header_font_size;
X      print  "F1 SF\n";
X   }
X   if ($sep_bars) {
X      print "% Some postscript code to draw horizontal bars.\n";
X      print "% Not implemented yet\n";
X   }
X}
X
Xsub end_page {
X   unless ($page_no == 0) {
X      print "showpage\n";
X      print "my_save restore\n";
X   }
X}
X
Xsub ps_string
X{
X   # Prepare text for printing
X   local($_) = shift;
X   s/[\\\(\)]/\\$&/g;
X   s/[\001-\037\177-\377]/sprintf("\\%03o",ord($&))/ge;
X   $_;    # return string
X}
X
X
X
X
X# This is interpreted by the GnuEmacs text editor.
X# Local Variables:
X# mode: C
X# eval: (message "Text to PostScript filter written in perl by Gisle Aas")
X# End:
SHAR_EOF
if test 8974 -ne "`wc -c a2ps`"
then
echo shar: error transmitting a2ps '(should have been 8974 characters)'
fi
#	End of shell archive
exit 0
--
Gisle Aas               |  snail: Boks 114 Blindern, N-0314 Oslo, Norway
Norsk Regnesentral      |  X.400: G=Gisle;S=Aas;O=nr;P=uninett;C=no
voice: +47-2-453561     |  inet:  Gisle.Aas@nr.no