Skip to main content
Participant
December 19, 2014
Question

Resolve anchored object position with ExtendScript

  • December 19, 2014
  • 1 reply
  • 2354 views

Hi,

I think this is a bug in inDesign ExtendScript API. If you try to run "resolve" method on anchored object it would always return you a coordinates relative to object's own left bottom corner.

Steps to reproduce:

1. Create TextFrame, fill it in with placeholder text.

2. Create a PageItem, for example rectangle.

3. Anchor rectangle anywhere in text.

4. Select rectangle and run the following script:

app.activeDocument.selection[0].resolve(AnchorPoint.CENTER_ANCHOR, CoordinateSpaces.SPREAD_COORDINATES, false)[0];

I'm expecting to get rectangle's center coordinates relative to spread's coordinates. If I would release anchor and run the same command again - the coordinates are correct.

InDesign CC 2014.1 on Windows 7 pro sp1 x64.

Please, advice how can I get anchored object's center coordinates in spread coordinates system.

Andrii

This topic has been closed for replies.

1 reply

Marc Autret
Legend
January 28, 2019

Hi Andrii,

That's indeed a bug, which probably comes from CS6's changes in coordinate management, or maybe CS5. (Things work fine in CS4.)

It is well-known that an anchored object (AO) belongs to a Story, which is not a geometric object. When an AO is visible (that is, when the associated anchor character is not in an overset region), its natural parent is the particular TextFrame that owns the anchor character. But this is not the actual parent of the AO in terms of coordinate spaces.

Unlike nested objects ('pasted into') which have a true parent space, AOs need a kind of bridge to fit into the hierarchy of coordinate spaces. Unfortunately, this 'patch' fails to connect AOs to regular Spread coordinate spaces in InDesign CS6 and later. The code

myAO.resolve( AnchorPoint.BOTTOM_LEFT_ANCHOR, CoordinateSpaces.SPREAD_COORDINATES )[0]

typically returns [0,0] whatever the location of the AO in the layout. So it seems that a fake coordinate space is created, based on the bottom-left corner of the AO's bounding box. [That's just my interpretation of what we can see, not a documented assertion.]

Of course you still have the option to use myAO.geometricBounds and make cautious calculations from those data, noting that resolve(...) is powerful enough to interpret ruler coordinates.

[EDIT: 20-July-2019]

The following lines are only of historical interest. A much better solution is to be proposed next.

I would suggest a different workaround. It is based on a strange but promising discovery. Apparently, AOs are associated to a more reliable structure at the PARENT_COORDINATES level. Here again, I don't think that's a true coordinate space, but since its origin coincides with the TOP-LEFT corner of the TextFrame container (for whatever reason!) we should be able to work from there.

Note. — Contrary to what one might think, the so-called 'parent space' of the AO is not the canonical inner space of the TextFrame. There are two noticeable hints about regular TextFrame spaces:

  • Unlike usual PageItem spaces, their origin is *initialized* at the center location of the bounding box: myTextFrame.resolve(AnchorPoint.CENTER_ANCHOR, CoordinateSpaces.INNER_COORDINATES)[0] returns [0,0] as long as the geometry of the frame is not altered.
  • Also, the space origin "follows the movement" of the text frame, that is, its coordinates relative to the pasteboard evolves in a way that preserves the inner coordinates of anchor points.

Anyway, the query

myAO.resolve( anyAnchorPoint, CoordinateSpaces.PARENT_COORDINATES )[0]

reveals the [x,y] coordinates of anyAnchorPoint in a coordinate system whose origin is the top-left corner of the TextFrame (not its center point). This coordinate system also seems to match the transform state of the TextFrame. Thus, what is left to us is to recover the TextFrame object from the AO and take advantage of transformation matrices to draw a 'coordinate path' up to the Spread origin.

Schematically, we have the following information:

  1. [x,y] in the 'fake space',
  2. The origin of the fake space (i.e top-left anchor) in the TextFrame space,
  3. The matrix that maps the TextFrame space to the Spread space.

