Delete a PDF page with AppleScript

Hi there,

would it be possible to delete the first page of an PDF with an AppleScript? Of course, doing it manually is already very easy in DT. But I’ve lots of PDFs where I have to remove the front page.

Cheers

DEVONthink doesn’t support the modification of PDF documents via AppleScript but some other apps (e.g. Skim, Adobe Acrobat or PDFpenPro) do.

Yes, it is possible. But it’s a bad idea, since you’d modify the file behind DT’s back. So, its internal housekeeping (indexing, whatever) will be off.

In order to make that work reliably, you’d have to

  • export your PDF one by one to the file system, remembering their location in DT
  • remove them from DT
  • modify them in the file system
  • import them from there again into DT
  • move them to their original location

That’s feasible, but requires some coding (and testing). What’s the problem with the first page?

That’s not necessary. If filesystem events shouldn’t automatically handle this, then the synchronize record ... command should be sufficient.

As I don’t want to do it with export/reimport: Can the GUI of the PDF Editor be scripted (clicking the delete button)?

In that case … here’s a JavaScript script that deletes the first page of all currently selected PDF documents:

ObjC.import('PDFKit');
(() => {
  const records = Application("DEVONthink 3").selectedRecords();
  records.filter(r => r.type() === 'PDF document').forEach(r => {
    const fileURL = $.NSURL.fileURLWithPath($(r.path()));
    const pdfDoc = $.PDFDocument.alloc.initWithURL(fileURL);
    pdfDoc.removePageAtIndex(0);
    pdfDoc.writeToFile($(r.path()));
  })
})()

The same as a script for a smart rule, which should only select PDFs:

ObjC.import('PDFKit');
function performsmartrule(records) {
  records.forEach(r => {
    const fileURL = $.NSURL.fileURLWithPath($(r.path()));
    const pdfDoc = $.PDFDocument.alloc.initWithURL(fileURL);
    pdfDoc.removePageAtIndex(0);
    pdfDoc.writeToFile($(r.path()));
  })
}

All that can also be done with AppleScript, of course. Just not by me :wink:

1 Like

Thanks a lot @chrillek, it works like a charm.

One question left. How to update the thumbnail in JXA?

This seems not to work:

Application("DEVONthink 3").updateThumbnail(r)

According to the scripting dictionary:

updateThumbnail
of: [Record] The record

this

Application("DEVONthink 3").updateThumbnail({of: r});

should work.

1 Like

Thanks, JXA is still a mystery for me. :grin:

use framework "PDFKit"
use scripting additions

tell application id "DNtp"
	if not (exists (content record)) then return
	
	set theRecord to (content record)
	if type of theRecord is PDF document then
		set theURL to current application's NSURL's fileURLWithPath:(path of theRecord)
		set thePDF to current application's PDFDocument's alloc()'s initWithURL:(theURL)
		if (thePDF's pageCount as integer) > 1 then
			thePDF's removePageAtIndex:(0) -- Zero-based index, so page 1 is 0
			thePDF's writeToFile:(recPath)
			update thumbnail of theRecord
		end if
	end if
end tell

:smiley:

Regardless of the programming language, the scripting dictionary tells you everything you need to know about parameters etc. It’s just a bit … weird.

And now @BLUEFROG delivered a version in AppleScript. Still not a mystery to me, but far to talkative :wink: There you can see, btw, that also in AS it is update thumbnail _of_ record.

I think the AppleScript variant works only with the currently selected record, but I may be wrong. Never used content record myself.

Stupid question: As far as I know, object's property is equivalent to property of object. So, why is everyone using property of object (like creation date of theRecord, but not theRecord's creation date) in pure AppleScript and as soon as ObjectiveC is involved, it is object's property like current application's NSURL's fileURL...?

Would
fileURLwithPath:(path of theRecord) of NSURL of current application
not work?

Yes, it is just processing the content record in this case.

Stupid question: As far as I know, object's property is equivalent to property of object . So, why is everyone using property of object (like creation date of theRecord , but not theRecord's creation date ) in pure AppleScript and as soon as ObjectiveC is involved, it is object's property like current application's NSURL's fileURL... ?

I cut my teeth on the more terse AppleScript form property of theThing though the possessive form theThing’s property is equivalent. In ASOC tutorials, the possessive form is most commonly used. And in my mind, it is much more explanatory since ObjC is a bit of a mystery to many (myself included to a large degree). The possessive form makes it more clear that, ”Ohhhh… stringWithString belong’s to NSString, so that must make it a method of NSString, right!?” :smiley:

And no, set theURL to fileURLWithPath:(recPath) of (NSURL of current application) doesn’t seem to be equivalent. It doesn’t even compile.


And here’s an updated version supporting multiple selected files with some more error trapping…

use framework "PDFKit"
use scripting additions
property NSURL : a reference to current application's NSURL
property pdfDoc : a reference to current application's PDFDocument
-- In a simple script like this, defining properties isn't really required but it's not a bad form to use

tell application id "DNtp"
	if selection is {} then return
	
	repeat with theRecord in (selected records)
		if type of theRecord is PDF document then
			set recPath to (path of theRecord)
			set theURL to (NSURL's fileURLWithPath:(recPath))
			set thePDF to (pdfDoc's alloc()'s initWithURL:(theURL))
			if (thePDF's pageCount as integer) > 1 then
				(thePDF's removePageAtIndex:(0)) 
				(thePDF's writeToFile:(recPath))
				update thumbnail of theRecord
			else
				log message info "This PDF only has one page" record theRecord
			end if
		else
			log message info "This is not a PDF" record theRecord
		end if
	end repeat
end tell
2 Likes

The possessive is closer to standard object property notation: as you can see, in JSObjC, it’s
$.PDFDocument.alloc.initWithURL(fileURL)
– as if the dot stood for the possessive. For people coming from OO languages, the property of object notation is irritating because the object comes last.

Probably because fileURL… is a method which must follow the object it’s called on. So, the two notations are not really equivalent after all.

1 Like