#!/bin/sh
##
#   Copyright (c) 1999, 2020, Oracle and/or its affiliates.
#
#  Component: Print Protocol Engine
#  Author: Patrick Caulfield
#
##############################################################################
#
# This script performs the conversion from print jobs to printers. In theory 
# it should be able to convert from any format to any other but we restrict 
# it to a reasonable, popular subset.
#
# The script acts as a filter, ie its standard input is read and converted 
# and sent to standard output. 
#
# Usage: 
# 	[ --printer <Printer> ] 
# 	[ --printerflags <PrinterFlags> ] 
# 	[ --datasample <DataSample> ] 
# 	[ --dataflags <DataFlags> ]
# 	[ --options <Options> ]
# 	[ --drivername <DriverName> ]
#
# Where:
#	Printer is derived from printertypes.txt, via ttaprinter.cgi.
#
#	PrinterFlags specifies what kind of end-of-line translation is required:
#	    0 = no processing
#	    1 = translate LF to CRLF
#	    2 = translate LF to CR
#	Also, a bit value of 16 indicates that this is a PDF print job.
#
#	DataSample is a short sample of the beginning of the print job, used
#	to decide what kind of data it is (PostScript, etc) and therefore
#	what kind of processing is necessary before sending it to the printer.
#
#	DataFlags specifies some processing options based on the data stream.
#	Options defined at present:
#	    1 = Passthru mode (copy the print data direct to the printer
#		without any filtering - typically used for jobs originating
#		from Windows application servers).  Even if passthru=1, we
#		still attempt PostScript -> PCL conversion.
#	    2 = Unconditional passthru mode - no data conversion under any
#		circumstances.
#
#	The --options and --drivername parameters are supplied to assist local
#	customization of this script.  The options are as supplied on an lp
#	command line when creating a print job (eg "lp -o landscape"), and the
#	drivername may help determine how the options may be implemented for
#	the target printer.
#
##############################################################################

TTA_FIFO="${TTADATADIR}/print/fifo"
TTA_SPOOLDIR="${TTADATADIR}/spool"
TMP="${TMPDIR:-/tmp}"

# Source any info about the conversion application
PRINT_CONV_CFG="${TTADIR}/var/info/registered-extensions/print/converter/print.cfg"
if [ -f "${PRINT_CONV_CFG}" ]
then
    source "${PRINT_CONV_CFG}" 2>/dev/null
fi

# To get a full execution trace in $TTADIR/var/log/print_error.log, 
# change the next line to TRACE="t".  You may also have to change
# the permissions on $TTADIR/var/log/print_error.log to make it 
# world-writable (alternatively, you can change ERRLOG to point to
# a different file).
TRACE=""
if [ -n "$TRACE" ]; then
    ERRLOG="${TTADIR}/var/log/print_error.log"
    [ -w "${ERRLOG}" ] && { exec 2>>${ERRLOG}; set -x; }
    : ==========================================================================
    : $0 `date +'%Y/%m/%d %H:%M:%S'`
fi

# To make this script generate simple PostScript output when printing
# non-PostScript data to a PostScript printer, set the next line to
# CREATE_POSTSCRIPT="t".  In most cases this should not be necessary,
# since most PostScript printers can handle plain text input.
CREATE_POSTSCRIPT=""

if [ -f $TTAMSG/common -a -f $TTAMSG/tta_print_converter ]; then
  . $TTAMSG/common
  . $TTAMSG/tta_print_converter
else
  echo "Unable to open message file: $TTAMSG/tta_print_converter"
  exit 1
fi


Log()
{
    Echo=""
    case "$1" in
    --echo) Echo="t"; shift
    esac
    echo "`date +'%Y/%m/%d %H:%M:%S'` $*" >${TTA_SPOOLDIR}/,print,$$
    chmod 666 "${TTA_SPOOLDIR}/,print,$$" 2>/dev/null
    echo ",print,$$" >${TTA_FIFO}
    [ "${Echo}" = "t" ] && echo "$*"
    return 0
}

# Sample parameters: you may have to change these according to your printer
# type, paper size, etc.
PDF_PAPERSIZE="letter"

# Change the next line to CONV_DEBUG="t" to get some extra conversion debug
CONV_DEBUG=""
if [ -z "$CONV_DEBUG" ]; then
    CONV_STDOUT="/dev/null" CONV_STDERR="${TTADIR}/var/log/print_error.log"
else
    CONV_STDOUT="${TMP}/tta$$.conv.1" CONV_STDERR="${TTADIR}/var/log/print_error.log"
fi

#
# Look for the text->PostScript converter on System V machines.
# You may want to customize the arguments for your site.
#
POSTPRINT="/usr/lib/lp/postscript/postprint"
POSTPRINT_ARGS=""

#
# Prints $1 roughly half-way down the page in 14pt Times-Roman
#
postscript_error()
{
  echo "%!PS"
  echo "clippath pathbbox /URY exch def /URX exch def /LLY exch def /LLX exch def"
  echo "/Cx LLX 35 add def /Cy URY 14 sub def"

  echo "Cx Cy moveto /Times-Roman findfont 14 scalefont setfont "
  echo "($1) show showpage"
  printf "\004"
}

