Snippet of the Week – Pages2PDF 🤖, a Sidequest with Automator and AppleScript

A couple of weeks ago, my better half asked me if there was an easy way to convert a ton of Pages documents to PDF. One that didn’t involve opening and printing each of them to PDF one by one. I didn’t know about any useful utilities, but I always wanted an excuse to try out AppleScript and Automator. So that’s what I did!

Well, what I thought was going to be an hour of tinkering, turned into at least half a day’s work spread out over multiple days. Turns out that while AppleScript is pretty powerful and Automator is useful, they’re also fiddly as hell! And add to that that Apple seems determined to neuter them without clear workarounds.

First off, the good news; there’s plenty of resources to help you with Automator and AppleScript, with the best one being by Sal Soghoian. He served as product manager for the Apple automation technologies for years and his website is full of great resources and snippets to learn from.

Now, the bad news. One of the more amazing features that Apple introduced in macOS recently, is improved sandboxing. This makes it harder for attackers to gain access to your system, which is a good thing! However, it makes life harder for any tools you want to make that need access to your file system. In AppleScript’s case, the export and save commands need to create files, which Automator has no sandbox access to!

After asking on Ask Different, a StackOverflow for Apple stuff, someone finally came with a solution for this;

open for access file _location
-- Make sure this line of code is executed before the `export` step, and make sure you don't put it in a `tell` block.

Yay, fiddly! But, that works! 🎉

So, without further ado, the completed AppleScript that I places inside an Automator service;

on run {input, parameters}
	repeat with _document in input
		tell application "Finder"
			set _directory to get container of file _document
			set _documentName to name of _document
			set _fileName to name of _document
			if _documentName ends with ".pages" then ¬
				set _documentName to text 1 thru -7 of _documentName
			set _PDFName to _documentName & ".pdf"
			set _incrementIndex to 1
			repeat until not (exists file _PDFName of _directory)
				set _PDFName to ¬
					_documentName & "-" & (_incrementIndex as string) & ".pdf"
				set _incrementIndex to _incrementIndex + 1
			end repeat
			set _location to (_directory as string) & _PDFName
		end tell
		open for access file _location
		tell application "Pages"
			open _document
			with timeout of 1200 seconds
				export front document to file _location as PDF
			end timeout
			close front document
		end tell
	end repeat
	return input
end run

I placed this in my Automator service, which accepts documents in Finder and uses them as input for the script. The end result gives you PDF for any document that can be opened by Pages (or undefined behavior for anything else, hah!). Or, if you just the service, you can download the workflow file here.

A service in Automator, which uses selected documents in Finder as input for the AppleScript

If you think the snippet is useful – or have an improvement – send me tweet or a toot!