Using Perl to Print PS

By Paul Murphy, author of The Unix Guide to Defenestration

There are situations in which what you want to do involves repeatedly printing the output of some process in the blanks on a pre-printed form. A client of mine, for example, routinely prints participant identification information on court provided forms used in civil litigation. They used to do this with typewriters, then with Word macros, and now in volume using Unix. Here's how.

First create a standard input file that has the information you need to print in some easily accessible form - simple delimited fields, one form per row, is easiest and what I'll use for this example but using one or more rows for each element isn't much harder as long as you insert something between the rows to clearly differentiate one "field" from the next.

Suppose, for example, that your input looks like:


Info for 1st form, 1st blank|Stuff for the 2nd blank|for the third
Info for 2nd form, 1st blank|Stuff for the 2nd blank|for the third
Info for 3rd form, 1st blank|Stuff for the 2nd blank|for the third

Now take the first line as a sample, put it in a file, and run a2ps on it:


% cat foo
Info for 1st form, 1st blank
Stuff for the 2nd blank
for the third

% a2ps foo -o foo.ps
[foo (plain): 1 page on 1 sheet]
[Total: 1 page on 1 sheet] saved into the file `foo.ps'

Now use vi to look at foo.ps and go right to the end where it says:


iso1dict begin
gsave
llx lly 12 add translate
/v 0 store
/x0 x v get 3.362408 add sx cw mul add store
/y0 y v get bfs th add sub store
x0 y0 moveto
(Info for 1st form, 1st blank) p n
(Stuff for the 2nd blank) N
(for the third ) N
(foo) (Page 1/1) (Oct 23, 03 13:22) title
border
grestore
(Printed by Paul) rhead
(foo) (1/1) (Thursday October 23, 2003) footer
end % of iso1dict
pagesave restore
showpage

Save everything down to and including the /x0 y0 moveto in a temp file -foo.hd in my case - and then delete it from your working file. Then delete the lines:


(foo) (Page 1/1) (Oct 23, 03 13:22) title
border
(Printed by Paul) rhead
(foo) (1/1) (Thursday October 23, 2003) footer

and change both the "p n" and the "N"s at the end of each line to just "p" while adding " X Y moveto" in front of each line that has one of your fields in it so that it looks like:


X Y moveto (Info for 1st form, 1st blank) p
X Y moveto (Stuff for the 2nd blank) p
X Y moveto (for the third ) p

and write the four lines:
grestore
end % of iso1dict
pagesave restore
showpage

to another temp file - in my case foo.tr - before deleting them from your working file.

The postscript display model is page-at-a-time, not line at a time like a regular line printer. The X and Y values position the field data anywhere on the page, starting with X=0 at the left hand side and Y=0 at the bottom. Thus:


40 720 moveto (Info for 1st form, 1st blank) p
180 510 moveto (Stuff for the 2nd blank) p
320 310 moveto (for the third ) p

produces a nice downward stairway pattern on the page.

Now to make all this come together, what you need is a perl script which first prints the header file (foo-hd), then processes its input fields to add the positioning line:


X Y moveto (field contents) p

for each field, and then prints the trailer (foo.tr):


$FS = '|';
print 'print each header row -619 of them - as a print statement (or use print << END)';

while (<>) {
($f1,$f2,$f3) = split(/[|\n]/, $_, 9999);

print '40 700 (' . $f1 . ") p\n";
print '40 670 (' . $f2 . ") p\n";
print '80 340 (' . $f3 . ") p\n";
}
print 'print each trailer file row -4 of them - as a print statement (or use print << END)';

You'll need to experiment to find the right X, Y values for each field - and you may want to figure out how to change font sizes or emphasis - but that's all there's to it.

Now
foo.pl input_file | lp -dPS_PRINTER

Will give you perfect, high volume, results every time - until they secretly revise the form, of course - then you'll waste a few hundred pages finding out!

Two notes:

  1. ghostscript usually sucks at previewing this stuff. Solaris 2.5.1 through eight have an openwin utility called pageview that works very well - otherwise just send it to the printer while you're experimenting.

  2. Any round brackets inside the field values have to be escaped - use something like
    sed 's/[()]/\\&/g' input_file | foo.pl | lp -dPS_PRINTER
    or just add this as step in the perl script.