#
# This is a PostScript header for printing text. 
# You may edit the parameters below if you want to change the format.
#
send_ps_header()
{
  cat 2>/dev/null <<EOF
%!PS
% Print text in a fixed font. 
%
% Set defaults
%
/point 10 def        % Point size of font to use
/fount (Courier) def % Name of font to use
/lmargin 35 def      % Left margin in points
/tmargin 35 def      % Right margin in points
/bmargin 35 def      % Bottom margin in points
/leading 2 def       % Leading in points
/title false def     % Whether to print a title and page numbers
/hdr () def          % Text of the title, may be blank if you just want page numbers
%
% Do not change anything below this line unless you really know what you are doing.
%
/page 0 def
/mystring 1024 string def  % Scratch area
/pagestring 10 string def  % Printable page number

/checkff
{
  (\14) search 
    {show pop showpage newpage} % Leave only bits after FF to process
    if
} bind def

/newpage
{
  /Cy URY point sub tmargin sub def Cx Cy moveto
  title {
        /page page 1 add def
        LLX URY 12 sub moveto           % setup header
        currentfont                     % save current font
        /Helvetica-Bold findfont 12 scalefont setfont
        hdr show 
        URX 108 sub URY 12 sub moveto   % page number
        (Page: ) show
        page 10 pagestring cvrs show
        setfont                         % restore font
        /Cy Cy 15 sub def               % Move below title
        Cx Cy moveto
      } if
} def           

/newline { 
 Cy point leading add sub /Cy exch def Cx Cy moveto
 Cy point bmargin add le {showpage newpage} if
} def

%
% This is the executable part of the header
%
fount cvn findfont point scalefont setfont  % Make the font a name

clippath pathbbox /URY exch def /URX exch def /LLY exch def /LLX exch def
/Cx LLX lmargin add def /Cy URY tmargin sub def

%
% Print the first header if required
%
newpage

%
% Send the file
%
{ currentfile mystring readline
  {checkff show newline}
  {showpage exit}
  ifelse
} loop
EOF

}

Filter()
{
    case "$1" in
    POSTSCRIPT)
	../bin/ttacrlf --psclean
    	;;
    1)	../bin/ttacrlf --lf2crlf
    	;;
    2)	../bin/ttacrlf --lf2cr
    	;;
    *)	cat 2>/dev/null
    esac
    return $?
}

##
# As released, this function contains some commented-out code to recognise
# PCL data (commented out because it can't be guaranteed to work for all
# variants of PCL data).  If you need to handle PCL data, you can try removing
# the "###" strings from the awk script below; depending on the exact format
# of your PCL data, you may also have to modify the comparison test to get it
# to work correctly.
#
# If you do this, you should also uncomment the other lines later in this
# script which also start with "###".
## 
GetDataType()
{
    [ -x "/usr/bin/nawk" ] && AWK="nawk" || AWK="awk"
    ${AWK} 'BEGIN {
	sample="'"$1"'"
	ps_start = index(sample, "%25!PS")
	if (ps_start > 0)
	    print "POSTSCRIPT " ps_start-1
###	else if (index(sample, "%1bE%1b") > 0)
###	    print "PCL"
        else if (index(sample, "%25PDF") > 0)
            print "PDF"
	else
	    print "UNKNOWN"
	exit
    }'
    return 0
}

##
# Script execution starts here.
## 
Printer="UNKNOWN" DataFlags="0" PassThru="0" DataSample="" Options="" DriverName="" PrinterName="" LineEndProcessing="0" PDFPrintJob=""
while [ -n "$1" ]; do
    case "$1" in
    --printer)
	Printer="$2"
	shift
	;;
    --printerflags)
	LineEndProcessing=`expr "$2" % 4`
	[ `expr "$2" / 16 % 2` -eq 1 ] && PDFPrintJob="t"
	shift
	;;
    --datasample)
	DataSample="$2"
	shift
	;;
    --dataflags)
    	DataFlags="$2"
	shift
	;;
    --options)
    	Options="$2"
	shift
	;;
    --drivername)
    	DriverName="$2"
	shift
	;;
    --printername)
    	PrinterName="$2"
	shift
	;;
    esac
    shift
done

PsStart=0
DataType=`GetDataType "${DataSample}"`
case "${DataType}" in
POSTSCRIPT*)
    PsStart=`expr "${DataType}" : '.* \(.*\)'`
    DataType="POSTSCRIPT"
esac

# PassThru is the low-order two bits of DataFlags
PassThru=`awk 'BEGIN {
    dataflags='"${DataFlags}"'
    print (dataflags % 4)
    exit
}'`

##
# Do the conversion
##

# PDF printer processing
# If data is PDF and Printer is PDF do not convert, just output data.
# Check the 16 bit
if [ "${DataType}" == "PDF" ] && [ -n "${PDFPrintJob}" ]; then
    cat <&0
    exit 0
