Showing posts with label Adobe BridgeTalk. Show all posts
Showing posts with label Adobe BridgeTalk. Show all posts

Wednesday, May 20, 2009

Using Adobe BridgeTalk synchronously

So let's face it : writing synchronous code is a lot easier than writing asynchronous code. I can write asynchronous code easily enough, but it looks so much more cluttered, and/or takes so many more lines of code. I'm a big believer in simplicity.

So, if you want to whip up a simple script, and it doesn't need to win design awards, then I suggest you check out xbytor's cool little trick for using BridgeTalk synchronously. (Search on that page for "Send a synchronous message".)

Nice one, xbytor.

And in case that forum goes down, here's a copy of the most pertinent bit. (See the original thread for more questions, answers and examples.)

First, some utility code :

// Send a synchronous message. The result is returned.
// If a result doesn't come back in 'timeout' seconds, undefined is returned.
BridgeTalk.prototype.sendSynch = function(timeout) {
var self = this;
self.onResult = function(res) {
this.result = res.body;
this.complete = true;
}
self.complete = false;

self.send();

if (timeout) {
for (var i = 0; i < timeout; i++) {
BridgeTalk.pump(); // process any outstanding messages
if (!self.complete) {
$.sleep(1000);
} else {
break;
}
}
}

var res = self.result;
self.result = self.complete = self.onResult = undefined;
return res;
};
// for typos, provide an alias
BridgeTalk.prototype.sendSync = BridgeTalk.prototype.sendSynch;

And now an example of how to use it :

function test() {
var bridgeApp = "bridge-1.0";

if (!BridgeTalk.isRunning(bridgeApp)) {
BridgeTalk.launch(bridgeApp);
}

var bt = new BridgeTalk();
bt.target = bridgeApp;
bt.body = "new Date().toString()";
var res = bt.sendSynch(10);
alert(res);
};

test();

Once again, mega kudos to xbytor. Some coding purists might gag at the internal polling loop, but remember, we're not talking about production scripts released to thousands of users worldwide - we're talking about helping every developer be more productive by writing their own scripts more easily.

Adobe BridgeTalk woes

As just another look at how mind-numbingly idiotic a large proportion of Adobe APIs are, consider this attempt to use BridgeTalk :

var bt = new BridgeTalk();
bt.send();
alert('The message was sent successfully.');

We haven't specified a recipient, and we haven't specified an actual message.

But run the code, and guess what? Apparently, the message was sent successfully.

Especially for such a poorly-documented class as BridgeTalk is, developers rely on meaningful error messages (including input validation error messages) to figure out what's going wrong and how on God's beautiful green earth we're supposed to use the thing.

My original script looked more like this :

function BridgeTalkResultHandler(result)
{
alert('Hurrah! The callback came!' + result.body);
}
var bt = new BridgeTalk();
bt.targetApp = 'illustrator';
bt.body = 'alert("hello");';
bt.onResult = BridgeTalkResultHandler;
bt.send();
alert('The message was sent successfully.');

Translation : get Illustrator to show a message. Just an elementary test.

But plug this into the ExtendScript ToolKit and run it, and all you get is meaningless nonsense.

'The message was sent successfully' is given every time you run it, but Illustrator doesn't budge.

Well, thought I, perhaps Illustrator refuses to interact with the user (e.g. with the alert method) under some circumstances. So I changed the script to app.activeDocument.close(), and I left a document open in Illustrator, so I could see if something happened. Still nothing.

I removed the onResult handler to see if somehow that was causing the problem. Nothing changed.

And I stripped out more and more things, until I got to the code at the start of this article.

The behaviour is identical, for every version of the script.

In other words, BridgeTalk is doing absolutely nothing - not even any validation.

I even tried crazy things like :

bt.targetApp = 'illustraaaaaaaaaaaator';

... but there wasn't any complaint about the non-existent target.

Examples of BridgeTalk are not as plentiful as one might wish for, but my original code looks very much like the few examples I found.

All I can say is : this is typical of Adobe APIs. Poorly documented, counter-intuitive, and requiring wasteful trial-and-error to master.

