Applescript to re-arrange pdfs captured from double sided documents scanned with a simplex scanner

Hi everybody,

I am a fresh DTP user, new to AS and trying to create an automated solution to re-sort pdf documents which are scanned with a simplex scanner in a fast and comfortable manner.
The work process looks like this:

  1. Scanning all the odd pages [1,3,5,…]
  2. Flipping the document an scanning the even pages backwards […6,4,2]
  3. Saving the scanned pdf file which looks like this [1,3,5,…,6,4,2]

Now I created a loop which declutters that scanned pages and sorts everything to [1,2,3,4,5,6,…]
I found it difficult to modify a pdf within applescript or without using automator, but I found a nice script by S. Stanley which helped a lot (https://macscripter.net/viewtopic.php?pid=192080#p192080)

use scripting additions
use framework "Foundation"
use framework "Quartz" -- required for PDF stuff

global inPath


tell application id "DNtp"
	try
		set this_selection to the selection
		if this_selection is {} then error "Please select some PDF Files"
		set folderPath to path of inbox
		
		
		set this_item to first item of this_selection
		if the type of this_item is equal to PDF document then
			set inPath to path of this_item
		end if
		
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink Pro" message error_message as warning
	end try
end tell

set folderPath to POSIX path of "/Users/Username/Library/Application Support/DEVONthink 3/Inbox/"


its splitPagesInPath:inPath savingInFolder:folderPath

on splitPagesInPath:inPath savingInFolder:folderPath
	try
		-- make URL of the PDF
		set inNSURL to current application's |NSURL|'s fileURLWithPath:inPath
		-- get name of PDF
		set docName to inNSURL's lastPathComponent()
		-- make URL of output folder
		set outFolderNSURL to current application's |NSURL|'s fileURLWithPath:folderPath
		-- make PDF document from the file
		set theDoc to current application's PDFDocument's alloc()'s initWithURL:inNSURL
		
		-- store media bounds of page 1; unnecessary in most cases
		set theBounds to (theDoc's pageAtIndex:0)'s boundsForBox:(current application's kPDFDisplayBoxMediaBox)
		
		-- count the pages
		set theCount to theDoc's pageCount() as integer
		
		set midpoint to round (theCount / 2) rounding up
		
		-- build new document's name
		set newDocName to (its addString:("-NEU") beforeExtensionIn:docName)
		-- make URL for new PDF
		set outNSURL to (outFolderNSURL's URLByAppendingPathComponent:newDocName)
		-- get page of old PDF
		set theNewPDFDocument to current application's PDFDocument's new()
		
		set k to 0
		repeat with i from 1 to midpoint
			set n to i - 1
			set m to theCount - i
			
			set thePDFPage to (theDoc's pageAtIndex:(n)) -- zero-based indexes
			(thePDFPage's setBounds:theBounds forBox:(current application's kPDFDisplayBoxMediaBox))
			(theNewPDFDocument's insertPage:thePDFPage atIndex:(k))
			set k to k + 1
			
			set thePDFPage to (theDoc's pageAtIndex:(m)) -- zero-based indexes
			(thePDFPage's setBounds:theBounds forBox:(current application's kPDFDisplayBoxMediaBox))
			(theNewPDFDocument's insertPage:thePDFPage atIndex:(k))
			set k to k + 1
			
		end repeat
		(theNewPDFDocument's writeToURL:outNSURL)
	on error error_message number error_number
		if the error_number is not -128 then display alert "Sub" message error_message as warning
	end try
	
	
end splitPagesInPath:savingInFolder:

on addString:extraString beforeExtensionIn:aPath
	set aString to current application's NSString's stringWithString:aPath
	set newString to current application's NSString's stringWithFormat_("%@%@.%@", aString's stringByDeletingPathExtension(), extraString, aString's pathExtension())
	return newString as text
end addString:beforeExtensionIn:

Since I am fairly new a few problems occur:

  • how can I catch the POSIX path of the global inbox?
  • the inbox does not recognize the document added in that matter until I restart Devonthink
    – can I update the inbox somehow?
  • I am not happy the script isn’t running completely inside the tell command for DNtp

It is still a bit clunky but it does the job pretty decent for everybody with a simplex scanner and right now I can avoid to buy a scansnap setup that way.

Best Regards and thanks in advance.

how can I catch the POSIX path of the global inbox?

For what purpose? This feels like you’re heading down a path you shouldn’t be going down.

Note: A third-party script like this, especially one from someone with DEEP knowledge like Shane Stanley is not one to be dissected and supported easily by other people.

Not sure what you mean by that?

Since this script is used when new documents are added to a databes I thought it’s viable to add it to the inbox. But please, if you have better solutions, give me a hint or advice.
Thank you.

You can add things to the Global Inbox, but you getting the path to the Inbox is leading you down a path you shouldn’t be going.

Read the section on Orphaned Files in the built-in Help > Documentation > Troubleshooting > Repairing a defective database (and it’s not just related to scripting) to see why it’s a very bad idea.

You would use the import command as shown in DEVONthink’s AppleScript dictionary. As a simple example…

tell application id "DNtp"
	import "~/desktop/book.bib" to incoming group
end tell

Thanks for the advice. I changed the script to temporarily save the file and then use the import command. This works quite decent and solves the problems.

This was quite an intrusion into the database idea and architecture by my side…

This was quite an intrusion into the database idea and architecture by my side…

What do you mean by that? :thinking:

This would be very useful. I currently use PDFpen for this, since it comes with a couple of built-in scripts: “reverse pages” and “merge every other”. I see that DTP3 now has Tools > PDF Document > Reverse Page Order, which is great, but it would be good to have DTP3 replicate the “merge every other” – or do the equivalent page re-ordering on an already-merged file.

@Osai, can you post the latest version of your script? @BLUEFROG, can we do this more elegantly keeping everything within DTP3’s “tell…” loop?

Nothing special. Just kudos for your hint.

@Chazzo

use scripting additions
use framework "Foundation"
use framework "Quartz" -- required for PDF stuff

global inPath
set tmpFolderPath to POSIX path of (path to home folder)

tell application id "DNtp"
	try
		set this_selection to the selection
		if this_selection is {} then error "Please select some PDF Files"
		
		set this_item to first item of this_selection
		if the type of this_item is equal to PDF document then
			set inPath to path of this_item
		end if
		
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink Pro" message error_message as warning
	end try
end tell



its splitPagesInPath:inPath savingInFolder:tmpFolderPath

on splitPagesInPath:inPath savingInFolder:tmpFolderPath
	try
		-- make URL of the PDF
		set inNSURL to current application's |NSURL|'s fileURLWithPath:inPath
		-- get name of PDF
		set docName to inNSURL's lastPathComponent()
		-- make URL of output folder
		set outFolderNSURL to current application's |NSURL|'s fileURLWithPath:tmpFolderPath
		-- make PDF document from the file
		set theDoc to current application's PDFDocument's alloc()'s initWithURL:inNSURL
		
		-- store media bounds of page 1; unnecessary in most cases
		set theBounds to (theDoc's pageAtIndex:0)'s boundsForBox:(current application's kPDFDisplayBoxMediaBox)
		
		-- count the pages
		set theCount to theDoc's pageCount() as integer
		
		set midpoint to round (theCount / 2) rounding up
		
		-- build new document's name
		set newDocName to (its addString:("-sorted") beforeExtensionIn:docName)
		-- make URL for new PDF
		set outNSURL to (outFolderNSURL's URLByAppendingPathComponent:newDocName)
		-- get page of old PDF
		set theNewPDFDocument to current application's PDFDocument's new()
		
		set k to 0
		repeat with i from 1 to midpoint
			set n to i - 1
			set m to theCount - i
			
			set thePDFPage to (theDoc's pageAtIndex:(n)) -- zero-based indexes
			(thePDFPage's setBounds:theBounds forBox:(current application's kPDFDisplayBoxMediaBox))
			(theNewPDFDocument's insertPage:thePDFPage atIndex:(k))
			set k to k + 1
			
			set thePDFPage to (theDoc's pageAtIndex:(m)) -- zero-based indexes
			(thePDFPage's setBounds:theBounds forBox:(current application's kPDFDisplayBoxMediaBox))
			(theNewPDFDocument's insertPage:thePDFPage atIndex:(k))
			set k to k + 1
			
		end repeat
		
		-- create a temporary file
		(theNewPDFDocument's writeToURL:outNSURL)
		set newPDFPosixPath to POSIX path of (tmpFolderPath & newDocName)
		
		-- import to DevonThink
		tell application id "DNtp" to import newPDFPosixPath to incoming group
		
		-- Delete temporary file
		
		tell application "System Events" to delete alias newPDFPosixPath
		
		
	on error error_message number error_number
		if the error_number is not -128 then display alert "Sub" message error_message as warning
	end try
	
	
end splitPagesInPath:savingInFolder:

on addString:extraString beforeExtensionIn:aPath
	set aString to current application's NSString's stringWithString:aPath
	set newString to current application's NSString's stringWithFormat_("%@%@.%@", aString's stringByDeletingPathExtension(), extraString, aString's pathExtension())
	return newString as text
end addString:beforeExtensionIn:

@Osai Sheesh! That’s fast. Thank you very much.

Just to recap for anyone new to this thread: Osai’s script operates on a single PDF with pages in the order: [1, 3, 5, 7, … 8, 6, 4, 2]. In my case that results from merging two files created by successive passes through my Brother DCP-7045N, whose flips the pages during the scan. In other words I don’t have to reverse the page order of the “evens” file before running the script.