Add Images to PDF

The script included below adds image files in a selected folder to a PDF. The default image folder and dialog position can be set at the top of the displayDialog handler, and the amount of JPEG compression can be set at the top of the makePDF handler. This script uses Shane’s Dialog Toolkit Plus script library, which can be downloaded from:

https://latenightsw.com/freeware/

It should be noted that recent versions of macOS have the native ability to add image files to a PDF. This script might be of use to those who are running older versions of macOS or who prefer batch processing of all image files in a folder.

use framework "AppKit"
use framework "Foundation"
use framework "Quartz"
use scripting additions
use script "Dialog Toolkit Plus" version "1.1.0"

on main()
	set {tiffFormat, dateSort, pdfWithImages, imageFolder} to displayDialog()
	set imageFolder to current application's |NSURL|'s fileURLWithPath:imageFolder
	set pdfFile to getPdfFile(imageFolder, pdfWithImages)
	set imageFiles to getImageFiles(imageFolder, dateSort)
	set fileCount to imageFiles's |count|()
	if fileCount = 0 then errorAlert("No image files were found in the selected folder")
	try
		makePDF(imageFiles, fileCount, pdfFile, tiffFormat)
	on error
		errorAlert("An error occurred while adding an image to the PDF")
	end try
	display notification "Images added to PDF: " & fileCount with title "PDF Created"
end main

on displayDialog()
	set dialogPosition to {} -- insert x and y coordinates if desired
	set defaultImageFolder to POSIX path of (path to home folder) -- change if desired
	set dialogWidth to 430
	set verticalSpace to 8
	set {theButtons, minWidth} to create buttons {"Cancel", "OK"} cancel button 1 default button 2
	set {tiffFormat, theTop, newWidth} to create checkbox "Convert images to TIFF format before adding them to the PDF" bottom verticalSpace max width dialogWidth
	set {dateSort, theTop, newWidth} to create checkbox "Add images to the PDF in order of their creation date" bottom theTop + verticalSpace max width dialogWidth
	set {pdfWithImages, theTop, newWidth} to create checkbox "Save the PDF in folder with images files" bottom theTop + verticalSpace max width dialogWidth
	set {optionsMessaage, theTop} to create label "The PDF will be saved on the desktop; images will be added to the PDF in the order of their file name; and images will be converted to JPEG format before being added to the PDF. These can be changed to:" bottom theTop + verticalSpace max width dialogWidth control size regular size
	set {imageFolder, theTop} to create path control defaultImageFolder bottom (theTop + verticalSpace) control width dialogWidth
	set {pathMessage, theTop} to create label "Choose or drag a folder containing image files to:" bottom theTop + verticalSpace max width dialogWidth control size regular size
	set theControls to {tiffFormat, dateSort, pdfWithImages, optionsMessaage, imageFolder, pathMessage}
	set {buttonName, controlsResults} to display enhanced window "Image to PDF" acc view width dialogWidth acc view height theTop acc view controls theControls initial position dialogPosition buttons theButtons without align cancel button
	if buttonName is "Cancel" then error number -128
	return {item 1, item 2, item 3, item 5} of controlsResults
end displayDialog

