• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
1

Indesign Script - Keep reference of nested pageItem in group while duplicating it

Contributor ,
Jul 06, 2023 Jul 06, 2023

Copy link to clipboard

Copied

I am on InDesign 2023. VisualStudioCode.

 

I have a group of nested textFrames. In code, I track the textFrames by their id. Now I need to duplicate these groups, but still keep track of the textFrame duplicates.

 

Example: Let's say I have a group of pageItems (can contain deeply nested or anchored page items):

  • Rectangle with id 1
  • TextFrame with id 2
  • TextFrame with id 3
  • (and many more)

I need to insert text in TextFrame with id 2. No problem by getting it by id.

 

Now i need to duplicate the wrapping group to a new location. No Problem too. On duplicating, the id of the textFrame inside the group changes and I obviously lose my reference of it.

So what i do is to use the label of the textFrame to store the id as a string and recursively look for that in the duplicated group. This way i can identify the duplicated textFrame and continue using it, but is very slow for large groups and tons of duplicates.

So i was wondering if there's a more efficient way of accomplishing this task to keep a reference of a nested page Item in a group while duplicating it.

Any help appreciated.

TOPICS
Scripting

Views

409

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 06, 2023 Jul 06, 2023

Copy link to clipboard

Copied

A couple of approaches:

1) change the .name of fhe target frame to its id, then reference that name in the new group. You'd need to know the depth of the group. 

2) Hash the indices of the object, ie grpup.pageItems[3].pageItems[1].pageIfems[5], then reconstruct that in the duplicate. That assumes you read the indicies of the target frame as you are making the duplicate. Would help to see your current code snippet. 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 06, 2023 Jul 06, 2023

Copy link to clipboard

Copied

Now that I am in front of a computer, here's a potential approach to get the nested array structure of the frame within its containing group:

 

 

var getNestedStructure = function(itm, parGroup) {
    var pgId = parGroup.id;
    var par = itm;
    var a = [];
    var c = itm.id;
    while (c !== pgId) {
        a.push(par.index); 
        par = par.parent;
        c = par.id;
    }
    return a;
}
//Use example
var arr = getNestedStructure(targetFrame, targetGroup);
var i = arr.length;
var aNewGroup = targetGroup.duplicate();
var dupeFrame = aNewGroup;
while(i--) { 
    dupeFrame = dupeFrame.pageItems[a[i]];
}

 

 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 06, 2023 Jul 06, 2023

Copy link to clipboard

Copied

In addition to Brian's suggestions (actually this ties in with his 2nd point) I would also suggest looking at PageItem.toSpecifier() method. Note that a DOM item's specifier will reflect how you asked for the reference. If you use pageItems.itemByID then the ID should be explicit in the specifier, but if you refer to an item by index then the specifier will reflect that. You can use this to your advantage. In your situation it might be a good approach to refer to the master item by ID, but it's children by index, so that when you duplicate the master item you might be able to reuse all the master items specifier strings and just replace the explicit ID with the duplicate's ID. Then use resolve(newSpecifierString) to get a relative reference to a child of the duplicate item.

- Mark 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jul 06, 2023 Jul 06, 2023

Copy link to clipboard

Copied

Hi all,

 

Interesting topic! I think @brian_p_dts's approach is the way to go, but in its current state it probably won't deal with anchored objects (AOs), since it is assumed that the obj.parent.pageItems[obj.index] scheme is idempotent. Yet a particular issue arises with AOs: their parent is a Character. In such a case, you have to go through the parentTextFrames property and find the index by yourself. I just wrote some code (in a hurry!) to illustrate this aspect and how we could face it:

 

https://gist.github.com/indiscripts/c2742505e0e18cf7669d0c9741197d9a

 

There are obviously other potential problems (e.g. Tables and Cells!), it's just a first draft. The function can optionally return a “partial specifier” like "/text-frame[2]/oval[0]/multi-state-object[0]/etc", that you can concatenate after the own specifier of the duplicated group, then call resolve(…) to access the subitem of interest.

 

Hope that helps.

 

Best,

Marc

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 06, 2023 Jul 06, 2023

Copy link to clipboard

Copied

Thanks @Marc Autret, that's what I was thinking, but with your usual next-level style! 🙂

 

I figured that since OP already has references to the items in the original group, then those specifiers could be leveraged for the duplicated group. I will be interested to hear how they go.

- Mark

 

P.S. @patMorita, if you haven't already, I recommend reading Marc's articles On everyItem() part 1 and part 2 which discuss DOM specifiers.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 06, 2023 Jul 06, 2023

Copy link to clipboard

Copied

Thank you all for these valuable approaches. I will write some examples based on them to test various cases. Especially the highly (cause User deliverd) dynamic nested groups might become an interesting task.

I will keep this discussion updated.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 07, 2023 Jul 07, 2023

Copy link to clipboard

Copied

@patMorita said:

"So what i do is to use the label of the textFrame to store the id as a string and recursively look for that in the duplicated group."

 

Hi,

I wonder if it would be faster to work with the group.allPageItems array.

You'll go through that array and test for the id of the text frame.

If found, note the index of the item as it is stored in the allPageItems array.

 

Then you should be able to find the duplicated item instantly with:

dupGroup.allPageItems[ notedIndex ]

 

Note: This method will fail with items that are stored in MSO states that are not active. Or also with items in button states that are not active.

 

In my example code below I walked through the array with a for loop.

This is a naive solution, because It will visit the array from start to finish. We could think of more clever solutions here.

Attached is the document.

 

// Two groups selected
// The id of the text frame in group g is: 245


// Just an example:
// app.documents[0].pageItems.itemByID( 245 ).getElements()[0] would return a text frame in group g.

var id = 245 ;


// Both groups are selected.
// The first selected group is the "original"
// The second selected group its "duplicate"

// The dup group was just added to the selection of the first group using a click while holding the shift key.

var g = app.selection[0];
var dup = app.selection[1];

var a = g.allPageItems;

for( var n=0; n<a.length; n++ )
{
	if(a[n].id == id )
	{
		app.select( dup.allPageItems[n] );
		break ;
	};
};

 

Regards,
Uwe Laubender
( Adobe Community Expert )

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jul 07, 2023 Jul 07, 2023

Copy link to clipboard

Copied

Hi Uwe,

 

Nice idea—and so much easier to implement 😉 The OP insisted that he was handling “large groups and tons of duplicates”, potentially with deep nested structures, so I was wondering if allPageItems could provide satisfactory performance in such a context (?) We know that using this property is time consuming, as it needs to resolve and create an actual JS array from all sub-components. In the solution proposed by @brian_p_dts and @m1b, we only want to identify a path that links one component to some parent. This involves only a branch of the tree, rather than the entire tree. Hence this is more economical in theory (even if it is much more complicated to do). But maybe, in practiceallPageItems does the trick just fine. Performance tests could answer that question.

 

Best,

Marc

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 07, 2023 Jul 07, 2023

Copy link to clipboard

Copied

LATEST

@Marc Autret said: "I was wondering if allPageItems could provide satisfactory performance in such a context (?)"

 

Well, yes. I agree. But maybe other things are known of items around or, in the best case, inside the text frame.

For example, if there would be an anchored image in the frame. Then one could loop the less larger allGraphics array of the original group. Just throwing in some ideas...

 

Regards,
Uwe Laubender
( Adobe Community Expert )

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines