如何以编程方式从OSX上的任何文档生成PDF?

时间:2008-11-09 19:35:22

标签: objective-c macos pdf applescript automator

我正在开发一个OSX项目,用户可以从中选择我需要生成PDF文档的文档集合(来自任何应用程序)。标准的“Macintosh打印”对话框有一个PDF按钮,其中包含许多与PDF相关的命令,包括“另存为PDF ...”。但是,我需要生成PDF文件而无需用户交互。理想情况下,我希望这适用于任何类型的文档。

这是我到目前为止所探讨的选项:

  • Automator动作。有一个用于Automator的PDF库,但它提供了处理PDF文件的操作,而不是生成它们。有一个Finder操作可以打印任何文件,但只能打印到真正的打印机。
  • AppleScript的。某些应用程序能够生成PDF文件(例如,如果您将'save doc in“test.pdf”'发送到Pages,它将生成PDF(但这仅适用于Pages - 我需要支持任何类型的文档)
  • 自定义打印机。我可以创建一个虚拟打印机驱动程序然后使用自动机操作,但我不喜欢在打印列表中将用户与额外的打印机混淆的想法。

我希望有一些方法可以与活动应用程序进行交互,就像用户执行以下步骤一样:

  1. 执行Cmd-P(打开打印对话框)
  2. 点击“PDF”按钮
  3. 选择“另存为PDF ...”(菜单中的第二项)
  4. 在保存对话框中输入文件名
  5. 点击“保存”
  6. 如果这是最好的方法(是吗?)那么真正的问题是:如何将UI事件发送到外部应用程序(击键,鼠标事件,菜单选择)?

    更新:只是澄清一点:我需要转换为PDF的文档是由其他应用程序创建的文档。例如,用户可能会选择Word文档或Numbers电子表格或OmniGraffle绘图或网页。共同点是这些文档中的每一个都有一个相关的应用程序,并且该应用程序知道如何打印它(OSX知道如何将打印输出呈现为PDF文件)。

    因此,Cocoa Dev Central的样本没有帮助,因为它们是关于从我的应用程序生成PDF。

5 个答案:

答案 0 :(得分:6)

我认为您可以使用AppleScript打开文档,然后使用applescript UI scripting来调用打印菜单。

例如:

tell application "System Events"
        tell window of process "Safari"
            set foremost to true
            keystroke "p" using {command down}
            delay 3
            click menu button "PDF" of sheet 2
            click menu item "Save as PDF…" of menu 1 of menu button "PDF" of sheet 2
            keystroke "my_test.file"
            keystroke return
            delay 10
        end tell

    end tell

答案 1 :(得分:3)

查看名为CUPS-PDF

的程序

它是OS X的虚拟打印机,它执行“另存为PDF”方法在通过普通打印机打印时所执行的操作,除了通过它的每个打印作业都会产生pdf输出。

安装后,您可以使用 lp 命令创建shell或AppleScripts。

例如,一旦设置了虚拟打印机,您就可以打印test.txt并将其自动另存为pdf。要使用AppleScript执行此操作,您将使用以下代码:

do shell script "lp -d CUPS_PDF test.txt"

CUPS-PDF应用程序将所有输出保存到/ Users / Shared / CUPS-PDF。我不确定您是否可以更改该路径,但您可以在脚本中检索文件并移动它。

但有几点需要注意。

首先, lp 命令无法打印.doc文件。我认为还有一些其他第三方应用程序可以让你这样做。

其次,CUPS-PDF应用程序在“系统偏好设置”的“打印机”面板中显示其名称中包含连字符,但CUPS将队列名称显示为具有下划线。因此,在命令行中,您需要引用CUPS队列名称,该名称是带有下划线的CUPS_PDF。

即使您没有发现通过 lp 命令构建脚本非常有用 并且仍然希望涉及GUI脚本,然后使用虚拟打印机可以为您节省一些步骤。

答案 2 :(得分:1)

你可以使用像这样的杯子

    on open afile
        set filename to name of (info for afile)
        tell application "Finder"
            set filepath to (container of (afile as alias)) as alias
        end tell
        set filepath to quoted form of POSIX path of filepath
        set tid to AppleScript's text item delimiters
        set AppleScript's text item delimiters to "."
        set filename to text item 1 of filename
        set AppleScript's text item delimiters to tid
        set afile to quoted form of POSIX path of afile
        do shell script "cupsfilter " & afile & " > " & filepath & filename & ".pdf"
    end open

答案 3 :(得分:1)

我在bash中为此创建了一个别名:

convert2pdf() {
  /System/Library/Printers/Libraries/convert -f "$1" -o "$2" -j "application/pdf"
}

答案 4 :(得分:0)

我在Automator的帮助下输入了以下代码(录制动作,然后将特定动作拖出“Watch Me Do”窗口以获取Applescript)。如果要从Safari以外的应用程序打印PDF,则可能必须运行相同的过程并围绕“打印”对话框调整此Applescript,因为每个程序可能具有不同的打印GUI。

# Convert the current Safari window to a PDF
# by Sebastain Gallese

# props to the following for helping me get frontmost window
# http://stackoverflow.com/questions/480866/get-the-title-of-the-current-active-window-            document-in-mac-os-x

global window_name

# This script works with Safari, you might have
# to tweak it to work with other applications 
set myApplication to "Safari"

# You can name the PDF whatever you want
# Just make sure to delete it or move it or rename it
# Before running the script again
set myPDFName to "mynewpdfile"

tell application myApplication
    activate
    if the (count of windows) is not 0 then
        set window_name to name of front window
    end if
end tell

set timeoutSeconds to 2.0
set uiScript to "keystroke \"p\" using command down"
my doWithTimeout(uiScript, timeoutSeconds)
set uiScript to "click menu button \"PDF\" of sheet 1 of window \"" & window_name & "\" of application process \"" & myApplication & "\""
my doWithTimeout(uiScript, timeoutSeconds)
set uiScript to "click menu item 2 of menu 1 of menu button \"PDF\" of sheet 1 of window \"" & window_name & "\" of application process \"" & myApplication & "\""
my doWithTimeout(uiScript, timeoutSeconds)
set uiScript to "keystroke \"" & myPDFName & "\""
my doWithTimeout(uiScript, timeoutSeconds)
set uiScript to "keystroke return"
my doWithTimeout(uiScript, timeoutSeconds)

on doWithTimeout(uiScript, timeoutSeconds)
    set endDate to (current date) + timeoutSeconds
    repeat
        try
            run script "tell application \"System Events\"
" & uiScript & "
end tell"
            exit repeat
        on error errorMessage
            if ((current date) > endDate) then
                error "Can not " & uiScript
            end if
        end try
    end repeat
end doWithTimeout