Here's a simple as pie exercise from an end-user perspective. In the configuration below, push the horizontal and vertical guides onto the target point. (Click on the image to see the whole landscape):

Goal: Reposition the guides so they intersect the target point.

We have custom rulers units (e.g. millimeters horizontally, inches vertically) and an arbitrary origin. It is not even mentioned if RulerOrigin is in “Per Page”, “Per Spread”, or “On Spine” mode. To make matters worse, both the spread and the page are transformed, independently. Of course this is not an everyday pattern, but I use it deliberately to reinforce the stability of my scripts, always keeping in mind that the user can click the fearsome Rotate Spread View feature.

The first notable thing is that, in such a situation, the Transform panel no longer shows any coordinates when one of my guides is selected:

InDesign cannot display X or Y locations of a Guide when coordinate space is disoriented.

If the GUI goes silent, it is because the guides are anchored to the Spread Coordinate Space, which then conflicts with ruler axes. The difference between a Guide object and a traditional PageItem is that the latter still has coordinates and bounding boxes in the different projection spaces. By nature a guide only has one location (abscissa or ordinate, depending on its orientation attribute) and it is only reflected in the spread canvas.

Note. - Even so, the Guide object exposes the resolve() and transformValuesOf() methods from InDesign 8.0, which means it has owned a private transformation matrix since then. It does not support transform() though.

There then remains a major problem: the location property — as well as the move() method — still expects coordinates in the ruler system, which is assumed to correlate with the user interface viewport and measurement units. In fact, we've already documented that things are more complicated regarding the Ruler System, but with regard specifically to guides, it seems that the whole question comes down to expressing a coordinate, dictated by the transform state of the spread, as a graduation in the ruler metrics.

It now remains to understand how this graduation is calculated. The simplest thing we can do is to display it thru alert(myGuide.location), which returns a value in the corresponding measurement unit (horizontal for vertical guides, vertical for horizontal guides). But with respect to the origin of the ruler system (see figure below), it does not seem easy to see what this position corresponds to.

The location (of a guide) does not tell us according to which axis we should consider it.

So I fumbled very naively starting from the code below:

 
// Let
// - `spd` be the Spread under consideration.
// - `pge` be some target Page in spd, e.g `spd.pages[0]`
// - `hg` be some Horizontal Guide on spd
// - `vg` be some Vertical Guide on spd.
 
const CSPB = +CoordinateSpaces.pasteboardCoordinates;
const CSIN = +CoordinateSpaces.innerCoordinates;
const APCC = +AnchorPoint.centerAnchor;
 
// 1. Ruler orig. in PASTEBOARD space, aka RL->PB translation [pt]
var roPB = pge.resolve( [[0,0],0], CSPB )[0];
 
// 2. PAGE-to-RULER matrix
var PG_RL = pge.transformValuesOf(CSPB)[0]          // PG->PB
           .translateMatrix( -roPB[0], -roPB[1] );  // PB->RL[pt]
 
// 3. Determine the Target location in RL[pt]
// (For my exercise, I chose the center of the page.)
var pos = PG_RL.changeCoordinates(pge.resolve(APCC,CSIN)[0]);
 
// 4. Reposition the guides.
vg.location = pos[0] + 'pt';
hg.location = pos[1] + 'pt';
 
 

This method of calculation seems pretty logical. We obtain the coordinates of the target point (the center of the page) in the ruler system, then we assign those very coordinates to the respective guides.

But this won't work in the most devious cases, as you see below:

Why don't the two guides meet at the target?

Let us, however, focus on the method used. First, we always rely on the PASTEBOARD_COORDINATES space as an absolute reference frame, because we know that it reflects the orientation and orthogonality of the Ruler system. Neither rotation nor shear can take place here, and one can also ignore unit scaling issues as long as one explicitly provides final coordinates in points using ...+'pt'. Then, whenever we need to convert a given location in ruler coordinates, we go through the associated Pasteboard matrix, but correcting it with an inverse translation relative to the ruler origin. That's all the Page-to-Ruler matrix (PG_RL in my snippet) is about.

The reason this doesn't quite work in all possible cases is that we don't (yet) use the SPREAD transform state. Our pos variable contains indeed coordinates (rx,ry) that point exactly to the target location, in the basic, orthogonal Ruler system.

(rx,ry) are valid ruler coordinates, but not those to be assigned to guides…

But as for Guide locations, InDesign requires a much more subtle value: the target location expressed in the perspective of the Spread space, that is, with respect to the orientation of its axes.

We now need to translate the target location into (X,Y).

Note, however, that the desired (X,Y) coordinates — X for the vertical guide, Y for the horizontal one — are in no way governed by the SPREAD_COORDINATES space origin. They still depend on the ruler origin and must fit that particular system.

There are definitely ways to convert (rx,ry) to (X,Y) using trigonometry, but I would like to illustrate a method strictly based on the InDesign DOM. What we need here is, roughly speaking, a tool that maps Ruler axes to Spread axes. In other words, we are looking for the PASTEBOARD-to-SPREAD matrix stripped of any translation component — since we remain glued to the ruler origin.

Steps 4a/4b/4c introduced in the revised code below provide the fix:

 
// FIXED VERSION
 
// Let
// - `spd` be the Spread under consideration.
// - `pge` be some target Page in spd, e.g `spd.pages[0]`
// - `hg` be some Horizontal Guide on spd
// - `vg` be some Vertical Guide on spd.
 
const CSPB = +CoordinateSpaces.pasteboardCoordinates;
const CSIN = +CoordinateSpaces.innerCoordinates;
const APCC = +AnchorPoint.centerAnchor;
 
// 1. Ruler orig. in PASTEBOARD space, aka RL->PB translation [pt]
var roPB = pge.resolve( [[0,0],0], CSPB )[0];
 
// 2. PAGE-to-RULER matrix
var PG_RL = pge.transformValuesOf(CSPB)[0]          // PG->PB
           .translateMatrix( -roPB[0], -roPB[1] );  // PB->RL[pt]
 
// 3. Determine the Target location in RL[pt]
// (For my exercise, I chose the center of the page.)
var pos = PG_RL.changeCoordinates(pge.resolve(APCC,CSIN)[0]);
 
// ------------------ FIX ---------------------
 
// 4a. PASTEBOARD-to-SPREAD matrix
var mx = spd.transformValuesOf(CSPB)[0]             // SP->PB
        .invertMatrix();                            // PB->SP
 
// 4b. Remove translation attributes
var tx = -mx.horizontalTranslation;
var ty = -mx.verticalTranslation;
mx = mx.translateMatrix( tx, ty );
 
// 4c. Refine the location accordingly
pos = mx.changeCoordinates(pos);
 
// --------------------------------------------
 
// 5. Reposition the guides.
vg.location = pos[0]+'pt';
hg.location = pos[1]+'pt';
 
 

Result:

Finally, the script positions the guides at the center of the page :-)

Note. - After some quick testing, I couldn't completely figure out how the Guide.move() method works under the same conditions. Perhaps I will elaborate on this later.


• See also:
Spaces and Transformations in InDesign (the definitive guide);
The Magic Parent Bounding Box.