Scripting PDF Security options

finder

(Phil Stokes) #1

I have a user that wants to automate creating PDFs that prevent copying content with a password. In the UI from any Print dialog, you’d hit PDF > Save as PDF… then ‘Security Options’. From there you’d click the checkbox to require password to copy as shown here:

Note that this is NOT the same as encrypting the pdf to require a password to open, which he specifically does not want.

I can do this with gui scripting (below), but aside from the yeuk! factor its far too fragile (user’s language is not English, for a start off, so all the ui element names are probably wrong for his mac).

Since the user is managing part of his workflow in Automator, I thought I’d look into whether I could whip up a quick custom Automator action in Xcode (something novel to try…), but I’m stuck trying to find a public API for that check box option.

I did find

@property(nonatomic, readonly) BOOL allowsCopying; 

The ability to copy content from a PDF document is an attribute unrelated to whether the document is locked or unlocked. It depends on the PDF permissions set by the document’s author.

This method only determines the desired permissions setting in the PDF document; it is up to the application to enforce (or ignore) the permissions.

This method always returns YES if the document is not encrypted. Note that in many cases an encrypted document may still be readable by all users due to the standard empty string password. For more details about user and owner passwords, see the Adobe PDF specification.

in Quartz > PDFDocument, but that method is marked as available in 10.12+, whereas this option has been available on macOS before that, so I’m guessing it’s not the same.

Anyone have any insights?

nb. Of course, I’m aware that once his PDF is open on someone else’s machine, copying the content is always going to be possible, even if it’s just taking a screenshot, but this is what the user’s asked for.

TIA

Phil

#######################
-->> DESCRIPTION
#######################
(*
 
An example of GUI scripting to save a Word.doc as a PDF with security option 'Require password to copy text, images and other content' set to 'ON'. A default password of '123456' is set in the VARIABLES section below.
 
This script was tested on macOS 10.12.6 (‘Sierra’) using Word for Mac 2011. The mac's default language was English.
Due to the requirements of GUI scripting, it is highly likely to fail if run in a different environment.
 
*)
#######################
-->> USAGE
#######################
(*
 
Change the password in the VARIABLES section below to something else.
Open a Word document and run the script.
Note the script runs on the frontmost document.
It does not save or close the Word document itself.
 
*)
 
#######################
-->> VARIABLES
#######################
(* example password *)
set thePassword to "123456"
 
 
#######################
-->> COMMANDS
#######################
 
tell application "Microsoft Word"
    activate
end tell
 
tell application id "com.apple.systemevents" -- System Events.app
    tell its application process "Microsoft Word"
        (* click the print menu item *)
        tell its menu bar 1
            tell its menu "File"
                tell its menu item "Print..." to perform action "AXPress"
                delay 0.5
            end tell
        end tell
        (* click the PDF button *)
        tell its window "Print"
            tell its menu button "PDF"
                perform action "AXShowMenu"
                tell its menu "PDF"
                    tell its menu item "Save as PDF…"
                        perform action "AXPress"
                    end tell
                end tell
            end tell
            delay 0.5
            (* click the Security Options button *)
            tell its sheet 1
                tell its group 1
                    tell its button "Security Options..."
                        perform action "AXPress"
                    end tell
                end tell
            end tell
        end tell
       
        (* select the PDF Security options window *)
        tell its window "PDF Security Options"
            set selected to true
            set focused to true
            (* click the checkbox to on *)
            -- NOTE: for some reason there is a delay of about 6 seconds here, I do not know why
            tell its checkbox "Require password to copy text, images and other content"
                if (value is equal to 0) then
                    perform action "AXPress"
                end if
            end tell
            (* add the password and confirm *)
            set tf to 1
            repeat 2 times
                tell text field tf
                    set its value to thePassword
                    perform action "AXConfirm"
                end tell
                set tf to 2
            end repeat
            tell its button "OK" to perform action "AXPress"
        end tell
        (* save the pdf with the default name and location - it's possible to set this in the script, but I didn't bother for this test  - if the document already exists it will halt the script here and ask if you want to replace it. *)
        tell its window "Print"
            tell its sheet 1
                tell its button "Save" to perform action "AXPress"
            end tell
        end tell
    end tell