on getPdfFile(imageFolder, pdfWithImages)
	if pdfWithImages is false then
		set targetFolder to current application's NSHomeDirectory()
		set targetFolder to current application's |NSURL|'s fileURLWithPath:targetFolder
		set targetFolder to targetFolder's URLByAppendingPathComponent:"Desktop" isDirectory:true
	else
		set targetFolder to imageFolder
	end if
	set sourceFolderName to imageFolder's lastPathComponent()
	set pdfFile to sourceFolderName's stringByAppendingString:" Images.pdf"
	set pdfFile to (targetFolder's URLByAppendingPathComponent:pdfFile isDirectory:false)
	repeat with i from 1 to 100
		set fileExists to (pdfFile's checkResourceIsReachableAndReturnError:(missing value))
		if fileExists as boolean is false then return pdfFile
		set pdfFile to (sourceFolderName's stringByAppendingString:(" Images " & i & ".pdf"))
		set pdfFile to (targetFolder's URLByAppendingPathComponent:pdfFile isDirectory:false)
	end repeat
	errorAlert("The PDF file could not be created")
end getPdfFile

on getImageFiles(theFolder, dateSort)
	set theExtensions to {"jpg", "png", "tiff"}
	set fileManager to current application's NSFileManager's defaultManager()
	set {theResult, isDirectory} to (theFolder's getResourceValue:(reference) forKey:(current application's NSURLIsDirectoryKey) |error|:(missing value))
	if isDirectory as boolean is false then errorAlert("The image folder is not recognized as a folder")
	set folderContents to fileManager's contentsOfDirectoryAtURL:theFolder includingPropertiesForKeys:{} options:4 |error|:(missing value)
	set thePredicate to current application's NSPredicate's predicateWithFormat_("pathExtension.lowercaseString IN %@", theExtensions)
	set theFiles to (folderContents's filteredArrayUsingPredicate:thePredicate)
	if dateSort is false then
		set sortDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:"path" ascending:true selector:"localizedStandardCompare:"
		return theFiles's sortedArrayUsingDescriptors:{sortDescriptor}
	else
		set filePaths to current application's NSMutableArray's new()
		set theKeys to {current application's NSURLPathKey, current application's NSURLCreationDateKey}
		repeat with anItem in theFiles
			(filePaths's addObject:(anItem's resourceValuesForKeys:theKeys |error|:(missing value)))
		end repeat
		filePaths's sortUsingDescriptors:{current application's NSSortDescriptor's sortDescriptorWithKey:(current application's NSURLCreationDateKey) ascending:true}
		set filePaths to (filePaths's valueForKey:(current application's NSURLPathKey))
		set fileURLs to current application's NSMutableArray's new()
		repeat with anItem in filePaths
			set theURL to (current application's |NSURL|'s fileURLWithPath:anItem)
			(fileURLs's addObject:theURL)
		end repeat
		return fileURLs
	end if
end getImageFiles

on makePDF(theFiles, fileCount, pdfFile, tiffFormat)
	set jpegCompression to 0.9 -- 0.0 is maximum compression and 1.0 is no compression
	set pdfDoc to current application's PDFDocument's new()
	repeat with i from 0 to (fileCount - 1)
		set theFile to (theFiles's objectAtIndex:i)
		set imageRep to (current application's NSBitmapImageRep's imageRepWithContentsOfURL:theFile)
		if tiffFormat as boolean is true then
			set theData to (imageRep's representationUsingType:(current application's NSTIFFFileType) |properties|:{NSTIFFCompression:(current application's NSTIFFCompressionLZW)})
		else
			set theData to (imageRep's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:jpegCompression})
		end if
		set theImage to (current application's NSImage's alloc()'s initWithData:theData)
		set thePDFPage to (current application's PDFPage's alloc's initWithImage:theImage)
		(pdfDoc's insertPage:thePDFPage atIndex:i)
	end repeat
	pdfDoc's writeToURL:pdfFile
end makePDF

on errorAlert(dialogMessage)
	display alert "An error has occurred" message dialogMessage as critical
	error number -128
end errorAlert

main()

Forum members occasionally express their dislike for long scripts, and my script in post 1 certainly fits in that category. The following is an abbreviated version–it lacks any options and has no error correction but might be preferred for its brevity.

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

set imageFolder to (choose folder)
set pdfFile to POSIX path of ((path to desktop as text) & "PDF Images.pdf")
tell application "Finder" to set imageFiles to (every file in imageFolder whose name extension is in {"jpg", "png", "tiff"}) as alias list
set imageFiles to current application's NSArray's arrayWithArray:imageFiles
set pdfDoc to current application's PDFDocument's new()
repeat with i from 0 to ((imageFiles's |count|()) - 1)
	set imageRep to (current application's NSBitmapImageRep's imageRepWithContentsOfURL:(imageFiles's objectAtIndex:i))
	set theData to (imageRep's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:0.9})
	set theImage to (current application's NSImage's alloc()'s initWithData:theData)
	set thePDFPage to (current application's PDFPage's alloc's initWithImage:theImage)
	(pdfDoc's insertPage:thePDFPage atIndex:i)
end repeat
pdfDoc's writeToFile:pdfFile

This script differs from those above in two respects. First, it works on image files selected in a Finder window. Second. it uses lossy compression (DCTDecode) with a JPG file and lossless compression (FlateDecode) otherwise. This permits high compression of JPEGs in a PDF without degrading the quality of a PNG or TIFF image in the same PDF.

-- revised 2022.11.19

use framework "AppKit" -- for NSImage
use framework "Foundation"
use framework "Quartz"
use scripting additions

on main()
	set pdfFile to POSIX path of (path to desktop) & "PDF Images.pdf"
	tell application "Finder" to set imageFiles to selection as alias list
	if imageFiles = {} then errorAlert("Finder window or file selection not found")
	try
		makePDF(imageFiles, pdfFile)
	on error
		errorAlert("The PDF could not be created. This often happens when a selected file cannot be read.")
	end try
end main

on makePDF(imageFiles, pdfFile)
	set imageFiles to current application's NSArray's arrayWithArray:imageFiles
	set pdfDoc to current application's PDFDocument's new()
	repeat with i from 0 to ((imageFiles's |count|()) - 1)
		set fileExtension to ((imageFiles's objectAtIndex:i)'s pathExtension())'s lowercaseString()
		set imageRep to (current application's NSBitmapImageRep's imageRepWithContentsOfURL:(imageFiles's objectAtIndex:i))
		if (fileExtension's isEqualToString:"jpg") then
			set theData to (imageRep's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:0.8})
		else
			set theData to (imageRep's representationUsingType:(current application's NSTIFFFileType) |properties|:{NSTIFFCompression:(current application's NSTIFFCompressionLZW)})
		end if
		set theImage to (current application's NSImage's alloc()'s initWithData:theData)
		set thePDFPage to (current application's PDFPage's alloc's initWithImage:theImage)
		(pdfDoc's insertPage:thePDFPage atIndex:i)
	end repeat
	pdfDoc's writeToFile:pdfFile
end makePDF

on errorAlert(dialogMessage)
	display alert "An error has occurred" message dialogMessage as critical
	error number -128
end errorAlert

main()

When you use POSIX path of (path to desktop)… the class is already text
So you do not need to declare it again

ex.

set pdfFile to POSIX path of ((path to desktop as text) & "PDF Images.pdf")
pdfFile's class --> text

set pdfFile to POSIX path of (path to desktop) & "PDF Images.pdf"
pdfFile's class --> text


Your handler on makePDF(imageFiles, pdfFile) arguments use a mix of ASobjC and Vanilla AS
My advice would be to set the NSArray inside the handler instead and before objectAtIndex: