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

Duplicate transformed object to a desired location

Community Beginner ,
Feb 15, 2018 Feb 15, 2018

Copy link to clipboard

Copied

Wow... I gotta say... InDesign takes the crown for making the simplest of tasks into a brain-exploding torture...

I have an object, and I want it duplicated. That's it.

In this picture I have three objects and I copy them with a same function. On the right side you can see the result.

Pic01.jpg

Objects without transformation work normal, but the rotated one... It keeps track of original anchor point and transformations, which is understandable.

So it applies my transformations to it's original top-left. But why does geometrical bounds property return the real values?

And how does this scripting language knows what method to use? In all other languages I used, the overloaded functions had to have different parameters.

The first one is array of units and the other one is array of numbers. So, does duplicate([x,y]) copies to [x,y] or by x,y?

Pic02.jpg

TOPICS
Scripting

Views

2.3K

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

correct answers 1 Correct answer

Community Expert , Feb 15, 2018 Feb 15, 2018

Savage.Vs  wrote

… I have an object, and I want it duplicated. That's it.

Hi,

if you use method duplicate() without an argument the duplicate of the item will not move* or be transformed.

*There is one exception, if the original is anchored to an insertion point.

When using the to argument the duplicate is moved relative to the upper left corner of the un-transformed original.

You could compensate for that and compare the position of path points.

original.paths[0].pathPoints[0].anchor compared to dupli

...

Votes

Translate

Translate
Guide ,
Feb 15, 2018 Feb 15, 2018

Copy link to clipboard

Copied

Hi Savage,

The properties geometricBounds and visibleBounds express bounding box coordinates in the “ruler system” only. This system is disconnected from actual coordinate spaces that deal with transformations.

myObj.duplicate([x,y]) positions the dup at location [x,y] in the ruler system.

myObj.duplicate(undefined,[x,y]) shifts the dup by [x,y] relative to the original.

@+

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 Beginner ,
Feb 15, 2018 Feb 15, 2018

Copy link to clipboard

Copied

Thank you, Marc, for your reply.

Can you tell me why my function doesn't work? I do get the correct coordinates in ruler system, but duplicate applies them to (for me) incorrect corner of the object.

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 ,
Feb 15, 2018 Feb 15, 2018

Copy link to clipboard

Copied

Savage.Vs  wrote

… I have an object, and I want it duplicated. That's it.

Hi,

if you use method duplicate() without an argument the duplicate of the item will not move* or be transformed.

*There is one exception, if the original is anchored to an insertion point.

When using the to argument the duplicate is moved relative to the upper left corner of the un-transformed original.

You could compensate for that and compare the position of path points.

original.paths[0].pathPoints[0].anchor compared to duplicate.paths[0].pathPoints[0].anchor and move the duplicate with the second argument of move(), the by argument: duplicate.move( undefined , [xD,yD] ) where D is the difference you see between the desired position and the actual position.

Regards,
Uwe

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 Beginner ,
Feb 15, 2018 Feb 15, 2018

Copy link to clipboard

Copied

Marc did show me the way to differentiate those methods. Before posting here, I tried duplicate(,[x,y]), which obviously didn't work

Didn't know about that undefined moment

I wrote here a bunch of text, reread what you wrote and.... Deleted it since you got me so lost

I need some time to think about that and thinker with it before coming back here

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 Beginner ,
Feb 17, 2018 Feb 17, 2018

Copy link to clipboard

Copied

Ok... So I posted today, but deleted it. I need someone to explain this new moment to me.

I have a master page with 12 objects.

Pic01.jpg

On page one I have 2 text frames.

Pic02.jpg

Now, I need to make copies of those objects on the page one (now there are 2, but it is a variable) as many times as there are objects on master page (now 12 but also variable). And position them just like the objects on master page are.

But look at this for a moment. If I run this code (I copied and simplified my code):

myCurrentPage = app.activeDocument.pages[0];

myNumeratorsPerItem = myCurrentPage.pageItems.length;

myNumerators = [];

myItemsBounds = GetMyItemsBounds();

for(i = 0; i < myCurrentPage.pageItems.length; i++){

  myNumerators.push(myCurrentPage.textFrames);

}

//alert(myNumerators[0].contents + ',' + myNumerators[1].contents);

CopyMyNumerators();

function CopyMyNumerators(){

  for(j = 0; j < 11; j++){ //j = myCurrentItemIndex

  for(i = 0; i < myNumeratorsPerItem; i++){ //i = myCurrentNumeratorIndex

  var myNumeratorsCopy = myNumerators.duplicate(undefined, [myItemsBounds[12 - j - 2][1] - 10, myItemsBounds[12 - j - 2][0] - 10]);

  }

  }

}

