9

Okay so here is what I want to do. I want to add a print option that prints whatever the user's document is to a PDF and adds some headers before sending it off to a device.

I guess my questions are: how do I add a virtual "printer" driver for the user that will launch the application I've been developing that will make the PDF (or make the PDF and launch my application with references to the newly generated PDF)? How do I interface with CUPS to generate the PDF? I'm not sure I'm being clear, so let me know if more information would be helpful.

I've worked through this printing with CUPS tutorial and seem to get everything set up okay, but the file never seems to appear in the appropriate temporary location. And if anyone is looking for a user-end PDF-printer, this cups-pdf-for-mac-os-x is one that works through the installer, however I have the same issue of no file appearing in the indicated directory when I download the source and follow the instructions in the readme. If anyone can get either of these to work on a mac through the terminal, please let me know step-by-step how you did it.

Katie
  • 253
  • 1
  • 3
  • 13
  • Maybe I'm misunderstanding your need, but Cocoa directly supports producing PDF from documents/`NSView`s, you don't need to install a print driver. Search the documentation for "PDF". – CRD Jun 26 '12 at 09:58
  • Here's a specific example: I have a document in Word that I want to send to my device. When I do File >> Print, I want a printer option that will launch my application and give me references to the PDF of the current word doc. (I don't much care exactly when the file becomes a PDF--I realize I can do that within my application). I don't need to display the PDF in a view, and I have used the PDFKit functionality to manipulate files but never from the printer option. If you still think searching the documentation is all I need, could you point me to more specific references? Thanks. – Katie Jun 26 '12 at 13:37
  • As I thought I might be, I was misunderstanding your need. In looking at CUPS you've picked a good place to look for what you're trying to do. – CRD Jun 26 '12 at 20:39

2 Answers2

15