elif [ -n "${PDFPrintJob}" ]; then
    PDF_INFILE="${TMP}/tta$$.ps" PDF_OUTFILE="${TMP}/tta$$.pdf" PDF_LOCKFILE="${TMP}/tta$$.lock"
    rm -f ${PDF_INFILE} ${PDF_OUTFILE} ${PDF_LOCKFILE}
    if [ ! -x "${PS_TO_PDF}" ]; then
	echo "can't run ${PS_TO_PDF}" >>${CONV_STDERR}
	echo "${m_printconv_getconverter}" >>${CONV_STDERR}
    else
	:>${PDF_LOCKFILE}
	../bin/ttacrlf --tail 120 240 ${PDF_OUTFILE} ${PDF_LOCKFILE} &
	tailpid=$!
	status=0
	Log "${m_printconv_format1}${m_printconv_format3}${PrinterName}"
	if [ "${PS_TO_PDF_INFILE}" = "-" ]; then
	    # PostScript to PDF Converter can accept input via stdin
	    if [ -z "${CONV_DEBUG}" ]; then
		${PS_TO_PDF} ${PS_TO_PDF_OPTIONS} - ${PDF_OUTFILE} >${CONV_STDOUT} 2>>${CONV_STDERR}; status=$?
	    else
		tee ${PDF_INFILE} | ${PS_TO_PDF} ${PDF_OPTIONS} - ${PDF_OUTFILE} >${CONV_STDOUT} 2>>${CONV_STDERR}; status=$?
	    fi
	else
	    # PostScript to PDF converter needs an input file
	    cat >${PDF_INFILE}
	    ${PS_TO_PDF} ${PS_TO_PDF_OPTIONS} ${PDF_INFILE} ${PDF_OUTFILE} >${CONV_STDOUT} 2>>${CONV_STDERR}; status=$?
	fi
	rm -f ${PDF_LOCKFILE}
	wait ${tailpid}
	[ -z "${CONV_DEBUG}" ] && rm -f ${PDF_INFILE} ${PDF_OUTFILE}
    fi
    exit 0
fi

case "${DataType}" in
UNKNOWN)
    Log "${m_printconv_format1}${m_printconv_format3}${Printer}${m_printconv_format4}${PassThru}"
    ;;
*)  Log "${m_printconv_format1}${m_printconv_format2}${DataType}${m_printconv_format3}${Printer}${m_printconv_format4}${PassThru}"
esac

case "${Printer}" in

POSTSCRIPT )
    case "${DataType}" in
    POSTSCRIPT )
	if [ "${LineEndProcessing}" -eq 0 -a "${PsStart}" -gt 0 ]; then
	    Filter POSTSCRIPT
	else
	    Filter 0
	fi
	[ "${PassThru}" = 0 ] && printf "\004" # ctrl+D = end-of-job
	;;
    * ) # Unrecognised data type
	if [ -z "${CREATE_POSTSCRIPT}" -o "${PassThru}" -gt 0 ]; then
	    Filter 0
	elif [ -x "${POSTPRINT}" ]; then
	    ${POSTPRINT} ${POSTPRINT_ARGS} -
	    printf "\004" # ctrl+D = end-of-job
	else
	    send_ps_header
	    Filter ${LineEndProcessing}
	    printf "\004" # ctrl+D = end-of-job
	fi
    esac
    ;;

PCL )
    # Use this case as a model for additional printer types where
    # conversion can be done.  You may need to supply different
    # parameters to the conversion command see tta_print_converter.cfg.
    case "${DataType}" in
    POSTSCRIPT )
	if [ "${PassThru}" -gt 1 ]; then
	    Filter 0
        elif [ -x "${CONVERTER}" ]; then
	    if [ "${CONVERTER_OUTFILE}" = "-" ]; then
		${CONVERTER} ${CONVERTER_OPTIONS} 2>>${CONV_STDERR}
	    else
		${CONVERTER} ${CONVERTER_OPTIONS} >${CONV_STDOUT} 2>>${CONV_STDERR}
		cat ${CONV_OUTFILE}
		[ -z "${CONV_DEBUG}" ] && rm -f ${CONV_OUTFILE}
	    fi
	elif [ "${PassThru}" -gt 0 ]; then
	    Filter 0
        else
	    echo "${m_printconv_getconverter}" >>${CONV_STDERR}
	    Filter ${LineEndProcessing}
        fi
	;;
#   If you have modified the GetDataType function in this script to recognise
#   PCL data, you should also remove the "#" comment characters from the few
#   lines below this point which start with "###".
### PCL )
###	Filter 0
###	;;
    * ) # Unrecognised data type
	if [ "${PassThru}" -gt 0 ]; then
	    Filter 0
	else
	    Filter ${LineEndProcessing}
	    printf "\014" # ctrl+L = form-feed
	fi
    esac
    ;;

* ) # Unrecognised printer type.  If a new recognised printer type is added to
    # this script, add a new case section for it above this default case.
    if [ "${PassThru}" -gt 0 ]; then
	Filter 0
    else
	Filter ${LineEndProcessing}
    fi

esac

exit 0
