Skip to content

Printing PCL to PDF

Anyone that ever worked on printing would know how much time can be wasted with root causing problems.  There are a lot of moving parts with different printers, drivers, conversions, operating systems, queue types and so forth.  Especially when special type of printing is required like barcodes.

One particularly painful area is printing and checking results.  A lot of time this is done to a printer not close by like on a manufacturing floor.  Or even across country.  And then depending on someone to check the printout, scan and email back to you introduce a cycle that can be very frustrating for everyone.

One good way to speed up the testing process is to print to PDF.  If you are just interested in generic printing to PDF and willing to tinker with CUPS you could look at CUPS-PDF which is fairly well documented.  In my case I had issues with the way PCL was being printed so I needed to do more than CUPS-PDF.  I might still circle back and see if I can get CUPS-PDF to do some kind pf PCL conversion but for now my solution that worked is documented below.

High level process is as follow:

1. Print from Solaris 11.1 to a remote queue using simple lp -d <queue> <file>

2. CUPS server identifies incoming file as text/plain mime type.  This is forced through adding *cupsfilter: "text/plain  0  -" to an Adobe distiller PPD. CUPS backend called "pdf" which converts to a file.  In my case the destination is /var/www/html/pdf since I want to share the pdf's through Apache.

3. CUPS backend "pdf" script use software called ghostpcl that is a subset of ghostscript to convert pcl to pdf.

4. Debugging Notes.

5. LPD vs IPP.

Details on above steps follow below.

Note copying any commands or scripts does not mean it is tested or working.  I provided below as guide but I tinker with the commands and scripts when editing the article.  I used Oracle Linux 6.5 in this instance so depending on your Linux variant files might have slightly different locations.

1. Printing from Solaris
Add the remote queue and print as follow. In my case I needed to match the printer name to the application so CUPS printer name is different from local queue.

# lpadmin -p testprinter -s lpd://
# lp -d testprinter /root/OUTPUT.TXT

2. Setup CUPS printer and accept incoming file as text/plain
You can also use other PPD files like the one that comes with the CUPS-PDF project but this one worked form me and available from Adobe.

# lpadmin -p pdfprinter -v pdf:/var/www/html/pdf -D "Generate PDF files" -E -P /usr/share/cups/model/distiller.ppd.gz
# chmod 777 /var/www/html/pdf

# pwd
# ls -l
total 20
-rw-r--r--. 1 root root 17172 Oct 6 13:43 pdfprinter.ppd

# grep plain pdfprinter.ppd
*cupsfilter: "text/plain 0 -"

# grep plain mime.*
mime.convs:text/plain application/pdf 0 -
mime.types:text/plain txt printable(0,1024)

# /etc/init.d/cups restart

3. Backend script

# pwd

# cat pdf
# -------------------------------------------------------------------
# "/usr/lib/cups/backend/pdf":
# -------------------------------------------------------------------
set -x
# filename of the PDF File
PRINTTIME=`date +%Y-%m-%d_%H.%M.%S`
# no argument, prints available URIs

