#!/bin/sh # /usr/local/bin/lpf_vsl by Julian Stacey # Installed from ~jhs/public_html/src/bsd/jhs/bin/local/lpf_vsl/lpf_vsl # See also: /usr/share/doc/handbook/printing.{html,latin1} # Alternative I haven't tried: /usr/ports/print/apsfilter & # Bug: date|lpr in /var/log/lpd-errs produces 2 x "[: Tue: unexpected operator" # I guess its the input format detector going wrong. # I never wrote it to work on one line files. # I don't much care. log=/var/log/lpf_vsl dbg=echo $dbg " " >> $log $dbg "`date`: lpf_vsl start" >> $log # man printcap: # The if filter is invoked with arguments: # if [-c] -wwidth -llength -iindent -n login -h host acct-file # I just ignore arguments delivered here for now. # JJLATER If the "-h host" component has a bjc in it, generate bjc not pcl. # gs="-sPAPERSIZE=a4 -dFIXEDMEDIA -dNOPAUSE -q -sDEVICE=ljet3 -sOutputFile=- -" # gs="/usr/local/bin/gs $gs -dBATCH -dSAFER" gs="/usr/local/bin/gs -sPAPERSIZE=a4 -dFIXEDMEDIA -dNOPAUSE -q -sDEVICE=ljet3 -sOutputFile=- -dBATCH -dSAFER -" # PDF Processing: gs now accepts PDF as input pdfin="$gs" $dbg "pdfin = $pdfin" >> $log # Previously, I used to try to convert PDF to PS with this: # pdf2ps="/usr/local/bin/pdf2ps -sPAPERSIZE=a4" # Avoid pdf2ps error "exec: gs: not found" in /var/log/lpd-errs # PATH=:/bin:/usr/bin:/usr/local/bin ; export PATH # Although man pdf2ps says # pdf2ps [ options ] input.pdf [output.ps] # The second parameter is not actually optional, defaulting to pipe if # absent, it's just: `basename "$1" \.pdf`.ps # From /usr/local/bin/pdf2ps: -c save pop -f "$1" tmp=/var/spool/output/lpd.lpf_vsl/$$.tmp # $$ is PID $dbg "tmp = $tmp" >> $log # Keep $tmp same as in /etc/printcap # chmod 755 `dirname $tmp` ; chown daemon:daemon `dirname $tmp` # Suppress Inter Field Seperator so it will not swallow the leading # white space on the first line of plain text input, between left # margin & first printable character. # IFS= # Read first characters of first line, to determine file type. # This syntax was not good enough: # read first_line # because a PCL file contains nulls \0 (a .tiff too), EG below from # ~/job/project/cd/label/4.6/box_vsl_i386.pcl # 033 E 033 & l 2 6 A 033 & l 0 o 0 l 0 # E 033 & l - 1 8 0 u 3 6 Z 033 * r 0 # F 033 & l 1 X 033 * r B 033 * p 0 x 0 # Y 033 * t 3 0 0 R 033 * p + 3 0 9 Y # 033 * r 1 A 033 * b 3 M 033 * b 3 W 037 # 333 p 033 * b 3 W 037 333 360 033 * b 3 W 037 # 333 370 033 * b 3 W 037 332 001 033 * b 3 W 037 # 333 374 033 * b 3 W 037 333 334 033 * b 3 W 037 # 332 003 033 * b 3 W 037 333 236 033 * b 4 W ? # 332 007 216 033 * b 3 W 037 333 017 033 * b 3 W # 037 333 007 033 * b 5 W 037 332 017 001 200 033 * b # 3 W 037 332 016 033 * b 4 W ? 332 036 003 033 * # b 5 W 037 332 034 001 300 033 * b 4 W ? 332 < # 001 033 * b 3 W 037 334 340 033 * b 7 W ? 332 # 8 \0 & 001 300 033 * b 5 W 037 332 x \b 370 033 # * b 9 W 037 332 p 001 360 E \0 ? 200 033 * b # 8 W 037 332 360 001 p & 007 360 033 * b 8 W 037 # 332 \0 001 \0 & \0 376 033 * b 4 W ? 344 037 300 # 033 * b 4 W ? 344 003 370 033 * b 4 W ? 344 # \0 ~ 033 * b 7 W _ 332 377 377 360 \b 016 033 * # b 5 W 037 331 001 013 \0 033 * b 3 W 037 331 003 # 033 * b 3 W 037 331 007 033 * b 8 W _ 332 340 # \0 \0 ' 377 376 033 * b 5 W 037 332 200 \b 001 033 # * b 5 W 037 332 \0 \b 003 033 * b 6 W ? 344 # 300 \0 004 007 033 * b 3 W 037 344 200 033 * b 3 # W 037 332 200 033 * b 5 W 037 331 003 020 \0 033 * # b 4 W ? 331 001 300 033 * b 2 M 033 * b 2 # 6 W 371 \0 003 \0 \0 \0 177 202 377 320 377 001 377 340 # 307 \0 001 \0 360 373 \0 003 \0 \0 003 200 033 * b 2 # 8 W 371 \0 003 \0 \0 \0 @ 202 \0 320 \0 001 \0 # 307 \0 003 \0 377 377 360 375 \0 003 \0 \0 001 300 033 * # b 3 M 033 * b 5 W 037 331 001 \n # None of the shell variables & other unix commands (EG echo printf etc) # tools I've tried cope with it, so to avoid writing a C program, I swallow # the first line whole & unchanged with head. # I lost some PCL data with this: # head -1 > $tmp # first_line=`cat $tmp` # So now I swallow whole file (tough if its real big, this proc. wont # work as a pipe. cat > $tmp ; first_line=`head -1 $tmp` # Restore Inter Field Seperator to space tab nl # IFS="\040\011\012" first_two_chars=`expr "$first_line" : '\(..\)'` first_four_chars=`expr "$first_line" : '\(....\)'` first_six_chars=`expr "$first_line" : '\(......\)'` $dbg "`date`: Detecting format." >> $log if [ "$first_four_chars" = "%!PS" ]; then $dbg "Format: POSTSCRIPT from GROFF" >> $log # PostScript: use Ghostscript to convert and print it # groff generated .ps files start: "%!PS-Adobe-3.0" # I do not need to emit $first_line for gs, as I tested ghostview # without first line, & it displayed OK, & ghostview uses gs internaly. # Example: # %!PS-Adobe-3.0 # %%Creator: Applixware $gs $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ "$first_four_chars" = "%!ps" ]; then $dbg "Format: POSTSCRIPT not from GROFF" >> $log $gs $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ `expr "$first_line" : '\(...\)'` = '.\"' ]; then $dbg "Format: GROFF" >> $log # Note the backslash while needed in the .rof file, used to be # discarded by # read first_line # before I received it here, however now using: # cat > $tmp ; first_line=`head -1 $tmp` # it is there. cat $tmp | groff -U -b -s -t -Tps -dformat=ps -dumlauts=p | \ $gs && rm $tmp && exit 0 # I could instead generate pcl direct from groff. $dbg "Format has failed" >> $log exit 1 elif [ `expr "$first_line" : '\(........\)'` = '%PDF-1.2' ]; then $dbg "Format: PDF-1.2" >> $log # from ps2pdf, groff, external $pdfin $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ `expr "$first_line" : '\(........\)'` = '%PDF-1.3' ]; then $dbg "Format: PDF-1.3" >> $log # From external # From Phil Walters I received %PDF-1.3 %âãÏÓ $pdfin $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ `expr "$first_line" : '\(........\)'` = '%PDF-1.4' ]; then $dbg "Format: PDF-1.4" >> $log # From external # From Companies House change of address form # %PDF-1.4?%?????? # 2544232302EECD00 # 5046D1E4D523F3DA # 0D=Carriage Return # From GEA Prog 2008_04.pdf # %PDF-1.4? # 254423230 # 5046D1E4A # 0A=New Line $pdfin $tmp && rm $tmp && exit 0 # GNU Ghostscript 7.07: Unrecoverable error, exit code 1 # Segmentation fault # However xpdf can display it, & can export it to a .ps file # which this can print. So I discovered this works OK generating .ps # pdftops $tmp - > t.ps # So merge the 2: # /usr/X11R6/bin/pdftops $tmp - | $pdfin && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ `expr "$first_line" : '\(........\)'` = '%PDF-1.5' ]; then # Never seen a 1.5, but presumably they exist. /usr/X11R6/bin/pdftops $tmp - | $pdfin && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ `expr "$first_line" : '\(........\)'` = '%PDF-1.6' ]; then # from mk@, scanner@msd. /usr/X11R6/bin/pdftops $tmp - | $pdfin && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ `expr "$first_line" : '\(....\)'` = '%PDF' ]; then $dbg "Format: PDF Unknown Version" >> $log # Not from ps2pdf, upper case $pdfin $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ `expr "$first_line" : '\(....\)'` = '%pdf' ]; then $dbg "Format: pdf unknown version" >> $log # not from ps2pdf, lower case $pdfin $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ $first_two_chars = '\033E' ]; then ( echo "PCL detected by /usr/local/bin/lpf_vsl" | mail -s "PCL from lpf_vsl" jhs ) $dbg "Format: PCL" >> $log # Escape E # HP/PCL: The big HP PCL 5 book recomends all PCL files should # start with this, & ghostscript generates it, so it's a safe bet. # Example: "^[E^[&l26A" from gs # I hope data in first line is not longer than $first_line can read ! # Some PCL files go berserk, & the whole damn file has no \n, # or just one at end. cat $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ `expr "$first_line" : '\(...\)'` = 'II*' ]; then $dbg "Format: TIFF" >> $log # From eg http://berklix.com/scanjet/ /usr/local/bin/tiff2ps -a $tmp | $gs && rm $tmp && exit 0 # The match pattern should have a null, ie 'II*\0' # but I haven't got the elif to match on that. $dbg "Format has failed" >> $log exit 1 elif [ "`echo \"$first_line\" | file -b -`" = "JPEG image data" ]; then $dbg "Format: JPG" >> $log /usr/local/bin/jpegtopnm < $tmp | /usr/local/bin/pnmtops \ | $gs && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 elif [ $first_six_chars = 'GIF89a' ]; then $dbg "Format: GIF" >> $log /usr/local/bin/gif2ps < $tmp | $gs && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # In a mail Removing Received: lines changes file report # From "ASCII mail text" # To: "ASCII HTML document text" OR "ASCII text" if a non MIME # so JJLATER trap "ASCII mail text". else $dbg "Format: Dangerously Assuming ASCII" >> $log # Hope its not a binary that will consume paper. # Plain text: print a form at the end to eject the last page. # EscE: Initialise printer to default state with EscE, # in case last use has left it in a mangled state # EG host computer might have died in mid print. # EscE: Initialise again for good measure: # maybe the printer might not recognise first Reset if it # gets swallowed into an embedded sequence, or perhaps the # printer is on a flakey dynamic multiplexer, & the first # couple of characters get lost, while the multiplexer is # switching the printer to a different host computer. # Unlike with a hayes escape, a pcl scape does not seem to # require a quiet period afterward, according to the big # HP PCL manual. # Esc&k2G: Tell printer to treat LF as CR+LF. # EscE Reset to default for next user (that on a dynamic # multiplexer may not even be Unix+lpd ) # A printer reset also has the side effect of causing # the printer to flush the last partial page (if any), whereas # If we sent a FF instead, we could end up printing an # extra blank page, if either { (A) the input stream of data had # terminated with a page aligning FF, or (B) the last line feed # happened to align the printer to end of page. printf "\033E\033&k2G" && cat $tmp && \ printf "\033E" && rm $tmp && exit 0 fi # About Trailing Form Feed: JJLATER # - The trailing Reset (\033E) Form Feed = New Page =Control L = (\014) # should be selective, sampling data at end of file, but it's not (yet), # - Printers can be set up to also require FF or not, # generatin their own instead. If both do FF you get a wasted page. # - If neither do it, the job doesn't print till next job, or until you manually # send an empty page. # - My HP is set up to generate its own trailing NP=FF # - My Brother is set up to want an FF from computer. # - Applixware.pcl does not have a terminating FF. # - Groff.pcl produced by groff -U -b -s -t -Tlj4 -dformat=lj4 # thing.rof > thing.pcl ) Have a terminating ^L^[E # - .pcl made by groff -U -b -s -t -Tlj4 -dformat=lj4 thing.rof > thing.pcl # Ends with ^L^[E (& has bits of words in, not pure bitmap), for CV ~55K. # - .pcl made by gs -sDEVICE=ljet3 -sOutputFile=thing.pcl -- thing.ps # also ends in ^L^[E (but seems to have no strings, just bit map, & is # much larger, for my CV ~650K) # JJLATER add a trap to rm $tmp # Should never get this far. echo "`date`: /usr/local/bin/lpf_vsl failure unrecognised format $tmp" | mail -s "lpf_vsl failure" root echo "`date`: /usr/local/bin/lpf_vsl failure unrecognised format $tmp" >> $log exit 2 # only on error. # /usr/share/doc/handbook/handbook93.html: # The text filter, confusingly called the input filter in LPD documentation, # handles regular text printing. Think of it as the default filter. LPD expects # every printer to be able to print plain text by default, and it is the text # filter's job to make sure backspaces, tabs, or other special characters do not # confuse the printer. If you are in an environment where you have to # account for printer usage, the text filter must also account for pages # printed, usually by counting the number of lines printed and comparing that # to the number of lines per page the printer supports. The text filter is # started with the following argument list: # [-c] -wwidth -llength -iindent -n login -h host acct-file