[alt.sources] New release of yet another text to postscript filter

aas@boeygen.nr.no (Gisle Aas) (10/18/90)

In june I posted a text to postscript filter written in perl that I
called a2ps. Later I discovered that this name was already used, so I
changed the name to i2ps, because thats what it is; an ISO to
postscript filter.  This version has some new features, and fewer bugs :-)

You need perl 3.0 patchlevel 28 or better.

The manual page lives together with the script. Just make a link from
the manual directory to the place where you keep your executables.

I2ps may be freely distributed and copied. Please report bugs,
problems, and suggestions to <Gisle.Aas@nr.no>

-------------------cut here-----------------------------------------
#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 10/18/1990 09:33 UTC by aas@boeygen
# Source directory /home/boeygen/aas
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#  16140 -r-xr-xr-x i2ps
#
# ============= i2ps ==============
if test -f 'i2ps' -a X"$1" != X"-c"; then
	echo 'x - skipping i2ps (File already exists)'
else
echo 'x - extracting i2ps (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'i2ps' &&
#!/usr/local/bin/perl
'di';
'ig00';
X
# "i2ps" text to PostScript filter written in perl by Gisle Aas, NCC 1990
# $Id: i2ps,v 1.5 90/10/18 10:26:45 aas Exp $
#
# Whish list:  (this may become a feature some time)
#     Marking (by some funny char) truncation and wrapping of lines
#     Faster execution (rewrite the hole thing in C?)
#     Parsing of backspace to produce bold and underlined fonts.
X
#
# $Log:	i2ps,v $
# Revision 1.5  90/10/18  10:26:45  aas
# Changed the name from a2ps to i2ps. Merged the manual-page with the
# program. I2ps now rejects garbage files. I2ps was confused about what
# to put in the header when some of the specified files did not exist.
# Some minor spelling corrections.
# 
# Revision 1.4  90/10/01  15:57:46  aas
# Simplify reencoding to ISO-Latin1. (newencode)
# Fixed problem with showpage after page level restore. Graphic state
# initialized on each page. Included the ISOLatin1Encoding-vector
# in the script. Linenumber on last line when the -l option is used.
# Linenumbers are moved to the left margin.
# 
# Revision 1.3  90/09/27  14:05:31  aas
# Cleaned up the use of A4 variables.
# 
# Revision 1.2  90/09/27  13:18:31  aas
# Removed sccs-stuff, replaced it with rcs-stuff.
# 
X
# Some configuration constants, meassured in points (1/72 inch)
sub page_top        { 841; }    # A4 = 297mm x 210mm = 841pt x 595pt
sub page_right_edge { 595; }
# Uncomment next line if your printer doesn't have iso encoding builtin.
#$isoencoding_not_builtin = 1; #true
X
# The next few entries are from the AFM file for Adobe's font Courier
sub cour_char_width     { 600; }   # The width of each char in 1000x1000 square
#sub underline_position  { -82; }   # Where underline goes relative to baseline
#sub underline_thickness {  40; }   # and it's thickness
X
# Parse command line for options and flags
$prog = substr(__FILE__,rindex(__FILE__,"/")+1,999);
require 'getopts.pl';
unless (&Getopts('nrth123s:b:lg')) {
X   print STDERR "Usage: $prog [-<options>] [file]...\n";
X   print STDERR "Options: -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 format\n";
X   print STDERR "         -3       set text in three columns format\n";
X   print STDERR "         -s<size> select new text fontsize, default 10pt\n";
X   print STDERR "         -g       don't reject garbage files\n";
X   print STDERR "         -n       norwegian 7bit-ascii encoding\n";
X   exit(1);
}
X
# Set default values, some based on command line options
$left_margin  = 80;
$right_margin = 40;
$tb_margin    = 45;
$font         = "Courier";
$font_size    = 10;		$font_size = $opt_s if ($opt_s > 0);
$header_font  = "Helvetica-Bold";
$header_font_size = 12;
$line_number_font = "Helvetica";
$line_number_size = 5;
X
$line_height = $font_size * 1.08;
$no_columns = defined($opt_2) ? 2 : defined($opt_3) ? 3 : 1;
$col_separation = 30;
$sep_bars = 0;  # false
$landscape = defined($opt_r);
$header_height = 30;
$show_header = !defined($opt_h);
$wrap_lines = !defined($opt_t);
$truncate_lines = !$wrap_lines; # don't change this
$norsk_ascii = defined($opt_n);
X
# Some initial values
$opt_b = &ps_string($opt_b) if ($opt_b);
$form_feed = 0; # false;
$page_no  = 0;
$line_no = 0;
if ($landscape) {
X    $top = &page_right_edge;
X    $right_edge = &page_top;
X    $left_margin = $right_margin; # this is a dirty one
} else {
X    $top = &page_top;
X    $right_edge = &page_right_edge;
}
$home_pos = $top - $tb_margin - ($show_header ? $header_height : 0);
$col_width = ($right_edge - $left_margin - $right_margin
X              - ($no_columns - 1) * $col_separation) / $no_columns;
$char_width = &cour_char_width * $font_size / 1000;
$chars_per_line = int ($col_width / $char_width + 1);
X
&prolog;
X
unshift(@ARGV,'-') if $#ARGV < $[;
FILE:
while ($FILEHAND = shift) {
X    unless (open(FILEHAND)) {
X        print STDERR "Can't open \"$FILEHAND\"\n";
X        next FILE;
X    }
X    if (!defined($opt_g) && -B FILEHAND) {
X        print STDERR "Skipping binary file \"$FILEHAND\"\n";
X        close(FILEHAND);
X        next FILE;
X    }
X    $file_name = &ps_string($FILEHAND);
X    $cur_pos = -1;     # this will force a new column next time
X    $cur_col = 100;    # this will force a new page next time
X    $line_no = 0;
X    LINE:
X    while (<FILEHAND>) {
X        chop;
X        $line_no++;
X        if (ord == 014) {		# form feed
X            s/.//;	# chop off first char
X            $cur_pos = -1; 
X            next LINE 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, same as calling
X            # "ps_string", but the overhead of calling a function is
X            # not acceptable here.
X            $text =~ s/[\\\(\)]/\\$&/g;
X            $text =~ s/[\000-\037\177-\377]/sprintf("\\%03o",ord($&))/ge;
X            # Calculate position
X            $x = $left_margin +
X		 ($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 || eof)) { # print line numbers
X                 printf "F2 SF($line_no)%.1f %.1f S F1 SF\n",
X                        $x + $col_width + 5, $cur_pos;
X            }
X        } while (length($_));
X    } # while (each line)
} # while (each file)
&end_page;
print "%%Trailer\n";
print "%%Pages: $page_no\n";
# printf "($prog: $page_no page%s for $user\n) print\n",
#     $page_no != 1 ? "s" : "";
X
#--end of main-------------------------------------------------------
X
X
sub prolog {
X   $user = getlogin || "(unknown)";
X   local($sec,$min,$hour,$mday,$mon,$year) = localtime;
X   $date = sprintf("(%s %d, %d) (%2d:%02d)",
X                    ('January','February','March','April','May','June',
X                     'July','August','October','November','Desember')[$mon],
X                     $mday, $year+1900, $hour,$min);
X   print "%!PS-Adobe-2.0\n";
X   print "%%Title: @ARGV\n" if (@ARGV);
X   print <<"EOT";
%%Creator: $prog, Text to PostScript filter in perl, (C) 1990 Gisle Aas, NCC
%%CreationDate: $date
%%For: $user
%%Pages: (atend)
EOT
X   print "%%DocumentFonts: $font";
X   print " $line_number_font" if ($opt_l);
X   print " $header_font" if ($show_header);
X   print "\n";
X   print <<"EOT";
%%EndComments
/S{moveto show}bind def
/M/moveto load def
/L/lineto load def
/SF/setfont load def
EOT
X    print <<"EOT" if ($isoencoding_not_builtin && !$norsk_ascii);
ISOLatin1Encoding where { pop } { ISOLatin1Encoding
[/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/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/.notdef/.notdef
/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/dotlessi/grave
/acute/circumflex/tilde/macron/breve/dotaccent/dieresis/.notdef/ring
/cedilla/.notdef/hungarumlaut/ogonek/caron/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]
def %ISOLatin1Encoding
} ifelse
EOT
X    print <<"EOT" if ($norsk_ascii);
%%BeginProcSet: reencode 1.0 0
/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
X      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
X   end
X   end
X   /FontName get exch definefont pop
} bind def
%%EndProcSet: reencode 1.0 0
EOT
X   print <<"EOT" if (!$norsk_ascii);
%%BeginProcSet: newencode 1.0 0
/NE { %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
X         /Encoding exch def
X         currentdict dup
X      end
X   end
X   /FontName get exch definefont pop
} bind def
%%EndProcSet: newencode 1.0 0
EOT
X   print "%%EndProlog\n%%BeginSetup\n";
X   if ($norsk_ascii) {
X      print "[8#133 /AE/Oslash/Aring 8#173 /ae/oslash/aring] dup\n";
X      print "/$font-ISO/$font RE\n";
X      print "/$header_font-ISO/$header_font RE\n" if ($show_header);
X   } else {
X      print "ISOLatin1Encoding /$font-ISO/$font NE\n";
X      print "ISOLatin1Encoding /$header_font-ISO/$header_font NE\n"
X         if ($show_header);
X   }
X   print "/F1/$font-ISO 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-ISO findfont $header_font_size scalefont def\n"
X        if ($show_header);
X   print "F1 SF\n";
X   print "%%EndSetup\n";
}
X
X
X
sub new_page {
X   &end_page unless ($page_no == 0);
X   $page_no++;
X   print "%%Page: $page_no $page_no\n";
X   print "%%BeginPageSetup\n";
X   print "/page_save save def\n";
X   printf "90 rotate 0 -%d translate %% landscape mode\n",&page_right_edge
X      if ($landscape);
X   print "0.15 setlinewidth\n" if ($show_header);
X   print "%%EndPageSetup\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
sub end_page {
X   unless ($page_no == 0) {
X      print "page_save restore\n";
X      print "showpage\n";
X   }
}
X
sub ps_string
{
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	# These next few lines are legal in both Perl and nroff.
X
.00;			# finish .ig
X 
'di			\" finish diversion--previous line must be blank
.nr nl 0-1		\" fake up transition to first page again
.nr % 0			\" start at page 1
';<<'.ex'; #__END__ #### From here on it's a standard manual page #########
.TH I2PS 1 "October 1990"
.SH NAME
i2ps \- convert ISO Latin1 text to PostScript
.SH SYNOPSIS
.B i2ps
[
.B \-nlrth23g
] [
.B \-b
.I "text"
] [
.B \-s
.I size
] [
.I filename
\&.\|.\|.
]
.SH DESCRIPTION
The
.B i2ps
program is used to print simple
text files (e.g. program listings) on a PostScript\*R device. 
The name
.B i2ps
stands for iso-to-postscript.  The program handles the whole
ISO Latin1 (ISO 8859/1) character set. 
Efforts have been made to ensure that 
.B i2ps
produces good looking and effective PostScript code.
The output conforms to Adobe's
document structuring conventions (version 2.1). The 
.B i2ps 
program
assumes that the page format on the output device is A4. 
(Change the definitions of &page_top and &page_right_edge if you
want something else.)
The meaning of form feed (^L) characters in the input stream is understood.
.PP
The
.B i2ps
program is written in
.I perl.
So if there is something you don't like about how
.B i2ps
works, you can "easily" fix it yourself.
.SH OPTIONS
.TP 5
.B \-n
use the norwegian version of ISO 646 (7-bit ascii) to encode the text.
Norwegian letters will replace the left/right braces, left/right
brackets, backslash and horizontal bar characters.
.TP 5
.B \-l
produces line numbers on each 5'th and the last line.
Nice for program listings.
.TP 5
.B \-t
truncate lines that are to long. The default is to wrap
long lines so that they continue on the next line on the printed output.
.TP 5
.B \-r
rotate the output page 90 degrees. This is called
.I landscape
mode by some people.
.TP 5
.B \-h
suppress generation of page headers.
.TP 5
.B \-2
use two column format.
.TP 5
.B \-3
use three column format.
.TP 5
.BI \-b " text"
the 
.I text
parameter replaces the default text in the header of the pages.
The default text is the filename of the file to be printed. There is no
default if the text comes from standard input.
.TP 5
.BI \-s " size"
specifies a new font size for the body text. The default is 10 point.
.TP 5
.B \-g
eat garbage. 
.B I2ps 
normally skips binary files. This option means "print it anyway".
.SH EXAMPLES
To print files on a PostScript printer:
.nf
X    i2ps -l *.[ch] | lpr
.fi
.PP
To find out how many pages i2ps will produce for a given file:
.nf
X    i2ps file.txt | tail -1
.fi
.PP
Our printer stacks the pages the wrong way. To fix it try:
.nf
X    cat file.txt | i2ps -b "some text" | psrev | lpr
.fi
.SH SEE ALSO
.BR perl (1),
.BR enscript (1),
.BR psrev (1),
.BR ISO8859 (7),
.BR PostScript (7)
.SH BUGS
There is no easy way to change the margins or the fonts that
.B i2ps
insists on using.
The
.B i2ps
filter ought to understand the meaning of the backspace character. It is used
to produce boldfaced and underlined text by programs like 
.BR nroff (1).
.SH AUTHOR
Gisle Aas, Norwegian Computing Center (NR), Oslo, Norway.
<Gisle.Aas@nr.no>
.ex
SHAR_EOF
chmod 0555 i2ps ||
echo 'restore of i2ps failed'
Wc_c="`wc -c < 'i2ps'`"
test 16140 -eq "$Wc_c" ||
	echo 'i2ps: original size 16140, current size' "$Wc_c"
fi
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