# case of wrong number of arguments
if [ $# -ne 5 -a $# -ne 6 ]; then
echo "Usage: pdf job-id user title copies options [file]"
exit 1
# get PDF directory from device URI, and check write status
if [ ! -d "$PDFDIR" -o ! -w "$PDFDIR" ]; then
echo "ERROR: directory $PDFDIR not writable"
exit 1
# generate output filename
if [ "$3" = "" ]; then
if [ "$2" != "" ]; then

if [ $# -eq 6 ]; then

# run ghostpcl
/usr/src/ghostpcl-9.15-linux-x86_64/pcl6-915-linux_x86_64 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dPDFSETTINGS="/ebook" -dAutoRotatePages=/All -dPDFFitPage -r300x300 -g2346x4100 -J"@PJL SET ORIENTATION = LANDSCAPE" -sOutputFile=$OUTPUTFILENAME $INPUTFILENAME


## Normally the PDF file will be emailed to the creating user.  Add emailing code here if needed. Emailing to who?
exit 0
# -------------------------------------------------------------------

4. Debugging Notes:

# grep debug cupsd.conf
LogLevel debug

# grep set pdf
set -x

The biggest pain in the whole process is to make sure CUPS applies only the filter for your backend and not run through any other filters.  Below is some of the lines in the log you want to pay attention to: "Auto-typing ...",  "File of type ..." ,  "Started backend ...". In my case if I see "Started filter ..."  it spells trouble.

# pwd

# grep "Job 134" error_log
I [07/Oct/2014:01:58:27 -0500] [Job 134] Adding start banner page "none".
I [07/Oct/2014:01:58:27 -0500] [Job 134] Queued on "pdfprinter" by "devuser".
D [07/Oct/2014:01:58:27 -0500] [Job 134] Auto-typing file...
D [07/Oct/2014:01:58:27 -0500] [Job 134] Request file type is text/plain.
I [07/Oct/2014:01:58:27 -0500] [Job 134] File of type text/plain queued by "devuser".
D [07/Oct/2014:01:58:27 -0500] [Job 134] job-sheets=none
D [07/Oct/2014:01:58:27 -0500] [Job 134] argv[0]="pdfprinter"
D [07/Oct/2014:01:58:27 -0500] [Job 134] argv[1]="134"
D [07/Oct/2014:01:58:27 -0500] [Job 134] argv[2]="devuser"
D [07/Oct/2014:01:58:27 -0500] [Job 134] argv[3]="standard input"
D [07/Oct/2014:01:58:27 -0500] [Job 134] argv[4]="1"
D [07/Oct/2014:01:58:27 -0500] [Job 134] argv[5]="document-name=/tmp/stdin-P3OQc job-originating-host-name= job-uuid=urn:uuid:0f507f16-0537-3307-6b15-b5134eda2bc5"
D [07/Oct/2014:01:58:27 -0500] [Job 134] argv[6]="/var/spool/cups/d00134-001"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[0]="CUPS_CACHEDIR=/var/cache/cups"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[1]="CUPS_DATADIR=/usr/share/cups"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[2]="CUPS_DOCROOT=/usr/share/cups/www"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[3]="CUPS_FONTPATH=/usr/share/cups/fonts"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[4]="CUPS_REQUESTROOT=/var/spool/cups"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[5]="CUPS_SERVERBIN=/usr/lib/cups"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[6]="CUPS_SERVERROOT=/etc/cups"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[7]="CUPS_STATEDIR=/var/run/cups"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[8]="HOME=/var/spool/cups/tmp"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[9]="PATH=/usr/lib/cups/filter:/usr/lib64/cups/filter:/usr/bin:/usr/sbin:/bin:/usr/bin"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[10]="SERVER_ADMIN=root@localhost"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[11]="SOFTWARE=CUPS/1.4.2"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[12]="TMPDIR=/var/spool/cups/tmp"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[13]="USER=root"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[14]="CUPS_SERVER=/var/run/cups/cups.sock"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[15]="CUPS_ENCRYPTION=IfRequested"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[16]="IPP_PORT=631"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[17]="CHARSET=utf-8"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[18]="LANG=en_US.UTF-8"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[19]="PPD=/etc/cups/ppd/pdfprinter.ppd"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[20]="RIP_MAX_CACHE=128m"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[21]="CONTENT_TYPE=text/plain"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[22]="DEVICE_URI=pdf:/var/www/html/pdf"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[23]="PRINTER_INFO=Generate PDF files"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[24]="PRINTER_LOCATION=VM"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[25]="PRINTER=pdfprinter"
D [07/Oct/2014:01:58:27 -0500] [Job 134] envp[26]="CUPS_FILETYPE=document"
I [07/Oct/2014:01:58:27 -0500] [Job 134] Started backend /usr/lib/cups/backend/pdf (PID 2170)
D [07/Oct/2014:01:58:27 -0500] [Job 134] + FILENAME=
D [07/Oct/2014:01:58:27 -0500] [Job 134] ++ date +%Y-%m-%d_%H.%M.%S
D [07/Oct/2014:01:58:27 -0500] [Job 134] + PRINTTIME=2014-10-07_01.58.27
D [07/Oct/2014:01:58:27 -0500] [Job 134] + '[' 6 -eq 0 ']'
D [07/Oct/2014:01:58:27 -0500] [Job 134] + '[' 6 -ne 5 -a 6 -ne 6 ']'
D [07/Oct/2014:01:58:27 -0500] [Job 134] + PDFDIR=/var/www/html/pdf
D [07/Oct/2014:01:58:27 -0500] [Job 134] + '[' '!' -d /var/www/html/pdf -o '!' -w /var/www/html/pdf ']'
D [07/Oct/2014:01:58:27 -0500] [Job 134] + OUTPUTFILENAME=
D [07/Oct/2014:01:58:27 -0500] [Job 134] + '[' 'standard input' = '' ']'
D [07/Oct/2014:01:58:27 -0500] [Job 134] + '[' devuser '!=' '' ']'
D [07/Oct/2014:01:58:27 -0500] [Job 134] + OUTPUTFILENAME=/var/www/html/pdf/devuser-2014-10-07_01.58.27.pdf
D [07/Oct/2014:01:58:27 -0500] [Job 134] + INPUTFILENAME=
D [07/Oct/2014:01:58:27 -0500] [Job 134] + '[' 6 -eq 6 ']'
D [07/Oct/2014:01:58:27 -0500] [Job 134] + INPUTFILENAME=/var/spool/cups/d00134-001
D [07/Oct/2014:01:58:27 -0500] [Job 134] + /usr/src/ghostpcl-9.15-linux-x86_64/pcl6-915-linux_x86_64 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -dAutoRotatePages=/All -dPDFFitPage -r300x300 -g2346x4100 '-J@PJL SET ORIENTATION = LANDSCAPE' -sOutputFile=/var/www/html/pdf/devuser-2014-10-07_01.58.27.pdf /var/spool/cups/d00134-001
D [07/Oct/2014:01:58:27 -0500] [Job 134] + chmod 644 /var/www/html/pdf/devuser-2014-10-07_01.58.27.pdf
D [07/Oct/2014:01:58:27 -0500] [Job 134] + exit 0
I [07/Oct/2014:01:58:27 -0500] [Job 134] Job completed.
D [07/Oct/2014:01:58:28 -0500] [Job 134] Unloading...

5. IPP versus LPD:
Note that I am using cups-lpd to accept on the lpd port on the CUPS server but I think it might not be necessary. IPP would also have worked. I had an odd CUPS issue that printing local on the CUPS server using lp worked but sending the file from a remote Solaris server CUPS insisted on tacking on a CUPS-BANNER which caused issues and even triggering the gziptoany filter and possibly more filters for ps.  Sounds like a bug with CUPS and the fix for to make sure no banner is added was here:

# grep server_args /etc/xinetd.d/cups-lpd
  server_args = -o job-sheets=none
Published inCUPSGhostpclGhostscriptPrinting