Copy link to clipboard
Copied
Objective:
Create a MacOS droplet. When images are dropped onto it (jpg, jpeg, png, tif, psb, psd, heic) it will grab those images, activate Illustrator and process them via an Illustrator ExtendScript JavaScript (script.jsx). If the droplet is simply opened without dropping files onto it, it'll activate Illustrator then will proceed with the script.jsx which will see no files were passed to it, thereby opening a dialog to accept the images and proceed with the image processing.
Methodology:
I'm using AppleScript to create the droplet that will accept the images to get them into a variable. It then activates Illustrator and tries to pass the variable into script.jsx with a do javascript command. That JavaScript will either proceed to process the images if there were any passed to it, or it will display its own dialog to capture the images. The droplet is created in Automator with Run AppleScript action and the script.jsx is saved in the droplet's Contents/Resources folder.
Question/Challenge:
How do I properly format/send the arguments from AppleScript so they are stored in the same format as you would get from the File.openDialog command within Illustrator Extendscript?
AppleScript Code:
on run {input, parameters}
set imagePaths to {}
-- Check if input is not empty
if input is not {} then
-- Get the file paths of dropped images
repeat with fileAlias in input
set posixPath to POSIX path of fileAlias
set fileExtension to (name extension of (info for posixPath)) as string
if fileExtension is in {"jpg", "png", "tif", "psd", "psb", "heic"} then
set end of imagePaths to posixPath
end if
end repeat
end if
set scriptName to "script.jsx"
set scriptPath to POSIX path of (path to me) & "Contents/Resources/" & scriptName
set scriptText to (do shell script "cat " & quoted form of scriptPath)
tell application "Adobe Illustrator"
activate
do javascript scriptText with arguments {imagePaths}
end tell
end run
JavaScript Code:
function main(imagePaths) {
if (imagePaths.length > 0) { // Input received
// Set the files variable to the incoming file list from Applescript droplet
var files = imagePaths;
} else { // No input received
// Prompt user to select images if none were supplied from droplet
var imageFormats = "*.jpg;*.jpeg;*.png;*.tif;*.psd;*.psb";
var files = File.openDialog("Select images(s) to add to layout:", imageFormats, true);
if (!files) {
// User canceled, exit script
alert("Script canceled by user.");
return;
}
// rest of code here
}
}
// Catch error if nothing is passed from AppleScript (no images dropped)
try {
main(arguments[0]);
} catch (e) {
// Set imagePaths to empty array
var imagePaths = [];
main(imagePaths);
}
@Chris.S, here's a solution that should work for you. Please note, since you are already using AppleScript, if the droplet is double-clicked (meaning no files are actually provided to the script), I use the much more native (and customizeable) AppleScript to prompt for user for the files. Then the script continues as if the choosen files were dropped on to it.
The dropped or choosen paths will come into Ai as an array where you can then make them `File` objects and do whatever you please. If you
...@Chris.S, sorry I completely missed the mention of Automator in your original post...
So, it seems when running a droplet the getting the `path to me` actually gets the path to the Script Editor app. to fix this, I just use Finder to get the `path to me` which works. As you can see in the script below, I cleaned up a few things and renamed a few variables but it is mostly the same. The really only update is grabbing the `dropletPath` and adding that with the `scriptPath` before using the `do jav
...Amazing! Thank you again for your help @jduncan. This did the trick.
BTW, the reason I moved "on run" below "on open" originally was because it would go straight to "on run" and not process the "on open" when put in Automator. I thought it was because it was doing on run first and that moving it below on open it would do the on open first. I guess I was wrong and that it was likely more to do with calling it from Automator.
This approach is way better than Automator. Thank you again!
Copy link to clipboard
Copied
@Chris.S, here's a solution that should work for you. Please note, since you are already using AppleScript, if the droplet is double-clicked (meaning no files are actually provided to the script), I use the much more native (and customizeable) AppleScript to prompt for user for the files. Then the script continues as if the choosen files were dropped on to it.
The dropped or choosen paths will come into Ai as an array where you can then make them `File` objects and do whatever you please. If you don't want to ask for the files from the user within AppleScript, you could simple run the script and check if no arguments are provided.
Hope this helps. Cheers!
droplet.app (AppleScript saved as an application)
property theExtensionsToProcess : {"jpg", "png", "tif", "psd", "psb", "heic"}
property scriptPath : "~/Desktop/script.jsx"
on run
set choosenItems to (choose file with multiple selections allowed)
open every item of choosenItems
end run
on open droppedItems
set paths to {}
repeat with a from 1 to length of droppedItems
set theCurrentItem to item a of droppedItems
set currentFilePath to POSIX path of theCurrentItem
set theExtension to (name extension of (info for currentFilePath)) as string
if (theExtensionsToProcess contains theExtension) then
set end of paths to currentFilePath
end if
end repeat
-- check if no valid paths were found (could add an alert)
if ((count of paths) is 0) then
return
end if
tell application "Adobe Illustrator"
activate
do javascript file scriptPath with arguments paths
end tell
end open
script.jsx
if (arguments.length > 0) {
var filePaths = [];
for (var i = 0; i < arguments.length; i++) {
filePaths.push(new File(arguments[i]));
}
alert(filePaths.length + " paths provided\n" + filePaths.join("\n"));
} else {
alert("No args provided.");
}
Copy link to clipboard
Copied
Thank you @jduncan! The .jsx code workked brilliantly. BTW: The reason I had the .jsx asking for the files if nothing was dropped on the AppleScript droplet is because that way I can use the same .jsx code whether it's run directly from Illustrator or embedded in the Applescript droplet wrapper.
Copy link to clipboard
Copied
@Chris.S, yeah that is what I figured... It's a simple fix, just update the `on run` block in the AppleScript to the code below, and then the else statement in the jsx script will be where you add you JSX choose file code.
on run
tell application "Adobe Illustrator"
activate
do javascript file scriptPath
end tell
end run
Copy link to clipboard
Copied
@jduncan, thank you. A few observations:
property theExtensionsToProcess : {"jpg", "png", "tif", "psd", "psb", "heic"}
property scriptPath : POSIX path of (path to me) & "Contents/Resources/Scripts/script.jsx"
-- property scriptPath : "~/Desktop/script.jsx"
on open droppedItems
set paths to {}
repeat with a from 1 to length of droppedItems
set theCurrentItem to item a of droppedItems
set currentFilePath to POSIX path of theCurrentItem
set theExtension to (name extension of (info for currentFilePath)) as string
if (theExtensionsToProcess contains theExtension) then
set end of paths to currentFilePath
end if
end repeat
-- check if no valid paths were found (could add an alert)
if ((count of paths) is 0) then
return
end if
tell application "Adobe Illustrator"
activate
do javascript file scriptPath with arguments paths
end tell
end open
on run
tell application "Adobe Illustrator"
activate
do javascript file scriptPath
end tell
end run
Copy link to clipboard
Copied
@Chris.S, sorry I completely missed the mention of Automator in your original post...
So, it seems when running a droplet the getting the `path to me` actually gets the path to the Script Editor app. to fix this, I just use Finder to get the `path to me` which works. As you can see in the script below, I cleaned up a few things and renamed a few variables but it is mostly the same. The really only update is grabbing the `dropletPath` and adding that with the `scriptPath` before using the `do javascript method`.
Also, I'm not sure why you had to move the `on run` method to the end but whatever works...
And to answer #1 from your response, if I had to guess the paths were being passed in from Automator as one string (not an array like straight AppleScript) and that is probably why it wasn't seeing all paths...
Let me know if this works the way you want.
property acceptedExtentions : {"jpg", "png", "tif", "psd", "psb", "heic"}
property scriptPath : "Contents/Resources/Scripts/script.jsx"
-- do this when the droplet is double-clicked
on run
runScript({})
end run
-- do this when files are dropped on the droplet
on open droppedItems
set paths to {}
-- get path to each dropped file
repeat with a from 1 to length of droppedItems
set theCurrentItem to item a of droppedItems
set currentFilePath to POSIX path of theCurrentItem
set theExtension to (name extension of (info for currentFilePath)) as string
-- add path to paths array if extension an accepted type
if (acceptedExtentions contains theExtension) then
set end of paths to currentFilePath
end if
end repeat
-- check if no valid paths were found (could add an alert)
if ((count of paths) is 0) then
return
end if
runScript(paths)
end open
-- run the script from within Ai with the supplied arguments
on runScript(args)
tell application "Finder"
set dropletPath to POSIX path of (path to me)
end tell
tell application "Adobe Illustrator"
activate
do javascript file (dropletPath & scriptPath) with arguments args
end tell
end runScript
Copy link to clipboard
Copied
Amazing! Thank you again for your help @jduncan. This did the trick.
BTW, the reason I moved "on run" below "on open" originally was because it would go straight to "on run" and not process the "on open" when put in Automator. I thought it was because it was doing on run first and that moving it below on open it would do the on open first. I guess I was wrong and that it was likely more to do with calling it from Automator.
This approach is way better than Automator. Thank you again!
Copy link to clipboard
Copied
@jduncan... any tips how to get droppedItems to report all of the files when there are multiple images with very long names (~50 characters)? It's failing/acting sporadically when I get about 4 or more of those guys in my list of dropped items, but no issues with lots of files with shorter names.
When running the simplified code below to troubleshoot, I noticed the count of droppedItems wasn't being reported correctly as I got about 5 of the long names/paths into the list. After further troubleshooting I believe it's due to the filename lengths and what droppedItems can store in one variable. Sometimes it will split droppedItems in 2 (as if 1 file was dropped on it, and then 3 more, running it twice). It doesn't seem to have a problem with many shorter filenames.
Simplified Applescript droplet:
on open droppedItems
set paths to {}
display alert "droppedItems:" message ((count of droppedItems) as text)
end open
Comparable JavaScript droplet (saved from Automator) with the same issue:
function run(input, parameters) {
var itemCount = input.length;
var app = Application.currentApplication();
app.includeStandardAdditions = true;
app.displayAlert("Number of dropped items:", {message: itemCount});
}
System: 2023 16" MPB M3 Max, 128GB, Sonoma 14.4.1
Tried restarting and running Disk Utility/First Aid.
On another note, I also had to add a delay if Illustrator wasn't already active from the original code as that was causing some problems for me, but I got that worked out I believe.
Copy link to clipboard
Copied
@Chris.S, this was a head scratcher... I tried with the list of files below and all seems okay. Obviously something is going wrong within AppleScript, and if the files were downloaded it seems that could be the issue (read here). When I added a single download file to the list below, it wasn't counted correctly. So, I ran `xattr -rc *` (from the terminal) within the directory containing all of the files and then it was counted correctly. Let me know if this helps?
here_is_a_really_long_name_that_will_probably_cause_some_problems_1.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_2.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_3.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_4.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_5.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_6.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_7.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_8.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_9.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_10.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_11.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_12.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_13.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_14.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_15.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_16.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_17.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_18.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_19.txt
here_is_a_really_long_name_that_will_probably_cause_some_problems_20.txt
little_longer_than_short_name_1.txt
little_longer_than_short_name_2.txt
little_longer_than_short_name_3.txt
little_longer_than_short_name_4.txt
little_longer_than_short_name_5.txt
little_longer_than_short_name_6.txt
little_longer_than_short_name_7.txt
little_longer_than_short_name_8.txt
little_longer_than_short_name_9.txt
little_longer_than_short_name_10.txt
little_longer_than_short_name_11.txt
little_longer_than_short_name_12.txt
little_longer_than_short_name_13.txt
little_longer_than_short_name_14.txt
little_longer_than_short_name_15.txt
little_longer_than_short_name_16.txt
little_longer_than_short_name_17.txt
little_longer_than_short_name_18.txt
little_longer_than_short_name_19.txt
little_longer_than_short_name_20.txt
short_name_1.txt
short_name_2.txt
short_name_3.txt
short_name_4.txt
short_name_5.txt
short_name_6.txt
short_name_7.txt
short_name_8.txt
short_name_9.txt
short_name_10.txt
short_name_11.txt
short_name_12.txt
short_name_13.txt
short_name_14.txt
short_name_15.txt
short_name_16.txt
short_name_17.txt
short_name_18.txt
short_name_19.txt
short_name_20.txt
Copy link to clipboard
Copied
Thank you @jduncan. I guess the length of the filenames was a red herring... I renamed them to A, B, C, etc. and still had the issue. These were transferred/downloaded to Box, so the culprit must lie there. Seems it's only some images though as it's fine for others that came from Box. Will play around with them and the proposed solution when I have more time next week. Funny, I had come across the link you shared but didn't think it applied here as I had some other files I was testing that also came from Box with shorter filenames which didn't have the issue which led me down the wrong "path"... go figure!
Copy link to clipboard
Copied
@jduncan , it definitely was the quarantining that was causing the odd behavior. Thank you for the tip! Below is the code I ended up with, using Stay Open when saving the droplet...
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
property filesToOpen : {}
property acceptedExtentions : {"jpg", "png", "tif", "psd", "psb", "heic"}
property scriptPath : "Contents/Resources/Scripts/script.jsx"
property paths : {}
on run
runScript({})
tell me to quit
end run
-- do this when files are dropped on the droplet
on open droppedItems
set my filesToOpen to my filesToOpen & droppedItems
-- cancel any pending performSelector: requests
current application's NSObject's cancelPreviousPerformRequestsWithTarget:me
-- handle files after a short delay in case further events are received
tell me to performSelector:"doOpen" withObject:(missing value) afterDelay:0.5
end open
on doOpen()
copy my filesToOpen to droppedItems
set my filesToOpen to {} -- reset for next time
-- get path to each dropped file
repeat with a from 1 to length of droppedItems
set theCurrentItem to item a of droppedItems
set currentFilePath to POSIX path of theCurrentItem
set theExtension to (name extension of (info for currentFilePath)) as string
-- add path to paths array if extension an accepted type
if (acceptedExtentions contains theExtension) then
set end of paths to currentFilePath
end if
end repeat
-- check if no valid paths were found
if ((count of paths) is 0) then
return
end if
runScript(paths)
end doOpen
-- run the script from within Ai with the supplied arguments
on runScript(args)
tell application "Finder"
set dropletPath to POSIX path of (path to me)
end tell
-- Construct full path to the script
set scriptFullPath to dropletPath & scriptPath
-- Check if Illustrator is running
tell application "System Events"
set illustratorRunning to (exists process "Adobe Illustrator")
end tell
if illustratorRunning then
-- Illustrator is open, execute the JavaScript directly
tell application "Adobe Illustrator"
activate
do javascript file scriptFullPath with arguments args
end tell
else
-- Illustrator is not open, launch it
tell application "Adobe Illustrator" to activate
-- Wait a moment for Illustrator to open
delay 1
-- Check if Illustrator opened successfully
tell application "System Events"
set illustratorRunning to (exists process "Adobe Illustrator")
end tell
if illustratorRunning then
-- Illustrator is now open. Run the JavaScript
tell application "Adobe Illustrator"
do javascript file scriptFullPath with arguments args
end tell
else
display alert "Error" message "Open Adobe Illustrator and try again."
end if
end if
tell me to quit
end runScript
on quit
continue quit
end quit
Copy link to clipboard
Copied
@Chris.S, this is great info. I'm glad you found out the best way to process those pesky quarantied files. I had planned to look into this week for my own knowledge but it seems you have it working. Thanks for sharing!