Tuesday, June 9, 2009

Automator workflow for interleaving odd and even pages of PDF scans

I have made an Automator workflow that combines two PDFs, by shuffling (interleaving them), but after reversing the second one (second in terms of creation time). This means that you can scan a whole lot of pages (odd sides), turn them over and scan the other sides (even sides), then right click, choose an Automator workflow to combine them. They become Combined.pdf in the same folder - the originals are moved to the Trash.

Uses the python script automator action

This was something also wanted here.

The automator action starts with a Get Selected Finder Items action, then has a Run Python Script action with the following code.

import os
from CoreGraphics import *

def createPDFDocumentWithPath(path):
print path
return CGPDFDocumentCreateWithProvider(CGDataProviderCreateWithFilename(path))
def duplexPages(writeContext, oddfile, evenfile):
# open PDFDocuments for each of the files.
odd = createPDFDocumentWithPath( oddfile )
even = createPDFDocumentWithPath( evenfile )

# confirm that the number of pages is the same
maxPages = odd.getNumberOfPages()
if ( maxPages <> even.getNumberOfPages() ):
print "Documents do not have the same number of pages"
else:
# Shuffle the pages
for pageNum in xrange(1, maxPages + 1):
# start at first odd page
page = odd.getPage(pageNum)
print 'Page', pageNum, 'from', oddfile
if page != None:
mediaBox = odd.getMediaBox(pageNum)
writeContext.beginPage(mediaBox)
writeContext.drawPDFDocument(mediaBox, odd, pageNum)
writeContext.endPage()
# start at last even page
page = even.getPage(maxPages + 1 - pageNum)
print 'Page', maxPages + 1 - pageNum, 'from', evenfile
if page != None:
mediaBox = even.getMediaBox(maxPages + 1 - pageNum)
writeContext.beginPage(mediaBox)
writeContext.drawPDFDocument(mediaBox, even, maxPages + 1 - pageNum)
writeContext.endPage()
def realFilesOnly( item ):
return item.find(".") <> 0

# from http://stackoverflow.com/questions/249785/os-x-determine-trash-location-for-a-given-path
def get_trash_path(input_file):
    path, file = os.path.split(input_file)
    if path.startswith("/Volumes/"):
        # /Volumes/driveName/.Trashes/
        s = path.split(os.path.sep)
        # s[2] is drive name ([0] is empty, [1] is Volumes)
        trash_path = os.path.join("/Volumes", s[2], ".Trashes", str(os.getuid()))
        if not os.path.isdir(trash_path):
            raise IOError("Volume appears to be a network drive (%s could not be found)" % (trash_path))
    else:
        trash_path = os.path.join(os.getenv("HOME"), ".Trash")
    return trash_path
def main(input, *args, **kwargs):
dirname = os.path.dirname(input[0])    # extract folder to work in
# print dirname
files = input
# print files
# files = os.listdir( dirname )
# files = filter(realFilesOnly, files) # filter out special files
# print files # enable to see which files are selected
if ( len(files) == 2 ):
# files[0] = os.path.join(dirname,files[0]) # add path to files 
# files[1] = os.path.join(dirname,files[1])

# odd file is assumed to be the earlier one
if ( os.path.getctime(files[1]) > os.path.getctime(files[0]) ):
odd = files[0]
even = files[1]
else:
odd = files[1]
even = files[0]

print "Identified Odd: ", odd
print "Identified Even: ", even

outputFilename = os.path.join(dirname, "Combined.pdf")
print "Output to: ", outputFilename

pageRect = CGRectMake (0, 0, 612, 792)
writeContext = CGPDFContextCreateWithFilename(outputFilename, pageRect)

duplexPages(writeContext, odd, even)

# delete the original files
trashPath = get_trash_path( odd ) # find location of trash
os.rename( odd, os.path.join(trashPath,os.path.split(odd)[1]) )
os.rename( even, os.path.join(trashPath,os.path.split(even)[1]) )
else:
print "Incorrect number of files"

return files


It can be saved as a Finder Plugin to appear on the context menu for Finder items.

1 comment:

Roger said...

I would like to use this script in OSX 10.6.

Tried pasting into automator using built in python shell script but without success.

I would be very grateful if anybody could get it working in 10.6 & tell me how!

Thanks for any response,

Roger