Virtual PDF Printer for our small office network – a step by step how to

Alternative title: How I got multiple cups-pdf printers on the same server. (I didn’t, but postprocessing let me work around the problem).

Preamble:

I have a small business. For years we’ve been creating PDFs from any computer on our network through a “virtual appliance’ called YAFPC (“Yet Another Free PDF Composer”).

The appliance originally ran on an old PC, then on a server that ran several other virtual machines. It had a neat web interface and would allow PDF printers to be created that would appear on the network for all of our users to use. It had one printer for plain A4 paper, one for A4 paper with a letterhead background, another one for an obscure use of mine, and so on. If you printed to it, it would email you the PDF (for any user, without any extra setup needed per user). It could also put the PDFs on one of our file servers or make them available from it’s built in file server.

If I remember correctly it cost £30 and ran since 2006 right through until today, November 2014. One of my best software investments!

However, Windows 8 came along and it no longer worked. Getting Windows 8 to print to it directly turned out to be impossible.  The program was not going to be updated or replaced with a new version. I managed a short term work around having windows 8 print to a samba printer queue which converted and forwarded to the YAFPC virtual appliance. There were problems, page sizes not be exact and so on but it worked in a fashion.

Roll forward to today when I’ve just got a new network PDF virtual printer working. It wasn’t so easy to do (some 20 hours I guess) so here are my setup notes for others to follow.  The final run through of these notes had it installed and working in about an hour.

These steps assume you know quite a bit about setting up linux servers. Please feel free to use the comments to point out errors or corrections, or add more complete instructions, and I’ll edit this post with the updates.  Also please suggest alternatives methods that you needed to use to meet your needs.

Overview – We are going to create:

  • a new Ubuntu based linux server as a virtual machine
  • Install CUPS, the Common Unix Printing System
  • Install CUPS-PDF, and extension that allows files to be created from the print queue
  • Create a postprocessing script that will run every time CUPS-PDF is used that will customise our PDF’s and send them where we want them (to our users).

Sounds simple, right 🙂

The actions are are formatted like this.
Notes are italic and begin with a #

Step 1:

You need to create a virtual appliance. My server is using KVM but you can use any flavour (VirtualBox, VMWare, Parallels, etc).  I won’t go into how creating this here.

Get the latest Ubuntu server from http://www.ubuntu.com/download/server. If you want to follow along with me, I used Ubuntu Server 14.04.
My virtual machine was set with 1GB Ram, 1 Processor and 8GB disk space.
My finished system currently uses under 2GB of disk space and each new PDF has been less than 1MB

Install ubuntu 14.04 server, these are the setting I chose.
 RAM: 1GB,
 Processor: 1
 Disk: 8GB
 Package Selections: Only OpenSSH server
 Local User Name: pierredf (for this example, but you can use any regular name, including your own.)
 IP address in this example is 10.18.6.202 but yours will be different!

Step 2:
Update the virtual appliance with the latest ubuntu patches. Between downloading 14.04 and getting things working 14.04.1 had been released so my final run through included updating to this.

sudo apt-get update
sudo apt-get dist-upgrade

Step 3:
Now I install CUPS from the standard ubuntu repo. Note I’ve not built any of the software for this solution so the Ubuntu version is often older versions.
Installing CUPS also installed Samba automatically.

sudo apt-get install cups

# edit the cups config file
sudo vim /etc/cups/cupsd.conf
# Find then edit/add the following
ServerAdmin yourname@example.com
Listen 10.18.6.202:631
# Save the file!

service cups restart

sudo cupsctl —remote-admin —remote-any —share-printers
# enables web interface and enables printing from any 
# address (e.g.: internet & your network) and allows 
# printers to be shared

# check the connection works - use a web browser to visit 
# the admin page. Don't move on until this works 
http://10.18.6.202:631
# replacing 10.18.6.202 with your IP!

Step 4:
Once cups is working (you can see the web interface), install CUPS-PDF

sudo apt-get install cups-pdf

