AppleScript InDesign - Miscellaneous


Indesign CS 2 | Indesign CS 1 | Indesign ver. 2 | Miscellaneous |





Index






Object reference - InDesign


This thread was posted on the Adobe Indesign Scripting forum on Dec 3, 2000.

I think this is an important thread, so I decide (with the agreement of Shane, Ole and Dave) to publish it.



Shane Stanley - 08:24pm Dec 3, 2000


Excuse the long posting, but this is an issue that seems to cause problems, and not just here. Hopefully this explanation will help clarify things, and not muddy them further. (And although it’s based on the facts, it’s not unreasonable to categorise it as just my opinion.)



References, the object reference property, and InDesign.

When to use the "object reference" property is a common source of confusion, not just in InDesign. Knowing why InDesign and other scriptable applications work the way they do can help clear it up.
First, consider a page item. If we say

page item 1
or
set myItem to page item 1

it’s fairly clear that we want a reference to the item -- returning the item itself wouldn’t make sense.

So using :

set myItem to page item 1

is the same as using:

set myItem to object reference of page item 1


And if you look at what’s returned, you get something like

"text frame id 97 of page id 107 of spread id 102 of document "Untitled-1" of application "InDesign 1.5".

Because this reference refers to the item by its unique id, it’s a robust reference; we can move the item to another page or create other items, and the reference still holds (the references to page and spread don’t matter in this case).
If we look in the dictionary, we can see there are three other ways of referring to a page item: "by numeric index, by ID, satisfying a test".
Numeric index is fragile -- it can change as soon as we create another item or move the item to another spread. Whether a page item satisfies a test can also change. Only using the id is 100% reliable, and that’s what the object reference uses. (We can also refer to them by tag, but tags aren’t necessarily unique.)
If we look at, say, swatches or styles, much the same holds true. Names aren’t necessarily unique, and object references are again based on ids. Here, the real advantage of using references is that they can save a lot of typing when you have to pass them as references -- they also avoid the trial and error often involved in building a reference in the correct form for its context.
But when we look at text classes, things are different. If we ask for "character 1 of parent story of text frame 1", there are two possible ways for an application to react: one is to return an object reference like all the other classes, and the other is to return the actual contents of the character.
InDesign 1.5 (and most scriptable applications) choose the latter, and for reasons that will hopefully become obvious.
Let’s say we want to deal with the first character of a story. You might be tempted to do something like this (assuming we’re already targetted at the story):

set myChar to object reference of character 1
tell myChar
   set font to "Helvetica" set point size to 72
end tell

But in reality, this dereferencing is unnecessary -- you can be more direct:

tell character 1
set font to "Helvetica"
   set point size to 72
end tell

You can see that InDesign understands that when we used "character 1" as a target, we obviously meant it in the sense of a reference -- it wouldn’t make sense any other way, so there’s no need to explicitly say "tell object reference of character 1". We gained nothing from using the reference in the first case, although it did no harm.
Now let’s look at an example using a paragraph. We could use:

set myPar to object reference of paragraph 3
tell myPar delete word 2
   set point size to 12
end tell

or we could use:

tell paragraph 3
   delete word 2
   set point size to 12
end tell

Here the second method has a big advantage: it does what we want. In the first case, you will find that not only is the third paragraph bold, but so too are one or more characters of the fourth paragraph.
The reason becomes clear when you look at a typical text object reference: it will be something like:

"text from character 408 to character 650 of text frame id 97 of page id 78 of spread id 73 of document "Untitled-1" of application "InDesign 1.5"


