#!/usr/local/bin/perl
# a2ps: an ASCII to PostScript transformer
# Copyright (C) 1998 J.P.M. de Vreught
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Contact information:
# Hans de Vreught <J.P.M.deVreught@cs.tudelft.nl>
# De Brink 70
# 2553 HA  The Hague
# The Netherlands

$version = "1.4.4";
&Defaults;
&Argument_Processing;
&Initialization;
$output = ">$output" if $output ne "" && $output !~ /^[|]/;
open(OUTPUT, $output) if $output ne "";
select(OUTPUT) if $output ne "";
$page = 0;
$line = 1;
while (<>) {
	&Print_Document_Header($ARGV) if $. == 1;
	s/^/\f/ if $line > 1 && $form_feed;
	$form_feed = (eof) ? 1 : 0;
	$_ = $_ . "\n" unless /\n/;
	@string = split(/\f/, $_);
	chop($string[$#string]);
	local($i);
	for ($i = 0; $i <= $#string; $i++) {
		&Print_Line(
			&Process_Line($string[$i]),
			$i < $#string ? "\f" : "\n"
		);
	}
}
&Print_Document_Header("/dev/null") if $. == 0;
&Print_Page_Trailer if $. > 0 && $line > 1;
&Print_Document_Trailer;
close(OUTPUT) if $output ne "";
exit 0;

sub Defaults {
	$header = 1;
	$page_format = "A4";
	$max_lines = 66;
	$vertical_stretch = 0.0;
	$horizontal_adjust = 0.0;
	$vertical_adjust = 0.0;
}

sub Argument_Processing {
	local($argument);
	while ($#ARGV >= 0) {
		return unless $ARGV[0] =~ /^-/;
		$argument = shift(@ARGV);
		if ($argument eq "-u") {
			&Usage(0);
		} elsif ($argument eq "-v") {
			&Version;
		} elsif ($argument eq "-h") {
			$header = 0;
		} elsif ($argument eq "-o") {
			$output = shift(@ARGV);
		} elsif ($argument eq "-p") {
			$page_format = shift(@ARGV);
			&Usage(1) unless $page_format eq "A4"
				|| $page_format eq "Letter";
		} elsif ($argument eq "-l") {
			$max_lines = shift(@ARGV);
			&Usage(1) unless $max_lines > 1;
			warn "Output OK?\n" if $max_lines < 60
				|| $max_lines > 72;
		} elsif ($argument eq "-s") {
			$vertical_stretch = shift(@ARGV);
			&Usage(1) unless $vertical_stretch > -100;
			warn "Output OK?\n" if $vertical_stretch < -5
				|| $vertical_stretch > 5;
		} elsif ($argument eq "-a") {
			$horizontal_adjust = shift(@ARGV);
			warn "Output OK?\n" if $horizontal_adjust < -35
				|| $horizontal_adjust > 35;
			$vertical_adjust = shift(@ARGV);
			warn "Output OK?\n" if $vertical_adjust < -35
				|| $vertical_adjust > 35;
		} elsif ($argument eq "--") {
			return;
		} else {
			&Usage(1);
		}
	}
}

sub Usage {
	local($status) = @_;
	select(STDERR) if $status;
	print <<"USAGE";
Usage: a2ps [-u|-v|-h|-o f|-p f|-l l|-s s|-a h v]... [--] [file]...
ASCII to PostScript transformer
   -u: usage (this page)
   -v: version
   -h: suppress header
   -o: send output to file or filter f
   -p: page format f (f is A4 or Letter)
   -l: number l of lines per page (l > 1; default 66)
   -s: stretch the line spacing by s percent (s > -100.0; default 0.0)
   -a: adjust picture h (default 0.0) points right
       and v (default 0.0) points up; 1 point = 1 / 72 inch
   --: treat all following arguments as input files
Use -l, -s, and -a with arguments close to the default.
USAGE
	exit $status;
}

sub Version {
	print <<"VERSION";
a2ps version $version extracted from ascii2postscript.$version.tar.gz

Copyright (C) 1996 J.P.M. de Vreught

This program comes with ABSOLUTELY NO WARRANTY.  This is free software, and
you are welcome to redistribute it under certain conditions. See the GNU
General Public License for more details.
VERSION
	exit 0;
}

sub Initialization {
	$offset = $header ? 2 : 0;
	$text_lines = $offset + $max_lines;
	$text_chars = 80;
	if ($page_format eq "A4") {
		# A4 = 210 mm x 297 mm
		$page_width = 595;
		$page_height = 843;
	} else {
		# Letter = 8.5 inch x 11 inch
		$page_width = 612;
		$page_height = 792;
	}
	$text_width = 480;
	$text_height = $page_height - ($page_width - $text_width);
	$text_height *= (100.0 + $vertical_stretch) / 100.0;
	$delta_width = -0.5;
	$delta_height = -2.3;
	$x_origin = $horizontal_adjust + ($page_width - 1 - $text_width) / 2;
	$y_origin = $vertical_adjust + ($page_height - 1 - $text_height) / 2;
	$llx = int($x_origin + $delta_width);
	$lly = int($y_origin + $delta_height);
	$rux = int($x_origin + $text_width + 1);
	$ruy = int($y_origin + $text_height + 1);
	$date = &create_date;
	$host = $ENV{'HOST'};
	$host = 'localhost' if $host eq "";
	$name = $ENV{'USER'};
	$name = $ENV{'LOGNAME'} if name eq "";
	if ($name eq "") {
		$creator = "anonymous\@$host (unknown user)";
	} else {
		$gcos = (getpwnam($name))[6];
		$creator = "$name\@$host ($gcos)";
	}
}

sub create_date {
	local($S, $M, $H, $d, $m, $y) = localtime;
	$S = "0" . $S if $S < 10;
	$M = "0" . $M if $M < 10;
	$H = "0" . $H if $H < 10;
	$d = "0" . $d if $d < 10;
	$m = (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)[$m];
	$y = "0" . $y if $y < 10;
	$date = "$y-$m-$d $H:$M:$S";
}

sub Print_Document_Header {
	local($file) = @_;
	print <<"POSTSCRIPT_HEADER";
%!PS-Adobe-1.0
%%DocumentFonts: Courier Courier-Bold
%%Title: $file
%%Creator: $creator
%%CreationDate: $date
%%Pages: (atend)
%%BoundingBox: $llx $lly $rux $ruy
%%EndComments
% Copyright: 1,/^%%EndProlog\$/ Hans de Vreught, 1996 (Freeware under GPL)
% Copyright: /^%%Trailer\$/,\$ Hans de Vreught, 1996 (Freeware under GPL)
% Simple constants
/X_Origin $x_origin def /Y_Origin $y_origin def
/Y_Height $text_height def /Total_Of_Lines $text_lines def
% Procedures and complex constants
/Start_Page {save} def
/Choose_Courier_Bold {/Courier-Bold findfont 10 scalefont setfont} def
/Choose_Courier {/Courier findfont 10 scalefont setfont} def
/Line_Spacing Y_Height Total_Of_Lines div def
/Y_Line {Line_Spacing Total_Of_Lines 5 -1 roll sub mul add} def
/Go_To_Line {X_Origin Y_Origin Y_Line moveto} def
/Go_To_Column {1 1 3 -1 roll {pop ( ) show} for} def
/Show_Line {Go_To_Line Go_To_Column show} def
/End_Page {restore showpage} def
% Save some space
/A {Start_Page} def
/B {Choose_Courier_Bold} def
/C {Choose_Courier} def
/S {Show_Line} def
/Z {End_Page} def
%%EndProlog
POSTSCRIPT_HEADER
}

sub Print_Document_Trailer {
	print <<"POSTSCRIPT_TRAILER";
%%Trailer
%%Pages: $page
POSTSCRIPT_TRAILER
}

sub Print_Page_Header {
	local($file, $page, $date) = @_;
	if ($header) {
		print <<"PAGE_HEADER";
%%Page: $page $page
A B
(File: $file   Page: $page   Date: $date) 0 1 S
C
PAGE_HEADER
	} else {
		print <<"PAGE_HEADER";
%%Page: $page $page
A C
PAGE_HEADER
	} 
}

sub Print_Page_Trailer {
	print <<"PAGE_TRAILER";
Z
PAGE_TRAILER
}

sub Process_Line {
	local($input) = @_;
	# Check if we can bail out right away:
	return $input unless $input =~ /[\r\b\t]/;
	local(@output) = split(/\r+/, $input);
	local($i, $j, $position);
	for ($i = 0; $i <= $#output; $i++) {
		$output[$i] = &Expand_Line($output[$i]);
		next if $i == $#output;
		$position = &End_Position($output[$i]);
		$output[$i] = $output[$i] . ("\b") x $position;
	}
	return join("", @output);
}

sub Expand_Line {
	local($input) = @_;
	local(@output) = split(//, $input);
	local($i, $j, $temp);
	local($position) = 0;
	for ($i = 0; $i <= $#output; $i++) {
		if ($output[$i] =~ /\t/) {
			$temp = $position;
			$position += 8;
			$position -= $position % 8;
			$output[$i] = (" ") x ($position - $temp);
		} elsif ($output[$i] =~ /[\b]/) {
			$position--;
			if ($position < 0) {
				$output[$i] = "";
				$position = 0;
			}
		} else {
			$position++;
		}
	}
	return join("", @output);
}

sub End_Position {
	local($input) = @_;
	local(@char) = split(//, $input);
	local($i);
	local($position) = 0;
	for ($i = 0; $i <= $#char; $i++) {
		if ($char[$i] =~ /[\b]/) {
			$position--;
		} else {
			$position++;
		}
	}
	return $position;
}

sub Print_Line {
	local($string, $end_of_string) = @_;
	local(@word) = split(/[\b]/, $string);
	local($i, @start, $head, $continue, $temp);
	$start[0] = 0;
	for ($i = 1; $i <= $#word; $i++) {
		$start[$i] = $start[$i - 1] + length($word[$i - 1]) - 1;
		$word[$i - 1] =~ s/ *$//;
	}
	$word[$#word] =~ s/ *$// unless $#word < 0;
	$continue = 1;
	while ($continue) {
		$continue = 0;
		&Print_Page_Header($ARGV, ++$page, $date) if $line == 1;
		for ($i = 0; $i <= $#word; $i++) {
			next if $word[$i] eq "";
			if ($start[$i] >= $text_chars) {
				$continue = 1;
				$start[$i] -= $text_chars;
				next;
			}
			$temp = $text_chars - $start[$i];
			($head, $word[$i]) = unpack("a$temp a*", $word[$i]);
			$continue |= $word[$i] ne "";
			&Print_String($head, $start[$i], $offset + $line);
			$start[$i] = 0;
		}
		$line++;
		next unless $continue && $line > $max_lines;
		&Print_Page_Trailer;
		$line = 1;
	}
	$line++ if $continue;
	return unless $end_of_string eq "\f" || $line > $max_lines;
	&Print_Page_Trailer;
	$line = 1;
}

sub Print_String {
	local($string, $start, $line) = @_;
	return if $string eq "";
	# Sanity processing
	$string = &Standard_Text_Encoding($string);
	# Check if we don't need backslashed octal characters:
	if ($string =~ /[\x20-\x7e]/) {
		$string =~ s/\\/\\\\/g;
		$string =~ s/\)/\\)/g;
		$string =~ s/\(/\\(/g;
		print "($string) $start $line S\n";
		return;
	}
	local($char);
	print "(";
	while ($string ne "") {
		$char = substr($string, 0, 1);
		substr($string, 0, 1) = "";
		if ($char =~ /[\x20-\x7e]/) {
			print "\\" if $char =~ /[\\\(\)]/;
			print $char;
		} else {
			$char = unpack("C", $char);
			printf("\\%03o", $char);
		}
	}
	print ") $start $line S\n";
}

sub Standard_Text_Encoding {
	local($string) = @_;
	# Filter out ASCII control characters:
	$string =~ s/[\x00-\x1f\x7f-\xa0\xff]/ /go;
	# Filter out unassigned characters (optional):
	$string =~ s/[\xb0\xb5\xbe\xc0\xc9\xcc\xd1-\xe0\xe2]/ /go;
	$string =~ s/[\xe4-\xe7\xec-\xf0\xf2-\xf4\xf6\xf7\xfc-\xfe]/ /go;
	return $string;
}
