[comp.lang.postscript] another label2ps

sarantos@notecnirp.Princeton.EDU (Sarantos Kapidakis) (03/24/89)

Here is my program label2ps, for printing labels in a postscript
printer.  In addition, any sequence of postscript commands can be
executed on every label, drawing any figure.
There are several options, for adjusting it to the user needs and
label sheet.
Nearly all the work is done in postscript
Also, I include a "labelsort" program, for completeness,
and manual pages.
sarantos
------------------------------ CUT HERE ------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	label2ps
#	label2ps.1
#	labelsort
#	labelsort.1
# This archive created: Thu Mar 23 18:43:03 1989
# By:	Sarantos Kapidakis (Princeton University CS Dept)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'label2ps'
then
	echo shar: "will not over-write existing file 'label2ps'"
else
cat << \SHAR_EOF > 'label2ps'
#!/bin/csh -f
set groups mode=centered nx=0 ny=0 feed deffile proc
set font=Times-Roman size=10 lines=0 rep=0 repx=1 copies=1
set wxo=18 wyo=39 wxe=594 wye=753 wx=180 wy=66 bx=18 by=6
set myargv=(`printenv LABEL2PS` $*:q)
set i=0
while($i < $#myargv)
	@ i++
	switch ($myargv[$i]:q)
	case -t:
		unset P
		breaksw
	case -P*:
		set P=$myargv[$i]
		breaksw
	case -W:
		if ($i + 4 > $#myargv) then
			echo ${0}: option $myargv[$i] requires 4 numeric arguments
			exit(2)
		endif
		@ i++; set wxo=$myargv[$i]:q
		@ i++; set wyo=$myargv[$i]:q
		@ i++; set wxe=$myargv[$i]:q
		@ i++; set wye=$myargv[$i]:q
		breaksw
	case -X:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set wxo=$myargv[$i]:q
		breaksw
	case -Y:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set wyo=$myargv[$i]:q
		breaksw
	case -x:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set wxe=$myargv[$i]:q
		breaksw
	case -y:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set wye=$myargv[$i]:q
		breaksw
	case -h:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set wy=$myargv[$i]:q
		breaksw
	case -w:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set wx=$myargv[$i]:q
		breaksw
	case -m:
		set feed=feed
		breaksw
	case -g:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires string argument
			exit(2)
		endif
		@ i++; set groups=$myargv[$i]:q
		breaksw
	case -d:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires string argument
			exit(2)
		endif
		@ i++; set deffile=($deffile $myargv[$i])
		breaksw
	case -ny:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set ny=$myargv[$i]:q
		breaksw
	case -nx:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set nx=$myargv[$i]:q
		breaksw
	case -by:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set by=$myargv[$i]:q
		breaksw
	case -bx:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set bx=$myargv[$i]:q
		breaksw
	case -T:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; @ wxo -= $myargv[$i]:q ; @ wxe -= $myargv[$i]:q
		@ i++; @ wyo -= $myargv[$i]:q ; @ wye -= $myargv[$i]:q
		breaksw
	case -j:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set j=$myargv[$i]:q
		@ wxo += $j ; @ wyo += $j ; @ wxe -= $j ; @ wye -= $j
		@ j *= 2 ; @ wx -= $j ; @ wy -= $j ; @ bx += $j ; @ by += $j
		breaksw
	case -c*:
		if ($myargv[$i]:q == '-c') then
			if ($i + 1 > $#myargv) then
				echo ${0}: option $myargv[$i] requires argument
				exit(2)
			endif
			@ i++; set colmode='-c'$myargv[$i]:q
		else
			set colmode=$myargv[$i]:q
		endif
		switch ($colmode:q)
		case -c2s:
			set wxo=27 wyo=72 wxe=585 wye=720 wx=256 wy=72 bx=46 by=24
			breaksw
		case -c3s:
			set wxo=27 wyo=48 wxe=585 wye=744 wx=162 wy=48 bx=36 by=24
			breaksw
		case -c2:
		case -c2l:
			set wxo=18 wyo=63 wxe=594 wye=729 wx=274 wy=90 bx=28 by=6
			breaksw
		case -c3:
		case -c3l:
			set wxo=18 wyo=39 wxe=594 wye=753 wx=180 wy=66 bx=18 by=6
			breaksw
		default:
			echo ${0}: ${colmode}: 'illegal form mode'
			exit(2)
		endsw
		breaksw
	case -p:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires argument
			exit(2)
		endif
		@ i++; set proc=$myargv[$i]:q
		breaksw
	case -#:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set copies=$myargv[$i]:q
		breaksw
	case -N:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set rep=$myargv[$i]:q
		breaksw
	case -l:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set lines=$myargv[$i]:q
		breaksw
	case -f:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires string argument
			exit(2)
		endif
		@ i++; set font=$myargv[$i]:q
		breaksw
	case -s:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set size=$myargv[$i]:q
		breaksw
	case -i:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires string argument
			exit(2)
		endif
		@ i++; set mode=$myargv[$i]:q
		if ("$mode" != normal && "$mode" != left && "$mode" != right && "$mode" != indent && "$mode" != centered) then
			echo ${0}: invalid argument to option -i: $myargv[$i]
			echo possible arguments: normal left right indent centered
			exit(2)
		endif
		breaksw
	case -n:
		if ($i + 1 > $#myargv) then
			echo ${0}: option $myargv[$i] requires numeric argument
			exit(2)
		endif
		@ i++; set repx=$myargv[$i]:q
		breaksw
	case -H:
cat << 'EOF'
-t: Put the output to the standard output(Default action).
-P*: Send the output to the corresponding printer.
-W xmin ymin xmax ymax: Set the device coordinate bounds.
-X xmin: Set the device coordinate xmin only.
-Y ymin: Set the device coordinate ymin only.
-x xmax: Set the device coordinate xmax only.
-y ymax: Set the device coordinate ymax only.
-h height: Set the height of the label.
-w width: Set the width of the label.
-m: Don't print the labels yet, wait for manual paper feed.
-g group: Print only the labels that in the '#' line contain any letter from the ``groups'' string.
-d deffile: At the end of the initialization, insert the contents of file deffile.
-bx dist: Specify the spacing between labels (horizontally).
-by dist: Specify the spacing between labels (vertically).
-ny rows: Set the number of rows to be printed.
-nx cols: Set the number of columns to be printed.
-T xdist ydist: Move everything by so many units in each direction.
-j dist: Ignore (don't use) dist units in each direction on the area of the label.
-c column_mode: Set the form of the label sheet,
	Available names for column_mode are: "2 3 2s 3s 2l 3l"
-p proc: Specify the postscript commands to execute on every label.
-# rep:	Print each page rep times, before proceeding to the next page.
-N rep: Set the number of times to repeat printing the labels.
-l lines: Specify the maximum number of lines on each label.  0 means no maximum.
-f font: Specify the font to be used for printing.
-s size: Specify the size of the font to be used for printing.
-i mode: Specify the line indentation mode: normal, left, right, indent & centered.
-n rep: Specify the number of times to print each label.
-H: Print this list (help) and exit.
'EOF'
		exit(0)
	case -*:
		echo ${0}: ${myargv[$i]}: 'illegal option'
		exit(2)
	default:
		@ i--
		break
	endsw
end
@ j = $i + 1
if ($?P) then
	if ($P == -P) set P=-P`printenv PSPRINTER`
	if ($P == -P) set P
	$0 $myargv[-$i]:q -t $myargv[$j-]:q | lpr $P
	exit
endif
set name=`printenv NAME`
if ("$name" != '') then
	if ("`printenv ORGANIZATION`" != '') \
		set name="$name, `printenv ORGANIZATION`"
	set name='('"$name"')'
endif

cat << EOF
%!PS-Adobe-1.0
%%Creator: $USER $name
%%Title: `hostname`:$cwd $0 $*:q
%%CreationDate: `date`
%%Pages: (atend)
%%DocumentFonts: $font
%%BoundingBox: $wxo $wyo $wxe $wye
%%EndComments
1 setlinecap 1 setlinejoin
0 setlinewidth

/worldlimits
{/ymax exch def /xmax exch def /ymin exch def /xmin exch def
 /yrange ymax ymin sub def /xrange xmax xmin sub def
} def

/fatal { dup print xmin ymin moveto show showpage quit } def
/feed {statusdict begin /manualfeed true def /manualfeedtimeout 300 def end} def

/box { 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath } def

/c_show { 1 index stringwidth pop sub 2 div 0 rmoveto show } def
/r_show { 1 index stringwidth pop sub 0 rmoveto show } def

/smallshow
{ gsave
  currentfont wx 2 index stringwidth pop div scalefont setfont show
  grestore
} def

/noleadingblanks {( ) {anchorsearch not {exit} if} loop} def
/blankslength	{dup noleadingblanks dup length 3 -1 roll length exch sub} def
/advancelines	{ysize mul ypos exch sub /ypos exch def xpos ypos moveto} def
/advanceline	{dup type /stringtype eq {1} if advancelines} def
/oneline	{advanceline dup stringwidth pop wx gt} def
/normalline	{oneline {smallshow} {show} ifelse} def
/leftline	{noleadingblanks oneline {smallshow} {show} ifelse} def
/rightline	{noleadingblanks oneline {smallshow} {wx r_show} ifelse} def
/centeredline	{noleadingblanks oneline {smallshow} {wx c_show} ifelse} def
/indentline	{oneline {smallshow} {blankslength size mul 0 rmoveto show} ifelse} def

$wxo $wyo $wxe $wye worldlimits
/wx $wx def	% width of each label
/wy $wy def	% height of each label
/by $by def	% y distance between labels
/bx $bx def	% x distance between labels
/nx $nx def	% number of columns (0 means maximum that can fit in the page)
/ny $ny def	% number of rows (0 means maximum that can fit in the page)
/#copies $copies def	% each page is copied so many times
/rx $repx def		% repeat each label (to adjacent labels) factor
/ry $rep def		% repeat the whole output factor
/font /$font def	% font to be used in the label
/size $size def		% size of letters in the label
/lines $lines def	% number of lines in the label
/userproc { $proc } def	% the user procedure, to draw at each label
/line /${mode}line load def % the line mode

/initlabel
{
  nx 0 eq {/nx xrange bx add wx bx add idiv dup 0 eq {pop 1} if def } if
  ny 0 eq {/ny yrange by add wy by add idiv dup 0 eq {pop 1} if def } if
  lines 0 gt {
    /ysize wy size sub lines 1 sub dup 0 gt {div} {pop pop size} ifelse def
    ysize size lt {(too many lines specified, decreace size) fatal} if} if
  font findfont size scalefont setfont
  /nxy nx ny mul def
  /nn 0 def
} def

% lines_array -> lines_array x y
/xylabel
{
  nn nxy eq {showpage /nn 0 def} if
  xmin wx bx add nn nx mod mul add
  ymin wy by add ny nn nx idiv sub mul by sub add
  /userproc load length 0 ne
  {gsave 2 copy wy sub translate userproc grestore} if
  /nn nn 1 add def
} def

% lines_array -> linesarray x y_vertical_centered
/cxylabel {xylabel wy 3 index length 1 sub ysize mul size add sub 2 div sub} def

/formlabel
{
  dup length
  lines 0 gt
    {lines gt {(too many lines on label, increace lines) fatal} if}
    { /ysize exch wy size sub exch 1 sub dup 0 gt {div} {pop pop size} ifelse
      size 1.2 mul 2 copy gt {exch} if pop def
    } ifelse
  rx {cxylabel size sub ysize add /ypos exch def /xpos exch def
    dup {line} forall} repeat pop
} def

/pictloop { nxy { xylabel pop pop } repeat } def
/pictpage { nn 0 eq {pictloop} if } def

$feed
initlabel
EOF
if ("$deffile" != '') cat $deffile
echo '%%EndProlog'
echo '%%Page: label 1'
if ($rep != '0') echo '['
set cmd
if ($rep == '0') set cmd='formlabel '
awk 'BEGIN{i=length(gr="'$groups:q'"); while(i > 0) g[substr(gr, i--, 1)]=1}\
	/^#/{if (q) {print "["a" ] '$cmd:q'"; a=""} if (gr=="") q=1; else { \
	i=length; while(i > 0 && (q=g[substr($0, i--, 1)]) == 0);} next} \
	q==1{a = a " (" $0 ")"} \
	END{if (q) print "["a" ] '$cmd:q'"}' \
	$myargv[$j-]
if ($rep != '0') echo '] ry {dup {formlabel} forall} repeat pop'
if ("$proc" != '') echo 'pictpage'
cat << EOF
showpage
%%Trailer
%%Pages: 1
EOF
exit(0)
SHAR_EOF
chmod +x 'label2ps'
fi
if test -f 'label2ps.1'
then
	echo shar: "will not over-write existing file 'label2ps.1'"
else
cat << \SHAR_EOF > 'label2ps.1'
.TH LABEL2PS 1 "February 3, 1989" local tools
.\" $Header$
.SH NAME
label2ps \- convert labels to postscript
.SH SYNTAX
.B label2ps
[
.BI \-P *
][
.B \-m
][
.BI \-g\  group
][
.BI \-c\  colmode
][
.BI \-#\  rep
][
.BI \-i\  mode
] [
.I other_options
] [
.B \-H
] [
.I files
]
.SH DESCRIPTION
.I label2ps
converts labels,
with format as in
.B labels
command,
to postscript.
.PP
.I label2ps
can select some of the labels for printing,
can use different fonts, size of letters,
line indentation mode,
different label papers,
and can handle very long lines, too.
The idea is that you print the labels into plain paper,
until the output is what you want,
changing some of the options each time.
.PP
.I label2ps
has 3 ways for repeating labels.
One is to repeat every label,
before proceeding to the next label,
the other is to repeat every page,
before printing the next page,
and the last one is to repeat printing everything,
after printing them,
without changing pages between different rounds of printing.
This last way, needs to remember all the labels,
and may not be able to handle very long lists of labels.
.PP
All options, except
.B \-P ,
need their argument to be separated with space(s) from them.
.PP
The available options are:
.TP
.B \-t
Put the postscript output to the standard output (default).
.TP
.BI \-P *
Send the postscript output to the corresponding printer.
If no printer is given (adjacent to the
.B \-P
option),
the environment variable
.B PSPRINTER
is used to decide which printer,
and if this is not defined,
the default printer is used.
.TP
.BI \-W\  "xmin ymin xmax ymax"
Set the device coordinate bounds.
This is useful for papers of non standard size.
.TP
.BI \-X\  xmin
Set the device coordinate xmin only.
.TP
.BI \-Y\  ymin
Set the device coordinate ymin only.
.TP
.BI \-x\  xmax
Set the device coordinate xmax only.
.TP
.BI \-y\  ymax
Set the device coordinate ymax only.
.TP
.B \-m
Don't print the labels yet, wait for manual paper feed.
Timed out is set to 5 minutes.
.TP
.BI \-g\  group
Print only the labels that in their
.B #
line (first line)
contain any letter from the
.I groups
string.
.TP
.BI \-d\  deffile
At the end of the initialization,
insert the contents of file
.I deffile ,
containing postscript definitions
(possibly font construction specifications).
It is called at the end of the prologue,
so it can overwrite any settings.
.TP
.BI \-bx\  dist
Specify the horizontal spacing between labels.
.TP
.BI \-by\  dist
Specify the vertical spacing between labels.
.TP
.BI \-ny\  rows
Set the number of rows to be printed.
Print only in these rows.
The size of the labels does not change in this way, see
.B \-c
for that.
.TP
.BI \-nx\  cols
Set the number of columns to be printed.
Print only in these columns.
The size of the labels does not change in this way, see
.B \-c
for that.
.TP
.BI \-T\  "xdist ydist"
Move everything on the paper by
.I xdist
units on the x-axis and
.I ydist
units on the y-axis.
Useful for printing labels on not well adjusted printers.
.TP
.BI \-j\  dist
Ignore (don't use, consider it junk)
.I dist
units in each of the four sides of the label.
Useful for printing labels on not well adjusted printers.
.TP
.BI \-c\  column_mode
Set the form of the label sheet,
changing many of the other options, for known label sheet formats.
Usually the names come from the number of columns.
Available names for
.I column_mode
are:
.B "2 3 2s 3s 2l 3l" ,
where the suffix
.B "s"
means shorter than the maximum
(for not well adjusted printers), and
.B "l"
means long, the maximum (which is the default).
More modes will be added for new types of papers,
to avoid typing the numbers every time.
It may be the most useful option.
.TP
.BI \-p\  proc
Specify any postscript commands to be executed on every label.
Usually it will be a postscript procedure,
defined in the included files
(see
.B \-d ).
It is executed once for each label printed,
before the text of the label,
and if no labels are given,
the function result will fill exactly one page of labels
(which can be duplicated using the
.BI \-#
option).
Only one procedure (or inline sequence of commands)
can be defined this way,
but the procedure itself can create other special effects,
like producing different picture on odd or even invocations.
The procedure is executed at the lower left corner of each label area,
without scaling or clipping, and should be bounded by the box:
.B "0 0 /wx /wy" .
The procedure is welcome to produce any side effects
(like pictures overlapping with other labels), 
or even to change even procedure definitions.
Similarly, the postscript code inserted using the
.B \-d
option, can change procedure definitions,
or program parameters.
But the final result is then the user's responsibility.
.IP
For example, the option
.BI \-p " \' 0 0 moveto wx wy box stroke \'"
will draw the border of each label, for testing the label fitting
on the paper.
.TP
.BI \-#\  rep
Print each page
.I rep
times, before proceeding to the next page.
Compare with option
.BI \-n " and " \-N .
.TP
.BI \-N\  rep
Set the number of times to repeat printing the labels.
The whole process will be repeated
.I rep
times.
Compare with option
.BI \-n " and " \-# .
.TP
.BI \-l\  lines
Specify the maximum number of lines on each label.
The default is 0, which means no maximum,
but has the disadvantage that spacing is not the same on all labels.
.TP
.BI \-f\  font
Specify the font to be used for printing.
The default is
.B Times-Roman.
.TP
.BI \-s\  size
Specify the size of the font to be used for printing.
The default is 10.
.TP
.BI \-i\  mode
Specify the line indentation mode:
.B normal
(each line is printed as it is),
.B left
(each line is printed left justified),
.B right ,
(each line is printed right justified),
.B indent ,
(spaces at the beginning of the line are given extra width),
and
.B centered
(each line is printed centered),
which is the default
.TP
.BI \-n\  rep
Specify the number of times to print each label.
All these prints will be adjacent to each other.
Compare with option
.BI \-N " and " \-# .
.TP
.B \-H
Print a helping list and exit.
.SH ENVIRONMENT
If
.B LABEL2PS
is defined,
it contains initial arguments to the
.I label2ps
program,
and if
.B PSPRINTER
is defined,
it is used as the default printer
(option
.B \-P
without (adjacent) argument).
Also
.B USER ,
.B NAME
and
.B ORGANIZATION ,
if defined,
are used, and inserted in comments.
.SH BUGS
Very long label listings (more than 1000 labels ?)
may exceed postscript limitations,
when using
\-N .
.SH AUTHOR
Sarantos Kapidakis
.SH "SEE ALSO"
pslabels(1),
labels(1),
labelsort(1),
zipsort(1)
SHAR_EOF
fi
if test -f 'labelsort'
then
	echo shar: "will not over-write existing file 'labelsort'"
else
cat << \SHAR_EOF > 'labelsort'
#!/bin/csh -f
# Sort labels, according to the last field of the first label line
# (the one after the ``#'' line).
# (The fields of the line are space/tab separated).

# If that field includes no trailing dots, only the part after the
# last of these dots will be used in comparisons.

# The input text should not include the character ``:'',
# otherwise replace it everywhere in this file with another character.

awk '/^#/{if (NR>1) print m; m = $0; next} \
	m!=""{e=length($NF); i=1 ; \
		while(e > 0 && substr($NF, e, 1) == ".") e--; \
		while((j=index(substr($NF, i, e-i), ".")) != 0) i+=j; \
		printf "%s %s:%s", substr($NF, i), $0, m; m=""} \
	{printf ":%s", $0} \
	END{printf "\n"}' $* | \
sort -df | \
awk -F: '{i=1; while(i++ < NF) print $i}'
exit(0)
# The older version follows, it does not handle dots.
# old #	awk '/^#/{if (NR>1) print "\n"; m = m $0; next} \
# old #		m!=""{printf "%s %s:%s", $NF, $0, m; m=""} \
# old #		{printf ":%s", $0} \
# old #		END{printf "\n"}' $* | \
# old #	sort -df | \
# old #	awk -F: '{i=1; while(i++ < NF) print $i}'
SHAR_EOF
chmod +x 'labelsort'
fi
if test -f 'labelsort.1'
then
	echo shar: "will not over-write existing file 'labelsort.1'"
else
cat << \SHAR_EOF > 'labelsort.1'
.TH LABELSORT 1 "February 15, 1989" local tools
.\" $Header$
.SH NAME
labelsort \- sort labels according to the last name
.SH SYNTAX
.B labelsort
[
.I files
]
.SH DESCRIPTION
.I labelsort
takes as input labels, and produces as output the same labels, but sorted.
The label format is as in the
.B labels
command.
.I labelsort
sorts labels according to the last field of the first label line
(the one after the ``#'' line).
The fields of the line are space/tab separated.
.PP
If that field includes no trailing dots, only the part after the
last of these dots will be used in comparisons.
.PP
The input text should not include the character ``:'',
otherwise replace it everywhere in this file with another character.
.PP
There are no options.
.SH AUTHOR
Sarantos Kapidakis
.SH "SEE ALSO"
labels(1),
zipsort(1),
label2ps(1)
SHAR_EOF
fi
exit 0
#	End of shell archive