Search code examples
applescriptmacos-big-sur

Applescript to Export all files in folder to pdf using Preview


I have a very large number of pdf's that I need to open in Preview, export to pdf, and close. (Some of the pdf's are pretty dodgy, so they need to be preprocessed this way.) I would like to automate this with AppleScript, but I am not making much progress. My best attempt so far is

tell application "Finder"
    set fl to files of folder POSIX file "/Users/mah/Desktop/Test" as alias list
end tell
repeat with f in fl
    tell application "Preview"
        open f
        tell application "System Events" to tell process "Preview"
            click menu item "Export as PDF…" of menu 1 of menu bar item "File" of menu bar 1
            delay 0.2
            click button "Save" of sheet 1 of window 1
        end tell
        close f
    end tell
end repeat

This code opens all of the files, but "click menu item" has no effect, and my attempt to "click button Save" gives the error "Can’t get sheet 1 of window 1 of process "Preview". This is also the wrong way to the file, but I cannot figure out a better way.


Solution

  • This works in Sierra. Let me know if it works in your OS. If you have a set folder that contains your pdfs, then you can replace 'choose folder' with a reference to it. The delays may require adjustment.

    Update: Changed source folder to one mentioned in question. Should now work with PDFs that are not associated with Preview (tested with Skim pdfs). Now exports to a specified folder (and could actually be changeable per file).

    -- set srcFolder to choose folder -- ad hoc
    set srcFolder to ((path to desktop) as text) & "Test" as alias
    --> set srcFolder to alias "Drive:Users:mah:Desktop:Test:"
    tell application "Finder"
        set rawFiles to (files of srcFolder whose name contains "pdf") as alias list
    end tell
    
    tell application "Preview"
        activate
        try
            close windows -- clean slate
        end try
        
        tell application "System Events"
            repeat with x in rawFiles
                tell application "Preview" to open contents of x
                --> open alias "Drive:Users:mah:Desktop:Test:skim-33print-on-demand.pdf"
                --> document "skim-33print-on-demand.pdf"
    
                tell process "Preview"
                    delay 0.2
                    click menu item "Export as PDF…" of menu "File" of menu bar item "File" of menu bar 1
                    delay 0.1
                    key code 5 using {command down, shift down} -- Go to folder…
                    delay 0.1
                    keystroke "~/Desktop/exports/" -- folder must exist
                    delay 0.1
                    key code 76 -- type Return (Go button)
                    delay 0.1
                    key code 76 -- type Return (Save button)
                    delay 0.1
                    click button 1 of window 1 -- click Close button
                end tell
            end repeat
        end tell
    end tell
    

    NB One of the requirements of the alias is that the object it references must exist at compile time.

    The specified folder may add a bit to the time required to save each pdf because of the delays for handling the UI scripting.† You can get around that by first doing a manual export as the dialogue should retain the last folder used as its target (and then remove those delays/actions). The exports folder should be empty when running the script otherwise the cancel/replace dialogue will be triggered which would require further steps in order to process. Finally, if the files do not contain 'pdf' as part of their filename then they will not be processed.

    † It's actually difficult to know for sure as when the delay is set to .1, the 'go' dialogue doesn't manifest. In Script Debugger, I ran it multiple times (on six files) with the three delays each set to .1 and also at .2 seconds. So, each loop should have increased by .3 seconds, or 1.8 seconds overall. That didn't happen. In all cases, it took between 11.5 and 12 seconds to process the six files, but there was typically a .2 seconds overall increase when the delays were set to .2 (11.6 vs 11.8 seconds). Finally, I removed the six lines of code and ran it and it took between 11.4 and 11.5 seconds, so I guess the whole issue is insignificant.