Unlike the reference to page items or swatches, text references have to rely on the position of the text in the text flow or story -- they can’t use a unique id because bits of text don’t (and obviously can’t) have a unique id. And as characters are added or removed, any reference by position is a moving target.
So the reason for using object references with page items and colors and the like -- that they are robust methods of reference -- falls down with text classes, where object reference becomes a more fragile method.
In the case of a character, it’s just as easy to refer by offset -- "character 36". In this case, an object reference will give you much the same. But once you’re using words, lines, paragraphs or text style ranges, the object reference becomes very fragile -- it needs to be used with care. More to the point, it carries no advantage to outweigh this risk -- it’s just as easy, if not easier, to simply say "tell paragraph 3".
And because, therefore, we rarely need to get an object reference, but often want to get the contents of a text item, it makes sense for applications to return the contents rather than the reference by default.
So why do we need the object reference property of text classes at all? Apart, perhaps, from some esoteric cases, we really only need it when we have no alternative -- when we have no direct reference. This happens sometimes when we are using a filter or whose clause.
For example, suppose you want to do something to every paragraph of a story that begins with a particular character. You might use something like this:

tell application "InDesign 1.5"
tell document 1
      set myColor to swatch "Test"
      tell parent story of text frame 1
         set fill color of paragraphs ¬
            whose contents begins with "#" to myColor
      end tell
   end tell
end tell


Here we use a reference for the swatch, and use the whose clause to address the paragraphs.
But suppose instead we want to find these paragraphs and then check the second word of them, before we decide what to do to them. In this case, it might make sense to get object references to the paragraphs simply because we don’t have any obvious alternative -- iterating through hundreds of paragraphs to find a handful is slow, paragraphs don’t have an index property we can use, and the inherent fragility of object references is in this case a fair trade-off for extra speed. (If you’re not actually adding or deleting characters, there is no problem; if you are, sometimes working from last to first is all that’s needed to avoid problems.) So we might do something like this:

tell application "InDesign 1.5"
   tell document 1
      tell parent story of text frame 1
      set myParas to (object reference of paragraphs whose contents begins with "#") as list
      repeat with i from (count of myParas) to 1 by -1
         if fill color of word 2 of (item i of myParas) = myColor then
            tell item i of myParas ...

Even in this case, we could have used the offset property instead:


tell application "InDesign 1.5"
   tell document 1
      tell parent story of text frame 1
      set myOffsets to (offset of paragraphs whose contents begins with "#") as list
      repeat with i from (count of myOffsets) to 1 by -1
         if fill color of word 2 of paragraph 1 of character (item i of myOffsets) = myColor then
            tell paragraph 1 of character (item i of myOffsets) ...


That’s starting to get harder to read, although it has the advantage of not causing problems if we start deleting bits of paragraphs.
Where the object reference would come into its own would be if we wanted to get a list of every paragraph of every story that met a certain criterion -- in this case, the object reference would have a clear advantage because it also tells us what story the blocks of text are in. Unfortunately, though, such whose clauses are not yet supported by InDesign.
The short version: Object references to non-text objects are a useful, reliable mechanism. They are returned automatically, so you never have to use the "object reference" property with them. Object references to text objects are fragile, and generally serve no useful purpose. Their use may make sense in some whose clauses, but even then they need to be used with care. And that’s why the decision was made to have text objects return their contents rather than a reference.




Shane


------------------------------------------------------------------------
Olav Kvern - 10:48am Dec 5, 2000 Pacific

First, I want to thank Shane for his awesome post. Great job!
And then, of course, I want to quibble a bit.<g>
re: "Object references to text objects are fragile, and generally serve no useful purpose."
The main time I use object references to text objects--apart from filter clauses-- is when I want to assign a text object to a variable. For example:

set myWord to word 3 of paragraph 1 of story 1 of active document

Would assign a string to myWord, so I use:

set myWord to object reference of word 3 of paragraph 1 of story 1 of active document

I do this because I might need to set some property of myWord-- if myWord is a string, attempting to set a property value will result in an error. In my opinion, if you want to do very much with a text object, you've *got* to assign it to a variable.

re: "Where the object reference would come into its own would be if we wanted to get a list of every paragraph of every story that met a certain criterion -- in this case, the object reference would have a clear advantage because it also tells us what story the blocks of text are in. Unfortunately, though, such whose clauses are not yet supported by InDesign."
As far as I know, you cannot do this in *any* application, not just InDesign, because you're using a list as the input to a filter clause. According to the AppleScript Language Guide, you cannot use a filter clause on a list. If this has changed, I rejoice!

Thanks,


Ole