From this we derive the coordinates of anyAnchorPoint relative to the Spread coordinate space.

Note that (3) is not necessarily a simple TRANSLATION, the parent text frame may undergo complex transformations relative to the spread.

Suggested code:

function inSpreadCoords(/*AnchoredObj*/obj,  txf,xy,t,mx)

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

// Given an anchored object `obj`, return the [x,y] coords of its

// center point in spread coordinate space. This function corrects

// the malfunction of `obj.resolve(<anchor>,<spreadCS>)` in CS6/CC.

{

    const CS=CoordinateSpaces, AP=AnchorPoint;

    // Get the parent TextFrame of obj.

    // ---

    txf = obj.parent.parentTextFrames[0];

   

    // Coords of the center anchor in txf's 'TopLeftCorner' system (TLC)

    // (You can specify another ANCHOR if needed ;-)

    // ---

    xy = obj.resolve( AP.CENTER_ANCHOR, CS.PARENT_COORDINATES )[0];

    // Coords of TLC's origin in the actual TextFrame space (TXF)

    // --> `t` reflects the translation TLC->TXF.

    // ---

    t = txf.resolve( AP.TOP_LEFT_ANCHOR, CS.INNER_COORDINATES )[0];

    // TLC->SPD  :=  TLC->TXF × TXF->SPD

    // ---

    mx = app.transformationMatrices.add(1,1,0,0,t[0],t[1]).               //   TLC->TXF

         catenateMatrix(txf.transformValuesOf(CS.SPREAD_COORDINATES)[0]); // × TXF->SPD

    // Apply mx to xy.

    // ---

    return mx.changeCoordinates(xy);

}

// Test.

// ---

alert( inSpreadCoords(app.selection[0]) );

HTH,

Marc

Marc Autret
Legend
July 20, 2019

Hi all,

By digging deeper into this topic I discovered a fun fact. Although you cannot trust myAnchoredObject.resolve(…) for usual coordinate spaces above the inner space, it turns out that myAnchoredObject.transformValuesOf(…) still works as expected in CS6-CC. That is, you can recover the transformation matrix that maps the inner space to the (true) spread coordinate space.

In summary. Given an anchored object AO,

  • The syntax AO.resolve( location, CoordinateSpaces.SPREAD_COORDINATES, …)[0] does not work as expected in CS6-CC, as if a fake spread coordinate space were considered, based on the bottom-left corner of AO's bounding box seen in the perspective of the parent text frame
  • But, the syntax AO.transformValuesOf( SPREAD_COORDINATES )[0] seems to return the actual inner-to-spread matrix, with respect to the regular spread coordinate space :-)

So, instead of embarking on complicated calculations (previous post), we just have to apply the inner-to-spread matrix on inner coordinates:

function inSpreadCoords(/*AnchoredObj*/obj,  xy,mx)

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

// [190720] New version based on the fact that -- unlike

// `obj.resolve` -- `obj.transformValuesOf` is reliable!

// ---

// Given an anchored object `obj`, return the [x,y] coords of its

// center point in spread coordinate space. This function corrects

// the malfunction of `obj.resolve(<anchor>,<spreadCS>)` in CS6/CC.

{

    const CS=CoordinateSpaces, AP=AnchorPoint;

    // Coords of the center anchor in AO's INNER SPACE

    // (You can specify another ANCHOR if needed ;-)

    // --- 

    xy = obj.resolve( AP.CENTER_ANCHOR, CS.INNER_COORDINATES )[0];

    // INNER-to-SPREAD matrix. In the context of `transformValuesOf()`,

    // CS.SPREAD_COORDINATES refers to the actual spread coord. space

    // (which would fail in the context of the `resolve()` method.)

    // --- 

    mx = obj.transformValuesOf( CS.SPREAD_COORDINATES )[0];

    // Simply apply mx to xy :-)

    // --- 

    return mx.changeCoordinates(xy);

}

In addition to being much simpler, this implementation has a huge advantage: it still works with nested items within the anchored object (e.g. when the anchored object is a Group and your target object is an element of that group.)

Best,

Marc