Every scripter, at some time or other, has been faced with performing some particular function with no clue how to do it. The dictionary, written cryptic-style, doesn’t help. They can’t find any reference on any BBS to their specific issue, and they can find no examples anywhere addressing their question. The technique I’m going to outline in this article is how I figure out for myself how to script any scriptable application. For our purposes here I will use QuarkXPress as an example but these principles can be applied to most, if not all, scriptable applications. The way to control an application is through setting its objects’ properties, therefore, creating an object and using AppleScript to return its properties is the best way to quickly figure out for ourselves how to script the application.
First let me make clear, this is not a “how-to” for scripting QuarkXPress. A number of far more qualified folks have written extensively on this subject already. Shirley Hopkins, Danny Goodman and Sal Soghoian, to name just a few. The goal of this piece will be to help you broaden your resourcefulness in finding answers on your own, without relying on finding or not finding, as the case may be, exact answers to your questions.
There are essentially two types of properties in any application; what I will call “Public” and “Private” property. Private property is those properties that the application reserves exclusively for its own use in the same way your home is your private property for your own use. Public property, much like a park, can be used by anyone, given certain rules are followed. This article will help you determine how to use these “public” properties and what rules govern their use.
Using Object Properties
So let’s begin. First, with an open document in QuarkXPress (or your favorite application), you can enter the following code in Script Editor:
tell application "QuarkXPressâ�??¢"
tell document 1
return properties of picture box 1 of page 1
end tell
end tell
The returned result looks like this:
{best type:null, class:picture box, default type:null, object reference:generic box 2 of spread 1 of document "bw_test.qxd" of application "QuarkXPressâ�??¢", anchored:false, background trap:default, color:null, content:picture content, index:1, locked:false, name:"", rotation:"0�?°", runaround:none runaround, selected:false, shade:"100%", box shape:rectangular, suppress printing:false, uniqueID:-2.147483646E+9, blend:{style:solid}, bounds:{"9.574\"","0.375\"","10.625\"","4.037\""}, corner radius:null, flipped horizontal:false, flipped vertical:false, frame:{color:color spec "Black" of document "bw_test.qxd" of application "QuarkXPressâ�??¢", gap color: null, gap shade:"100%", inside trap: default, outside trap:default, shade:"100%", style:solid, width:"0 pt"}, skew:"0�?°", text outset:"1 pt"}
QuarkXPress will return a record of all of the properties of “picture box 1”. This record tells us everything we need to know about controlling and manipulating picture boxes (or any other object) via AppleScript. It is important to know, however, which properties in the record are public and which are private. The rule of thumb that I use is that any of these properties that I can manually change within QuarkXPress via some dialog box are public property and can be changed with AppleScript. The properties hi-lited in yellow do not appear in any control in QuarkXPress and therefore are private property (cannot be changed via AppleScript).
Parameter Types
In determining what types of parameters the application requires, you simply look at what type of parameters your application has returned. I can tell from the returned properties pictured belowshown above that QuarkXPress prefers the “bounds” (hi-lited in blue) of a picture box to be a list; that it prefers “suppress printing” (hi-lited in green) to be either true or false (boolean); that it prefers “box shape” (hi-lited in purple) to be a class; and that it prefers “blend” (hi-lited in red) to be a record.
In the case of measurements, however, you can usually pass an integer or real number as well. It is also important to note, in the case of a string parameter, whether the string is enclosed in quotation marks or not. If the returned property’s parameters are not enclosed in quotation marks, the parameter is a class, defined either by the application or the system.
Acceptable Terms or Phrases
The next issue to understand is what terms your application will recognize in the new parameters. This is easily determined by looking at our returned properties and simply noting what terms are used; whether the parameter returned is a boolean (as with “suppress printing”), name (as with “color”) or class (as with “box shape”). To determine what other terms are acceptable, you can either look this property up in the application dictionary or change the property manually in QuarkXPress and run the “return properties” script again.
Potential Gotchas
A couple of potential pitfalls when deciphering the properties record can occur. After a little time, these will become second nature and, in most cases, intuitive, so when you move between applications, you will know how to avoid these pitfalls.
Object References as Parameters
If you ask QuarkXPress to return the color of an object, it will return color spec “the color” of document “my document” of application “QuarkXPress”. A good rule of thumb to follow is: if the parameter will be another object, as in the case of color, the application will return the object reference. You can set a parameter using the object reference but in most cases this is not only unnecessary, but can cause problems when trying to run your script on another document. It is safer and easier to refer to these “objects as parameters” by their name. So in the case of a color in QuarkXPress, simply refer to it as “Red” when using it as a property.
Measurement Units
Another potential gotcha, is when defining measurements. Most applications allow you to change the measurement units in the application or document preferences. If you do not specify the measurement unit in your script (“10 mm” or “1 pt”) and pass a measurement parameter as an integer or real number, the application will use the measurement unit set in the preferences as the default. Again, when running your script on a different document, you will get unexpected, and possibly undesirable, results if that document’s preferences has a different unit of measure specified. It is generally safer to specify the unit of measure in the property parameters.
Returning a Class as String
As explained above, some properties’ parameters are a class. In QuarkXPress “box shape” is one such instance. Let’s say I want to do something to every picture box on a page, but only “rectangular” picture boxes. If I inadvertently coerce the property to a string, I may not get the results I expect. With “box shape” the property I see is ‘rectangular’, but when coerced to a string I find it becomes “PBRC”*.
*This may vary from one version to the next in some applications. QuarkXPress 4 coerces ‘rectangular’ to “PBRC”, while version 5 simply coerces the class ‘rectangular’ to the string “rectangular”. This can be determined by simply returning the property in its default form and then as a string.
Records as Parameters
In some cases, a record is required to define the parameters of a property. These cases follow the same rules as explained in the next paragraph. Say for instance, that I want to change the width of the frame around my picture box. In order to do this, I have to be sure to address the command to the proper object. The width is an object of the frame, which is an object of the picture box, which is an object of the page, etc.
To figure out to whom to address AppleScript commands, it is important to understand the application’s object hierarchy. This follows the same principle when addressing a letter to someone. A city is within a state. A street is within a city. A building is within (on) a street. A room is within a building. The room, the building, the street and the city are all within the state but if you send a letter that does not specify each of these items, the post office will not know how to deliver your letter. It is the same with commands sent to the application. If you fail to include the “address” of each object, the application will not know how to deliver your command. Object hierarchy, though daunting at first, is really very simple and easy to figure out. Ask yourself is the object I want to create or modify an attribute or part of another object? Does this object appear on or within another object? Any object belongs to whatever object it is a part of. Width (in the case of a frame) is an attribute or appears on a frame, therefore it belongs to the frame. A frame is part of a picture box, therefore it belongs to the picture box. A picture box appears on a page, therefore it belongs to the page. A page appears in a document therefore it belongs to the document. A document appears in an application, therefore it belongs to the application. This is a specific example from QuarkXPress, but again, this principle holds in all applications.
tell application "QuarkXPressâ�??¢"
tell document 1
tell page 1
set width of frame of picture box 1 to 2
end tell
end tell
end tell
Manipulating Object Properties
Once I understand the properties associated with each object, what type of parameter is required for each property, and what the acceptable parameters are, I am almost ready to unleash my imagination, empowered by my new-found understanding of my application. However, there are some further issues, an understanding of which, will help make scripting my application even easier.
First, when creating or modifying objects, it is generally a good idea to not try to do too much at once. Setting properties individually is safer than trying to set them all at once. In my experience, QuarkXPress does not respond well, or sometimes at all, when trying to set all of the properties of an object at once. When creating new objects, I generally create the object, a picture box for instance, with the bare minimum properties such as the bounds. I then set each property individually. This is, admittedly slower if I intend to change all of the properties, but it can also avoid potential errors. I am not certain of this, but it may be that when defining multiple properties, the application prefers them in a certain order. Feel free to correct me if you know for certain why this happens.
Second, you only need to specifically set those properties which either have no defaults or whose defaults are not what you want. As in the case of the width of the frame of a picture box, the default is “0” and therefore, if I want a visible frame, I have to specify it.
And last, using object references as much as possible is a very reliable way to refer to an object to be manipulated. This insures that the changes are made to the intended recipient. It also allows for fewer lines of code. If I tell QuarkXPress to set myObject to “object reference of picture box 1 of page 1 of document 1”, I do not need to enclose the commands inside nested “tell” statements (see figurecode below).
tell application "QuarkXPressâ�??¢"
set myObject to object reference of picture box 1 of page 1 of document 1
set width of frame of myObject to 3
end tell
This article has hopefully given you some good tools for figuring out the basics of scripting almost any scriptable application. You should be ready to unleash your imagination to create powerful automation solutions. As stated in the beginning of this article, I have used QuarkXPress to demonstrate the techniques that I use to quickly figure out how to script applications with which I have never worked. These techniques have worked very well for me and I hope they will for you as well.
One last point I will make is that the most valuable resource for any scripter is script code made available by other scripters who may be more advanced or more experienced with a particular application. When looking at others’ code, try to deconstruct, line-by-line if necessary, the concepts behind what is being done, rather than follow the specifics of the code. This is, in my humble opinion, the best way to not only learn how to script, but to improve scripting abilities and expand your repertoire of ideas.