#edit the cups-pdf config file
sudo vim /etc/cups/cups-pdf.conf

#Find and edit/add the following
Label 1
# this prefixes “job_” to the file names which will 
# stop prints with the same title overrighting each other. 
# You can also use 'Label 2' to have a postfix, or 0 to 
# leave it unchanged. Geek fact – you get to feel smug 
# seeing how many PDFs your setup has created over time.

AnonUser pierredf
# The post processing script runs as this user so you need
# the PDFs to be created by this user. If you used a
# different user name, put that here instead

PostProcessing /usr/bin/cups_postprocess
# This should go in the postprocessing section

Step 5:
We need to create a Printer via the cups web interface.

# Using your web browser
https://10.18.6.202:631/admin
# replacing 10.18.6.202 with your IP!
# the user name is the system user name password, eg: pierredf

Add Printer
> cups-pdf Virtual Printer
>
> Name: PDF-Generator
> Description: PDF-Generator
> Location: Outputs to Vine>roots>PDF_Printer
> Share this printer = checked
>
> Make: Generic
> Model: Generic CUPS-PDF Printer (en)

# Note: My mac used the Description as the printer name, so I kept # description and printer name the same, with location the note for # users as to where they can find their pdfs.

Step 6:
Now we add ‘pdftk’ the PDF Tool Kit, to our ubuntu install. This program lets us easily manipulate PDFs and we’ll use it to create our letterheaded paper prints.

sudo apt-get install pdftk

Step 7:
Prepare the postprocessing script that we pointed to in step 4.

sudo touch /usr/bin/cups_postprocess
sudo chmod 755 /usr/bin/cups_postprocess
#makes the script executable.

Step 8
The script wont be allowed to run yet. When called AppArmor will stop it.

sudo vim /etc/apparmor.d/usr.sbin.cupsd

# Add the end of the file /etc/apparmor.d/usr.sbin.cupsd,
# before the last }, add this line:

/usr/bin/cups_postprocess Ux
# using 'Ux' is bad, turning off AppArmor is also bad
# I read to use the 'rPx' flag but it doesn't seem to
# work (apparmor denied error)
#
# Example:
# My file looks like this.
#===============================
#  /var/log/cups/cups-pdf_log w,
#  /var/spool/cups/** r,
#  /var/spool/cups-pdf/** rw,
#  # Added postprocessing script
#  /usr/bin/cups_postprocess Ux
#}
#================================

# See blog comment from Alex, you may need a comma after Ux if you get an error.

sudo service apparmor restart

Step 9
Now add the content to the postprocessing script.

There are lots of comments in the file below explaining what is happening.  You’ll need to set your own values in several places. Eg: the destination location which you’ll set up in step 13.

 sudo vim /usr/bin/cups_postprocess
#!/bin/bash

FILENAME=`basename $1`
DIRNAME=`dirname $1`

# The script is called by:
# postprocessing commandline built
# (/usr/bin/cups_postprocess /var/spool/cups-pdf/ANONYMOUS/Title_of_your_print.pdf username printuser)
# username is the user running this script. Remember you changed this to be your regular system user name. If you skipped that step it will be 'nobody'
# the printuser is the username the print client gave. It's probably the user account on their pc.
# You can use these as variables in this script, $1 is the file name, $2 the username and $3 the client print user.

# I want PDF on blank pages to be called user-bl-job-name.pdf
PBLANK="$DIRNAME"/"$3"-bl-"$FILENAME"
# I want PDF letterhead pages to be called user-lh-job-name.pdf
PLET="$DIRNAME"/"$3"-lh-"$FILENAME"

# Rename the default to blank style name
mv $1 $PBLANK

# Use pdftk to set the letterhead as the page background.
pdftk $PBLANK multibackground /etc/cups/Letterhead.pdf output "$PLET"