If it was open-source, or some cheapo product, I'd excuse it. But when they want to charge thousands for their suite of products, you expect a LOT better than this.



Got it working - bt.target is the correct property name, not bt.targetApp. I'm not sure whether I read a bad example, or whether I misread an example, but in either case, my thesis stands - Adobe APIs are a dog to work with except only when everything is working perfectly. A helpful input validation error message, such as 'target property is required', would have saved me valuable time! C'mon Adobe, you know you can do better than this... What's worst is that Microsoft is now actually pretty good at making meaningful error messages (something they were hopeless at ten years ago) - so it's possible to have a moderately-self-documenting API, and that means Adobe has dropped the ball.

Tuesday, May 19, 2009

Automate Adobe Flash using VB.NET or C#

Adobe is very thin as usual on technical details, so I've had to poke around and try different things.

Here's what I've found :

* .NET can communicate directly with Illustrator, but not directly with Flash, nor with BridgeTalk.

* .NET can send javascript to Illustrator, including javascript which utilises BridgeTalk to communicate between Illustrator and Flash.

* So it is possible to automate both Illustrator and Flash using VB.NET/C#/other-.NET-languages, but it requires .NET <-> Illustrator <-> BridgeTalk <-> Flash.

* In VB.NET, you connect to Illustrator as follows :

Dim app As Object = CreateObject("Illustrator.Application")

* To get Illustrator to run arbitrary javascript :

app.DoJavaScript(myJavaScript);

e.g. to get Illustrator to greet us :

app.DoJavaScript("alert('Hello!');");

* To get Illustrator to return a useful value from a script we have sent it, don't use the "return" keyword - just include the return value as the final expression in the script block.

e.g. :

Dim ReturnValueFromIllustrator As Object = app.DoJavaScript("1 + 2;")
MsgBox(ReturnValueFromIllustrator) ' Shows "3"

* The .NET <-> Illustrator leg is synchronous, even though the Illustrator <-> BridgeTalk <-> Flash legs are ASYNCHRONOUS. (Yeah - just to make life more interesting.)

Hmmm - in short, I wouldn't say it's "easy", but it is possible.

Maybe Adobe will enter the 21st century sometime and really make all their apps easily cross-scriptable, and their scripting engines a lot more robust. Until then...

... your brother in the pain of scripting Adobe products.



P.S. If you're using C#, or if you're using VB.NET and want a better Intellisense experience (and better runtime performance, and better compile-time checking) than just using VB.NET's CreateObject method, add a reference to the "Adobe Illustrator CS4 Type Library". Once you've done that, you can connect to Adobe Illustrator as simply as "Dim app As New Illustrator.Application" (or in C#, "Illustrator.Application app = new Illustrator.Application();").

Thursday, May 14, 2009

Cross-scripting Adobe Illustrator and Adobe Flash

21st century, expensive products, same company makes them all, and sells them all in the same package.

You'd think they'd play happily together.

And Adobe documentation here and there hints that they do.

But finding out how is mighty difficult.

You fire up the ExtendScript Toolkit, and the documentation makes it sound like you can do almost anything from there.

Play around with Adobe Illustrator scripting in the ExtendScript Toolkit - sweet!

But now try to script Adobe Flash...

... oops, um, well, there doesn't really seem to be a way to do it.

Does Adobe document that you can't? Well, not that I've managed to find.

In fact, one other commentator stopped short of giving a definitive answer that Flash isn't supported by the ExtendScript Toolkit, and instead just said that it seems it isn't.

C'mon Adobe, give us clear, informative, unambiguous documentation!

In fact, if you put "#target flash" in a script in the ExtendScript Toolkit, and try to run it, it actually launches Flash, and then tells you that it failed to connect to the Flash scripting engine. What the???

An Adobe brochure about Flash CS3 claims that it is now compatible with ExtendScript, but frankly, I don't see how, and we're now up to CS4!!! ("Flash CS3 also adds ExtendScript and BridgeTalk support for cross-application scripting.")

So after many hours of wasted time, I've figured out how to make Illustrator and Flash sing to each other.

I'm not impressed by the obtuse steps required.

And I'm not impressed by the extremely non-definitive documentation on Adobe's part.

Here's how you do it :

1) Your Adobe Illustrator scripts will be written using the ExtendScript language, whereas your Adobe Flash scripts must be written using a language called JSFL. Both languages are javascript-based, and have many similarities, but they also have many differences. (e.g. native filesystem access in ExtendScript is done via the File class, whereas in JSFL it's done via the FLfile class. Hello, any reason for that, anybody?)

2) Adobe Bridge is not Adobe BridgeTalk. The names are very similar, but the use is very different, and unfortunately, the difference is not clearly articulated in Adobe docs.

