#! /bin/bash
########
# pstoturboprint - CUPS spooler filter for turboprint driver system
#
# stdin = postscript
# conversion to bitmap image data using ghostscript
# conversion to printer data using tpprint
#
# Copyright 2002-2011 ZEDOnet
# Version 2.00
# Created 2.10.2002
# 07.04.2003 process substitution >(...) removed (problems with bash 2.05)
# 08.05.2003 set PATH variable (gs sometimes not found)
# 22.10.2003 named pipe between ghostscript and tpfilter (fixes problem with
#            ghostscript writing messages to stdout even if -q set)
# 07.04.2005 1.92-2 optionally use cupsraster format
# 22.09.2005 1.93 don't use cupsraster - problems with some gs versions
# 12.06.2007 TurboPrint 2 extensions manual duplex, preview
# 21.02.2008 added reverse printing order for duplex
# 14.10.2008 moved second part of duplex into separate print job
# 05.01.2009 min. gnu gs version with cupsraster device: 8.61 instead of 8.60
# 10.07.2009 remove quotes (') from document title
# 15.07.2009 also accept PDF as input format
# 02.02.2010 added quotes (') around print settings
# 25.02.2010 pass on options to duplex print job
# 23.09.2010 set tpprint argument -y (copies) only for cups device
# 16.11.2010 set number of copies also for PDF documents
# 17.03.2011 allow quotes (') in printer name
# 19.09.2011 acrobat reader duplex printing fix
# 17.10.2011 workaround for "garbled font" bug in gs9.0 (DisableFAPI)
# 29.11.2011 use zedonet version of gs if present
# 19.02.2013 workaround for copies bug in Ubuntu 12.10
########
PATH=/bin:/usr/bin:/usr/local/bin
PATH=${PATH}:/usr/bin/TeX:/usr/X11R6/bin
PATH=${PATH}:/sbin:/usr/sbin
#export PATH

########
# get Turboprint paths
########
eval $(cat "/etc/turboprint/system.cfg")
LOGFILE="$TPPATH_LOG/turboprint/print.log"
TPSTDIN="$TPPATH_FILTERS/tpstdin"
TPPRINT="$TPPATH_BIN/tpprint"
#TPGS="$TPPATH_SHARE/gs/bin/gs"
TPGS="gszedo"

########
# job cancelling & cleanup procedure
########
cleanup()
{
	echo "job cancelling procedure - TPPID=$TPPID GSPID=$GSPID PVPID=$PVPID" >> $LOGFILE
	########
	# terminate gs & tpprint background process
	# (tpprint should eject current page)
	########
	if [ $TPPID ] ; then
		kill -INT $TPPID >> $LOGFILE
	fi
	if [ $GSPID ] ; then
		kill -TERM $GSPID >> $LOGFILE
	fi
	if [ $PVPID ] ; then
		kill -TERM $PVPID >> $LOGFILE
	fi
	if [ $TPPID ] ; then
		COUNT=0
		while [ $COUNT -lt 30 ] ; do
			sleep 1
			COUNT=$[ $COUNT + 1 ]
			ps $TPPID &> /dev/null
			if [ $? -ne 0 ] ; then
				COUNT=30
			fi
		done
		# if not terminated within timeout, kill using TERM
		# sleep 30
		kill -TERM $TPPID >> $LOGFILE
	fi
	########
	# wait for child process
	########
	wait
	########
	# remove temp files
	########
	rm $PSFILE &> /dev/null
	rm $DUPLEXFILE1 &> /dev/null
	rm $DUPLEXFILE2 &> /dev/null
	rm $CHUNKFILE &> /dev/null
	rm $VARFILE &> /dev/null
	rm $PVFIFO &> /dev/null
	rm $PVFIFO2 &> /dev/null
	rm $FIFOFILE &> /dev/null
	rm $FIFOFILE2 &> /dev/null
	########
	# wrap around log file
	########
	$TPSTDIN --cutlogfile $LOGFILE 128
	echo "------------ END of tpprint filter ------------" >> $LOGFILE
	########
	# send remaining data from stdin to null device
	########
	cat > /dev/null
	exit 0
}

########
# signal handling
########
trap 'cleanup' 15

