From: lindig@ips.cs.tu-bs.de (Christian Lindig)
Newsgroups: comp.lang.postscript
Subject: Solution: PS-Program for labeling binders
Date: 26 Sep 92 07:00:22 GMT
Reply-To: lindig@ips.cs.tu-bs.de
Organization: Inst. f. Informatik, TU Braunschweig, FRG


Hi,

a couple of days ago I asked for a program for labeling binders. A
nice solution was sent to me from Chris Kent Kantarjiev
<cak@parc.xerox.com>. His pslabel(1) needs an unix environment (the
ps-code is embeded in a shell script). Pslabel can be taken for
making labels of different sizes and is easy to adopt to personal
needs. A shell archive at the end of the article contains pslabel and
its manpage.

Christian 

------------------------------------------------------------------------
 Christian Lindig  				 lindig@ips.cs.tu-bs.de
------------------------------------------------------------------------
#!/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 09/26/1992 06:47 UTC by lindig@ips.cs.tu-bs.de
# Source directory /usr/users/infbsps1/lindig/misc
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   9304 -rwxrwxr-x pslabel/pslabel
#   4035 -rw-rw-r-- pslabel/pslabel.1
#
# ============= pslabel/pslabel ==============
if test ! -d 'pslabel'; then
    echo 'x - creating directory pslabel'
    mkdir 'pslabel'
fi
if test -f 'pslabel/pslabel' -a X"$1" != X"-c"; then
	echo 'x - skipping pslabel/pslabel (File already exists)'
