Friday, October 9, 2009

Display Workflow-Related Meta-Info For Your Users From An InDesign Script


When developing workflow software around Adobe® InDesign® or Adobe® InDesign Server®, as a scripter, you often find yourself attaching 'meta-info' or metadata in some form or shape to various page elements in an InDesign document.

In this blog post, the samples should work with InDesign CS and above on Mac as well as on PC, and I'll use ExtendScript for InDesign for the scripts.

But the information presented here can be applied just as well in AppleScript or VBScript. The syntax is slightly different, but it works equally well.

Meta-info would be any info that is not directly related to the page layout per se, but that needs to be associated with elements of the page layout.

Examples of meta-info you might like to attach to a page item would be things like invoice data, prices, product codes, file names, script labels, XML tags, user names, document history information, destinations, job ticket data...

In many cases, this meta-info can remain hidden from the end-user and not be displayed on the page layout at all - the data is typically picked up by various automated components of your workflow (scripts, applications, plug-ins...) and used to control processes and routing of info and documents.

One of the most popular methods to attach meta-info to InDesign page items is to use the extractLabel and insertLabel methods. These two essentially allow you to 'attach' any number of arbitrary strings to any page item, where you identify each of the strings you want to stash away with a key string.

For example, try the following script.

Open or create a document, select a page item and run the following script. (Be careful: the script has no error checking whatsoever, so it is not user-friendly. You need to select exactly one page item, and with the item selected, you need to run the script via the Scripts Palette).

// Script1.js
var theItem = app.selection[0];
theItem.insertLabel("com.rorohiko.statusquo","Whatever you want tada tada whatever you need tada tada");

Nothing apparent will seem to happen - that's OK. Now run the second script, while keeping the same page item selected:

// Script2.js
var theItem = app.selection[0];
var theData = theItem.extractLabel("com.rorohiko.statusquo");
alert(theData);

This shows how you can attach a string to a page item. I do recommend that you use a globally unique string for the keys, for example, like I did, based on my reversed domain name. That helps ascertain that you don't accidentally use the same key as another scripter.

For example, if your domain name is yourcompany.be, and you want to store, say, a user name, you should write something like:

theItem.insertLabel("be.yourcompany.username","John");

instead of

theItem.insertLabel("username","John");

The latter will also work, of course, but you always run the risk that your end user might try to run two scripts from two different developers.

Imagine she tries to use a script created by yourcompany.be and also another script created by rorohiko.com, and both scripts would use the same key username for slightly different purposes. Lots of weirdness would result.

Better be safe than sorry - so if you use be.yourcompany.username, and I use com.rorohiko.username as the key to insertLabel / extractLabel, there will never be a conflict - are we cool on that?

The string you store in the keyed location can be pretty much anything - I often store a very looong string, for example, with carriage returns separating individual records, and tabs separating individual columns, and then use .split to convert the string in to a table.