# this file is created with 600 but it will need 660 on the other server so we can change that here
chmod 660 $PLET
# the original file had world read. No file on our server should be world readable, so fix that before the copy
chmod 660 $PBLANK
# Now we can use scp to transfer the two files onto the server
# Note - I also had to 'chmod g+s PDF_Printer' on the destination to force new files sent there
# to have the group permissions. Otherwise it arrived with user:pierredf and group:pierredf so no one
# else could open/read the PDFs

# use scp to send the new files to the remote server
scp $PBLANK user@some.other.server.com:/path/to/send/files/
scp $PLET user@some.other.server.com:/path/to/send/files/

### Things I'm still working on ###
# Use the user name to determine who to email the files too.


# IF user matches THEN try and email ELSE send to the general account

# sanity - check file size, if email greater than than Y MB
# message: did not attach file, go look on the server
# else
# message: attach files


# clean up the remote folder.
# this could be done by cron on the remote server, but it's handy
# to just do it from this script. Obviously only runs when a PDF
# is printed, but that's fine for my needs.
# Any files greater than 3 days old to be deleted
# (mtime = n * 24 hours ago)

# remote directory
ssh remoteuser@your.other.server.com 'find /path/to/send/files/* -mtime +3 -exec rm {} ;'
# local directory
find /var/spool/cups-pdf/ANONYMOUS/* -mtime +3 -exec rm {} ;

Step 10
Add the template PDF file(s).  The postprocess script adds a background to the print, which is our company letterhead.  If the template file is missing the postprocess script will fail and not tell you why!

Here is an example you can download to get you started: ExampleMultipageLetterhead

Put the template(s) in:  /etc/cups and name it Letterhead.pdf

sudo chmod 664 /etc/cups/Letterhead.pdf
sudo chown pierredf /etc/cups/Letterhead.pdf
#chown the file to your user name
sudo chgrp pierredf /etc/cups/Letterhead.pdf
#chgrp the file to your user group (same as user name was fine for me)

You’ll notice this is a 2 page template. If you print a single page you’ll only get the first page of the template used. If you print a multipage document the 2nd and subsequent pages will all use the 2nd page of the template.  It’s like having a letterhead with a logo for your front page and continuation sheets for subsequent pages. Perhaps you’d think of it as having a cover page and different internal pages.

To do this the post processing script uses the line:
pdftk $PBLANK multibackground /etc/cups/Letterhead.pdf output “$PLET”
and it is the ‘multibackground’ command that organises this ability.
If you use a single background you could try
pdftk $PBLANK background /etc/cups/Letterhead.pdf output “$PLET”
instead.

Step 11
Create an SSH key for the new server. Add this key to your file server so that SCP will work without asking for a password.

# Log into remote server first
ssh remoteuser@your.other.server.com
# you should be asked for a password. You should be asked
# to confirm the remote server RSA fingerprint to confirm
# it's authenticity.

ssh-keygen -t rsa
# Accept defaults, don't set password

ssh-copy-id remoteuser@your.other.server.com
# Use your password for the other server. This will copy
# the new key to the other server.

# see if it works
ssh remoteuser@your.other.server.com
# you should connect to the remote server without
# needing a password.
exit

Step 12
Add your new network printer to your client operating system as you would any other network printer.

# Some tips: Get your printer url
# from http://10.18.6.202:631/printers/
# where 10.18.6.202 is replaced by your servers IP, then
# click on your new printer's link. The url of the page
# is the printer's address you can use when adding a printer.
# Example - mine is:
# https://10.18.6.202:631/printers/PDF-Generator

These settings worked for me:

Windows 8
----------
Open "Advanced Printer Setup"
Choose "The printer that I wanted isn't listed"
Select a shared printer by name
Enter your printer url, eg:
    https://10.18.6.202:631/printers/PDF-Generator

For the printer model
Vendor = Microsoft
Driver = PS Class

Windows 7
---------
Vendor = Generic
Driver = MS Publisher Colour
Mac OS X
--------
Settings > Printers & Scanners > '+' key to add new printer
Printer appeared on the list, set itself up automatically.

Step 13
Create a directory on your remote server to receive the files

#On your remote server
mkdir /someplace/PDF_Printer
chmod 775 /someplace/PDF_Printer
chown youruser PDF_Printer
chgrp yourgroup PDF_Printer
chmod g+s PDF_Printer
#this forces new files in the directory to have the group settings.

Step 14
Print!

You should see:
the PDF’s generated in /var/spool/cups-pdf/ANONYMOUS/
and also see them on your remote file server

Step 15
Remove old PDF’s from the local and remote directory.

NB: The following is already included in the script at step 9

# clean up the remote folder.
# this could be done by cron on the remote server, but it's handy
# to just do it from this script. Obviously only runs when a PDF
# is printed, but that's fine for my needs.
# Any files greater than 3 days old to be deleted
# (mtime = n * 24 hours ago)

# remote directory
ssh remoteuser@your.other.server.com 'find /path/to/send/files/* -mtime +3 -exec rm {} ;'
# local directory
find /var/spool/cups-pdf/ANONYMOUS/* -mtime +3 -exec rm {} ;

Step 16
Email the PDFs

Fortunately, my users are happy getting their PDFs from the shared folder.  The PDF prints arrive almost instantly and the folder is tidied up automatically so everyone is happy.  I didn’t have to work the detail out here so if you do, please let me know and we can share your setup to help others.  Setting up email is easy but not trivial in todays world.  The steps you’ll need to work out are:

  • Setting up a MTA (mail transfer agent – I suggest Exim4)
  • Adding SPF and DKIM to your DNS settings and server, or set up an email account/method of relay through a server that has this setup (otherwise you’ll have delivery issues in the future)
  • Add to the script above code that will attach the files to an email, providing the attachments are less than X? Mb in size (I’d suggest X? is less than 10Mb, but that will depend on your users email accounts). If they are larger than that, just let the user know where they can get their PDF from (probably the shared folder)
  • Delete the file once the email is sent
  • Make sure any bounces get reported to you

I’m going  to add to the script to allow for emailing the files and cleaning up the remote directory.   I plan to have this updated before the end of December 2014, so if you’re reading this and I haven’t updated it – let me know!

Solving bugs

Problem: Letterhead background isn’t showing but PDF size has increased as if it is there.
Solution: Pages printed from a web page (test chrome & firefox) cannot have ‘background’ added by pdftk.  I also found the same behaviour from the CAD design software we use in our business.  pdftk does add the background but the printing page has a white background, not transparent, so the letterhead doesn’t show through.  Print a word/writer document and see if it works. If you frequently need to print from web pages with background, try using pdftk’s ‘stamp’ instead. This overlays your letterhead on top of the printing document, so as long as your letterhead has transparency, this will work.

Problem: PDF appears in /var/spool/cups-pdf/ANONYMOUS but nothing else happens.
Solution: In cups-pdf.conf set “LogType 4”. The logs will show the command that is calling the post processing script. You can run this command yourself (it specifies the file that has been created).  If might fail and tell you why.
After that, try running each part of the script (eg the line; pdftk…. then scp…. etc) separately to see which bit is stopping. I had a number of problems I solved with this approach, including lots of file write/read permission issues.

13 thoughts on “Virtual PDF Printer for our small office network – a step by step how to”

  1. great post.
    But I’m getting a problem printing to cups-pdf when using windows 8.1 pro

    I can open a simple notepad and print with success from windows 8.1 to cups-pdf
    But if I try to open some picture or bigger document, the documents takes too long to print to cups-pdf.

    Can you try to print using your printer details in win 8.1 “Print test Page” ?
    win8.1 > printers > printer property > print test page…like those pages that windows try to print as test after finish install the printer…

    Even this simple test I have difficult to print to cups. It prints, but it takes too long to finish.

    Did you try it ?

    Did you try to print using windows 8.1 ?

    1. Hello Manuel,

      Yes, we use it with Windows 8.1 and it’s almost instantaneous, even for large prints. I’ll email you our print test pages in case it helps you spot the problem.

      For fault finding I’d try:
      a) turn off PostProcessing (step 4, a # in front of “PostProcessing /usr/bin/cups_postprocess”). Does that help? If so that postprocessing script is the cause of the slow down. Check for write permissions
      b) Check the various logs for clues
      c) Watch ‘top’ as you send a print – is there a process that runs and is busy?

      Hope that helps,
      Steve

  2. At least in v 16.04.1, you need to have a comma added to the end of the apparmor.d addition so it looks like this:

    /usr/bin/cups_postprocess Ux,

    Without the ending comma you will get an error when trying to restart the service

    1. Thanks for the comment. It might be working for me without the comma as it happens to be the last line in my file. I’ve tidied the post up and highlighted this to help other readers.

  3. Have you tried playing with the margins on the input pdf? I tried adding pdfcrop to your script and could never get it to work.

    1. I’ve never needed to, but I can’t see a reason that wouldn’t work.

      step 1) does it work just from a command line?
      step 2) if you put your command (once you know it’s working) after ‘# Use pdftk to set the letterhead as the page background.
      pdftk $PBLANK multibackground /etc/cups/Letterhead.pdf output “$PLET”‘ what happens?
      Actually, you might want to just replace that line with your line. The input is $PBLANK and the output is $PLET, those variables set earlier in the script

  4. Hi, I just want to say thanks for the article and this is exactly the kind of article we could do with more of on the web. It solves a stated problem in clear, reproducible steps. No 10-page clickthrough to get to apt-get install cups.

    Recently I’ve been battling with CUPS, specifically the upgrade from Ubuntu Server 12.04 to 16.04 via 14.04 causing a lot of headaches. At least now I have a well-documented process similar to the above that works for us. I inadvertently installed CUPS-PDF as part of my troubleshooting and I’m hoping to put it to good use now that it’s there, it could really play a role for us. Thanks again.

  5. Hey mate,

    Thank you for sharing the article, well done!

    I’m getting stuck on step 12, I cannot find the PDF printer trying to add from a Windows PC.
    I was wondering if I need to join the Linux into my domain?

    Cheers,
    Vini

    1. Hi Vini,
      I haven’t used windows domains in a long time. I think your logic could be right so if you can work out how to make your linux join your windows domain you might then see the network printer.
      Alternatively, I think you can type the IP address when adding a printer in windows ( Add printer > [wait] > The printer that I want wasn’t listed > Add Printer by using a TCP/IP Address ) which may be simpler.
      I hope that helps, please post again with what worked to help others.

      1. Hi,
        I have made some progress and added the printer successfully on my Windows machine, but when I try to print a file nothing happens, it is not saved in the default folder(PDF) of Ubuntu, however, I can see it is in the print queue.

        Step 13 is create a directory, could you please clarify how to perform it ? On my local Windows machine (name xx-pc-oo1).
        Thanks
        Vini

        1. Hi Vini,
          It’s possible but I’m not sure how without research (sorry – too busy to do that for you).
          For my use, the script here copies the generated PDFs from this new server into another linux server using the SCP command in Step 9
          # use scp to send the new files to the remote server
          scp $PBLANK user@some.other.server.com:/path/to/send/files/
          scp $PLET user@some.other.server.com:/path/to/send/files/

          I’d guess you create a file in windows the normal way (right click > new folder), then ( right click > properties > sharing ).
          Then you’d need to modify the ‘scp’ command above to copy the files to your windows share. I don’t think scp will work though. I think you might have to ‘mount’ your shared directory (so it now appears to the PDF server as a local location, like /mnt/windows_share ) then instead of scp you could cp (scp = secure copy, works over an SSH connection between servers, cp = copy, the normal copy command you’d type on a command line).

          Before you work out how to copy the new PDFs between servers, you might like to verify the PDFs are being created. They are saved on the local server at; /var/spool/cups-pdf/ANONYMOUS/

          I hope that helps

Leave a Reply

Your email address will not be published. Required fields are marked *