########
# write some info to logfile
########
echo >> $LOGFILE || LOGFILE="/dev/null"
echo "#######################################################" >> $LOGFILE
echo "NEW PRINT JOB $(date) (pstoturboprint 2.48-1)" >> $LOGFILE
QUOTE="'"
QUOTEESC="'\''"
JOBID="$1"
USER="$2"
TITLE="${3//$QUOTE/$QUOTEESC}"
COPIES="$4"
OPTIONS="$5"
PPDFILE="${PPD//$QUOTE/$QUOTEESC}"
QUEUE=${PPDFILE##*/}
QUEUE=${QUEUE%.ppd}
echo "job-id $JOBID" >> $LOGFILE
echo "user $USER" >> $LOGFILE
echo "title $TITLE" >> $LOGFILE
echo "copies $COPIES" >> $LOGFILE
echo "options $5" >> $LOGFILE
echo "file $6" >> $LOGFILE
echo "ppdfile $PPDFILE" >> $LOGFILE
echo "queue $QUEUE" >> $LOGFILE

########
# temporary files and pipes
########
CHUNKFILE="$TPPATH_TEMP/pstoturboprint$$.chunk"
VARFILE="$TPPATH_TEMP/pstoturboprint$$.var"
PSFILE="$TPPATH_TEMP/pstoturboprint$$.ps"
DUPLEXFILE1="$TPPATH_TEMP/pstoturboprint$$.duplex1"
DUPLEXFILE2="$TPPATH_TEMP/pstoturboprint$$.duplex2"
FIFOFILE="$TPPATH_TEMP/pstoturboprint$$.fifo"
FIFOFILE2="$TPPATH_TEMP/pstoturboprint$$.fifo2"
PVFIFO="$TPPATH_TEMP/pstoturboprint$$.pvfifo"
PVFIFO2="$TPPATH_TEMP/pstoturboprint$$.pvfifo2"

########
#  call tpstdin to read a chunk from the postscript file 
#  to evaluate ppd-style settings
########
# this file holds the first chunk (up to 64K) of stdin
# search for %%EndSetup up to 1MB max. chunk size
########
if [ -z $6 ] ; then
	$TPSTDIN --pscut $CHUNKFILE >> $LOGFILE
	CHUNKFILE2=$CHUNKFILE
else
	$TPSTDIN < $6 --pscut $CHUNKFILE >> $LOGFILE
	CHUNKFILE2=$6
fi

# some additional debug output
#echo "$TPPRINT -v2 -l$LOGFILE --ppdfile=$PPDFILE --psfeatures $CHUNKFILE $VARFILE" >> $LOGFILE
#echo "PPD file:" >> $LOGFILE
#ls -l $PPDFILE >> $LOGFILE
echo "process info: $(id)" >> $LOGFILE
echo "test directory access:" >> $LOGFILE
#ls -l -d /etc/turboprint >> $LOGFILE
#ls -l -d /usr/share/turboprint >> $LOGFILE
ls -l -d "$TPPATH_SHARE/printers" >> $LOGFILE
#ls -l /etc/turboprint/system.cfg >> $LOGFILE
#echo "chunk file:" >> $LOGFILE
#ls -l $CHUNKFILE >> $LOGFILE
ldd $TPPRINT >> $LOGFILE
# print URI
lpstat -v $QUEUE >> $LOGFILE

########
# convert additional options to pass them to tpprint
########
# set the options parameter as the new command line
TPOPTION=""
DUPLEXMODE=0
DUPLEXPAGES=0
DUPLEXJOBID=0

for i in $5; do
	OPTION=${i%%=*}
	VALUE=${i##*=}
	if [ "$OPTION" = "turboprint" ] ; then
		TPOPTION="$TPOPTION -$VALUE"
	fi
	if [ "$OPTION" = "turboprintduplexmode" ] ; then
		DUPLEXMODE="$VALUE"
	fi
	if [ "$OPTION" = "turboprintduplexpages" ] ; then
		DUPLEXPAGES="$VALUE"
	fi
	if [ "$OPTION" = "turboprintduplexjobid" ] ; then
		DUPLEXJOBID="$VALUE"
	fi
	# wrong -media attribute sometimes sent by CUPS
        #if [ "$OPTION" = "media" ] ; then
		TPOPTION="$TPOPTION '---$i'"
	#fi
done

########
# call tpprint with postscript chunk
# to extract settings that are required to call gs
########
FEATURECOMMAND="$TPPRINT -v2 -l$LOGFILE '--username=$USER' '--ppdfile=$PPDFILE' --psfeatures $TPOPTION $CHUNKFILE $VARFILE"
echo "$FEATURECOMMAND" >> $LOGFILE
eval "$FEATURECOMMAND >> $LOGFILE"
echo "----------- Start of var file -----------" >> $LOGFILE
cat $VARFILE >> $LOGFILE
echo "----------- End of var file -----------" >> $LOGFILE
eval $(cat $VARFILE)

########
# optional manual duplexing
# 1. pass: chunk + stdin -> tpstdin -> gs -> fifo -> tpprint
#                                   -> duplexfile
# 2. pass: duplexfile    -> tpstdin -> gs -> fifo -> tpprint
#
# $DUPLEX: (setting coming from PPD file)
#    0=none 1=DSC (sorted by tpstdin using DSC) 
#    2=compatible (sorted by tpprint)
#    3=DSC reverse - 1st pass rev.order cass feed (sorted by tpstdin using DSC)
# $DUPLEXMODE: (mode for tpprint)
#    0=none 1=DSC front 2=DSC rear 
#    3=compat.front 4=compat.rear 5=compat.rear.reverse
#
########
if [ ! $PREVIEW ] ; then
	PREVIEW=0
fi
if [ ! $DUPLEX ] ; then
	DUPLEX=0
fi
# call tpstdin for optional splitting / rearranging of the postscript file
# using DSC comments, e.g. for duplex or reverse printing
# skip if this is already the print job with rear sides
	
if [ $DUPLEXMODE -eq 0 ] ; then
	$TPSTDIN --parsedsc $CHUNKFILE2 --preview=$PREVIEW --duplex=$DUPLEX $PSFILE $DUPLEXFILE1 $DUPLEXFILE2
	DUPLEXPAGES=$?
	echo "tpstdin --parsedsc result=$?" >> $LOGFILE
	if [ $DUPLEX -gt 0 ] ; then
		if [ $DUPLEX -eq 2 ] ; then
			# manual duplex , tpprint sorts out pages
			DUPLEXMODE=3
		else
			# manual duplex using postscript DSC structure
			if [ $DUPLEXPAGES -gt 0 ] ; then
				DUPLEX=1
				DUPLEXMODE=1
			else
				# postscript doesn't contain DSC
				# fall back to: tpprint sorts out pages
				DUPLEX=2
				DUPLEXMODE=3
			fi
		fi
	fi
	DUPLEXPAGES2=$DUPLEXPAGES
	DUPLEXPAGES=0 # currently only used for second job (rear sides)
else
	$TPSTDIN --parsedsc $CHUNKFILE2 --preview=$PREVIEW --duplex=0 $PSFILE $DUPLEXFILE1 $DUPLEXFILE2
fi

########
# build command line for ghostscript
# -sDEVICE= name of output format
# -r resolution
# -g output image size in pixels
# -dSAFER interpret postscript in read-only mode
#         remark: removing -dSAFER is a workaround for color space
#                 bug in gs9.01
# -dDisableFAPI=true workaround for font-bug in gs9.00
# -dNOPAUSE don't wait after page end
# -dBATCH don't wait after file end
# -sOutputFile=- write to stdout
# -q don't write debug output to stdout
# - instead of input file: read from stdin
########
#
case $GSCOLORMODE in
 0) GSDRIVER="pbmraw"; GSBITS=1 ; PVDRIVER="pbmraw" ; PVBITS=1 ;;
 1) GSDRIVER="pgmraw"; GSBITS=8 ; PVDRIVER="pgmraw" ; PVBITS=8 ;;
 2) GSDRIVER="ppmraw"; GSBITS=24 ; PVDRIVER="ppmraw" ; PVBITS=24 ;;
