Skip navigation
Currently Being Moderated

[JS CS5] difference between CS3 and 5 with paragraph.contents?

Feb 14, 2011 12:54 PM

This is making me crazy. I'm updating some scripts that ran fine in CS3 so that they function in CS5. I've run into an odd (to me) behavior.

 

After running the following in CS4:

 

var myParagraph = myStory.paragraphs.item(myParagraphCounter);

myParagraph.contents = "<LI>"+myParagraph.contents

 

myParagraph.contents returns "<LI> the text of the original paragraph"

 

But after running the same lines in CS5:

 

myParagraph.contents returns only "the text of the original paragraph"

 

What's going on here? This seems like a real basic thing...I think I'm missing something obvious.

 
Replies
  • Currently Being Moderated
    Feb 15, 2011 6:52 AM   in reply to KeithGilbert

    I'm not sure, but replacing contents like that is dangerous.

     

    I would do something like this:

     

    myParagraph.insertionPoints[0].contents = "<LI>";

     

    Does that work as expected?

     

    Harbs

     
    |
    Mark as:
  • Currently Being Moderated
    Feb 15, 2011 6:47 AM   in reply to KeithGilbert

    Hi Keith,

     

    Actually the dangerous part is that you can really mess up the contents. Replacing text contents destroys any non-text items (such as inline objects and tables).

     

    Also, formatting of text will shift by the difference in the number of characters when you replace contents.

     

    HTH,

    Harbs

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 22, 2011 1:54 PM   in reply to Harbs.

    Actually I think there might be a bug here somewhere.  I have just duplicated KeithGilbert's problem.

     

    If you have a paragraph with contents of a certain length, say 6 characters: "Short."

     

    Then you do something like myParagraph.contents="Something Longer.";

     

    What happens is wierd. In your document, it works fine, the paragraph gets longer and all is well in the universe.  However, in your script, you only get the 6 character suffix, in this case you would see something like:

    $.writeln(myParagraph.contents);  // output is: Short.
    myParagraph.contents="Something Longer."; //document shows "Something Longer." as the new, updated paragraph.
    $.writeln(myParagraph.contents);  // output is: onger.
     
    

     

    To me, the mismatch between the script behaviour and the inDesign application behaviour here smells funny.  However, I'm happy to change my approach if there is an easier way to do simple text replacements, I just have been doing it this way without issues until I tried doing multiple things to the same paragraph in a script.

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 22, 2011 10:38 PM   in reply to Brian.T.Tanner

    @Brian:
    Same story with me. It must be a bug!

     

    At first I tried this:

     

    $.writeln(myParagraph.contents);  // output is: Short.
    myParagraph.contents="Something Longer."; //document shows "Something Longer." as the new, updated paragraph.
    mydoc.recompose();
    $.writeln(myParagraph.contents);  // output is still: onger.
    

     

    But to no avail.

     

    What does the trick was: first delete the contents, then apply the new contents

     

    $.writeln(myParagraph.contents);  // output is: Short.
    myParagraph.contents="";
    myParagraph.contents="Something Longer."; //document shows "Something Longer." as the new, updated paragraph.
    $.writeln(myParagraph.contents);  // output is: Something Longer.
    

     

    Tested with InDesign CS5 (7.0.4) on Mac OSX 10.6.8.

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 22, 2011 11:57 PM   in reply to Laubender

    Follow up:

    You can also refresh your variable "myParagraphs" to make this work.

     

    See also the following link (it's in german, but the scripting part should be self-explanatory):

    http://www.hilfdirselbst.ch/gforum/gforum.cgi?post=476391#476391

     

    Uwe

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 23, 2011 4:11 AM   in reply to Laubender

    It's probably because InDesign is caching the Paragraph to prevent from rebuilding it each time you access it. That's a good thing!

     

    The fact that changing the contents does not trigger a refresh is probably a bug, but replacing text contents is not usually a wise course of action no matter...

     

    Harbs

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 23, 2011 7:14 AM   in reply to KeithGilbert

    Hi all,

     

    From the strict perspective of the story “plain text” contents, the statement:

     

    myParagraph.contents = "XXX" + myParagraph.contents;

     

    should lead to a correct output in the DOM (although Harbs' solution is definitely the right way, for many reasons.)

    For my part I cannot reproduce the bug—neither in CS5 nor in CS5.5—but if it happens that the text is not accordingly updated in InDesign, I think this should be reported as a bug.

     

    However, the code above is a problem in the perspective of the JavaScript object, myParagraph, which actually is a Text specifier. We need to keep in mind that myParagraph is superficially encoded as a path in the DOM, and internally resolved, each time we send a command, as a range of character indices within the parent story. To realize this, let's study the following code:

     

    // Declares myParagraph, e.g.:

    // ---

    var myParagraph = app.activeDocument.stories[0].paragraphs[1];

     

    // Displays the 'unresolved' path:

    // ---

    alert( myParagraph.toSpecifier() );

    // => sth like: /document[@id=1]/story[0]/paragraph[1]

     

    // Resolves myParagraph *now* and displays the underlying path:

    // ---

    alert( myParagraph.getElements()[0].toSpecifier() );

    // => sth like: (.../character[4] to .../character[7])

     

    The problem in directly affecting the myParagraph.contents property is that this mutely resolves the specifier at the moment we are sending the command, then the character range is replaced by something different—a string that may contain more characters than the allocated range, a string that may ou may not contain multiple "\r", etc.—while the specifier itself, myParagraph, is not intrinsically updated in the sense that it still points out to the original, 'unresolved', path. Under some circumstances this may lead to very very weird issues.

     

    Another point is that we have no clue about how and when the contents property is internally updated. There are many reasons to suppose that a cache is used, which may backup the string in the JS object just after we read the text. Anyway, I don't think that the = operator works here as we could expect to. A statement like: myParagraph.contents = "XXX" + myParagraph.contents first evaluates the right-sided value—which resolves the specifier and 'hits' the contents,—then a command is sent to ID that changes the text but does not seem to re-affect the contents property of the JS object:

     

    var myParagraph = app.activeDocument.stories[0].paragraphs[1];

     

    // Changes the contents (the wrong way)

    myParagraph.contents = "XXX" + myParagraph.contents;

     

    // Attempt to display the contents property

    alert( myParagraph.contents );

    // => you will generaly obtain an out-of-sync string

     

    Interestingly, you will also observe that myParagraph.index is out of sync after the change. The character index has been shifted by "XXX".length, which should not be the case if the properties of the Paragraph entity were properly updated.

     

    But once again we have to remember that the Paragraph object is a fake entity—basically a Text object—that addresses an index range within the story. So each time we change the story contents upstream of the paragraph or at the paragraph level, we need to resolve again the original specifier—provided that it is still meaningful—if we want to access to fresh properties. We can do that through myParagraph = myParagraph.getElements()[0] since this recalculates the Text offsets from the original path, /document[@id=1]/story[0]/paragraph[1], but I consider this is a bad practice in this particular case, because myParagraph is then attached to an immutable character range.

     

    A better way to quickly re-resolve any Text entity without re-affecting the specifier is to use the underestimated .texts[0] shortcut:

     

    var myParagraph = app.activeDocument.stories[0].paragraphs[1];

     

    // Changes the contents (the wrong way)

    myParagraph.contents = "XXXX" + myParagraph.contents;

     

    // Accessing the property

    alert( myParagraph.contents );            // WRONG (out of sync)

    alert( myParagraph.texts[0].contents );    // CORRECT

     

    The last line works because the final specifier, myParagraph.texts[0], needs the parent location, myParagraph, to be resolved first. Therefore, the paragraph scope is recalculated and all is fine.

     

    @+

    Marc

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 23, 2011 7:23 AM   in reply to Marc Autret

    Super interesting contribution Marc. Thank you for sharing your obviously advanced understanding of way the scripting environment interacts with the document.  Only question is: how do you know these things and where can I learn more about them? I am a computer scientist now building some specialized software for creating documents automatically from templates, and I will be using inDesign scripting to do a lot of the heavy lifting for template integration, but I'm piecing how everything works together from scripting tutorials and guides from Adobe and from Grant Gamble's book. Are there better resources to learn the finer details from?  I ran into this weirdness with paragraph.contents and set aside a few hours to isolate the issue and make sure I wasn't crazy. Would be nice if there was a way to figure out the right thing other than bugging people here in the forums and trial-and-error based on the not-so-great object model viewer in the toolkit.  Any suggestions for resources?

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 23, 2011 7:33 AM   in reply to Brian.T.Tanner
     
    |
    Mark as:
  • John Hawkinson
    5,572 posts
    Jun 25, 2009
    Currently Being Moderated
    Nov 23, 2011 10:15 AM   in reply to Brian.T.Tanner

    Brian:

    Are there better resources to learn the finer details from?  I ran into this weirdness with paragraph.contents and set aside a few hours to isolate the issue and make sure I wasn't crazy....

    Any suggestions for resources?

    Harbs gave you the shotgun, but in this specific case, there's really no substitute for a pair of blog posts Marc himself wrote about specifiers and their resoluion: See "On ‘everyItem()’ – Part 1" http://indiscripts.com/post/2010/06/on-everyitem-part-1 and of course keep reading to Part 2, which is the juicier half anyhow if I remember correctly.

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 23, 2011 1:26 PM   in reply to John Hawkinson

    @John

    A big thank you for mentioning that article. I am glad to see that it is enjoyed by experienced scripters

     

    @Brian

    > Only question is: how do you know these things and where can I learn more about them?

    ‘Knowledge’ is purely empirical in all these areas, which Adobe has so poorly documented. IMHO the best sources of information are living here, on this forum! I learned a lot from them.

     

    @+

    Marc

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 23, 2011 2:06 PM   in reply to Brian.T.Tanner

    Brian.T.Tanner wrote:

    [..] Would be nice if there was a way to figure out the right thing other than bugging people here in the forums ...

     

    Please bug us. We learn from each other, as there is virtually no in-depth documentation on the inner workings of Javascript in InDesign.

     

     

    Brian.T.Tanner wrote:

     

    ... and trial-and-error based on the not-so-great object model viewer in the toolkit.  Any suggestions for resources?

     

    Insert Loud (COUGH) Here.

     
    |
    Mark as:
  • Currently Being Moderated
    Apr 2, 2012 7:09 AM   in reply to Marc Autret


    Hi Folks.  I'm back to inDesign scripting after many months away.

     

    Reviewing this thread again has been very helpful.  I'd like to pose an additional question because you all might have a simple answer.  I have paragraphs with specially identified text in them, like:

         The best person to ask is the <bossTitle>.
    

     

    To replace these, I do something very simple like:

            thisParagraph.contents = thisParagraph.contents.replace("<bossTitle>", "Big Boss Man");
    

     

    This mostly works fine.  However, I've just noticed that it does something funny with formatting. If I have two paragraphs, and the one that follows the replacement is using a header style like:

         The best person to ask is the <supervisorTitle>.
         Title Of Important Section
    

     

    When the contents are replaced, the paragraph style of "Title Of Important Section" gets changed.  I presume this has to do with some of the issues we have talked about in the thread previously.

     

    So my question is this: If I want to replace tagged text like <bossTitle> within a paragraph, what is the "right" way to do it? Is it a 1 or 2-liner, or does it get very messy?

     

    In the meantime I'll go trawl through the forums again, but I pretty sure I came up empty last year when I was looking for this.

     

    Thanks in advance.

     
    |
    Mark as:
  • Currently Being Moderated
    Apr 2, 2012 8:05 AM   in reply to Brian.T.Tanner

    Hi Brian,

     

    If you don't use the usual changeText() / changeGrep() methods, I suggest you specifically target the character range of the found text:

     

    var mySearch = "<bossTitle>",
        myReplace = "Big Boss Man",
        p = thisParagraph.texts[0].contents.indexOf(mySearch),
        q = 0 <= p && (-1 + p + mySearch.length);
     
    if( q )
        thisParagraph.characters.itemByRange(p, q).texts[0].contents = myReplace;
    else
        alert( mySearch + " not found!" );
    

     

    @+

    Marc

     
    |
    Mark as:
  • Currently Being Moderated
    Apr 2, 2012 8:59 AM   in reply to Marc Autret

    Perfect.  Works beautifully and does not screw up the formatting of the following paragraph.

     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (1)

Answers + Points = Status

  • 10 points awarded for Correct Answers
  • 5 points awarded for Helpful Answers
  • 10,000+ points
  • 1,001-10,000 points
  • 501-1,000 points
  • 5-500 points