function GetMyItemsBounds(){

  var myItemsBounds = [];

  for(i = 0; i < 12; i++){

  myItemsBounds.push(app.activeDocument.masterSpreads[0].pageItems.geometricBounds);

  }

  return myItemsBounds;

}

If I run the code with the line 8 off, I get the result on the left (each smaller text frame is copied twice, one on top of another).

But if I delete the comment and show alert, I get result on the right. How is this possible that this alert on/off can change the result of my code???

Pic3.jpg

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 Beginner ,
Feb 18, 2018 Feb 18, 2018

Copy link to clipboard

Copied

Ok... Now that I have some object I want to duplicate, I use this code. (again edited versions of the code for simplicity)Wooow... Nice... Ever since I started writing this script, I've been doubting myself. I've started going crazy. But now I see that it's not me...

function CopyMyNumerators(){

  for(j = 0; j < myNumberOfItemsPerPage - 1; j++){ //j = myCurrentItemIndex

    for(i = 0; i < myNumeratorsPerItem; i++){ //i = myCurrentNumeratorIndex

      var myDuplicatePositionX = myItemsBounds[myNumberOfItemsPerPage - j - 2][1] - myOriginalsCoordinates[1];

      var myDuplicatePositionY = myItemsBounds[myNumberOfItemsPerPage - j - 2][0] - myOriginalsCoordinates[0];

      var myNumeratorsCopy = myNumerators.duplicate(undefined, [myDuplicatePositionX, myDuplicatePositionY]);

    }

  }

}

This works. It copies them set by set. If I want to copy piece by piece, I just change the order of loops. This makes total sense and it works.

But no matter which version I use, no matter the order of duplicates created, this code always gives the same result.

for(i = 0; i < myCurrentPage.pageItems.length; i++){myCurrentPage.textFrames.contents = i+'';}

Pic01.jpg

This makes no sense at all. Ever. What is the mechanism that InDesign uses to determine index of items? I wrote about this in the message I deleted yesterday because I thought maybe the problem was with me.

Also, the same outcome with this code.

for(i = 0; i < myCurrentPage.pageItems.length; i++){myCurrentPage.textFrames.contents = myCurrentPage.textFrames.index+'';}

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 ,
Feb 18, 2018 Feb 18, 2018

Copy link to clipboard

Copied

Hi Savage,

(Answering #5.)

The effect of your alert(…) statement is that it resolves the specifiers myNumerators[0] and myNumerators[1] by invoking the property contents. Each time you call a method or a property on a DOM specifier you force the subsystem to resolve the underlying object. This is a very important concept in InDesign scripting.

Let's try to make it clearer from your code. You have an array myNumerators—let's just call it A—that contains myPage.textFrames[0] and myPage.textFrames[1]. In other words,

A = [ myPage.textFrames[0], myPage.textFrames[1] ];

These elements are known as DOM specifiers. You can think of them as paths in the document hierarchy. Note that myPage itself is a specifier. So the first element of A, so far, is something like the path <document>/page[0]/textFrames[0]. As long as this element is not resolved, it does not actually points out to a real text frame, it just contains the above path, an URL if you prefer.

So if something change in the document that makes the path …/page[0]/textFrames[0] denote a new object, your A[0] element will in fact refer to something different. That's exactly what happens when you call myNumerator.duplicate(…) in you CopyMyEnumerators function. Indeed, the creation of new objects changes the indexing in the stack of DOM objects. At some point, A is no longer the original text frame, so A.duplicate(…) acts on an unexpected entity.

But this does not happen when your alert line is uncommented, because it actually changes the denotation of A. Then, the underlying specifiers are resolved, which mean that the paths of the form <document>/page[0]/textFrames[0] are internally rewritten in a way that make them points out to a specific DOM object based on its unique ID, something like <document>/textFrames[id=123]. Hence, each A will now refer to something stable and your CopyMyEnumerators function will work as expected.

The key idea, when you want to re-use a specific object while changing or creating other objects in the document, is to resolve its specifier. The easiest way to to so is to call the getElements() method available on any individual or plural specifier.

In your example, you just have to do something like this,

var myItems = doc.pages[0].textFrames.everyItem().getElements();

and that's it.

Suggested solution:

function getMasterBounds(/*Document*/doc,/*uint*/masterIndex)

// -------------------------------------

// => [T,L,B,R][]

{

    return doc.masterSpreads[masterIndex].pageItems.everyItem().geometricBounds.reverse();

}

function dupItems(/*PageItem[]*/items,/*[T,L,B,R][]*/where,  n,w,t,dx,dy,u)

// -------------------------------------

    n = items.length;

    for( w=1 ; w < where.length ; ++w )

    {

        t = where;

        dx = t[1]-10;

        dy = t[0]-10;

        for( i=-1 ; ++i < n ; items.duplicate(u, [dx,dy] ) );

    }

}