# CMYK 
 4) GSDRIVER="bmp32b"; GSBITS=32 ; PVDRIVER="ppmraw" ; PVBITS=24 ;;
# GrayPhoto
 5) GSDRIVER="ppmraw"; GSBITS=8 ; PVDRIVER="pgmraw" ; PVBITS=8 ;;
# CMYK mixblack
 6) GSDRIVER="bmp32b"; GSBITS=32 ; PVDRIVER="ppmraw" ; PVBITS=24 ;;
 *) GSDRIVER="ppmraw"; GSBITS=24 ; PVDRIVER="ppmraw" ; PVBITS=24 ;;
esac

GSCOMMANDLINE="gs -sDEVICE=$GSDRIVER -r${GSXDPI}x$GSYDPI -g${GSWIDTH}x$GSHEIGHT\
 -dSAFER -dDisableFAPI=true -dNOPAUSE -dBATCH"

# optional noprint mode (calculate ink usage only)
if [ ! $NOPRINT ] ; then
	NOPRINT=0
fi
if [ $NOPRINT -eq 1 ] ; then
	NOPRINT="-n"
else
	NOPRINT=""
fi

########
# build command line for tpprint
#
# -a0    : ignore image aspect
# -e1    : ignore unprintable page margins
#          (image created by gs includes margins)
# -sp    : print size of the image in pixels
# -v2    : verbose level 2 = print all messages
# -l     : set name of logfile
# - -    : read image from stdin and send printer data to stdout
########
TPCOMMANDLINE="$TPPRINT -a0 -e1 -s${TPWIDTH}x$TPHEIGHT -p${TPXOFFSET}x$TPYOFFSET -v2 -l$LOGFILE $TPOPTION '--ppdfile=$PPDFILE' --psheader=$CHUNKFILE --jobid=$JOBID '--username=$USER' '--title=$TITLE' $NOPRINT" 

