Search code examples
applescript

Stuck on AppleScript to add password to PDF in Preview


trying to add a password to a PDF in AppleScript. The below code gets through

   click menu item 23 -- Export Menu item

but fails on the next line with an "invalid index" error "Can't get window 1..."

... and then guessing my next line for the checkbox will need something similar on the end.

Thanks in advance

Tim

set myFolder to path to desktop as string
set myFile to myFolder & "test.pdf"

tell application "Preview"
    open myFile
    activate
end tell

tell application "System Events"
    tell process "Preview"
        tell menu bar 1
            tell menu bar item "File"
                tell menu "File"
                    click menu item 23 -- Export Menu item
                    click button "Permissions..."  of sheet 1 of window 1
                    click checkbox "Require Password To Open Document"
                    text field 1
                    keystroke "1234"
                    text field 2
                    keystroke "1234"
                    text field 3
                    keystroke "1234"
                    text field 4
                    keystroke "1234"
                    keystroke return
                    keystroke return
                end tell
            end tell
        end tell
    end tell
end tell

Solution

  • The example AppleScript code, shown below, was tested in Script Editor under macOS Monterey with Language & Region settings in System Preferences set to English (US) — Primary and worked for me without issue1.

    • 1 Assumes necessary and appropriate settings in System Preferences > Security & Privacy > Privacy have been set/addressed as needed.

    The example AppleScript code is coded to check whether or not the [] Use keyboard navigation to move focus between controls checkbox is checked on the System Preferences > Keyboard > Shortcuts tab, as that would affect how one would programmatically navigate the various dialog boxes using the tab key. It's coded to then branch one way to the other to handle the state of that checkbox.

    Here is an example of how I would code it:

    Example AppleScript code:

    property myPassword : "1234"
    
    set myFolder to path to desktop as string
    set myFile to myFolder & "test.pdf"
    
    --  # Get the value of AppleKeyboardUIMode to determine if the
    --  # 'Use keyboard navigation to move focus between controls'
    --  # checkbox is checked on the System Preferences >  
    --  # Keyboard > Shortcuts tab.
    
    set keyboardNavigation to my checkAppleKeyboardUIMode()
    
    --  # Open the target file in Preview.          
    
    tell application "Preview"
        open myFile
        activate
    end tell
    
    --  # Use System Events to export the document 
    --  # and navigate the ensuing dialog boxes. 
    
    tell application "System Events"
        tell application process "Preview"
            
            tell its menu "File" of ¬
                menu bar item "File" of menu bar 1 to ¬
                click (its first menu item whose name is "Export…")
            
            repeat until exists button "Permissions…" of sheet 1 of window 1
                delay 0.1
            end repeat
            
            tell its sheet 1 of window 1 to click button "Permissions…"
            
            repeat until exists ¬
                checkbox "Require Password To Open Document" of ¬
                sheet 1 of sheet 1 of window 1
                delay 0.1
            end repeat
            
            tell its sheet 1 of sheet 1 of window 1
                
                click checkbox "Require Password To Open Document"
                delay 0.1
                
                if keyboardNavigation = 0 then
                    --  # Use keyboard navigation to move focus
                    --  # between controls is not checked.
                    key code 48 -- # tab key
                    delay 0.1
                    key code 48 -- # tab key
                    delay 0.1
                    keystroke myPassword
                    delay 0.1
                    key code 48 -- # tab key
                    delay 0.1
                    keystroke myPassword
                    delay 0.1
                    key code 48 -- # tab key
                    delay 0.1
                    keystroke myPassword
                    delay 0.1
                    key code 48 -- # tab key
                    delay 0.1
                    keystroke myPassword
                else
                    --  # Use keyboard navigation to move
                    --  # focus between controls is checked.            
                    key code 48 -- # tab key                
                    delay 0.1
                    keystroke myPassword
                    delay 0.1
                    key code 48 -- # tab key                
                    delay 0.1
                    keystroke myPassword
                    delay 0.1
                    repeat 6 times
                        key code 48 -- # tab key
                        delay 0.1
                    end repeat
                    keystroke myPassword
                    delay 0.1
                    key code 48 -- # tab key                
                    delay 0.1
                    keystroke myPassword
                end if
                
                delay 0.1
                click button "Apply"
                delay 0.1
                
            end tell
            
            click button "Save" of sheet 1 of window 1
            delay 0.1
            if exists button "Replace" of sheet 1 of sheet 1 of window 1 then ¬
                click button "Replace" of sheet 1 of sheet 1 of window 1
            delay 0.1
            
            click button 1 of window 1 -- # Close the document.
            
        end tell
    end tell
    
    
    
    --  ## Handler ##
    
    on checkAppleKeyboardUIMode()
        
        --  # Get the fully qualified POSIX pathname of the target .plist file.
        
        set thePropertyListFilePath to ¬
            the POSIX path of ¬
                (path to preferences from user domain as string) & ¬
            ".GlobalPreferences.plist"
        
        --  # Get the value of AppleKeyboardUIMode to determine if the
        --  # 'Use keyboard navigation to move focus between controls'
        --  # checkbox is checked on the System Preferences >  
        --  # Keyboard > Shortcuts tab.
        
        try
            tell application "System Events" to ¬
                tell the property list file thePropertyListFilePath to ¬
                    return the value of ¬
                        the property list item "AppleKeyboardUIMode"
        on error
            return 0
        end try
        
    end checkAppleKeyboardUIMode
    

    Notes:

    One of the issues in the code of the question was the use of three . instead of an ellipsis in: click button "Permissions..."

    Also, the remaining code would not have executed because it is wrapped in a tell menu "File" statement. The example AppleScript code presented herein focuses in the proper places for the events to take place when compared to how its coded in the question.

    Note that UI Scripting is kludgy at best and prone to failure due to changes in the hierarchical UI element structure of the OS and or application being scripted and or timing issues where the use of the delay command may be necessary and or its value adjusted.

    Case in point, I originally coded this to avoid using tabbing and keystroking, however it presented mixed results in that it worked sometimes and not other times, and why I recorded to do those very things. (UI Scripting Ugh!)

    Also, as the first dialog box in the export process is not changing the name of the document or the location it is being saved, the password protected document may end up in a different location than from where it was opened. Additional coding required to modify that possible behavior.



    Note: The example AppleScript code is just that and sans any included error handling does not contain any additional error handling as may be appropriate. The onus is upon the user to add any error handling as may be appropriate, needed or wanted. Have a look at the try statement and error statement in the AppleScript Language Guide. See also, Working with Errors. Additionally, the use of the delay command may be necessary between events where appropriate, e.g. delay 0.5, with the value of the delay set appropriately.