Save as PDF – Writing your own PDF menu items

When you click the “PDF” button in a Tiger Print dialog, you’re presented with a menu that allows you to “Print As PDF” in various different ways. It turns out that all but the last of the items in the lower half of the menu are Automator workflows, stored in “/Library/PDF Services/”. This obviously allows the possibility of writing your own menu items if none of the current ones do what you want.

I recently wrote such an item for my own use that moves the PDF file to a certain folder on my desktop, creating the folder if necessary and renaming the file if necessary to avoid a clash, and setting the creator and file codes so that the file opens by default in Adobe Reader rather than in Preview.

Automator has an action in its Library (under “Automator”) called “Run AppleScript”, which does what the name suggests. Since AppleScript is my area of competence, I used this action as the basis for my workflow.

The template script that comes with the action isn’t very informative:

on run {input, parameters}
	(* Your script goes here *)
	return input
end run

A little research reveals that ‘input’ is a list of the items passed to the action and ‘parameters’ is a record of the settings for the action as used in the current workflow. In the intended “PDF menu” situation, the input list contains a single alias to the PDF file that the system’s just created. The four properties of the record are ‘|temporary items path|’ (the POSIX path of a temporary folder in the user’s Temporary Items folder), ‘|ignoresInput|’ (a boolean representing that setting in the Automator action), ‘|action|’ (not sure “ its always ‘item id 3’ for me), and ‘|source|’ (the text of the script). These can probably all be safely ignored here, but as this is my first (and possibly only) Automator script, I’ve been slightly formal and allowed for no action to take place if “Ignore Results from Previous Action” is set at the input to the action in the workflow. I can’t imagine that’s of any use in an AppleScript-running action, but what the hell… :wink:

The script is pasted into the Automator action and the workflow can be saved either in the local “PDF Services” folder or the user’s. Its name appears automatically in the “PDF” menu next time that’s accessed.

-- Save PDF to folder "PDF Saves" for Acrobat, by Nigel Garvey 19th/22nd October 2006
-- To be run as an Automator workflow from the "PDF" menu button in Print dialogs.
-- Saves the PDF files created to a folder called "PDF Saves" on the Desktop, creating
-- the folder if it doesn't exist. The files are typed to open in Adobe (Acrobat) Reader.

on run {input, parameters} -- Automator parameters: {<list of alias>, <record of this action's configuration settings>}
	if not (|ignoresInput| of parameters) then
		-- Create the target folder if necessary and get a list of the names of its existing items.
		set targetFolderName to "PDF Saves" as Unicode text
			set targetFolder to ((path to desktop as Unicode text) & targetFolderName) as alias
		on error
			tell application "Finder" to set targetFolder to (make new folder at desktop with properties {name:targetFolderName}) as alias
		end try
		set takenNames to (list folder targetFolder)
		repeat with thisItem in input
			set {name:thisName, folder:isFolder} to (info for thisItem)
			if not (isFolder) then
				-- If this item's a file (!), check its name isn't already taken in the target folder. Rename it if necessary.
				if (thisName is in takenNames) then
					set thisName to getNewName(thisName, takenNames)
					tell application "Finder" to set thisItem's name to thisName
				end if
				-- Update the list of taken names.
				set end of takenNames to thisName
				-- Move the file to the destination folder, set it to open in Adobe Reader, and add it to this action's output.
				tell application "Finder"
					move thisItem to targetFolder
					tell result to set {file type, creator type} to {"PDF ", "CARO"} -- ('result' must be a Finder reference)
				end tell
			end if
		end repeat
	end if
	return input -- Since the moved file(s) are represented by alias(es), their new locations are still tracked in the input list.
end run

-- Modify 'currentName' so that it doesn't clash with any names already taken.
on getNewName(currentName, takenNames)
	set dot to "." as Unicode text
	set copyStr to "copy" as Unicode text
	set spaceCopy to " copy" as Unicode text
	set spaceCopySpace to " copy " as Unicode text
	set astid to AppleScript's text item delimiters
	-- Split the current name into its root and extension.
	if (currentName contains dot) then
		set AppleScript's text item delimiters to dot
		tell currentName to set {rootName, ext} to {text 1 thru text item -2, dot & text item -1}
		tell currentName to set {rootName, ext} to {it, ""}
	end if
	-- If the root part looks like a "copy" name, remove "copy" and the number (if any).
	tell rootName
		if (it contains spaceCopy) and ((count words) > 1) and ((word -1 is copyStr) or ((word -2 is copyStr) and (my isInteger(word -1)))) then
			set AppleScript's text item delimiters to spaceCopy
			set rootName to text 1 thru text item -2
		end if
	end tell
	set AppleScript's text item delimiters to astid
	-- Keep inserting " copy " (with an incrementing number) until an untaken name is found.
	set newName to rootName & spaceCopy & ext
	set copyNum to 1
	repeat while (newName is in takenNames)
		set newName to rootName & spaceCopySpace & copyNum & ext
		set copyNum to copyNum + 1
	end repeat
	return newName
end getNewName

-- Test whether a piece of text represents an integer.
on isInteger(s)
		return (s mod 1 is 0)
	on error
		return false
	end try
end isInteger