########
# check if zedonet ghostscript is installed
# => use instead of pre-installed version
# to avoid problems with certain versions of ghostscript
########
GSZEDODEVICE=0
if [ $TPUSE_GSZEDO -eq 1 ] ; then
	GSRESULT=$($TPGS --help | grep Ghostscript)
	GSVERSION=${GSRESULT:16:4}
	if [ "$GSVERSION" = "Zedo" ] ; then
		GSZEDODEVICE=1
	fi
fi
echo "GSRESULT=$GSRESULT GSVERSION=$GSVERSION GSZEDODEVICE=$GSZEDODEVICE" >> $LOGFILE
if [ $GSZEDODEVICE -eq 1 ] ; then
	GSCOMMANDLINE="$TPGS -sDEVICE=zedo -dZCopies=1 -dZBits=$GSBITS -sZPixSize=${GSWIDTH}x$GSHEIGHT -r${GSXDPI}x$GSYDPI -dSAFER -dNOPAUSE -dBATCH"
fi

# pdf input file => set number of copies (otherwise copied by gs)
# - removed, workaround for Ubuntu 12.10 -
#if [ $PDFFILE -ne 0 -o $GSZEDODEVICE -eq 1 ] ; then
#	TPCOMMANDLINE="$TPCOMMANDLINE -y$COPIES"
#fi

# put together first chunk and remainder of stdin

INPUTFILTER="$TPSTDIN --paste $CHUNKFILE2"

if [ $DUPLEXMODE -gt 0 ] ; then
	TPCOMMANDLINE="$TPCOMMANDLINE --duplex=$DUPLEXMODE --duplexjobid=$DUPLEXJOBID --pages=$DUPLEXPAGES"
fi

# send output of ghostscript to tpprint
COMPLETEPIPE="$INPUTFILTER | $GSCOMMANDLINE  -sOutputFile=$FIFOFILE - >> $LOGFILE"

# optional preview images generated by ghostscript
#   tpstdin -> chunk
#   chunk + stdin -> tpstdin -> duplex-part-1, duplex-part-2
#   duplex-part-x -> gs -> pvfifo
#   duplex-part-x -> gs -> fifo
#   pvfifo, fifo -> tpprint

if [ -s $DUPLEXFILE1 ] ; then
	# $DUPLEXFILE1 exists and has size greater zero
	echo "Setting INPUTFILE to $DUPLEXFILE1" >> $LOGFILE
	INPUTFILE=$DUPLEXFILE1;
else
	if [ -e $PSFILE ] ; then
		echo "Setting INPUTFILE to $PSFILE" >> $LOGFILE
		INPUTFILE=$PSFILE 
	fi
