Merge PDF contents thru apple script

Hi all,

I need to merge many PDF into a single PDF file. I have 5 PDFs with different contents. All i need to do is Merge these files into a single PDF file thru apple script. Any help?

-Rekha

Hi

Check this out:
http://www.iis.ee.ic.ac.uk/~g.briscoe/joinPDF/

Actually i need to do this thru Apple Script.

Hi

If you download it! then i think there is a few various scripts in the download which do what you want!!

You can also do it in Automator ( which you can if need have applescript run)

Actions:
Get selected finder Items.

Sort Finder Items:
Options ( by, comments.)( in, ascending order) (show Action When Run)

Combine PDF pages:
Options (Combine documents by, Appending pages)
Rename Finder Items : (I also chose not to ADD a copy )
Options ( choose what you want , I chose (Make Sequnetial, new name., place number after name)
)

Move Finder Items:
Options ( choose your options )

save as Finder Plugin,

Example:
I have two pdfs.
In the spotlight comments of the files (which is empty) I type a 1 in the file I want to be first and 2 in the one I want to be second.

Select the files and control click to get the contextual Automator pdf merger plugin, and run it.

I have the show sort options ticked. In case I want to change my method, but the one set is, ( by, comments.)( in, ascending order). So the file with a 1 in the spotlight comments will be first and the one with the 2 will be next, and so on…

Hi Rekha,
as pidge said the use of joinPDF means that you can use just applescript and do shell scripts to combine pdfs and it works really well! I was looking to do the same thing as you a while ago and recently revisited it, and with some help in direction from the legend StefanK I’ve got it work! The Combine_PDFs handler is the part that you should look to incorporate in your script as it uses the joinPDF function. Hope this helps?

--try
tell application "Finder"
	set sourcefolder to folder "Export_Objects:Downsample:Success" as alias
	set inputfolder to folder "Export_Objects:Downsample:Input" as alias
	set CombinedPDFfolder to folder "Export_Objects:Combined PDF" as alias
end tell

tell application "Finder"
	set Success_PDF_List to name of every file of sourcefolder
	if Success_PDF_List ≠ {} then
		set search_string to characters 1 thru 14 of item 1 of Success_PDF_List as string
	else
		error number -128
	end if
	set Input_PDF_List to name of every file of inputfolder
	if Input_PDF_List = {} then
		my Combine_PDFs(sourcefolder, search_string, CombinedPDFfolder)
	else if (Input_PDF_List as string) contains search_string then
		repeat until Input_PDF_List as string does not contain search_string
			delay 30
			set Input_PDF_List to name of every file of inputfolder
		end repeat
		my Combine_PDFs(sourcefolder, search_string, CombinedPDFfolder)
	else if (Input_PDF_List as string) does not contain search_string then
		my Combine_PDFs(sourcefolder, search_string, CombinedPDFfolder)
	end if
end tell

on Combine_PDFs(sourcefolder, search_string, CombinedPDFfolder)
	set startpath to (path to desktop as string) & "Export_Objects:temp" as alias
	set startpathPOSIX to POSIX path of startpath
	
	set filepath1 to (path to desktop as string) & "Export_Objects:Downsample:Success"
	set filepath1POSIX to POSIX path of filepath1
	
	set destpath to startpathPOSIX & search_string & "-" & "combined.pdf"
	tell application "Finder"
		set files_to_process to name of every file of the sourcefolder whose name contains search_string
		set RawScript to "joinPDF " & "'" & destpath & "'" & space
		repeat with thefile in files_to_process
			set fullpath to "'" & filepath1POSIX & "/" & thefile & "'" & space
			set RawScript to RawScript & "'" & filepath1POSIX & "/" & thefile & "'" & space
		end repeat
	end tell
	
	do shell script RawScript
	
	set deletescript to "rm "
	repeat with thenewfile in files_to_process
		set mynewfullpath to "'" & filepath1POSIX & "/" & thenewfile & "'" & space
		set deletescript to deletescript & filepath1POSIX & "/" & thenewfile & space
	end repeat
	
	do shell script deletescript
	
	set combinedPDF_alias to (path to desktop as string) & "Export_Objects:temp:" & search_string & "-" & "combined.pdf" as alias
	tell application "Finder" to move combinedPDF_alias to CombinedPDFfolder with replacing
