Adobe CS & CS2 Scripters

Got some time on your hands? then maybe you can help me!!
Here is a script that I’ve been working on and would like some feed back (no insults please this is my first attempt at doing a script for someone else to use).
It should use an image file to posterize and an illustrator file containing symbols (the order of which is based from darkest parts of image top symbol down to lightest bottom symbol. I used a transparent square box behind the art of each symbol. Most of how this is supposed to work you can see from the code.

Where I need help is finding where I can either speed things up & error handling. It is slow so avoid trying anything but smaller images. Who knows you may be rewarded with a nice poster graphic for your efforts. Thanks.

tell application "Illustrator CS"
	activate
	set user interaction level to interact with all
	--
	if (count of documents) is less than 1 then
		set noDoc to "You have no Illustrator file open" & return & "do you want to find it?"
		set FindDoc to display dialog noDoc buttons {"Cancel", "Find it"} default button 2 with icon note
		if button returned of FindDoc is "Find it" then
			set theFile to choose file with prompt "Please locate the file you want to be used" without invisibles
			tell application "Finder"
				set theFile to theFile as alias
			end tell
			tell application "Illustrator CS"
				open theFile
			end tell
		end if
	end if
	set docRefA to the current document
	tell docRefA
		if (count of symbols) is less than 2 then
			display dialog "Your document needs at least" & return & ¬
				"2 symbols to create a pattern." buttons {"Cancel"} default button 1
		end if
		if (count of symbols) is greater than 64 then
			display dialog "This script only works with" & return & ¬
				"upto 64 symbols to create a pattern." buttons {"Cancel"} default button 1
		end if
		set pageWidth to get width
		set pageHeight to get height
		set ROx to pageWidth / 2
		set ROy to pageHeight / 2
		set ruler origin to {ROx, ROy}
		make new layer at beginning of docRefA with properties ¬
			{name:"Symbols Test", visible:true}
		make new symbol item at beginning of layer "Symbols Test" with properties ¬
			{symbol:symbol 1, position:{0, 0}}
		set Symbol_x to width of page item 1
		set Symbol_y to height of page item 1
		set Symbol_1x to Symbol_x as integer
		set Symbol_1y to Symbol_y as integer
		if Symbol_1x ≠ Symbol_1y then
			display dialog "Your symbols are not defined" & return & ¬
				"using a square box." & return & ¬
				"The trick is to use a transparent" & return & ¬
				"square box behind each one." buttons {"Cancel"} default button 1
		end if
		--
		repeat with a from 2 to (count of symbols)
			make new symbol item at beginning of layer "Symbols Test" with properties ¬
				{symbol:symbol a, position:{0, 0}}
			set Symbol_2x to width of page item 1 as integer
			set Symbol_2y to height of page item 1 as integer
			if Symbol_1x ≠ Symbol_2x or Symbol_1y ≠ Symbol_2y then
				display dialog "Your symbols are not" & return & ¬
					"of an equal size." buttons {"Cancel"} default button 1
			end if
			delete page item 1
		end repeat
		delete layer named "Symbols Test"
		display dialog "Your symbols passed the basic size tests" giving up after 2
		--
		set OldLayers to count of layers
		set properties of every layer to {visible:false}
		--
		set MySymbols to get count of symbols
		repeat with b from 1 to MySymbols
			set SymbolName to get name of symbol b as string
			set LayerName to SymbolName & " Layer" as string
			make new layer at end of docRefA with properties {name:LayerName, visible:true}
		end repeat
	end tell
end tell
--
tell application "Adobe Photoshop CS"
	activate
	set display dialogs to never
	set UserPrefs to properties of settings
	set ruler units of settings to pixel units
	--
	if (count of documents) is less than 1 then
		set noDoc to "You have no Photoshop image open" & return & "do you want to find it?"
		set FindDoc to display dialog noDoc buttons {"Cancel", "Find it"} default button 2 with icon note
		if button returned of FindDoc is "Find it" then
			set theFile to choose file with prompt "Please locate the image you want to be used" without invisibles
			tell application "Finder"
				set theFile to theFile as alias
			end tell
			tell application "Adobe Photoshop CS"
				open theFile
			end tell
		end if
	end if
	set docRefB to the current document
	tell docRefB
		repeat
			set TheTiles to display dialog "How many tiles do you want" & return & ¬
				"to use when making this mosaic?" default answer "400" with icon note
			try
				set HowMany to (text returned of TheTiles) as integer
				exit repeat
			on error
				display dialog "Integers only please!" with icon caution
			end try
		end repeat
		if HowMany ≤ 500 then
			display dialog "Have a break." & return & "Go put the kettel on" & return & ¬
				"make a nice cup of tea!!!" buttons {"Cancel", "Continue"} default button 2 with icon note
		end if
		if (HowMany > 500) and (HowMany ≤ 2500) then
			display dialog "Go for lunch this may take some" & return & ¬
				"time, say no to McDonalds." & return & ¬
				"This script was given to you by a veggie!!!" buttons {"Cancel", "Continue"} default button 1 with icon note
		end if
		if HowMany > 2500 then
			display dialog "You would be better off investing" & return & ¬
				"in a plug-in to do this kind of thing" buttons {"Cancel", "Continue"} default button 1 with icon note
		end if
		if (mode is not grayscale) then
			change mode to grayscale
		end if
		if (bits per channel is sixteen) then
			set bits per channel to eight
		end if
		if (count of art layers) > 1 or (count of layer sets) > 0 or ((count of art layers) is 1 and not background layer of layer 1) then flatten
		set docHeight to height
		set docWidth to width
		set pixelWidth to ((docWidth * HowMany / docHeight) ^ 0.5) as integer
		set pixelHeight to (docHeight * pixelWidth / docWidth) as integer
		resize image height pixelHeight width pixelWidth resolution 72
		adjust layers using posterize with options ¬
			{class:posterize, level:MySymbols}
	end tell
end tell
--
set theLevels to {0}
--
set thesplit to 255 / (MySymbols - 1)
repeat with c from 1 to (MySymbols - 1)
	set the end of theLevels to (thesplit * c) div 1
end repeat
--
repeat with d from 1 to pixelHeight
	set theValues to {}
	tell application "Adobe Photoshop CS"
		activate
		tell docRefB
			repeat with e from 1 to pixelWidth
				select region {{e - 1, d - 1}, {e, d - 1}, {e, d}, {e - 1, d}}
				set f to histogram of channel "Gray"
				set theGray to {}
				repeat with g from 1 to (count of theLevels)
					set h to (item g of theLevels) as number
					if item (h + 1) of f is 1 then
						set theGray to g
					end if
				end repeat
				copy (theGray as number) to end of theValues
				deselect
			end repeat
		end tell
	end tell
	--
	tell application "Illustrator CS"
		activate
		tell docRefA
			set ROx to -((pixelWidth * Symbol_x) / 2) + (pageWidth / 2)
			set ROy to ((pixelHeight * Symbol_y) / 2) + (pageHeight / 2)
			set ruler origin to {ROx, ROy}
			repeat with i from 1 to pixelWidth
				set j to (item i of theValues) as number
				make new symbol item at beginning of layer (j + OldLayers) with properties ¬
					{symbol:symbol j, position:{0 + (Symbol_x * (i - 1)), 0 - (d * Symbol_y)}}
			end repeat
		end tell
	end tell
end repeat
--
tell application "Adobe Photoshop CS"
	set ruler units of settings to ruler units of UserPrefs
	close docRefB saving no
end tell
--
tell application "Illustrator CS"
	activate
	beep 3
	display dialog "All done. You may want to save this!!!" & return & ¬
		"Sorry not able to resize the page" buttons {"Cancel"} default button 1
end tell

Hey Mark

Great! script not that slow! However only went upto 800, didn’t pick the best image gotta say but could see
where you were going.

cheers

Hey Mark - I didn’t try running your script yet - but I do a lot of Adobe Applescripting (mainly Indesign and Pshop) and thought I could offer a little feedback.

For example - I notced that you are tell(ing) Illustrator in a few places - where you are already inside of an Illustrator tell, and telling the finder to do things that you don’t have to tell the Finder to do.
ie:
[applescript tell application “Finder”
set theFile to theFile as alias
end tell
tell application “Illustrator CS”
open theFile
end tell



you can simply do this in place of those 6 lines - becasue you are already in the Illustrator tell block.

set theFile to choose file with prompt "Please locate the file you want to be used" without invisibles
open theFile


another quick thing although this sometimes makes scripts harder to follow.

ie: instead of:

set pageWidth to get width
set pageHeight to get height
set ROx to pageWidth / 2
set ROy to pageHeight / 2



try:

set ROx to (width / 2)–or maybe set ROx to (get width / 2)
set ROy to (height / 2)



I have CS2 - so this might not work with CS - but I think if you went through the script and did a few of these things - you might get a little speed back, individually they are nothing, but collectively might add up to something.

Just my 2 cents, it really looks like a great script though.

Chris

Chris, thanks. I think I see what you mean about the finder bit that would be for making a list item to alias but I have no lists just file. I do tend to over word a few things too. I will run through and try some tidying. What I have yet to understand is if pulling this apart to be performed by sub routines has any difference on how this script may preform or if in this case it would be of no advantage. Do sub routines operate any faster or are they just for a better way to just assign certain tasks on request. On a side note I have just downloaded iMaginePhoto to see what I can do with this if any thing but thats for another day.

Mark - as far as adding subroutines, It really depends I guess, but for the most part I don’t think speed is what you gain with them, mainly portability, and repetition. You can create cleaner scripts with them, which makes them shorter and sometimes easier to read. Sometimes I think they make things more confusing though actually.

The best way to speed up a script is to limit the calls to a program to the bare miinimum to get the job done. I don’t think that handlers will speed up your script, the main reasons to use them is to organize you scripts for reusable code. The main thing that I can see that is slowing your script down is the selection and histogram calls to photoshop, and I don’t think that you can speed that up without a faster processor. From looking at it a little this is my attempt at gaining a bit of speed:

set TheTime to current date

tell application "Adobe Illustrator"
	activate
	set user interaction level to interact with all
	--
	if (count of documents) is less than 1 then
		set FindDoc to display dialog ("You have no Illustrator file open" & return & "do you want to find it?") buttons {"Cancel", "Find it"} default button 2
		if button returned of FindDoc is "Find it" then
			set theFile to (choose file with prompt "Please locate the file you want to be used" without invisibles) as alias
			open theFile
		end if
	end if
	set docRefA to the current document
	tell docRefA
		set MySymbols to get count of symbols
		if MySymbols is less than 2 then
			display dialog "Your document needs at least" & return & ¬
				"2 symbols to create a pattern." buttons {"Cancel"} default button 1
		end if
		if MySymbols is greater than 64 then
			display dialog "This script only works with" & return & ¬
				"upto 64 symbols to create a pattern." buttons {"Cancel"} default button 1
		end if
		copy {width, height} to {pageWidth, pageHeight}
		set ROx to pageWidth / 2
		set ROy to pageHeight / 2
		set ruler origin to {ROx, ROy}
		make new layer at beginning of docRefA with properties ¬
			{name:"Symbols Test", visible:true}
		make new symbol item at beginning of layer "Symbols Test" with properties ¬
			{symbol:symbol 1, position:{0, 0}}
		set Symbol_x to width of page item 1
		set Symbol_y to height of page item 1
		set Symbol_1x to Symbol_x as integer
		set Symbol_1y to Symbol_y as integer
		if Symbol_1x ≠ Symbol_1y then
			display dialog "Your symbols are not defined" & return & ¬
				"using a square box." & return & ¬
				"The trick is to use a transparent" & return & ¬
				"square box behind each one." buttons {"Cancel"} default button 1
		end if
		--
		repeat with a from 2 to MySymbols
			make new symbol item at beginning of layer "Symbols Test" with properties ¬
				{symbol:symbol a, position:{0, 0}}
			set Symbol_2x to width of page item 1 as integer
			set Symbol_2y to height of page item 1 as integer
			if Symbol_1x ≠ Symbol_2x or Symbol_1y ≠ Symbol_2y then
				display dialog "Your symbols are not" & return & ¬
					"of an equal size." buttons {"Cancel"} default button 1
			end if
			delete page item 1
		end repeat
		delete layer named "Symbols Test"
		--display dialog "Your symbols passed the basic size tests" giving up after 2
		--
		set OldLayers to count of layers
		set properties of every layer to {visible:false}
		--
		repeat with b from 1 to MySymbols
			set SymbolName to get name of symbol b as string
			set LayerName to SymbolName & " Layer" as string
			make new layer at end of docRefA with properties {name:LayerName, visible:true}
		end repeat
	end tell
end tell
--
tell application "Adobe Photoshop CS2"
	activate
	set display dialogs to never
	set UserPrefs to properties of settings
	set ruler units of settings to pixel units
	--
	if (count of documents) is less than 1 then
		set FindDoc to display dialog ("You have no Photoshop image open" & return & "do you want to find it?") buttons {"Cancel", "Find it"} default button 2
		if button returned of FindDoc is "Find it" then
			set theFile to (choose file with prompt "Please locate the image you want to be used" without invisibles) as alias
			open theFile
		end if
	end if
	set docRefB to the current document
	tell docRefB
		repeat
			set TheTiles to display dialog "How many tiles do you want" & return & ¬
				"to use when making this mosaic?" default answer "400" with icon note
			try
				set HowMany to (text returned of TheTiles) as integer
				exit repeat
			on error
				display dialog "Integers only please!" with icon caution
			end try
		end repeat
		if HowMany ≤ 500 then
			display dialog "Have a break." & return & "Go put the kettel on" & return & ¬
				"make a nice cup of tea!!!" buttons {"Cancel", "Continue"} default button 2 with icon note
		end if
		if (HowMany > 500) and (HowMany ≤ 2500) then
			display dialog "Go for lunch this may take some" & return & ¬
				"time, say no to McDonalds." & return & ¬
				"This script was given to you by a veggie!!!" buttons {"Cancel", "Continue"} default button 1 with icon note
		end if
		if HowMany > 2500 then
			display dialog "You would be better off investing" & return & ¬
				"in a plug-in to do this kind of thing" buttons {"Cancel", "Continue"} default button 1 with icon note
		end if
		if (mode is not grayscale) then
			change mode to grayscale
		end if
		if (bits per channel is sixteen) then
			set bits per channel to eight
		end if
		if (count of art layers) > 1 or (count of layer sets) > 0 or ((count of art layers) is 1 and not background layer of layer 1) then flatten
		set docHeight to height
		set docWidth to width
		set pixelWidth to ((docWidth * HowMany / docHeight) ^ 0.5) as integer
		set pixelHeight to (docHeight * pixelWidth / docWidth) as integer
		resize image height pixelHeight width pixelWidth resolution 72
		adjust layers using posterize with options ¬
			{class:posterize, level:MySymbols}
	end tell
end tell
--
set theLevels to {0}
--
set thesplit to 255 / (MySymbols - 1)
repeat with c from 1 to (MySymbols - 1)
	set the end of theLevels to (thesplit * c) div 1
end repeat
--
tell application "Adobe Photoshop CS2"
	activate
	tell docRefB
		set TheGrid to {}
		repeat with d from 1 to pixelHeight
			set theValues to {}
			repeat with e from 1 to pixelWidth
				select region {{e - 1, d - 1}, {e, d - 1}, {e, d}, {e - 1, d}}
				set f to histogram of channel "Gray"
				set theGray to {}
				repeat with g from 1 to (count of theLevels)
					set h to (item g of theLevels) as number
					if item (h + 1) of f is 1 then
						set theGray to g
					end if
				end repeat
				copy (theGray as number) to end of theValues
				deselect
			end repeat
			copy theValues to end of TheGrid
		end repeat
	end tell
end tell
tell application "Adobe Illustrator"
	activate
	tell docRefA
		set ROx to -((pixelWidth * Symbol_x) / 2) + (pageWidth / 2)
		set ROy to ((pixelHeight * Symbol_y) / 2) + (pageHeight / 2)
		set ruler origin to {ROx, ROy}
		repeat with d from 1 to count of TheGrid
			repeat with i from 1 to count of item d of TheGrid
				set j to (item i of item d of TheGrid) as number
				make new symbol item at beginning of layer (j + OldLayers) with properties ¬
					{symbol:symbol j, position:{0 + (Symbol_x * (i - 1)), 0 - (d * Symbol_y)}}
			end repeat
		end repeat
	end tell
end tell
set TotalTime to (current date) - TheTime
display dialog TotalTime

The main thing that I did was to build a nested list for the all the values taken from the photoshop file and then bring the list into Illustrator and draw it all at once instead of switching between applications. Other observations are that it appears to run a lot faster from Script editor (about 1/3 the time on a value of 50) than as an application bundle or a script launced from the Illustrator menue. It might be worth it to look into builing it into an AppleScript Studio application, but if it were me I would just run it from Script Editor.