Hi @dublove, here is one approach...
- Mark
/**
* @file Align Caption Frame To Graphic Frames.js
*
* Usage:
* 1. Select the caption, a text frame
* 2. Also select one or more graphic frames
* 3. Run script
*
* Script will align the left and right edges of the caption frame
* to the left and right extremes of the graphic frame(s), and
* also align the caption frame above or below the graphic frame(s).
*
* @author m1b
* @version 2025-04-23
* @discussion https://community.adobe.com/t5/indesign-discussions/how-to-modify-this-script-to-select-multiple-images-and-a-text-box/m-p/15282394
*/
function main() {
var settings = {
// distance between the graphic frame(s) and caption frame, in points
gap: 0,
// whether to adjust the final positioning to account for text frame insets [T, L, B, R].
adjustForTextFrameInsets: [false, true, false, true],
};
var doc = app.activeDocument,
items = doc.selection,
graphicFrames = getThingsWithFilter(items, function (item) { return item.hasOwnProperty('graphics') && item.graphics.length > 0 }, false),
captionFrame = getThingsWithFilter(items, function (item) { return item.hasOwnProperty('texts') && item.texts.length > 0 }, true);
if (0 === graphicFrames.length)
return alert('Please include one or more graphic frames in your selection.');
if (!captionFrame)
return alert('Please include one caption text frame in your selection.')
app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;
// insets to adjust for text frame insets
var insets = [
settings.adjustForTextFrameInsets[0] ? captionFrame.textFramePreferences.insetSpacing[0] : 0,
settings.adjustForTextFrameInsets[1] ? captionFrame.textFramePreferences.insetSpacing[1] : 0,
settings.adjustForTextFrameInsets[2] ? captionFrame.textFramePreferences.insetSpacing[2] : 0,
settings.adjustForTextFrameInsets[3] ? captionFrame.textFramePreferences.insetSpacing[3] : 0,
];
var minimumHeight = captionFrame.texts[0].characters[0].pointSize
+ (captionFrame.textFramePreferences.insetSpacing[2] + captionFrame.textFramePreferences.insetSpacing[0]);
var left = Infinity,
right = -Infinity,
top = Infinity,
bottom = -Infinity,
anchor;
for (var i = 0; i < graphicFrames.length; i++) {
left = Math.min(graphicFrames[i].geometricBounds[1], left);
right = Math.max(graphicFrames[i].geometricBounds[3], right);
top = Math.min(graphicFrames[i].geometricBounds[0], top);
bottom = Math.max(graphicFrames[i].geometricBounds[2], bottom);
}
if (top < captionFrame.geometricBounds[0]) {
// align caption below graphic frames
top = bottom + settings.gap;
bottom = top + minimumHeight;
anchor = AnchorPoint.TOP_LEFT_ANCHOR;
}
else {
// align caption above graphic frames
bottom = top - settings.gap;
top = bottom - minimumHeight;
anchor = AnchorPoint.BOTTOM_LEFT_ANCHOR;
}
// new bounds, adjusting for insets
captionFrame.geometricBounds = [
top - insets[0],
left - insets[1],
bottom + insets[2],
right + insets[3],
];
// now expand to fit the caption text
while (captionFrame.overflows)
captionFrame.resize(CoordinateSpaces.PARENT_COORDINATES, anchor, ResizeMethods.ADDING_CURRENT_DIMENSIONS_TO, [0, 2]);
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Align Caption To Graphic Frames');
/**
* Returns any things which pass the `filter` function.
* @author m1b
* @version 2025-04-23
* @param {Array<*>} things - the things to search.
* @param {Function} filter - a function that, given a thing, returns true when it matches.
* @param {Boolean} [returnFirstThing] - whether to return the first thing only (default: false).
* @returns {Array<*>}
*/
function getThingsWithFilter(things, filter, returnFirstThing) {
var found = [];
for (var i = 0; i < things.length; i++) {
if (
'Group' === things[i].constructor.name
&& things[i].pageItems.length > 0
) {
var moreThings = getThingsWithFilter(things[i].pageItems.everyItem().getElements(), filter, returnFirstThing);
if (
returnFirstThing
&& undefined != moreThings
&& 'Array' !== moreThings.constructor.name
)
return moreThings;
if (moreThings.length > 0)
found = found.concat(moreThings);
continue;
}
if (!filter(things[i]))
continue;
if (returnFirstThing)
return things[i];
found.push(things[i]);
}
if (
returnFirstThing
&& 0 === found.length
)
return;
return found;
};
Edit 2025-04-22: added support for grouped items.
Edit 2025-04-23: fixed bug relating to minimum height of caption frame, added support for adjusting for text frame insets.
Edit 2025-04-23: added finer control over each text frame inset and fixed another bug relating to minimum height.