The way to go is this:

  • Set up a print queue with any driver you like. But I recommend to use a PostScript driver/PPD. (A PostScript PPD is one which does not contain any *cupsFilter: ... line.):

  • Initially, use the (educational) CUPS backend named 2dir. That one can be copied from this website: KDE Printing Developer Tools Wiki. Make sure when copying that you get the line endings right (Unix-like).

  • Commandline to set up the initial queue:

    lpadmin \
        -p pdfqueue \
        -v 2dir:/tmp/pdfqueue \
        -E \
        -P /path/to/postscript-printer.ppd
    
    The 2dir backend now will write all output to directory /tmp/pdfqueue/ and it will use a uniq name for each job. Each result should for now be a PostScript file. (with none of the modifications you want yet).
  • Locate the PPD used by this queue in /etc/cups/ppd/ (its name should be pdfqueue.ppd).

  • Add the following line (best, near the top of the PPD):

    *cupsFilter: "application/pdf  0  -"
    (Make sure the *cupsFilter starts at the very beginning of the line.) This line tells cupsd to auto-setup a filtering chain that produces PDF and then call the last filter named '-' before it sends the file via a backend to a printer. That '-' filter is a special one: it does nothing, it is a passthrough filter.
  • Re-start the CUPS scheduler:

    sudo launchctl unload /System/Library/LaunchDaemons/org.cups.cupsd.plist
    sudo launchctl load /System/Library/LaunchDaemons/org.cups.cupsd.plist
  • From now on your pdfqueue will cause each job printed to it to end up as PDF in /tmp/pdfqueue/*.pdf.

  • Study the 2dir backend script. It's simple Bash, and reasonably well commented.

  • Modify the 2dir in a way that adds your desired modifications to your PDF before saving on the result in /tmp/pdfqueue/*.pdf...


Update: Looks like I forgot 2 quotes in my originally prescribed *cupsFilter: ... line above. Sorry!

Kurt Pfeifle
  • 86,724
  • 23
  • 248
  • 345
  • This sounds like exactly what I need! Thanks for taking the time to write this all out. However, please bear with me as my unfamiliarity with CUPS and printer terms makes me ask minute step-by-step questions that you might have already answered. The link to the KDE Printing Developer Tools Wiki specifies that 2dir should be saved in /usr/lib/cups/backend/, however I don't seem to have a cups directory in /usr/lib/. Have I missed something? Also, the last flag to set up the initial queue, I assume I am supposed to replace the path, but with what exactly? – Katie Jun 28 '12 at 20:26
  • 1
    @Katie: Sorry, I had Linux in mind with that path. On Mac OS X you'll have to use `/usr/libex/cups/backend/`. The last `-P` flagged path argument you can replace with `/User/katie/postscript-printer.ppd`... but of course you should put a real PostScript printer PPD there with that name. – Kurt Pfeifle Jun 28 '12 at 21:13
  • Okay, I got that sorted out--pdfqueue is a printer option. I believe I followed the other instructions, however when I try to print, I get a dialog that says "This printer has been paused. Do you want it to resume printing?" With options to Cancel, Add to Queue, or Resume. Hitting either Add to Queue or Resume adds the print job so I can see it in the pdfqueue printer GUI that popped up, but does not seem to respond to resume and just sits there. /tmp/pdfqueue doesn't seem to exist, so I'm not quite sure where this file is going. Any suggestions? – Katie Jun 29 '12 at 18:39
  • @Katie: Add a line saying `LogLevel debug` in your `/etc/cups/cupsd.conf` file and re-start CUPS (`sudo launchctl ...` commands above). – Kurt Pfeifle Jun 29 '12 at 22:14
  • @Katie: Oh, it's possibly my mistake -- the `*cupsFilter: ...` line I prescribed to you above is missing a pair of quotes. Add them please, restart CUPS and try again. Sorry for that. – Kurt Pfeifle Jun 29 '12 at 22:17
  • I changed the `*cupsFilter:...` line, but still seem to be experiencing the same problem. I've added the `LogLevel debug` in the cupsd.conf file but don't seem to see any of the echo statements (that is what I'm looking for, right?) I even retyped in a terminal text editor the beginning of the 2dir bash file up until the end of the first chunk of echos to make sure it was not the newline issue. – Katie Jul 02 '12 at 20:25
  • @Katie: After changing `LogLevel` (or anything else) in `cupsd.conf`, you need to restart CUPS before it takes effect: `sudo launchctl (un|)load /System/Library/LaunchDaemons/org.cups.cupsd.plist`. Can you execute the `2dir` script in a Terminal so that it doesn't spit out Bash errors? – Kurt Pfeifle Jul 10 '12 at 16:17
  • Hi @KurtPfeifle i was unable to generate output using above method. i am using mac os – Mohammad Sadiq Shaikh Mar 31 '16 at 13:51
  • @MohammadSadiqShaikh: Your info does not provide enough details to even start thinking about what could have gone wrong. You do not even tell which version of OS X you run (there have been changes to CUPS in between different versions, you know?)... – Kurt Pfeifle Mar 31 '16 at 15:12
  • i want it to run on yosemite 10.10.5 & above. – Mohammad Sadiq Shaikh Apr 04 '16 at 05:36
6

I really wish I could accept two answers because I don't think I could have done this without all of @Kurt Pfeifle 's help for Mac specifics and just understanding printer drivers and locations of files. But here's what I did:


  1. Download the source code from codepoet cups-pdf-for-mac-os-x. (For non-macs, you can look at http://www.cups-pdf.de/) The readme is greatly detailed and if you read all of the instructions carefully, it will work, however I had a little trouble getting all the pieces, so I will outline exactly what I did in the hopes of saving someone else some trouble. For this, the directory with the source code is called "cups-pdfdownloaddir".

  2. Compile cups-pdf.c contained in the src folder as the readme specifies:

    gcc -09 -s -lcups -o cups-pdf cups-pdf.c

    There may be a warning: ld: warning: option -s is obsolete and being ignored, but this posed no issue for me. Copy the binary into /usr/libexec/cups/backend. You will likely have to the sudo command, which will prompt you for your password. For example:

    sudo cp /cups-pdfdownloaddir/src/cups-pdf /usr/libexec/cups/backend

    Also, don't forget to change the permissions on this file--it needs root permissions (700) which can be changed with the following after moving cupd-pdf into the backend directory:

    sudo chmod 700 /usr/libexec/cups/backend/cups-pdf

  3. Edit the file contained in /cups-pdfdownloaddir/extra/cups-pdf.conf. Under the "PDF Conversion Settings" header, find a line under the GhostScript that reads #GhostScript /usr/bin/gs. I did not uncomment it in case I needed it, but simply added beneath it the line Ghostscript /usr/bin/pstopdf. (There should be no pre-cursor # for any of these modifications)

    Find the line under GSCall that reads #GSCall %s -q -dCompatibilityLevel=%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=pdfwrite -sOutputFile="%s" -dAutoRotatePage\ s=/PageByPage -dAutoFilterColorImages=false -dColorImageFilter=/FlateEncode -dPDFSETTINGS=/prepress -c .setpdfwrite \ -f %s Again without uncommenting this, under this I added the line GSCall %s %s -o %s %s

    Find the line under PDFVer that reads #PDFVer 1.4 and change it to PDFVer, no spaces or following characters.

    Now save and exit editing before copying this file to /etc/cups with the following command

    sudo cp cups-pdfdownloaddir/extra/cups-pdf.conf /etc/cups

    Be careful of editing in a text editor because newlines in UNIX and Mac environments are different and can potentially ruin scripts. You can always use a perl command to remove them, but I'm paranoid and prefer not to deal with it in the first place.

  4. You should now be able to open a program (e.g. Word, Excel, ...) and select File >> Print and find an available printer called CUPS-PDF. Print to this printer, and you should find your pdfs in /var/spool/cups-pdf/yourusername/ by default.


*Also, I figured this might be helpful because it helped me: if something gets screwed up in following these directions and you need to start over/get rid of it, in order to remove the driver you need to (1) remove the cups-pdf backend from /usr/libexec/cups/backend (2) remove the cups-pdf.conf from /etc/cups/ (3) Go into System Preferences >> Print & Fax and delete the CUPS-PDF printer.


This is how I successfully set up a pdf backend/filter for myself, however there are more details, and other information on customization contained in the readme file. Hope this helps someone else!

Katie
  • 253
  • 1
  • 3
  • 13
  • 1
    I thought your ultimate goal was to *'print whatever the user's document is to a PDF and **add some headers before sending it off to a device**...'*. So how did you solve the last part? (My answer only covered the first part -- I didn't know from your question what the detailed requirements for the second part were.) – Kurt Pfeifle Jul 10 '12 at 16:25
  • I ended up separating out the header issue from generating a PDF. Once I had the PDF generated, I could manipulate the data through Cocoa's PDFDocument within my application before sending it to the device. – Katie Jul 10 '12 at 17:38
  • hi Katie, im getting this error (sampletopdf) stopped with status 13 – Mohammad Sadiq Shaikh Mar 31 '16 at 10:07
  • "Download the source code from codepoet [cups-pdf-for-mac-os-x](https://bitbucket.org/codepoet/cups-pdf-for-mac-os-x/overview)." → 404 Repository not found – haba713 Mar 27 '23 at 13:51