http:// www.jms1.net / osx-pdf-services.shtml

Creating a PDF service under OS X

Most OS X users already know how to save something- pretty much anything- as a PDF file. It's simple- just hit Command-P (or choose "File -> Print" from the menu), choose the "PDF" button at the bottom of the Print dialog, and choose "Save as PDF..." from the resulting menu.

However, below that option are some other items, things like "Mail PDF" or "Save PDF to iPhoto". Obviously these items are coming from somewhere, but I figured they were using some kind of secret plug-in interface which is not available outside of Apple, like the secret plug-in interface for Mail.app which makes it so hard to add support for PGP signatures and encryption.

What started this was finding a web site in the UK, macosxtips.co.uk, which is similar to macosxhints.com (where I was already a regular reader.) One of the pages on this site explained that if you create a "~/Library/PDF Services" directory, any "aliases" (the Mac version of a symbolic link) you create within that folder will appear as menu items under the "PDF" button, and when you select those items, the PDF files will automatically be saved in those directories.

The same web site had another page where the author had written an Automator workflow to not only save the PDF to a specific directory, but also append the date and time to the filename, because a second "Print to PDF" operation using the same Directory Alias menu item would result in the second PDF overwriting the first one, if the titles were the same.

I should explain that I'm not a big fan of Automator. I can see where it's useful for some people, especially non-technical people, but I've been writing my own programs since I was about eleven years old, and I've spent the last thirteen years building and running Internet service providers- I'm used to dealing with the nitty-gritty details. To me, Automator is too simple- it's like a candy coating on top of Applescript.

However, like the author of the forum post, I have a directory full of "web receipts", copies of the final order confirmations whenever I order something online, and the filenames I use for these PDF files all START with the date and time, rather than having them at the end. And I started wondering if it would be possible to create a custom PDF menu items which not only stored the file in a certain directory, but renamed it so that it started with the date and time.

My first step was a Google search for osx automator "pdf services". The first link was a forum post about a custom workflow for generating files for a "Sony Reader" (which I'm assuming is a handheld device for reading "e-books".) The thing which got my attention was the fact that the author had used what looked like a shell script to call a program to convert the PDF to whatever format he needed.

So I downloaded his Automator workflow, fired up Automator, and opened his file. Turns out the workflow consists of one single action, "Run shell script", with the script being what was in the forum post. And installing the workflow as a "PDF Service" (an item on the "PDF" button) involved saving the workflow file to the same "~/Library/PDF Services" directory where the first page had talked about creating directory aliases.

Then I started playing with Automator. I started with a blank workflow, added one "Run shell script" action, and watched what Automator did when I changed the shell from "/bin/tcsh" to "/bin/sh", and then changed the "Pass input:" option from "to stdin" to "as arguments". It generated this script fragment:

for f in "$@"
do
    echo "$f"
done

Of course it was obvious that Apple had included just enough to show how it works- the system calls our script with something, probably filenames, on its command line, and our script needs to do something with those filenames.

I tried playing around with it a bit. It turns out that the stdout and stderr channels don't go anywhere (at least not that I could find) so I added some lines within the script to make it send that output to a file on my desktop. Then I expanded their starter script a bit, and verified that the argument on the command line (I never saw more than one) is the filename of the generated PDF file, in a temporary directory (just begging to be moved to somewhere more useful.)

I also found that Automator has a "Save as Plug-in..." command, which saves the workflow in the appropriate directory to be used as a plug-in for any of several programs... and one of these options is "Print Workflow", which saves it to the same "~/Library/PDF Services" directory we've been looking at. However, it saves it as a ".app" file.

First I wondered why, but then I realized that if it saved it as a ".workflow" file, it would need to run Automator in the background every time we wanted to use it. By bundling it into a stand-alone app, it avoids having to load Automator into memory just for something as simple as moving a PDF file to a specific directory.

So then I got even more curious- the system recognizes directory aliases, Automator workflows, and stand-alone apps as PDF Services plug-ins, so what else would it recognize? Would it recognize a normal UNIX executable, like a shell script, without needing Automator to be involved at all?

One quick experiment later, and YES, it will run a shell script (and presumably would run programs compiled from C source as well.) However, when it calls a shell script (or other program) directly, there are two command-line options which are not present for a shell script within an Automator workflow. The first is the title of the document, which is pretty useful (and which isn't available to scripts within an Automator workflow.) The second is this big long ugly string of low-level information about the PDF file itself- details which may be needed by some programs, but which are not needed if you're doing is moving the file to a specific directory, with a specific name.

So, without any further discussion, here's the script.

#!/bin/sh OUTDIR="$HOME/Downloads/Web Receipts" TSFORMAT="%F" ######################################## # get title from PDF Services system # ignore Automator settings TITLE="${1:-}" shift shift ######################################## # make sure the output directory exists mkdir -p "$OUTDIR" if [ ! -d "$OUTDIR" ] then echo "ERROR: $DIR does not exist or is not a directory" exit 1 fi ######################################## # apparently it's possible to be called with multiple filenames - ??? # use a loop to make sure we handle all files listed on the command line for f in "$@" do ######################################## # generate new filename TIMESTAMP=`date "+$TSFORMAT"` NEWFILE="$OUTDIR/$TIMESTAMP $TITLE" # no ".pdf" on end ######################################## # if that name already exists, add numbers to the end # until we find one which doesn't exist yet if [ -e "$NEWFILE.pdf" ] then n=1 while [ -e "$NEWFILE $n.pdf" ] do n=$(( $n + 1 )) done NEWFILE="$NEWFILE $n" fi ######################################## # rename the file into place mv "$f" "$NEWFILE.pdf" done

To use it, simply save it in your "~/Library/PDF Services" directory, with the filename being whatever text you want to have appear in the menu on the "PDF" button. Then set its permissions so that it's executable (using a command like "chmod 755 'Save to Folder XYZ'".

You can also create multiple copies of the script with different names, to store the files in different locations. The "OUTDIR=" line at the top sets which directory the file will be stored in, and the "TSFORMAT=" line sets the format for the timestamp. The "%F" tag means the date in YYYY-MM-DD format, read "man strftime" for the full list of tags available on your system. (Be careful using ":" in your time format, Finder converts it to "/" when it shows the filename.)

For example, one of the other scripts I use on my own system has "%F.%H%M%S" for the format, which formats the date to look like "2024-04-20.114146".