var doc = app.properties.activeDocument;

var bounds = getMasterBounds(doc,0);

var myItems = doc.pages[0].textFrames.everyItem().getElements();

dupItems(myItems,bounds);

@+

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 Beginner ,
Feb 18, 2018 Feb 18, 2018

Copy link to clipboard

Copied

Thank you, Marc, for your reply. Will have to take a deeper look into it.

I don't think this is normal at all what InDesign is doing. When working with pointers, before you add new elements, you need to readdress the ones being affected. If I understood on the first read (will read again), InDesign does this, but it's lazy. If I tell it to, it will do it. If not, I get random results. If it wants to give my index number to another object, sure give it away. But ask who is trying to reach that object and share with them the new address.

I have several problems and dozens solutions which fail in a single, different aspect. I believe that in the end I will just give up trying.

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 ,
Feb 18, 2018 Feb 18, 2018

Copy link to clipboard

Copied

I realize this might be a bit confusing—and very frustrating!—if you are new to InDesign scripting. But that's just the way the DOM works. Once you understand how DOM specifiers are handled everything then becomes much clearer.

You'll find a very similar example here:

Reference to textFrame changes when changing geometricBounds

and some educational material here:

Indiscripts :: On ‘everyItem()’ – Part 1

Indiscripts :: On ‘everyItem()’ – Part 2

Hope that helps.

@+

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 Beginner ,
Feb 18, 2018 Feb 18, 2018

Copy link to clipboard

Copied

Thank you, I will read into it, but I doubt it can explain this.

function CopyMyNumerators(){

  for(j = 0; j < myNumberOfItemsPerPage - 1; j++){ //j = myCurrentItemIndex

    for(i = 0; i < myNumeratorsPerItem; i++){ //i = myCurrentNumeratorIndex

      var myDuplicatePositionX = myItemsBounds[myNumberOfItemsPerPage - j - 2][1] - myOriginalsCoordinates[1];

      var myDuplicatePositionY = myItemsBounds[myNumberOfItemsPerPage - j - 2][0] - myOriginalsCoordinates[0];

      var myNumeratorsCopy = myNumerators.duplicate(undefined, [myDuplicatePositionX, myDuplicatePositionY]);

      myNumeratorsCopy.contents = myNumeratorsCopy.index+'';

    }

  }

}

You know what happens? It copies the small one, sets contents to 0. Copies the large one, contents = 2. Copies the small one, contents = 1. Copies the large one, contents = 4. Etc... Hahahaha... How is this even possible xD It in invokes duplicate each time it runs through the loops and it assigns different indexes. Should be 0,1,2,3... But no, InDesign likes it random... Or, what I thought the other day, that newest object gets index 0, then all should be 0.

Then, speaking of bounds, I make A4 document and page bounds returns 296.9999999999, something like that. This is a problem for me. So I ask if 297 = math.ceil(296.999999999) and I get NO

But, if I only use like 5 decimal points (296.99999) then it's true

I know that when comparing real numbers you ask if abs(your number - 297) < 0.0006 (example), but I am comparing integers here. 297 is integer and math.ceil() returns integer. My head hurt with this one....

I understand that InDesign is very complex, but that is not good enough. Do not give me option to use contents if it doesn't work. Working sometimes and in some cases is not good enough. You can use duplicate with coordinates, but don't, you don't get what you wanted. And so many more...

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 ,
Feb 18, 2018 Feb 18, 2018

Copy link to clipboard

Copied

Hi again Savage,

Do you mean that

alert( 297 == Math.ceil(296.999999999) ); // should display 'true'

displays false on your platform?

Indeed that's a serious bug!

The other facts you mention depend on the z-order of objects (the index property) which indeed is modified when you create new objects. This topic has been discussed in other threads, in particular Re: Stacking Order of pageItems in CS5

@+

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 Beginner ,
Feb 22, 2018 Feb 22, 2018

Copy link to clipboard

Copied

LATEST

Hi, Marc.

Tried to recreate 297 == Math.ceil(296.999999999) bug, but no luck. I cannot remember the exact code and I have no backup of it, since it didn't work

Anyhow, finished the script. If you want to see what I came up with, let me know and I will send it to you for debugging

Thanks to all who helped me.

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