I recently had to make a tool for resizing and compressing images. It was a complete nightmare. AppleScript has to be one of the worst development environments I've ever worked with.
One of the most fascinating things about the design is the concept of the tell application
clause. I don't know exactly how it works, but depending on what bundle you are within, the same syntax/code can have different results - or even what appears to be the same result, but further on in the program it doesn't work as expected. The syntax is also overly verbose, as can be seen in the example code. There appear to be multiple ways of doing the same thing, but sometimes with subtle differences.
Another thing I found overly complex is accessing and manipulating file paths. In, say, Ruby, we have Pathname
and String
for representing paths. It is all fairly straight forward. In AppleScript, we have alias
(not exactly paths - kind of like a hard link to a file), string
, file
, folder
. You can't have a file, folder or alias to a file that does not exist yet. Also, you have POSIX paths ("/a/b/c"), and also Mac OS paths ("Volume:a:b:c"). The "Image Events" tool was very peculiar, it required an alias as created in "System Events", the ones returned by "Finder" did not appear to work correctly.
Another fun fact, there is no simple to use function to tell if an alias is a file or folder (as far as I can tell). The only way I could do this was to create a "disk item" and check whether the derived class was "folder" or "file".
There is also no public type system. You just get errors when executing code. The documentation is also fairly difficult to access - you have to load up specific "dictionaries" of documentation and while they are individually fairly well structured, it is hard to cross reference material.
This code lets you drop items onto the application icon. It then descends into any directories you dropped and finds all image files. These files are then copied to the named folder you specify on the desktop. Once they have been resized as necessary, they are then compressed using ditto.
AppleScript has a lot of interesting ideas in terms of syntax and making code readable. I think that the access it provides to other applications is fantastic when compared to other similar tools. However, it has a high cost of entry for someone used to shell scripting / programming as its programming constructs are not consistent. I might need to use AppleScript again, and I'm sure as I use it more I will understand its peculiarities, but for now, I'm happy to be done with it.
Source code
-- Released under the MIT License.
-- Copyright, 2009, by Samuel Williams.
--
-- Drop files on top of this application to produce a zip archive on the desktop containing compressed JPEGs at a maximum size of 800px.
on open some_items
-- If we drop a single folder onto the icon, we pull the name from the folder name
set target_name to "Images"
if (count of some_items) = 1 then
set first_item to get item 1 of some_items
tell application "System Events"
set target_item to disk item (first_item as string)
if class of target_item is folder then
set target_name to name of first_item
end if
end tell
end if
display dialog "Name of Archive" default answer target_name buttons {"OK"} default button 1
set target_name to text returned of the result
tell application "Finder"
set desktop_path to folder "Desktop" of home
make new folder at desktop_path with properties {name:target_name}
set destination to (folder target_name of desktop_path) as alias
set archive_path to (POSIX path of (desktop_path as text)) & target_name & ".zip"
end tell
repeat with this_item in some_items
tell application "System Events"
set source_item to disk item (this_item as string)
end tell
process_path(source_item, destination)
end repeat
set archive_command to "/usr/bin/ditto -c -k -rsrc " & quoted form of (POSIX path of destination) & " " & quoted form of archive_path
do shell script archive_command
tell application "Finder"
activate
delete folder target_name of desktop_path
reveal file (target_name & ".zip") of desktop_path
end tell
end open
to process_path(source, destination)
-- Figure out if it is a file or folder, and recurse appropriatly
tell application "System Events"
set is_folder to class of source is folder
if is_folder then
set sub_items to items in source
end if
end tell
if is_folder then
repeat with this_item in sub_items
process_path(this_item, destination)
end repeat
else
tell application "System Events"
set source_alias to (path of source) as alias
end tell
resize_and_save(source_alias, destination)
end if
end process_path
to resize_and_save(image_alias, destination_alias)
tell application "Image Events"
launch
set opened_image to open image_alias
if class of opened_image is image and not dimensions of opened_image = {} then
set dim to dimensions of opened_image
-- We only resize if the dimension is bigger than 800
if (first item of dim > 800 or second item of dim > 800) then
scale opened_image to size 800
end if
save opened_image in destination_alias as JPEG with compression level medium
close opened_image
end if
end tell
end resize_and_save
This script, when files are dragged on top of the application icon, will resize them proportionally so the maximum dimension is 800px. It will then ask the user for a name and create an archive on the desktop containing all the images.
The compiled application is available.