Generate images from Text Replacement using Applescript automation from JSON

Talk about Pixelmator Pro, share tips & tricks, tutorials, and other resources.
User avatar

2022-12-22 19:05:05

Hey everyone,

I just wanted to thank everyone from the Pixelmator team for allowing us to automate some image generation. There's only a few resources out there but I was able to cobble together something that's working and gets the job done, and I wanted to share for others out there. This is just my work in progress, and I still have to figure out how to select subsections of text and change their colors :disappointed_relieved: among a few other tasks, but this is some of the hardest -get-off-the-ground- kind of work for others looking to do something similar to what I'm doing here.

I wish there was a better language than Applescript to automate these tasks with (If you're reading this Pixelmator team, is there some Swift integration planned for the future?) because Applescript is just majorly lacking for doing even basic tasks like JSON Serialization...but I digress! Sometimes we work with only the tools we have.

If you find this useful please check out my youtube channel over at https://youtube.com/echohack

So my main problem here is that I need to generate TONS of assets (hundreds and hundreds) for using in my videos, socials, etc. and I want to do this programmatically because I need to regenerate these assets every few months based on changing data. Doing this by hand takes about 50 hours of work! Every time! So it's a prime candidate for automation. (Obligatory XKCD) https://xkcd.com/1319/

So I have a big blob of JSON data that I want to programmatically iterate over, insert into Pixelmator, and generate a bunch of images from a template and then export all of them.

Again nothing here is optimized, but this does work! Some refactoring into methods-- uh-- Handlers is probably called for here. Whatever. It's Applescript, it's allowed to suck a bit OK? :wink:

One annoying thing here is that there doesn't seem to be a way to replace text *only* on the currently selected layer, and also there isn't an easy way to select the *next* layer in a list. So instead of wrestling Applescript's nonsense, I'm just generating them all from a single template and copy-pasting that template, and then using layer visibility's boolean as a kludge to tell the iterator that I'm done with that layer. It's not something I'd put in professional code per se but hey it works here. This does have the annoying effect of copying and pasting all over the place, which is kind of slow, especially as you get into the hundreds of grouped layers. Oh well. Don't use this to generate 100,000 images ok?

use framework "Foundation"
use scripting additions
--------------------------------------------------------------------------------
# Helper Libraries
property ca : a reference to current application
property NSData : a reference to ca's NSData
property NSDictionary : a reference to ca's NSDictionary
property NSJSONSerialization : a reference to ca's NSJSONSerialization
property NSString : a reference to ca's NSString
property NSUTF8StringEncoding : a reference to 4

on JSONtoRecord given item:fp
	local fp
	
	set JSONdata to NSData's dataWithContentsOfFile:fp
	
	set [x, E] to (NSJSONSerialization's ¬
		JSONObjectWithData:JSONdata ¬
			options:0 ¬
			|error|:(reference))
	
	if E ≠ missing value then error E
	
	tell x to if its isKindOfClass:NSDictionary then ¬
		return it as record
	
	x as list
end JSONtoRecord
--------------------------------------------------------------------------------


set home to the path to home folder
set f to the POSIX path of home & "blob.json"
set testRecord to JSONtoRecord given item:f

tell application "Pixelmator Pro"
	
	# Generate the Images
	tell the front document
		set visible of every layer to true
		repeat with jsonList in (testRecord as list)
			repeat with listItem in (jsonList as list)
				if visible of current layer then
					set name of current layer to (|Class| of listItem) & " - " & (|listName| of listItem)
					copy
					set listCategory to (|Category| of listItem)
					set listName to (|Name| of listItem)
					set listEffect to (|Effect| of listItem)
					replace text "listEffect" with listEffect
					replace text "listName" with listName
					replace text "listCategory" with listCategory
					set visible of current layer to false
					paste
				end if
			end repeat
		end repeat
	end tell
	
	# Export the images
	set exportLocation to choose folder with prompt "Choose where to export the images:"
	tell the front document
		set visible of every layer to false
		set visible of the last layer to true
		repeat with currentLayer in every layer
			set visible of currentLayer to true
			export for web to ((exportLocation as text) & name of currentLayer & ".png") as PNG with properties {compression factor:100}
			set visible of currentLayer to false
		end repeat
	end tell
end tell