end Combine_PDFs

There is a very easy way to do this by using a python script from an Automator action:

Useable with do shell script.

As long as you’re running Mavericks or later, this will do it for you:

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

set inFiles to (choose file of type {"pdf"} with prompt "Choose your PDF files:" with multiple selections allowed)
set destPosixPath to POSIX path of (choose file name default name "Combined.pdf" with prompt "Save new PDF to:")
its combineFiles:inFiles savingTo:destPosixPath

on combineFiles:inFiles savingTo:destPosixPath
	--  make URL of the first PDF
	set inNSURL to current application's class "NSURL"'s fileURLWithPath:(POSIX path of item 1 of inFiles)
	-- make PDF document from the URL
	set theDoc to current application's PDFDocument's alloc()'s initWithURL:inNSURL
	-- loop through the rest
	set oldDocCount to theDoc's pageCount()
	set inFiles to rest of inFiles
	repeat with aFile in inFiles
		--  make URL of the next PDF
		set inNSURL to (current application's class "NSURL"'s fileURLWithPath:(POSIX path of aFile))
		-- make PDF document from the URL
		set newDoc to (current application's PDFDocument's alloc()'s initWithURL:inNSURL)
		-- loop through, moving pages
		set newDocCount to newDoc's pageCount()
		repeat with i from 1 to newDocCount
			-- get page of old PDF
			set thePDFPage to (newDoc's pageAtIndex:(i - 1)) -- zero-based indexes
			-- insert the page
			(theDoc's insertPage:thePDFPage atIndex:oldDocCount)
			set oldDocCount to oldDocCount + 1
		end repeat
	end repeat
	set outNSURL to current application's class "NSURL"'s fileURLWithPath:destPosixPath
	-- save the new PDF
	(theDoc's writeToURL:outNSURL)
end combineFiles:savingTo:

You will have to put it in a script library under Mavericks.

Hi Shane,
I would like to use your script but without having the operator rely on having to browse to the PDF files. For instance, I have a folder on the Desktop called “View PDFs” that will always have 2 single-page PDFs. I have had no luck trying to modify your script whereas it would select all the files within my View PDFs folder. Is there anyway you could “Please” help me out and modify your script so it can be hardcoded to where it selects all the files of a given folder without the need of a choose dialog?

Thanks in advance,
-Jeff

Something like:

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


set inFolderPosix to POSIX path of ((path to desktop as text) & "View PDFs")
set destPosixPath to POSIX path of ((path to desktop as text) & "Combined.pdf")
its combineFilesIn:inFolderPosix savingTo:destPosixPath

on combineFilesIn:inFolderPosix savingTo:destPosixPath
	set dirURL to current application's class "NSURL"'s fileURLWithPath:inFolderPosix
	set theURLs to current application's NSFileManager's defaultManager()'s contentsOfDirectoryAtURL:dirURL includingPropertiesForKeys:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
	-- make PDF document from the URL
	set theDoc to current application's PDFDocument's alloc()'s initWithURL:(theURLs's firstObject())
	-- loop through the rest
	set oldDocCount to theDoc's pageCount()
	repeat with i from 1 to ((theURLs's |count|()) - 1)
		--  make URL of the next PDF
		-- make PDF document from the URL
		set newDoc to (current application's PDFDocument's alloc()'s initWithURL:(theURLs's objectAtIndex:i))
		-- loop through, moving pages
		set newDocCount to newDoc's pageCount()
		repeat with i from 1 to newDocCount
			-- get page of old PDF
			set thePDFPage to (newDoc's pageAtIndex:(i - 1)) -- zero-based indexes
			-- insert the page
			(theDoc's insertPage:thePDFPage atIndex:oldDocCount)
			set oldDocCount to oldDocCount + 1
		end repeat
	end repeat
	set outNSURL to current application's class "NSURL"'s fileURLWithPath:destPosixPath
	-- save the new PDF
	(theDoc's writeToURL:outNSURL)
end combineFilesIn:savingTo:

AWESOME!!!

This works wonderfully.

Not sure if you need (or care) to know since you were obviously working quickly - I needed to change “inFolder” to “inFolderPosix” in the follow line:

set dirURL to current application’s class “NSURL”'s fileURLWithPath:inFolder

new line:
set dirURL to current application’s class “NSURL”'s fileURLWithPath:inFolderPosix

Thank you very much (once again) Shane!
-Jeff

This is basically the same, with an additional step that allow to visualise in a table several infos for the chosen PDFs, and the possibility to leave some of those PDFs out (so they won’t be part of the combined final PDF), and also to reorganise their order (by dragging them up or down) which will affect how they will be joined in final combined PDF.
I am sure it can be improved…

Ciao
L.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use framework "Foundation"
use framework "AppKit" -- for NSImage
use framework "Quartz" -- required for PDF stuff
use script "Myriad Tables Lib" version "1.0.9"

set inFiles to (choose file of type {"public.image", "com.adobe.pdf"} with prompt "Choose your  files (images or PDFs):" with multiple selections allowed) -- is a list of alias
set destPosixPath to POSIX path of (choose file name default name "Combined.pdf" with prompt "Save new PDF to:") -- is a POSIX path

# let's order the files
# get information about contents
set {theJoin, theNames, theSizes, theKinds, theFiles} to {{}, {}, {}, {}, {}}
tell application "Finder"
	repeat with i in inFiles
		set end of theJoin to true
		set end of theNames to displayed name of i
		set end of theSizes to size of i
		set end of theKinds to kind of i
		set end of theFiles to i as alias
	end repeat
end tell
-- convert from "columns" to "rows"
set theData to swap columns and rows in {theJoin, theNames, theSizes, theKinds, theFiles}
-- show table
set myTable to make new table with data theData column headings {"Join", "Name", "Size", "Kind"} with prompt "Choose files to join by checking the checkbox, and the order in which the files willl be joined by dragging them up or down." editable columns {1} with multiple selections allowed
modify table myTable with alternate backgrounds and row dragging
-- modify columns in table myTable date format "d MMM yyyy, H:mm"
modify columns in table myTable columns list {4} head alignment align center
modify columns in table myTable columns list {2} with bold type
set theResult to display table myTable
-- extract the files, which will be returned as file references
set theNames to extract column 5 from values selected of theResult
set NewList to extract column 5 from values returned of theResult
set NewList2 to {PDFName:extract column 5 from values returned of theResult, PDFValue:extract column 1 from values returned of theResult}
set NewList3 to {}
repeat with i in values returned of theResult
	if item 1 of i then
		set end of NewList3 to item 5 of i
	end if
end repeat

my combineFiles:NewList3 savingToPDF:destPosixPath
on combineFiles:inFiles savingToPDF:destPosixPath
	--  make URL of the first file
	set inNSURL to current application's |NSURL|'s fileURLWithPath:(POSIX path of item 1 of inFiles)
	-- make PDF document from the URL
	if (inNSURL's pathExtension()'s isEqualToString:"pdf") as boolean then
		set theDoc to current application's PDFDocument's alloc()'s initWithURL:inNSURL
	else
		set theDoc to my pdfDocFromImageURL:inNSURL
	end if
	-- loop through the rest
	set oldDocCount to theDoc's pageCount()
	set inFiles to rest of inFiles
	repeat with aFile in inFiles
		--  make URL of the next PDF
		set inNSURL to (current application's |NSURL|'s fileURLWithPath:(POSIX path of aFile))
		-- make PDF document from the URL
		if (inNSURL's pathExtension()'s isEqualToString:"pdf") as boolean then
			set newDoc to (current application's PDFDocument's alloc()'s initWithURL:inNSURL)
		else
			set newDoc to (my pdfDocFromImageURL:inNSURL)
		end if
		-- loop through, moving pages
		set newDocCount to newDoc's pageCount()
		repeat with i from 1 to newDocCount
			-- get page of  PDF
			set thePDFPage to (newDoc's pageAtIndex:(i - 1)) -- zero-based indexes
			-- insert the page into main PDF
			(theDoc's insertPage:thePDFPage atIndex:oldDocCount)
			set oldDocCount to oldDocCount + 1
		end repeat
	end repeat
	set outNSURL to current application's |NSURL|'s fileURLWithPath:destPosixPath
	-- save the main PDF
	(theDoc's writeToURL:outNSURL)
end combineFiles:savingToPDF:

on pdfDocFromImageURL:inNSURL
	set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:inNSURL
	set theSize to theImage's |size|()
	set theRect to {{0, 0}, theSize}
	set theImageView to current application's NSImageView's alloc()'s initWithFrame:theRect
	theImageView's setImage:theImage
	set theData to theImageView's dataWithPDFInsideRect:theRect
	return current application's PDFDocument's alloc()'s initWithData:theData
end pdfDocFromImageURL:

The OP has a good solution and I wanted to raise a related matter. I’m rewriting my PDF AppleScripts to use ASObjC–which I’m just learning–and I had a question about error correction.

I ran Shane’s script with 3 PDF files in the source folder and everything worked as expected. To introduce an error, I created a text file in the folder and changed its extension to PDF. I reran Shane’s script and it threw an error:

What is the best way to handle this error? For now, I will include the code that calls the handler in a try statement, but this is a bit cumbersome because the handler is called at numerous points in my script. Thanks.

You can use a try, or a test for missing value:

if theDoc is missing value then -- something went wrong

Thanks Shane. I had this wrong. The line that causes the error is actually the prior line so this can be dealt with something like:

set theURLs to current application's NSFileManager's defaultManager()'s contentsOfDirectoryAtURL:dirURL includingPropertiesForKeys:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)

if theURLs = missing value then -- catch error
	display alert "The error message"
	error number -128
end if

set theDoc to current application's PDFDocument's alloc()'s initWithURL:(theURLs's firstObject())

It can be more helpful if you sow the relevant error:

set {theURLs, theError} to current application's NSFileManager's defaultManager()'s contentsOfDirectoryAtURL:dirURL includingPropertiesForKeys:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(reference)

if theURLs = missing value then -- catch error
	display alert (theError's localizedDescription() as text)
	error number -128
end if

Thanks Shane. I’ll use that.

This is proabably more of a question for Shane, since it involves his code below. I am wondering how the sequencing of the pages is determined. I have experienced mixed results on different machines in terms of what file becomes page 1 vs. page 2. For all intents and purposes - In my situation, I am always dealing with just 2 separate PDFs in the same folder on the desktop that is called “View PDFs” - Inside that “View PDFs” folder will always be two PDFs that are similarly named except for their filename’s ending. For exmple, thsi folder will contain two spearare PDFs named as follows:
AD03103_side1.pdf
AD03103_side2.pdf

What I am seeking is a method to always have the file that contains “side1” become page 1, and the file containing “side2” to become page 2. However, I am not sure how to incorporate that logic into the code seen below. I understand this is a lot to ask - so if it cannot be easily done, please ignore.

Thanks,
-Jeff

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


set inFolderPosix to POSIX path of ((path to desktop as text) & "View PDFs")
set destPosixPath to POSIX path of ((path to desktop as text) & "Combined.pdf")
its combineFilesIn:inFolderPosix savingTo:destPosixPath

on combineFilesIn:inFolderPosix savingTo:destPosixPath
	set dirURL to current application's class "NSURL"'s fileURLWithPath:inFolderPosix
	set theURLs to current application's NSFileManager's defaultManager()'s contentsOfDirectoryAtURL:dirURL includingPropertiesForKeys:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
	-- make PDF document from the URL
	set theDoc to current application's PDFDocument's alloc()'s initWithURL:(theURLs's firstObject())
	-- loop through the rest
	set oldDocCount to theDoc's pageCount()
	repeat with i from 1 to ((theURLs's |count|()) - 1)
		-- make URL of the next PDF
		-- make PDF document from the URL
		set newDoc to (current application's PDFDocument's alloc()'s initWithURL:(theURLs's objectAtIndex:i))
		-- loop through, moving pages
		set newDocCount to newDoc's pageCount()
		repeat with i from 1 to newDocCount
			-- get page of old PDF
			set thePDFPage to (newDoc's pageAtIndex:(i - 1)) -- zero-based indexes
			-- insert the page
			(theDoc's insertPage:thePDFPage atIndex:oldDocCount)
			set oldDocCount to oldDocCount + 1
		end repeat
	end repeat
	set outNSURL to current application's class "NSURL"'s fileURLWithPath:destPosixPath
	-- save the new PDF
	(theDoc's writeToURL:outNSURL)
end combineFilesIn:savingTo:

Jeffkr. You directed your question to Shane but I couldn’t resist responding, just FWIW.

It appears that your source folder contains only two PDF’s and if that’s the case you might want to consider using the Finder. It returns the PDF files generally sorted by name and Finder has a sort function if you’d rather sort in some other manner. Finder can be abysmally slow but in this case it might be worth considering.

Anyways, my suggestion is:

use framework "Foundation"
use framework "Quartz"
use scripting additions

set sourceFolder to ((path to desktop as text) & "View PDFs:")
set targetFile to ((path to desktop as text) & "Combined.pdf")

tell application "Finder"
	set sourceFiles to (every file in folder sourceFolder whose name ends with ".pdf") as alias list
end tell

mergeFiles(sourceFiles, targetFile)

on mergeFiles(sourceFiles, targetFile)
	set firstFile to current application's class "NSURL"'s fileURLWithPath:(POSIX path of item 1 of sourceFiles)
	set firstDoc to current application's PDFDocument's alloc()'s initWithURL:firstFile
	set firstDocCount to firstDoc's pageCount()
	repeat with anItem in (rest of sourceFiles)
		set aFile to (current application's class "NSURL"'s fileURLWithPath:(POSIX path of anItem))
		set aDoc to (current application's PDFDocument's alloc()'s initWithURL:aFile)
		set aDocCount to aDoc's pageCount()
		repeat with i from 1 to aDocCount
			set thePDFPage to (aDoc's pageAtIndex:(i - 1))
			(firstDoc's insertPage:thePDFPage atIndex:firstDocCount)
			set firstDocCount to firstDocCount + 1
		end repeat
	end repeat
	set mergedFile to current application's class "NSURL"'s fileURLWithPath:(POSIX path of targetFile)
	(firstDoc's writeToURL:mergedFile)
end mergeFiles

Thank you very much Pavine,
I am pretty sure you are on the right track, however can you assist in modifying the script a bit further so it actually combines the 2 pdfs? It is probably just some syntax error?

use framework "Foundation"
use framework "Quartz"
use scripting additions

set sourceFolder to ((path to desktop as text) & "View PDFs")
set targetFile to ((path to desktop as text) & "Combined.pdf")

tell application "Finder"
	set sourceFiles to (every file in folder sourceFolder whose name ends with ".pdf") as alias list
end tell

my mergeFiles(sourceFiles, targetFile)

on mergeFiles(sourceFiles, targetFile)
	set firstFile to current application's class "NSURL"'s fileURLWithPath:(POSIX path of item 1 of sourceFiles)
	set firstDoc to current application's PDFDocument's alloc()'s initWithURL:firstFile
	set firstDocCount to firstDoc's pageCount()
	repeat with anItem in (rest of sourceFiles)
		set aFile to (current application's class "NSURL"'s fileURLWithPath:(POSIX path of anItem))
		set aDoc to (current application's PDFDocument's alloc()'s initWithURL:aFile)
		set aDocCount to aDoc's pageCount()
		repeat with i from 1 to aDocCount
			set thePDFPage to (aDoc's pageAtIndex:(i - 1))
			(firstDoc's insertPage:thePDFPage atIndex:firstDocCount)
			set firstDocCount to firstDocCount + 1
		end repeat
	end repeat
	set mergedFile to current application's class "NSURL"'s fileURLWithPath:(POSIX path of targetFile)
	(firstDoc's writeToURL:mergedFile)
end mergeFiles