Adobe Bridge is a moderately useless file cataloging and file browsing tool.

In contrast, Adobe BridgeTalk is a simple but powerful inter-process communication tool.

So, we're going to use Adobe BridgeTalk to communicate between Illustrator and Flash.

3) It is very hard to find Adobe BridgeTalk documentation. (e.g. Google-ing for "adobe bridgetalk reference" is close to useless.)

I suspect this is in part because BridgeTalk is SO simple that there's hardly anything to document, and thus, Adobe is content to let developers learn by example.

And learn by example is how I had to do it.

4) One of the best sources of examples of Adobe BridgeTalk scripting is actually in the Adobe Bridge SDK. Yes, that sounds confusing, but it's true.

Download said Adobe Bridge SDK. It's about 4MB of compressed files. Unzip, and inside are a few interesting PDFs, and a bunch of jsx files showing cool tricks using BridgeTalk (amongst other things).

(I suggest you read the short section "Communicating with Other Applications" in the "Bridge CS4 JavaScript Guide.pdf" that comes in the docs folder in the Adobe Bridge SDK.)

5) In brief, BridgeTalk sends javascript as a string, from one application to another (e.g. Illustrator to Flash), and sends the value of the last-executed-statement in said script back to the calling application. It's a bit obtuse, but it's very powerful.

6) Note that BridgeTalk is ASYNCHRONOUS. And there is no synchronous mode. So you have to be patient with writing the extra code to handle breaking a single synchronous procedure into smaller asynchronous steps.

7) There is an extremely-poorly-documented means of launching ANY application via BridgeTalk. e.g. you can run a Windows batch file, or trigger your source code versioning system, or invoke a custom tool you wrote in a non-Adobe language. You can pretty much run anything you like.

It's based around a function called "system".

As with most Adobe stuff, this "system" function is extremely poorly documented.

The only way I've found to use it is to call app.system('command-line.exe bla bla'); from within Bridge.

So - get this - if you want to fire up a useful external program (e.g. source code control) from within a script that is automating Illustrator or Flash, you need to use BridgeTalk to send a script to Bridge, and the script you send to Bridge needs to include the app.system(<command-line>) invocation. Talk about convoluted!

But at least it works.

Oh - and app.system(...) is SYNCHRONOUS, unlike BridgeTalk which is ASYNCHRONOUS. Just to make sure your neurons are really getting a workout...

8) If you've understood everything to this point, and want a cool trick to make it much easier to generate lengthy scripts that get shared between different Adobe products, you can use the toSource() function, as described by Justin of Ajar Productions in an article entitled "Easy (and Readable) ExtendScript to JSFL Workflow".

Remember : JSFL = scripting the Flash* development ("authoring") environment, whilst ExtendScript is used e.g. for Illustrator. Justin's article covers writing an ExtendScript for Adobe Illustrator, that also uses BridgeTalk to get Flash to do some things, yet all within a single script file, and without much messy code. Very nice work, Justin!

(* Technically, JSFL is used for a few other products, not just Flash, but for my present purposes, it is accurate enough to say JSFL = scripting the Flash development environment.)



Conclusion?

Adobe makes things horribly painful to learn.

Oh - and to use. e.g. obtuse error messages.

But despite the pain along the way, you can actually accomplish a huge amount. If you have spare hair and a high pain threshold, you might find (as I am finding) that ExtendScript, JSFL and BridgeTalk are extremely rewarding tools to be able to use.