Monday, September 05, 2005

 

Script of the Day -- Proxy Aware Align Graphic in Frame

If you're in the situation where you find yourself importing images (or any kind of graphic, really, but for me it is almost always images) into a pre-existing frame, you are likely frustrated by the absence of any kind of alignment tools for aligning a graphic within its frame. If the frame was empty before, the image aligns at top left of the frame. If it previously contained a graphic, the incoming image aligns itself where the previous one was.

Sometimes (even often) these are good choices, but when they're not, you are faced with futzing about to get the image where you want it. It was this problem that caused me to conceive of this script. It allows you to select either the image or its frame (note that I limited this version to rectangular frames because so far that's all I've ever needed; it could be expanded to allow other shaped frames, and I will certainly do that should the need arise in my work). Before running the script, choose a proxy point on the Control panel or Transform palette and the script aligns the graphic at the point on the rectangle's border that corresponds to the point you chose. (I often use Fit Frame to Content immediately after running this script, but I decided not to build it in because there are times when I don't want that.)

I think this is the first script I've posted here that uses a switch statement.
//DESCRIPTION: Align image in rectangular frame based on proxy

if ((app.documents.length != 0) && (app.selection.length != 0)) {
 var mySel = app.selection[0];
 var myMsg = "Please select a graphic or its rectangular frame.";
 if (mySel.constructor.name != "Rectangle") {
  mySel = mySel.parent;
 }
 if (mySel.constructor.name != "Rectangle") {
  errorExit(myMsg);
 }
 if (mySel.graphics.length != 1) {
  errorExit(myMsg);
 }
This first part of the script checks to make sure that the selection is appropriate, i.e., a rectangle or its contents. If it isn't, the script exits with a helpful error message
 imgBounds = mySel.graphics[0].geometricBounds;
 myAnchor = app.layoutWindows[0].transformReferencePoint;
 frameBounds = mySel.geometricBounds;
These three lines collect the information we need to perform the alignment function: the graphic bounds in imgBounds, the currently active "proxy point" in myAnchor, and the rectangle's bounds in frameBounds.

Why did I make them global variables? Laziness. I'm going to fix that in the posted version because these variables don't need to be global and having them so might cause problems downstream if I incorporate this script into some larger script -- that's the main reason for being careful about using local variables in a simple script: one day, it might not be so simple or you might want to reuse it in a less simple situation.

Now we're ready to put the switch into operation. Depending on which proxy point is active, a different move is necessary -- it's all a matter of simple geometry. What's more, it doesn't matter what measurement units the user has activated.
 switch (myAnchor) {
  case AnchorPoint.topLeftAnchor :
   mySel.graphics[0].move([frameBounds[1],frameBounds[0]]);
   break;
  case AnchorPoint.topCenterAnchor :
   xLoc = (frameBounds[3] + frameBounds[1] -(imgBounds[3] - imgBounds[1]))/2;
   mySel.graphics[0].move([xLoc,frameBounds[0]]);
   break;
  case AnchorPoint.topRightAnchor :
   xLoc = frameBounds[3] - (imgBounds[3] - imgBounds[1]);
   mySel.graphics[0].move([xLoc,frameBounds[0]]);
   break;
  case AnchorPoint.leftCenterAnchor :
   yLoc = (frameBounds[2] + frameBounds[0] - (imgBounds[2] - imgBounds[0]))/2;
   mySel.graphics[0].move([frameBounds[1],yLoc]);
   break;
  case AnchorPoint.centerAnchor :
   xLoc = (frameBounds[3] + frameBounds[1] -(imgBounds[3] - imgBounds[1]))/2;
   yLoc = (frameBounds[2] + frameBounds[0] - (imgBounds[2] - imgBounds[0]))/2;
   mySel.graphics[0].move([xLoc,yLoc]);
   break;
  case AnchorPoint.rightCenterAnchor :
   xLoc = frameBounds[3] - (imgBounds[3] - imgBounds[1]);
   yLoc = (frameBounds[2] + frameBounds[0] - (imgBounds[2] - imgBounds[0]))/2;
   mySel.graphics[0].move([xLoc,yLoc]);
   break;
  case AnchorPoint.bottomLeftAnchor :
   yLoc = frameBounds[2] - (imgBounds[2] - imgBounds[0]);
   mySel.graphics[0].move([frameBounds[1],yLoc]);
   break;
  case AnchorPoint.bottomCenterAnchor :
   xLoc = (frameBounds[3] + frameBounds[1] -(imgBounds[3] - imgBounds[1]))/2;
   yLoc = frameBounds[2] - (imgBounds[2] - imgBounds[0]);
   mySel.graphics[0].move([xLoc,yLoc]);
   break;
  case AnchorPoint.bottomRightAnchor :
   xLoc = frameBounds[3] - (imgBounds[3] - imgBounds[1]);
   yLoc = frameBounds[2] - (imgBounds[2] - imgBounds[0]);
   mySel.graphics[0].move([xLoc,yLoc]);
   break;
 }
And that's the guts of the script. Note that if you have just placed a graphic in the previously empty rectangle then the top-left choice does nothing. Also, the center choice is already available on the Object menu: Fitting/Center Content.

The reset of the script is just clean-up and you'll recognize it as the tail end of my "selection framework" that I've used in a variety of scripts so far:
} else {
 errorExit();
}

// +++++++ Functions Start Here +++++++++++++++++++++++

function errorExit(message) {
 if (app.version != 3) { beep() } // CS2 includes beep() function.
 if (arguments.length > 0) {
  alert(message);
 }
 exit(); // CS exits with a beep; CS2 exits silently.
}
I can't believe it took me so long to get around to this script. I had become quite adept at copying and pasting values from one field to another in the Control panel!

Comments: Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?