fi
if [ $INPUTFILE ] ; then
	# complete postscript file present, don't use tpstdin
	# read from file instead of input filter
	echo "Reading from $INPUTFILE" >> $LOGFILE
	COMPLETEPIPE="$GSCOMMANDLINE -sOutputFile=$FIFOFILE $INPUTFILE >> $LOGFILE"
fi
if [ $PREVIEW -gt 0 ] ; then
	# tpprint reads also thumbnails
	# thumbnails are created by preview process:
	# gs reads from $INPUTFILE and writes to $PVFIFO
	TPCOMMANDLINE="$TPCOMMANDLINE --preview=$PVFIFO"
	PREVIEWPIPE="gs -sDEVICE=$PVDRIVER -r${PVXDPI}x$PVYDPI -g${PVWIDTH}x$PVHEIGHT -dSAFER -dDisableFAPI=true -dNOPAUSE -dBATCH -sOutputFile=$PVFIFO $INPUTFILE >> $LOGFILE"
	if [ $GSZEDODEVICE -eq 1 ] ; then
		PREVIEWPIPE="$TPGS -sDEVICE=zedo -dZCopies=1 -dZBits=$PVBITS -sZPixSize=${PVWIDTH}x$PVHEIGHT -r${PVXDPI}x$PVYDPI -dSAFER -dNOPAUSE -dBATCH -sOutputFile=$PVFIFO $INPUTFILE >> $LOGFILE"
	fi
	mkfifo $PVFIFO >> $LOGFILE
fi

########
# now execute ghostscript & other pipe commands
########
echo "GSCOMMANDLINE=$GSCOMMANDLINE" >> $LOGFILE
echo "TPCOMMANDLINE=$TPCOMMANDLINE" >> $LOGFILE
echo "COMPLETEPIPE=$COMPLETEPIPE" >> $LOGFILE
echo "PREVIEWPIPE=$PREVIEWPIPE" >> $LOGFILE
echo "----------- Start of print job -----------" >> $LOGFILE
mkfifo $FIFOFILE >> $LOGFILE
eval "$TPCOMMANDLINE $FIFOFILE - &"
TPPID=$!
# if print job is finished, kill tpprint process
#tpsetup --monitor $JOBID $TPPID >> $LOGFILE &
if [ $PREVIEW -gt 0 ] ; then
	eval "$PREVIEWPIPE 9> $PVFIFO &"
	PVPID=$!
fi
eval "$COMPLETEPIPE 9>$FIFOFILE &"
GSPID=$!
# wait until tpprint background process has ended
wait $TPPID
RESULT=$?
echo "------------ End of print job - result = $RESULT ------------" >> $LOGFILE

########
# optional: second pass for manual duplex (dsc or non-dsc variant)
# DUPLEX=2: regular duplex DUPLEX=3: reverse odd pages (cassette feed)
########
if [ -s $DUPLEXFILE2  -a $RESULT -eq 0 ] ; then
	# $DUPLEXFILE2 exists and has size greater zero
	if [ $DUPLEX -eq 1 ] ; then
		# DSC mode, tpstdin sorted pages
		DUPLEXMODE=2
	else
		if [ $DUPLEX -eq 2 ] ; then
			# manual duplex , tpprint sorts out pages
			DUPLEXMODE=4
		else
			# hint user to reverse paper stack
			DUPLEXMODE=5
		fi
	fi
	# create second print job with rear sides using lp command
	DUPLEXOPTIONS="-o media=$DUPLEXMEDIA"
	for i in $OPTIONS; do
        	DUPLEXOPTIONS="$DUPLEXOPTIONS -o $i"
	done
	echo "create second print job with rear sides using lp command:" >> $LOGFILE
	DUPLEXJOB="lp -d '$QUEUE' -U '$USER' -t '${TITLE}-duplex' -o turboprintduplexmode=$DUPLEXMODE -o turboprintduplexpages=$DUPLEXPAGES2 -o turboprintduplexjobid=$JOBID $DUPLEXOPTIONS $DUPLEXFILE2"
	echo "$DUPLEXJOB" >> $LOGFILE
	eval "$DUPLEXJOB &>/dev/null"
fi

########
# cleanup
########
cleanup

