Combine Multiple images into one image

Hey,

I’m looking for a way to combine multiple images together to form one image in a grid for easy printing.

Basically, I have a bunch of small thumbnails that I want to turn into an image or PDF that I would be able to easily print.

I would like to do this with built in utilities in Mac, so AppleScript, ASObjC, shell, etc. I don’t want to use imageMagik or a third party utility like that. I can’t seem to find a pure AppleScript way of doing it but maybe you guys can help.

If it were a function, the functionality I would be looking for would be as follows:


its combineImages:listOfImagePaths saveTo:POSIXPath numOfColumns:3 numOfRows:5

This would basically create a 3x5 grid of images. If there are more than 15 (3x5=15) images, then it would create a second grid with the remaining images and so on. Let me know if this makes sense or if you guys have any questions. :smiley:

I’m pretty sure you’re not going to accomplish this with pure Applescript, shell, and built-in utilities, you would have to go to ASObjC and Core Image. That’s beyond me.

Relying on included applications, under iPhoto I think you could have Applescripted importing the pictures to iPhoto, generating a contact sheet to your criteria, and saving it. It might have been a PDF, but you probably could have Applescripted Preview or something else to rasterize it. But iPhoto’s gone and Photos doesn’t have that functionality.

Since you haven’t provided code for your attempt to create a solution, but appear to be asking someone else to write the whole program for you, I don’t think you’re likely to get a lot answers.

Yeah, I knew it was kinda a long shot. I’ve been looking through different ways to do it but have really come up empty with anything but ASObjC but I’m still super inexperienced with any of the image manipulation abilities of it. Yeah, pure applescript isn’t going to work. Back in the day I was able to accomplish this with Shake but that isn’t really an option anymore. I know this is possible with ASObjC but haven’t really been able to find enough teaching materials on this side of it to really be able to accomplish what I need.

Really all I would need in ASObjC would be to combine two images and be able to specify where they combine. I could create the rest of the logic in order to create the proper columns and rows. I’m going to play around with this a little bit but I’m not super confident I’ll be able to get it to work.

Writing this comment though, has helped me work through some possible answers. Thanks t.spoon for your post. :slight_smile: It may have given me the inspiration to figure this out. I will post if I get anywhere with these problems.

If I can’t get that to work, I think I may try to do it through Numbers. Set up the sheet and the do it that way. It’s not as clean as I would want it but it’s an application that is fairly prevalent in the Apple world and it would still accomplish what I would need it to do for the most part. Haven’t really dealt with scripting in Numbers but I’m going to try that if I can’t get ASObjC to work.

So, I actually think I got it working. I’ll need to clean it up a little and and change some the static variables to make it allow more than two inputs but I think I got it. :slight_smile: I just needed a little kick in the butt.

Here’s the code: (Special thanks to Shane for the roots of this code from an earlier post.)



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

-- classes, constants, and enums used
property NSString : a reference to current application's NSString
property NSJPEGFileType : a reference to 3
property NSImage : a reference to current application's NSImage
property NSBitmapImageRep : a reference to current application's NSBitmapImageRep
property NSAlphaFirstBitmapFormat : a reference to 1
property NSCompositeSourceOver : a reference to 2
property NSTIFFFileType : a reference to 0
property NSPNGFileType : a reference to 4
property NSTIFFCompressionLZW : a reference to 5
property NSGraphicsContext : a reference to current application's NSGraphicsContext
property NSCalibratedRGBColorSpace : a reference to current application's NSCalibratedRGBColorSpace


-- Accepts two images each as POSIX paths as a list right now.  Save Path needs to be POSIX path
on combineImages:POSIXPath saveTo:savePath
	
	-- Set up the Save Path
	set newPath to NSString's stringWithString:savePath
	set theExt to newPath's pathExtension() as text
	
	-- load images and get the size
	set image1 to NSBitmapImageRep's imageRepWithContentsOfFile:(item 1 of POSIXPath)
	set image2 to NSBitmapImageRep's imageRepWithContentsOfFile:(item 2 of POSIXPath)
	set {width:theWidth1, height:theHeight1} to image1's |size|()
	set {width:theWidth2, height:theHeight2} to image2's |size|()
	
	-- Double the width
	set newWidth to theWidth1 + theWidth2
	
	if theHeight1 ≥ theHeight2 then
		set newHeight to theHeight1
	else
		set newHeight to theHeight2
	end if
	
	-- make new bitmapImage
	set newImage to (NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:newWidth pixelsHigh:newHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:true isPlanar:false colorSpaceName:(NSCalibratedRGBColorSpace) bitmapFormat:NSAlphaFirstBitmapFormat bytesPerRow:0 bitsPerPixel:32)
	
	-- store the existing graphics context
	NSGraphicsContext's saveGraphicsState()
	
	-- set graphics context to new context based on the new bitmapImageRep
	(NSGraphicsContext's setCurrentContext:(NSGraphicsContext's graphicsContextWithBitmapImageRep:newImage))
	
	image1's drawInRect:{origin:{x:0, y:0}, |size|:{width:theWidth1, height:theHeight1}} fromRect:{origin:{x:0, y:0}, |size|:{width:theWidth1, height:theHeight1}} operation:NSCompositeSourceOver fraction:1.0 respectFlipped:true hints:(missing value)
	image2's drawInRect:{origin:{x:theWidth1, y:0}, |size|:{width:theWidth2, height:theHeight2}} fromRect:{origin:{x:0, y:0}, |size|:{width:theWidth2, height:theHeight2}} operation:NSCompositeSourceOver fraction:1.0 respectFlipped:true hints:(missing value)
	
	-- restore graphics state
	NSGraphicsContext's restoreGraphicsState()
	
	-- save bitmapImageRep as image
	if {"tif", "tiff"} contains {theExt} then
		set theData to (newImage's representationUsingType:NSTIFFFileType |properties|:{NSTIFFCompression:NSTIFFCompressionLZW})
	else if {"jpg", "jpeg"} contains {theExt} then
		set theData to (newImage's representationUsingType:NSJPEGFileType |properties|:{NSImageCompressionFactor:1, NSImageProgressive:true})
	else if theExt = "png" then
		set theData to (newImage's representationUsingType:NSPNGFileType |properties|:{NSImageInterlaced:true})
	else -- unsupported type
		return false
	end if
	
	set theResult to (theData's writeToFile:newPath atomically:true)
	return (theResult as boolean)
	
end combineImages:saveTo:

You’re on the right track. The rest is all the relevant calculations of offsets and sizes, and how you do that depends a bit on whether your images are uniform, scaling, page size, etc.

Thanks Shane. I agree, I think the hard part is over. I’m not exactly sure how all of the ASObjC code is actually working and what it is all doing but I think I have enough of a grasp of it to manipulate it to work the way I want it to.

In this particular case, I know the images are always going to be equal to each other, so I won’t really need to worry about a lot of that but just for flexibility in the future, I’m going to add some use cases in for that type of thing. I’m sure at the end of the day, my code will be a little clunky, (because I only know a little ASObjC and a lot of AppleScript) but I should be able to get something cooking. I will post back when I get it all working down the line or if I run into any other troubles.

I’m glad my (nearly void of information) post ended up being helpful!

I think some code from your solution is likely to end up being helpful to me,

Thanks,

Tom.