------------------------------------------------------------------------
Shane Stanley - 01:56pm Dec 5, 2000 Pacific

> re: "Object references to text objects are fragile, and generally serve no
> useful purpose."
> The main time I use object references to text objects--apart from filter
> clauses--is when I want to assign a text object to a variable. For example:
> set myWord to word 3 of paragraph 1 of story 1 of active document
>
> Would assign a string to myWord, so I use:
> set myWord to object reference of word 3 of paragraph 1 of story 1 of
> active document
>
> I do this because I might need to set some property of myWord--if myWord is a
> string, attempting to set a property value will result in an error. In my
> opinion, if you want to do very much with a text object, you've *got* to
> assign it to a variable.


I think you're trying to work around a problem that just isn't there. You might _like_ to assign a text object to a variable, but it's nearly always unnecessary. In the above example, if you need to set some property, you could just say:


tell word 3 of paragraph 1 of story 1 of active document
set...

or

set fill color of word 3 of paragraph 1 of story 1 of active document to ...

Interposing a reference gains you nothing -- it just costs you a slither of time and a few bytes of memory. And you risk the problem
of the reference being out of date.
I'm scratching to find an example where you can't do it this way. So I have to ask: what's the point of using a variable?

> re: "Where the object reference would come into its own would be if we wanted
> to get a list of every paragraph of every story that met a certain criterion
> --
> in this case, the object reference would have a clear advantage because it
> also tells us what story the blocks of text are in. Unfortunately, though,
> such whose clauses are not yet supported by InDesign."
>
> As far as I know, you cannot do this in *any* application, not just InDesign,
> because you're using a list as the input to a filter clause. According to the
> AppleScript Language Guide, you cannot use a filter clause on a list. If this
> has changed, I rejoice!

I do it in QuarkXPress all the time, but that doesn't necessarily contradict what you see in ASLG. Take a typical example:

tell application "QuarkXPress 4.11"
   tell document 1
      object reference of every paragraph of every story whose name of style sheet is "*Intro"
   end tell
end tell


I don't know that this is really filtering on a list any more than if the "of every story" part was cut out. I suspect it's more just a matter of fuller implementation. I do know that it works and it's good ;-)


Shane


------------------------------------------------------------------------
Dave Saunders - 02:07pm Dec 5, 2000 Pacific


I almost always use a variable for the paragraph. Usually, I do:

set myPara to object reference of paragraph 1 of character 1 of selection

or sometimes I'll go for the story and get the paragraph index (using the snappy subroutine you provided me.
But I rarely use variables to hold references within the paragraph.
I can see how it might make the syntax easier to read in VB, though, and were I in Ole's shoes and needing to produce stuff in both languages, I can see how I might have a standard way of doing things that works in both and not think to much about which is best in either.
Even now, I still do "PageMaker" things in my scripts that turn out to be unnecessary. For instance, in my PageMaker scripts, it was essential that the insertion point be at the start of the paragraph when a paragraph style script is called. In my InDesign scripts, this is not necessary, as long as it's somewhere in the paragraph, yet some of my scripts move it to the start, anyway.




Dave



------------------------------------------------------------------------
Olav Kvern - 02:41pm Dec 5, 2000 Pacific


Shane asked: "I'm scratching to find an example where you can't do it this way. So I have to ask: what's the point of using a variable?"

Um...convenience, mostly--it's a bother typing or copying the complete object reference every time you want to use it? Because it's good programming practice?<g>

re: "object reference of every paragraph of every story whose name of style sheet is "*Intro""


Odd. I would think that "every story" would be a list, and that this line would not work because of AppleScript's limitations on filter clauses. I'll take a look (have to find a copy of QuarkXPress--is it still being sold?<g>).
Thanks,


Ole


------------------------------------------------------------------------
Shane Stanley - 02:48pm Dec 5, 2000 Pacific

> I can see how it might make the syntax easier to read in VB, though, and were
> I in Ole's shoes and needing to produce stuff in both languages, I can see how
> I might have a standard way of doing things that works in both and not think
> too much about which is best in either.