end tell
 
#######################

(Shane Stanley) #2

The latest header files show allowsCopying as available in 10.4 – I suspect the 10.12 thing is a case of a method of the same name being reincarnated as a property. But it’s still read-only, so no use to you.

However, writeToFile:withOptions: or writeToURL:withOptions: is probably what you want, and the key in the dictionary you provide is, I think, kCGPDFContextAllowsCopying.

Try something like:

set theDict to current application's NSDictionary's dictionaryWithObjects:{true} forKeys:{current application's kCGPDFContextAllowsCopying}
theDoc's saveToFile:thePath withOptions:theDict

You should also be able to use kCGPDFContextUserPassword if you want to set a password.


(Phil Stokes) #3

Thanks, Shane. Yeah, I didn’t notice that was a read-only property, duh, but this does look promising:

kCGPDFContextAllowsCopying
Whether the document allows copying when unlocked with the user password. The value of this key must be a CFBoolean object. The default value of this key is kCFBooleanTrue.

Now for setting about translating Apple’s legacy Automator docs into something that represents modern parlance. :thinking:


(Shane Stanley) #4

Have fun. Although I have a vague recollection that there’s an updated ASObjC sample project somewhere.


(Phil Stokes) #5

It’s actually easier to do it in Objective-C. There’s an objective-c run handler - (nullable id)runWithInput:(nullable id)input error:(NSError *_Nullable)error specifically for the job.

I more or less completed it. For some reason, I couldn’t get Xcode to run it in the debugger though, which slowed me up a lot - adding Automator as the executable in the build scheme just made it launch and then quit - but then I hit a more devastating problem.

While Preview will indeed honour the setting, other pdf apps don’t (image below is PDF Expert happily letting me cut and paste from the same doc that Preview throws the password on), so the ‘security’ this is providing is really rather weak. I don’t know exactly what the user is protecting, but basically it isn’t going to help him much.

I’ve downed tools on this till I hear back from him as I don’t want to spend more time on something that is essentially pointless.

Still, I learned how to make Automator actions, so that’s a bonus I s’pose!


(Shane Stanley) #6

Did you set a user password?


(Phil Stokes) #7

I only set a password to prevent copying as shown in the dialog box in my OP., i.e.

PDFDocument *pdfDoc = [[PDFDocument alloc] initWithURL:input];
            NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                                             noCopy, kCGPDFContextAllowsCopying,
                                             password, kCGPDFContextOwnerPassword,
                                             @"", kCGPDFContextUserPassword, nil];
            [pdfDoc writeToFile:outputFile withOptions:dict];

The user specifically wants to allow others to open and print. He’s aware of the implications of that.


(Shane Stanley) #8

Yeah, I meant something other than an empty string.


(Phil Stokes) #9

Not sure what the effect of that would be, but I’ll give it a whirl when I get back to this later today.


(Phil Stokes) #10

Completely unrelated question, but just on the off-chance that you’ve seen this before:

After messing about with the Automator project, I notice now that all my other Xcode projects show the target under ‘Products’ in the Navigator view in red ink. Even after successfully building and running, and even though the ‘Show in Finder’ happily shows them (normally, that means they can’t be found, but that’s not the case here).

I guess the Automator project messed with some global Xcode setting (I saw some StackEx posts suggesting the pre-Configuration Builds Products Path might be responsible, but I couldn’t effect a solution from the suggestions).

I guess I’ll try the new Xcode users list unless you’ve got anything that springs to mind?


(Shane Stanley) #11

The Derived Data location is the only thing I can think of.


(Phil Stokes) #12

No, I’d already been through the usual Xcode troubleshooting stages.

I never did figure it out, but updating to Xcode 9 resolved the issue.