Scripting Photoshop With JavaScript and Ruby
You might not be aware of this, but Photoshop has had scripting support for some time now. It even comes with it’s own JavaScript debugger that adds a reflection interface. It has well documented support for JavaScript, AppleScript, and Visual Basic, all of which have their own set of problems. Thanks to the efforts of Apple on RubyOSA, we can now use Ruby to script this behemoth of an application.
Scripting, Before you Begin
I think the developers at Adobe were a little Bipolar when creating the scripting API for Photoshop. Some things are simple and straight forward, while others are extremely complex, causing you to go bat-shit crazy trying to make sense of it all.
If you’re ready to brave the storm, you can find the documentation for Photoshop Scripting in Photoshop/Scripting Guide/*. There is documentation for all 3 supported languages, and some simple examples for all of them. If you’re wanting to go hard core, there is a forum of dedicated Photoshop scripters who’ve posted some great examples of some of the more complex things like creating UI’s through scripts.
RubyOSA
There are two AppleScript bridges that I’m aware of: RubyOSA and rb-appscript. I’m using RubyOSA because I found it to be more Ruby-like; there are rumors it will be included with Leopard; and it’s an official Apple project. With that being said, rb-appscript is fully capable of scripting Photoshop and the author makes a convincing case as to why you should use rb-appscript over RubyOSA. I’ll leave that up to you, but for me, RubyOSA fits my style.
After you’ve installed RubyOSA via the directions on the site, you might want to generate some rdoc documentation for it (use –ri for RI docs).
$ rdoc-osa --name 'Adobe Photoshop CS2' --op ~/Documents/PhotoshopRuby
I found the Allison template by Evan Weaver to be much easier to navigate than any of the standard rdoc templates, not to mention it’s just a swanky template that’s much easier on the eyes than anything rdoc has to offer.
$ rdoc-osa --name 'Adobe Photoshop CS2' --op ~/Documents/PhotoshopRuby --template ./allison/allison.rb
Photoshop, Bend To My Mercy!
Quickly, we’ll create a new document.
require 'rubygems'
require 'rbosa'
app = OSA.app('Adobe Photoshop CS2')
app.settings.ruler_units = OSA::AdobePhotoshopCS2::E440::PIXEL_UNITS
app.make(OSA::AdobePhotoshopCS2::Document, [], :with_properties => {
:name => 'Ruby Rocks',
:width => 500,
:height => 500
})
The first couple lines of code include rubygems and rubyosa. The next thing we’ll do is get a pointer to the Photoshop Application. I’m using CS 2, but I’m sure this would work with other version of Photoshop as well.
After that, I want to make sure the ruler units are pixels. This is an odd name for a module, and there are plenty more that look just like it with the weird, seemingly arbitrary numbers. The JavaScript API is full of crazy stuff like this, which makes it a bitch to use for a lot of things.
Finally, the meat of the code. The last bit of code calls make, which is an all-encompasing function for creating documents and layers…although I had problems creating layers with it. Basically, we pass it a class type, specifying what we want to make, the second argument is at, which specifies the location where the element should be made at, but it’s useless here, finally we pass in some options like name, width, and height.
You can run this directly from TextMate (cmd + r) and you should see Photoshop create a new 500px X 500px document, with the name ‘Ruby Rocks’. Pretty cool stuff! **Note: You should probably open Photoshop before running these commands—even though I think Photoshop will automatically open, after 20 minutes of course)
What about a more complex example?
require 'rubygems'
require 'rbosa'
app = OSA.app('Adobe Photoshop CS2')
app.settings.ruler_units = OSA::AdobePhotoshopCS2::E440::PIXEL_UNITS
app.instance_eval do
def create_document(options = {})
make(OSA::AdobePhotoshopCS2::Document, [], :with_properties => {
:name => 'Ruby Rocks',
:width => 500,
:height => 500
}.merge(options))
end
def add_layer(name, kind)
kinds = %w(NORMAL GRADIENTFILL PATTERNFILL TEXT SOLIDFILL)
do_javascript %(
var doc = app.activeDocument;
var layer = doc.artLayers.add();
layer.name = "#{name || ''}";
layer.kind = LayerKind.#{kinds.detect {|k| k.downcase == kind} || 'NORMAL'};
)
current_document.art_layers[0]
end
end
app.create_document(:name => 'Schweet')
layer = app.add_layer('A text layer', 'text')
texto = layer.text_object
texto.size = 40
texto.contents = "This is some text"
Ok, so what’s going on here? I created two new instance methods as a shortcut for creating new documents and new layers. After you get beyond the instance_eval bits, is the working code. I’m creating a new text layer, then setting the font size, and finally supplying some text to it.
But what about that do_javascript line in the add_layer method? It’s a pretty handy method, especially considering I couldn’t for the life of me figure out how to add a new layer using pure Ruby. Supposedly make is responsible for adding a new layer too, but I couldn’t get it to work. This is where do_javascript comes in handy considering there are numerous JavaScript examples floating in the wild and Photoshop comes with a special plugin called Script Listener which generates JavaScript based on the actions you manually perform in Photoshop. For example, if you select a 50px round brush with the Script Listener plugin installed, it will generate the JavaScript code to automate that process.
How useful is any of this?
Thats up for debate. Scripting Photoshop is one of those things when you need it, it’s indispensable. Here are a few cases where it could come in handy:
- Adding names to images based on product information stored in a database
- Automating the process of adding Metadata to your images.
- Executing a series of actions on a group of Photos via
do_action
If you give this a spin, let me know what you manage to accomplish.
Sorry, comments are closed for this article.



Discussion
... this has made my day.
This is a great find, I’ll be delving into this later tonight. Thanks!
thank you for introducing me to RubyOSA :) I recently stumbled ober rb-appscript but it didn’t feel that rubyish to me. with RubyOSA I feel more comfortable even if it might not be as powerful. thanks a lot.
How about scripting the gimp :)
http://www.zweknu.org/blog/index.rhtml?s=p%7C480&c=T&ft=T
Adobe Photoshop CS3 add MATLAB scripting as well, which should be exciting for anyone doing heavy image processing in technical fields.
This is great, I spent days trying to figure out the applescript equivilent a couple of years ago.
That’s very nice, thank you :-)
ichigo: “I recently stumbled ober rb-appscript but it didn’t feel that rubyish to me.”
Yeah, Ruby and Apple events have distinctly different ways of doing business (OO vs. RPC+queries), so a bit of mismatch is pretty much inevitable. I think folks who design relational-object mappers for relational databases often encounter similar problems (the Apple Event Object Model is partly based on the relational model, btw).
As you can guess, appscript and RubyOSA deal with this mismatch in different ways. Appscript trades some syntactical Pythonisms/Rubyisms for a level of functionality and application compatibility that’s equal to AppleScript’s (modulo any really rare and obscure application glitches I’ve yet to hear of). It wears the differences on its sleeve, if you like, so can feel a bit strange at first – users soon seem to get used to it though. In contrast, RubyOSA tries to hide away all the differences behind a pure OO facade which gives it a bit more ‘Ruby-ish’ syntax, though it loses some features, is more prone to hidden gotchas, and runs into application compatibility problems more often than AppleScript/appscript. But you pays your moneys and takes your choice.
...
BTW, here’s a pure Ruby version of Justin’s second example using appscript for comparison:
Photoshop’s scripting implementation can be a little quirky at times; if you get stuck, a good place to ask is Apple’s AppleScript-Users mailing list. One thing I’ve changed in my version is that the script no longer names documents when creating them, leaving PS to generate unique names itself. That’s because when PS returns references it likes to identify documents by name, which can cause confusion when two or more documents have the same name. You can sort of get around this by always creating your own references to the frontmost document, but that gets pretty clumsy and it’s cleaner and easier to just use references returned by PS. Adding unique suffixes to your own names would be easy enough though, e.g.:
HTH
has
(With apologies for length, bad Textile escaping [’=>’ should’ve been ’=>‘], etc.:)
RubyOSA rocks!
Thanks for the interesting example of how it can be used to script Photoshop. We hacked together a version of the Marshmallow bot a while back that broadcasts into Campfire your currently playing iTunes song title and link to the lyrics, just to get our feet wet with RubyOSA. We called it Kumbaya.
I’m excited about the possibility of the next iteration of the Mac OS using RubyOSA as it’s scripting language-of-choice and interested in seeing what other cool things people are doing with it.
Thanks again and keep hacking!
For the windows users this article on Scripting Photoshop I wrote about a year ago might come in handy.
Has a C# code example just after the first pic.
Has, thanks for the example with rb-appscript. Short and sweet!
Can you tell me the why you explicitly define `get` and `set` when ruby handles these perfectly fine (e.g. def text=(txt)) ?
Justin: “Can you tell me the why you explicitly define `get` and `set` when ruby handles these perfectly fine”
Oh man, you had to ask. ;) I started writing a response here but it was turning into an essay, and seeing as it’s something that does get asked from time to time I’ve finally stuck it on its own page over at the appscript site:
http://rb-appscript.rubyforge.org/faq.html
It’s kind of a long story, as to really understand the ‘why appscript does X’ one needs to understand a bit about how Apple events work, and what makes them so different to the other IPC systems that most folks are more familiar with.
The brief answer is that there are very good technical reasons for doing it, not least “that’s the way that Apple events actually work, after all”. Not that I didn’t also spend a lot of hours trying to think of a viable way to make it all work ‘implicitly’ (like in AppleScript) back when I originally started (Python) appscript. However, it just wasn’t possible without making compromises elsewhere; compromises that would have stopped appscript from working as well as AppleScript, and were therefore judged unacceptable. And FWIW, three years on and plenty other hard-knock lessons later, I’m confident I made the right decision on this issue at least, though I daresay the education/awareness side could still be better.
(In fact, there were already two other Python-AE bridges available at the time; it was dissatisfaction with those that made me roll up my sleeves and write appscript myself. I may not know much about programming, but, as a longtime AppleScript ‘expert’, I know what I like.:)
Anyway, go read the appscript ‘FAQ’ page, and if your brain is still working by the end of it, go check out the two papers mentioned at the end of it (they’re on the links page) if you want to learn more. And if you’ve any other questions, feel free to ask away (though if you could maybe manage some simple ones next time…<g>)
HTH
has
This rules! There’s also another Ruby-AppleScript bridge called RubyAEOSA, but it’s deprecated and not so Ruby-ish.
Can you see my socks? No! They’re long gone.
I had no idea you could do this type of thing. Particularly like the idea of running actions once a new document is set up.
And I suppose you can run save actions?
Scott, you can do that and more.
However, on the topic your talking about you can have actions auto run for specific Photoshop events by going to File->Scripts->Scripts Events Manager.
From there you can select from different types of events and what scripts or actions to run when an event occurs (e.g. New Document created).
Nice but there are so many COM references to photoshop already out there on the web but has any one managed to save their master pieces it the one stumbling block thats got me stumped.
I managed to code a few line of pure ruby but couldnt save it at resorted back to javascript
t1993@encytemedia.com
en2090@encytemedia.com
ei2uisk1ffqrr drqhcpyk3tnms8snj vlusxmpglhre
wow – interesting article. Until now I work with PHP but I think Ruby is the future.
Greetings from Germany Sven