Yes, I can certainly see that, and I don't envy anyone the job of having a foot in each camp.
I guess I'm just trying to explain that the frustrations that Ole and others have expressed with object references and text are, to a degree, a result of the way they are doing things, which in turn is based on the way they do or have done things elsewhere. If you do things the way AS was designed, the frustrations largely disappear. I think that's true of most languages.
(Historical tidbit: The first scriptable version of QXP had no object reference property for text classes. It wasn't until people started playing around with complex whose clauses that its desirablbility was realised.)


Shane


------------------------------------------------------------------------
Shane Stanley - 03:20pm Dec 5, 2000 Pacific

> Um...convenience, mostly--it's a bother typing or copying the complete object
> reference every time you want to use it?


So you type:

set myWord to object reference of word 3 of paragraph 1 of story 1 of active document
tell myWord...

instead of:

tell word 3 of paragraph 1 of story 1 of active document


This is convenience? ;-)
OK, I take your point if you want to refer to it several times. But I'd counter by saying that because of the inherent fragility of a text reference, you're more likely to get into trouble doing that very thing. So you end up having to re-set the reference, and there goes your convenience (plus a few milliseconds). Because of this, using the full statement might even be called good programming practice ;-)

> Because it's good programming practice?<g>

But what's good programming practice in one language is not necessarily good programming practice in another. AppleScript is different because it was designed for people with no programming background, and it requires a different approach.
I teach AppleScript, and the hardest people to teach are those with some programming experience. That's because they have firm views on how things should work, and they get frustrated when it doesn't happen how they think it should. It's hard to get them to go with the flow.
(And I go through much the same thing every time I sit down and think, "I really must learn REALBasic.")
By the same token, it's quite easy to teach to people who have programmed in a lot of languages. They understand that you don't try to write C in perl or Fortran in C++. They've been through the whole process before. (And I envy them enormously.)


> re: "object reference of every paragraph of every story whose name of style
> sheet is "*Intro""
>
> Odd. I would think that "every story" would be a list, and that this line
> would not work because of AppleScript's limitations on filter clauses. I'll
> take a look

That's from a script I actually use; I can give you plenty of other examples. They are very useful, as are constructions like:

set color of characters -3 thru -2 of lines 2 thru 6 of every story to "Sundays"

(IMO, the power of QXP's whose clauses is what makes many of its quirks bearable.)


> (have to find a copy of QuarkXPress--is it still being sold?<g>).


Depending on what happens with ID scripting, I might be able to offer you a really good deal ;-)


Shane

------------------------------------------------------------------------
Olav Kvern - 04:01pm Dec 5, 2000 Pacific


Shane wrote: "(And I go through much the same thing every time I sit down and think, "I really must learn REALBasic.")"

RealBasic is lots of fun. But I'm hoping that they improve the AppleScript support a great deal--right now, you can only exchange strings with embedded AppleScripts (not objects) and you can't use filter clauses in your scripts.

re: "By the same token, it's quite easy to teach to people who have programmed in a lot of languages. They understand that you don't try to write C in perl or Fortran in C++. They've been through the whole process before."
I know a few (mostly AppleScript, Basic, C, C++, and PostScript)--but that just means I think of programming in a more abstract way and then map those concepts onto a specific language. This means that I'm still likely to grouse about things I don't like (why doesn't C understand the concept of a string, for example?).<g>
re: "OK, I take your point if you want to refer to it several times."
Bingo. The other thing is that I like to use a single variable name to refer to a number of different object references in the course of a single script's execution. At one point, myWord might be word 1 of paragraph 2, for the next it might be word 6 of paragraph 12.
But, yes, I see your point, as well, and will try to eliminate extraneous object references in my example scripts when I'm working with text objects.


Thanks,


Ole






Notes :

Ole Kvern (on March 10, 2003): "For whatever it's worth, I still think AppleScript character x through character y indexed references are too fragile, and I'd *still* rather have a solid object reference to pass off to a handler."



If you want some contact for Shane Stanley: http://scriptingmatters.com/aspro.













Last updated June 23, 2006


Valid HTML 4.01!