else
echo 'x - extracting pslabel/pslabel (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'pslabel/pslabel' &&
#! /bin/sh
X
# pslabel -- create notebook spine labels with PostScript
# Chris Kent, DEC WRL, Tue Dec 23 14:58:38 1986
#	modified by kent, Mon May 18 11:33:13 1987
#	modified by kent, Wed Sep 23 17:58:47 1987
#
# pslabel [options] string1 string2 ...
# pslabel [] file		(takes one label per line)
# pslabel []			(reads stdin)
#	
# options:	-B	black background	(default is white)
#		-b	bold text		(default is plain)
#		+b	non-bold text
#		-F	font family		(default is Helvetica)
#		-h	height of label
#		-l	left justify		(default is centered)
#		-S	stack arguments onto single label
#		-s	point size		(default is 18)
#		-T	label type		(sets h, w, point size)
#		-w	width of label
#
X
BlackBackground=false
Bold=false
FontFamily=Helvetica
Size=0
DefaultSize=18
Height=0
Width=0
OtherWidth=0
Left=false
Stack=false
X
# be sure to change dimensions below, too!
X
case $0 in
*file-*)	Width=2.0
X	Height=0.625
X	OtherWidth=3.5
X	DefaultSize=28
X	;;
*drawer-*)	Width=2.75
X	Height=1.05
X	OtherWidth=5.5
X	DefaultSize=48
X	;;
*tape-*)	Width=3.125
X	Height=0.71
X	OtherWidth=3.25
X	DefaultSize=28
X	;;
*binder-*)Width=7.0
X	Height=1.9
X	OtherWidth=$Width
X	DefaultSize=70
X	;;
esac
X
X
TmpFile=/tmp/pslabelT$$
ArgFile=/tmp/pslabelA$$
trap "rm -f $TmpFile $ArgFile;exit" 0 1 2 3 15
X
if [ $# -eq 0 ]; then
X	echo Usage: $0 [-Black] [-bold] [-F font] [-hN] [-wN]
X	echo "	" [-l] [-S] [-s pointSize] [-T labelType] [labels] [files]
X	exit 1
fi
X
while [ $# -gt 0 ] ; do
X	case "$1" in
X	-B*)	BlackBackground=true
X		Bold=true ;;
X	-b*)	Bold=true ;;
X	+b*)	Bold=false ;;
X	-F*)	if [ $# -le 1 ]; then
X			echo "-F needs a font family name"
X			exit 1
X		fi
X		shift; FontFamily="$1" ;;
X	-h*)	Height=`expr "$1" : '-h\(.*\)'` 
X		;;
X	-l)	Left=true
X		;;
X	-P*)	PRINTER=`expr "$1" : '-P\(.*\)'`
X		export PRINTER
X		;;
X	-s*)	Size=`expr "$1" : '-s\(.*\)'`
X		;;
X	-S)	if $Stack ; then
X			echo "" >> $ArgFile
X		else
X			Stack=true
X		fi
X		;;
X	-T)	if [ $# -le 1 ]; then
X			echo "-T needs a label type"
X			echo "choose from file, drawer, tape or binder"
X			exit 1
X		fi
X		shift
X		# change dimensions above, too!
X		case "$1" in
X		file)	Width=2.0
X			Height=0.625
X			OtherWidth=3.5
X			DefaultSize=28
X			;;
X		drawer)	Width=2.75
X			Height=1.05
X			OtherWidth=5.5
X			DefaultSize=48
X			;;
X		tape)	Width=3.125
X			Height=0.71
X			OtherWidth=3.25
X			DefaultSize=28
X			;;
X		binder)	Width=7.0
X			Height=1.9
X			OtherWidth=$Width
X			DefaultSize=70
X			;;
X		esac
X		;;
X	-w*)	Width=`expr "$1" : '-w\(.*\)'`
X		if test -z "$Width"; then
X			Width=$OtherWidth
X		fi
X		;;
X	-*)	echo $0: $1 is not an option >&2
X		exit 1
X		;;
X	*)	if [ -r "$1" ]; then
X			cat $1 >> $ArgFile
X		else
X			echo $1 >> $ArgFile
X		fi
X	esac
X	shift
done
X
if [ -z "$Size" ]; then
X	Size=$DefaultSize
X	echo point size missing, using default of $DefaultSize
fi
if [ $Size = 0 ]; then
X	Size=$DefaultSize
fi
X
if [ \( -z "$Height" \) -o \( "$Height" = 0 \) ]; then
X	Height=1
X	echo height missing, using default of 1 inch
fi
X
X
Font=$FontFamily
if $Bold ; then
X	Font=`echo $Font | sed 's/-/%/`
X	Font=`echo $Font | sed 's/\$/%/`
X	Font=`echo $Font | sed 's/%/-Bold/`
X	Font=`echo $Font | sed 's/%\$//`
fi
cat > $TmpFile << fUnKyStUfF
%!
% pslabel prolog -- last modified by kent, Wed Sep 23 18:06:30 1987
%
X
% string width proc justify -
% break string into lines no longer than width. When a line is found,
% execute proc.
X
/wordBreak ( ) def
/lineBreak (\0) def
/justify {
X    /proc exch def
X    /width exch def
X    /textString exch def
X    
X    width 0 eq { /width fullWidth def } if
X
X    /brkWidth wordBreak stringwidth pop def
X    /curWidth 0 def
X    /lastWordBreak 0 def
X    /startChar 0 def
X    /moreText textString def
X    /nextWord () def
X    
X    {	moreText length 0 eq nextWord length 0 eq and
X          { exit } if
X	moreText wordBreak search
X	  { /nextWord exch def pop
X	    /moreText exch def
X	  }
X	  { /nextWord exch def
X	    /moreText () def
X	  } ifelse
X	/wordWidth nextWord stringwidth pop def
X	curWidth wordWidth add width gt
X	  { curWidth 0 eq
X	      { nextWord proc
X		/startChar lastWordBreak def
X		/curWidth 0 def
X	      }
X	      { textString startChar lastWordBreak startChar sub 1 sub
X		    getinterval proc
X		/startChar lastWordBreak def
X		/curWidth wordWidth brkWidth add def
X	      } ifelse
X	  }
X	  { /curWidth curWidth wordWidth add brkWidth add def
X	  } ifelse
X	/lastWordBreak lastWordBreak nextWord length add 1 add def
X    } loop
X    /lastChar textString length def
X    startChar lastChar le
X      { textString startChar lastChar startChar sub
X		getinterval proc
X      } if
} def
X
% string height width start shrinkToFit bbox size
% using "justify", get string to fit in the height by width box.
% start is the biggest point size to try. size is the size to use;
% bbox is the bounding box of the text path.
%
% it should probably do a binary search.
X
/shrinkToFit {
gsave
X    /ps exch def
X    /width exch def
X    /height exch def
X    /textString exch def
X
X    /fmesg (" failed at ) def
X    /msgString textString length fmesg length add 5 add string def
X    msgString 0 (") putinterval
X    msgString 1 textString putinterval
X    msgString textString length 1 add fmesg putinterval
X    /psLoc msgString length 4 sub def
X
X    {	/nLines 0 def /maxWidth 0 def
X	font ps scalefont setfont
X	textString width 
X	{   stringwidth pop dup
X	    maxWidth gt
X	    {	/maxWidth exch def }
X	    {	pop } ifelse
X	    /nLines nLines 1 add def }
X	   justify
X	/ury nLines 1.1 mul ps mul def
X	/lly ps .2 mul neg def
X	ps 1 eq { exit } if		% sanity check
X	ury lly sub height .9 mul le { exit } if
X	msgString psLoc ps 4 string cvs putinterval
X	msgString print (\n) print
X	/ps ps 1 sub def
X    } loop
X    
X    0				    % llx
X    nLines 1 sub
X	ps mul 1.1 mul 
X	ps .2 mul add neg	    % lly
X    maxWidth			    % urx
X    1.1 ps mul			    % ury
X    ps
grestore
} def
X
/startJob {
X    /font FontName findfont def
X    newpath clippath pathbbox
X	/maxY exch def /maxX exch def /minY exch def /minX exch def
X    /yMargin 18 def
X    /yLimit maxY minY sub yMargin sub neg def
X    /smtx matrix def
X    Left 0 eq
X      { /showLine { showCenter } def }
X      { /showLine { showLeft } def }
X      ifelse
X    H 0 eq
X      { /H PS 1.5 mul def
X        /W 0 def
X      } if
X    /fullWidth maxX minX sub def
} def
X
/beginPage {
X    maxX minX add 2 div maxY yMargin sub H 2 div sub translate
X    /y 0 def
} def
X
/endPage {
X    showpage
} def
X
/endLabel {
X    /y yStart H sub yMargin sub def
X    y H sub yLimit le { endPage beginPage } if
} def
X
/showCenter { dup stringwidth pop -2 div y moveto show advance } def
/showLeft { W -2 div ( ) stringwidth pop add y moveto show advance } def
/advance { /y y pointSize 1.1 mul sub def } def
/in { 72 mul } def
X
/setPosition {
X    /ury exch def /urx exch def /lly exch def /llx exch def
X    realWidth 0 eq
X        { /realWidth urx llx sub ( ) stringwidth pop 2 mul add def } if
X    smtx currentmatrix pop
X	0 yStart translate newpath 
X	0 0 moveto
X	realWidth -2 div ury lly add 2 div H 2 div add moveto
X	0 H neg rlineto
X	realWidth 0 rlineto
X	0 H rlineto
X	closepath
X    smtx setmatrix
} def
X
/l {	    % string l -
X    /s exch def
X
X    /yStart y def
X    /realWidth W def
X
X    0.0 setgray 0.1 setlinewidth
X    s H W PS shrinkToFit
X    dup /pointSize exch def font exch scalefont setfont
X    s W {showLine} justify
X    setPosition
X    Black 1 eq
X      { fill 
X	1.0 setgray
X	/y yStart def
X	s W {showLine} justify	    % sigh, must do it twice
X      } { stroke } ifelse
X    endLabel
} def
X
/Stack {
X    /lines exch def
X
X    /yStart y def
X    /realWidth W def
X
X    /widths lines length array def
X    /height 0 def
X    /maxWidth 0 def
X    /index 0 def
X
X    0.0 setgray 0.1 setlinewidth
X    font 1 scalefont setfont
X
X    lines {			    % calculate all the widths
X	stringwidth pop dup widths index 3 -1 roll put
X	dup maxWidth gt
X	  { /maxWidth exch def }
X	  { pop } ifelse
X	/index 1 index add def
X    } forall
X
X    /hRatio lines length 1.1 mul H div def
X    W 0 ne
X	{ /wRatio maxWidth W div def }
X	{ /wRatio 0 def } ifelse
X    wRatio hRatio gt		    % fill to x or y dimension?
X      { /pointSize 1 wRatio div def }
X      { /pointSize 1 hRatio div def }
X      ifelse
X    /pointSize .9 pointSize mul def
X    pointSize PS gt		    % specified point size smaller?
X      { /pointSize PS def } if
X    font pointSize scalefont setfont
X
X    0				    % llx
X    lines length 1 sub
X	pointSize mul 1.1 mul 
X	pointSize .2 mul add neg    % lly
X    maxWidth pointSize mul	    % urx
X    1.1 pointSize mul		    % ury
X    setPosition
X
X    Black 1 eq {
X	fill 
X	1.0 setgray
X    } { stroke } ifelse
X
X    /y yStart def
X    lines {
X	showLine
X    } forall
X	
X    endLabel
} def
% end fixed prolog
fUnKyStUfF
X
if $BlackBackground ; then
X	echo "/Black 1 def"					>> $TmpFile
else
X	echo "/Black 0 def"					>> $TmpFile
fi
X
if $Left ; then
X	echo "/Left 1 def"					>> $TmpFile
else
X	echo "/Left 0 def"					>> $TmpFile
fi
X
echo "/FontName /$Font def"					>> $TmpFile
echo "/PS $Size def"						>> $TmpFile
echo "/H $Height in def"					>> $TmpFile
echo "/W $Width in def"						>> $TmpFile
echo startJob							>> $TmpFile
echo beginPage							>> $TmpFile
if [ -r $ArgFile ] ; then
X	cat $ArgFile 
else
X	cat
fi |	\
X    if $Stack ; then
X	echo "["						>> $TmpFile
X	sed -e "s/^/(/" -e "s/\$/)/" -e "s/^()\$/] Stack [/"	>> $TmpFile
X	echo "] Stack"						>> $TmpFile
X    else
X	sed -e "s/^/(/" -e "s/\$/) l/"				>> $TmpFile
X    fi
echo endPage							>> $TmpFile
lpr -h $TmpFile
SHAR_EOF
chmod 0775 pslabel/pslabel ||
echo 'restore of pslabel/pslabel failed'
Wc_c="`wc -c < 'pslabel/pslabel'`"
test 9304 -eq "$Wc_c" ||
	echo 'pslabel/pslabel: original size 9304, current size' "$Wc_c"
fi
# ============= pslabel/pslabel.1 ==============
if test -f 'pslabel/pslabel.1' -a X"$1" != X"-c"; then
	echo 'x - skipping pslabel/pslabel.1 (File already exists)'
else
echo 'x - extracting pslabel/pslabel.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'pslabel/pslabel.1' &&
.TH PSLABEL 1 "5 October 1987" DECWRL
.SH NAME
pslabel \- print labels on PostScript printers
.SH SYNOPSIS
.B pslabel
[ option ] ...
[ file ] ...
.SH DESCRIPTION
.I Pslabel
produces a printed copy of one or more labels for various uses. 
Depending on the options selected, it can print labels suitable for binder
spines, Pendaflex file folders, tape seals, drawer labels, or labels of an
arbitrary size.
Each label is centered in a box the size of the appropriate label holder.
To make labels fit, 
.I pslabel
breaks multi-word labels into multiple lines and reduces
the point size as needed.
.PP
.I Pslabel 
can take label text from standard input, a named file, or the argument
list.
If text arguments are given and are not the name of a file, 
.I pslabel
uses them as the text for labels.
If a filename is given,
.I pslabel
reads label text, one label per line, from that file.
If neither is given,
.I pslabel
reads from standard input until end of file.
.PP
Options that apply are
.TP
.B \-b
Print using bold letters.
.TP
.B \-B
Print white text on a black background.
This automatically selects
.BR \-b .
.TP
.B +b
Don't print with bold letters. 
Probably only useful in conjunction with
.BR \-B .
.TP
.BI \-F \ fontFamily
Use 
.I fontFamily
to print the labels. 
.I fontFamily
may be any valid PostScript font family name installed in the destination
printer.
.TP
.BI \-h N
Print labels 
.I N
inches high.
If no label type is selected with
.B \-T
(below), the default height is just high enough to enclose a single line
of text in the specified point size.
.TP
.B \-l
Left justify the lines of the label. 
The default is to center them.
.TP
.B \-S
Rather than printing each argument as a separate label, stack the arguments one
above the other in a single label.
Use additional 
.B \-S
arguments (or blank lines in an input file)
to separate the components of additional labels.
.TP
.BI \-s N
Use characters that are
.I N
points high.
The default (starting) point size is 18.
.TP
.BI \-T \ labelType
Set height, width, and point size for a label of type
.IR labelType .
Valid choices are
.IR file ,
.IR drawer ,
.IR tape ,
and
.IR binder .
.TP 
.BI \-w N
Print labels
.I N
inches wide.
If 
.I N
is missing, and a specific label type has been selected, then the
alternate width for that label type is selected.
The default width is the entire width of the paper.
.PP
For example,
.RS
pslabel \-T drawer \-b ``Cookies and other goodies''
.RE
prints an easily visible drawer label.
.PP
.RS
pslabel \-T file -w \-F Helvetica-Oblique ``Incriminating evidence''
.RE
prints a label for Pendaflex folder label in an alternate font, while
.RS
pslabel \-T file \-w \-F Helvetica-Oblique ``Incriminating evidence''
.RE
fits the same label for third-cut file folder.
.PP
You might use
.RS
pslabel \-B \-s24 \-F NewCenturySchlbk ``Collected Christmas Carols''
.RE
to print a label for the edge of a document bound in a Cheshire binder.
.PP
To more explicitly specify the format, use 
.BR \-S .
For example
.RS
pslabel -T file -S ``1986 Accounts'' Public -S ``1986 Accounts'' Actual
.RE
generates two stacked labels with the specified layout.
.PP
In all cases, note the use of double quotes to protect multi\-word arguments.
.SH NOTES
To eliminate the need for the 
.B \-T
flag, install links of the form
.IB labelType \-label
for valid values of
.IR labelType .
.PP
The various standard widths and heights may need to be tuned for your
particular office supplies.
.PP
This program creates a rather long PostScript program, and may take quite
some time trying out different point sizes while getting a label to fit.
In this event, it prints messages in the printer status log to let you
know it hasn't gone nuts.
.SH BUGS
Undoubtedly many dealing with option processing. 
In particular, it is entirely too easy to specify bogus font names, 
which cause the job to abort with no notification other than that in the
printer log file. In particular, some fonts (such as AvantGarde) don't
have a Bold face.
.SH AUTHOR
Christopher A. Kent (DECWRL)
SHAR_EOF
chmod 0664 pslabel/pslabel.1 ||
echo 'restore of pslabel/pslabel.1 failed'
Wc_c="`wc -c < 'pslabel/pslabel.1'`"
test 4035 -eq "$Wc_c" ||
	echo 'pslabel/pslabel.1: original size 4035, current size' "$Wc_c"
fi
exit 0