Applescript 使用预览将文件夹中的所有文件导出为 pdf

Applescript to Export all files in folder to pdf using Preview

我有大量 pdf 需要在预览中打开、导出为 pdf,然后关闭。 (有些 pdf 非常不可靠,因此需要以这种方式对其进行预处理。)我想使用 AppleScript 将其自动化,但我没有取得太大进展。到目前为止我最好的尝试是

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

此代码打开所有文件,但“单击菜单项”无效,我尝试“单击按钮保存”时出现错误“无法获取 sheet 1 of window 1 of process "Preview". 这也是错误的文件方式,但我想不出更好的方式。

这适用于 Sierra。让我知道它是否适用于您的 OS。如果您有一个包含 pdf 的集合文件夹,则可以将 'choose folder' 替换为对它的引用。延迟可能需要调整。

更新: 将源文件夹更改为有问题的文件夹。现在应该可以处理与预览无关的 PDF(使用 Skim pdf 测试)。现在导出到指定的文件夹(实际上每个文件都可以更改)。

-- 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别名的要求之一是它所引用的对象在编译时必须存在。

由于处理 UI 脚本的延迟,指定的文件夹可能会增加保存每个 pdf 所需的时间。†您可以通过首先进行手动导出来解决这个问题,因为对话应该保留最后一个用作目标的文件夹(然后删除那些 delays/actions)。当 运行 脚本时,exports 文件夹应该为空,否则将触发 cancel/replace 对话框,这需要进一步的步骤才能处理。最后,如果文件不包含 'pdf' 作为其文件名的一部分,那么它们将不会被处理。

† 实际上很难确定,因为当延迟设置为 .1 时,'go' 对话不会出现。在 Script Debugger 中,我 运行 多次(在六个文件上)将三个延迟分别设置为 .1 和 0.2 秒。因此,每个循环应该增加 0.3 秒,或者总共增加 1.8 秒。那没有发生。在所有情况下,处理六个文件的时间都在 11.5 到 12 秒之间,但当延迟设置为 0.2 时(11.6 对 11.8 秒),总体增加通常为 0.2 秒。最后,我删除了六行代码和 运行 它花了 11.4 到 11.5 秒,所以我想整个问题无关紧要。

正如@Mockman 所指出的,完成相同任务的另一种方法是使用免费的命令行 pdf 工具 mutool。我写了一个 Python 脚本来做到这一点。处理约 600 个文件需要数十秒。如果其他人有类似的问题,我的代码如下。感谢@Monkman 和@Robert Kniazidis 的帮助。

#   Usage: $ python CleanPDFs.py /Users/mah/Desktop/Test 
# 
#   This script requires the installation of mutool, a free command line pdf tool, using
#       brew install mupdf-tools
#   If you need to install brew first, follow the instructions under "Install Homebrew" at
#       https://brew.sh
#

import os
import argparse
import subprocess


def main():
    
    # Read in the arguments and validate
    parser = argparse.ArgumentParser(description="Prepare a folder of downloads from Canvas for upload to Gradescope")
    parser.add_argument('subFolder', type = str, help = 'Path to folder of Canvas submissions')
    args = parser.parse_args()
    subFolder = args.subFolder

    if not os.path.isdir(subFolder):
        print(f'ERROR: {subFolder} is not a valid directory.')
        exit()

    cwd = os.getcwd()
    os.chdir(subFolder)

    # "Clean" all of the pdfs using mutool, outputting to a temporary file
    print("Cleaning pdfs.")
    for fn in os.listdir():
        if not fn.endswith('.pdf'):
            os.remove(fn)
        else:
            fn_out = fn + '_out'
            result = subprocess.run(["mutool", "clean", "-s", "-g", fn, fn_out])
    
    # Delete all of the unclean files
    for fn in os.listdir():
        if fn.endswith('.pdf'):
            os.remove(fn)

    # Now reclean all of the files using mutool, outputting to the original file name
    #   This may no longer be necessary, but it is very fast, so what the hey…
    print("Recleaning pdfs.")
    for fn in os.listdir():
        fn_out = fn[:-4]
        result = subprocess.run(["mutool", "clean", "-s", "-g", fn, fn_out])
         
    os.chdir(cwd)
            
        
if __name__ == '__main__':
    main()