const kMyDataKey = "com.rorohiko.keyforimportantdata";
//... more code ...
// Little table of data with columns separated by tabs
// and lines separated by carriage returns
// 12 13 Kris
// 15 17 John
var myImportantData = "12\t13\tKris\r15\t17\tJohn\r";
theItem.insertLabel(kMyDataKey,myImportantData);
//... more code ...
var theData = theItem.extractLabel(kMyDataKey);
theData = theData.split("\r");
for (var recordIdx = 0; recordIdx <>
{
theData[recordIdx] = theData[recordIdx].split("\t");
}
//... more code ...

Or you could encode the data you want to stash into a big XML formatted string - that works really well too - something like:

const kMyDataKey = "com.rorohiko.thecoolKeyForXMLData";
//... more code ...
var myImportantData = "";
myImportantData += "<data>"
myImportantData += "<record>"
myImportantData += "<x>12</x><y>13</y><name>Kris</name>";
myImportantData += "</record>"
myImportantData += "<record>"
myImportantData += "<x>15</x><y>17</y><name>John</name>";
myImportantData += "</record>"
myImportantData += "</data>"
theItem.insertLabel(kMyDataKey,myImportantData);
//... more code ...
var theData = theItem.extractLabel(kMyDataKey);
// Use the built-in XML parser to parse the XML data back into its components
//... more code ...

Or any other scheme you can come up with to convert the data you want to store into a string- you can do things like ASCIIEncode or ASCII85 the data if you want to store binary info in a pure ASCII string - your imagination is the limit.

Now, what if you wanted some of this data to be visible to the end-user?

For example, the end-user could use one of your scripts to tag individual page items, maybe to let your automated workflow 'see' where it needs to insert a header and where it needs to put an image, and so on.

Wouldn't it be great if you could give the user some visual feedback in this regard?

That's where our APID ToolAssistant comes in. APID ToolAssistant was conceived as a 'scripters toolkit' and it offers a whole range of functionality that could help the serious scripter.

You might have heard about APID ToolAssistant, because it offers a very 'fine grained' event model that allows scripts to be triggered by user interaction with the document - but APID ToolAssistant has a lot more to offer than just some event-driven facilities.

One of the coolest features in the APID ToolAssistant toolkit is that it offers the scripter a way to display any 'meta-info' next to a page item. To display this meta-info, APID ToolAssistant can add little, non-printing 'labels' (also known as adornments) to any page item, and in those labels, you can display anything you like: some text, or a PNG image like a logo or an icon, all under the control of your script.

These adornments are not page items - they are instead very similar in nature to the selection handles and the overset text indicator on page item frames. They are nothing more than visual cues for the user, and they are not visible in the printed end-result.

The way APID ToolAssistant works its magic is by adding some new methods and attributes to the InDesign object model. There is a pair of methods called setDataStore / getDataStore which behave very similar in many respects to how the built-in insertLabel / extractLabel work - i.e. you store data identified by a unique key.

But there's a twist - some of the unique keys have a special meaning - they are 'magical'.

If a key starts with $ADORNMENT_ and ends with $, it tells APID ToolAssistant the stored data is something that has to be interpreted as content for a little info-label.

Install APID ToolAssistant 1.0.47 or higher (you can download it from http://www.rorohiko.com/apidtoolassistant ), and enter the following script:

// Script3.js
var theItem = app.selection[0];
theItem.setDataStore("$ADORNMENT_com.rorohiko.sample$","Hello World");

Just like with the first sample scripts you need to select a single page item, and then run the script.

Is that cool or what? (If your APID ToolAssistant has been installed for a while, it might have reverted to unlicensed mode, in which case you'd see 'DEMO: Hello World' in the little info-label).

Now, this is only a simple example - instead of a simple string "Hello World" you can actually provide an array with data to setDataStore, and through various parameter values ask APID ToolAssistant to display the label on the left, right or bottom sides of the frame.

Or you can ask it to change the background color. Or for really fancy stuff, you can display a PNG image instead of text. The possibilities are endless.

To get some ideas about what you could do with this functionality, have a look at what I've done with our FrameReporter tool at http://www.rorohiko.com/framereporter . FrameReporter is really not much more than a very thin layer of code on top of APID ToolAssistant.
For more detailed info, you need to look at the information provided in the Active Page Items Developer toolkit - http://www.rorohiko.com/activepageitemsdeveloper .

The necessary documentation is included in the free demo download - you don't need to purchase the toolkit if all you want to do is use this 'meta-info-label' feature. Check the reference manual to find out about all the variations of the adornment features.

So, keep in mind - if you want to show meta-info to an end-user, all you need is a US$25 license for APID ToolAssistant (to get rid of those 'DEMO:' prefixes added by the unlicensed version), and you can script away!

More info can also be found here:

2 comments:

lavie said...

Thanks, but what do I do for the "Library" object, which has no insertLabel() or extractLabel() methods?

Kris Coppieters said...

Ah - you're out of luck there; if they don't support script labels, they also cannot display these adornments.