indd: Breite und Höhe eines PageItems, auch wenn dieses gedreht etc ist

Das Problem

Die meisten Gelegenheitsscripter, die in InDesign wissen wollen, wie hoch oder breit ein Objekt ist, gehen so vor:

var gb = item.geometricBounds,
    w = gb[3] - gb[1],
    h = gb[2] - gb[0];

geometricBounds gibt die Koordinaten der vier Ecken der BoundingBox des Elements in der Reihenfolge [ top, left, bottom, right ].
Aus right-left und bottom-top ergibt sich dann die Größe. Einfach, woll?

Superlösung, solange man garantieren kann, dass das Element nicht gedreht oder geschert wurde.

Es geht übrigens auch schief, wenn die Seitenansicht in der Seitenpalette gedreht wurde.

Wie aber kann man diesem Problem auch mit transforierten Objekten auf den Pelz rücken?

Die Lösung

Wenn wir die Koordinaten dieser drei Punkte haben, können wir die Entfernungen mit recht einfacher Mathematik berechnen.

An diese Punkte kommen wir mit item.resolve( … ) ran.

Hier also die Funktion, die ich heute endlich geschrieben hab. (MaW: Sie ist noch relativ ungetestet; es mag besondere Situationen geben, an die ich noch nicht gedacht hab. Aber das gilt eigentlich für jedes InDesign-Script.)

function get_size( item ) {
  var 
    // Zu CoordinateSpaces siehe die exzellente Serie von Marc Autret:
    // http://www.indiscripts.com/post/2014/03/coordinate-spaces-and-transformations-1
    cs = CoordinateSpaces.SPREAD_COORDINATES,
    // Die .resolve Funktion gibt die Koordinate eines Ankerpunkts 
    // relativ zum Koordinatensystem.
    // Bei gedrehten oder gescherten Objekten kriegen wir so die gedreht/gescherte Ober-
    // und linke Kante raus und können daraus die untransformierten Längen berechnen
    tl = item.resolve( AnchorPoint.TOP_LEFT_ANCHOR, cs ),
    bl = item.resolve( AnchorPoint.BOTTOM_LEFT_ANCHOR, cs ),
    tr = item.resolve( AnchorPoint.TOP_RIGHT_ANCHOR, cs ),
    br = item.resolve( AnchorPoint.BOTTOM_RIGHT_ANCHOR, cs ),
    // transformValuesOf gibt anscheinend immer ein Array mit einem Element zurück
    // Ich weiß nicht, ob das irgendwo dokumentiert ist oder anders sein kann
    tvo = item.transformValuesOf( cs ),
    ccra = tvo[0].counterclockwiseRotationAngle,
    csa = tvo[0].clockwiseShearAngle,
    // Einfacher Pythagoras hier
    top_edge = get_dist( tl[0], tr[0] ),
    left_edge = get_dist( tl[0], bl[0] ),
    // Weder Drehung noch Scherung ändert was an der Länge der Oberkante
    effw = top_edge,
    // Die länge der linken Kante wird durch eine Scherung verlängert zur Hypothenuse 
    // des Dreiecks, wo die Ankathete die eigentliche Länge ist.
    effh = csa ? left_edge * Math.cos( deg2rad( csa ) ) : left_edge,
    // .resolve liefert Punkt. Also geb ich UnitValues zurück
    ewuv = new UnitValue( effw, "pt"),
    ehuv = new UnitValue( effh, "pt"),
    // So rechnet man ein UnitValue in skalare Millimeterangaben um.
    ewmm = ewuv.as('mm'),
    ehmm = ehuv.as('mm');
 
  return { w: ewuv, h: ehuv, tl: tl[0] }
 
  function get_dist( p1, p2 ) {
    return Math.sqrt( Math.pow ( p2[1]-p1[1], 2 ) + Math.pow( p2[0]-p1[0], 2 ) );
  }
  function deg2rad( x ) {
    x = x % 360;
    if ( x < 0 ) x = 360 + x;
    return x * ( 2 * Math.PI ) / 360;
  }
}

Erwähnenswert ist vielleicht noch, dass eine Drehung die Kantenlängen nicht verändert. Die gewünschte Länge ist also übern Pythagoras einfach berechnet.

Eine Scherung hingegen ändert die linke und rechte Kante. Die aus den beiden Punkten berechnete Länge ist daher die Hypothenuse eines Dreiecks mit dem Scherwinkel oben und der Ankathete als gewünschtem Wert. Den man dann natürlich über den Cosinus rauskriegt.

Die Math. Funktionen wollen alle Winkelangaben in Radians haben, also müssen wir das auch noch umrechnen.