Skip to main content
dublove
Legend
October 10, 2026
Answered

Is it possible to preserve the image and the outermost frame, and remove any unnecessary frames?

  • October 10, 2026
  • 11 replies
  • 55 views

Hello everyone.
This image has been inserted into the text and contains unnecessary frames.
Now I just want to keep the innermost image and outermost frame, delete the remaining extra frames, and keep the image in its original inserted state in the text.
Can it be implemented using scripts?

Thanks

 

    Correct answer Manan Joshi

    Try the following code

    function findImage(container) {
    while(container.pageItems.length)
    container = container.pageItems[0]
    if(container)
    return container
    else
    return ""
    }

    var container, img
    for(var i = 0; i < app.documents[0].stories.length; i++) {
    var rectCol = app.documents[0].stories[i].rectangles
    for(var j = 0; j < rectCol.length; j++) {
    container = rectCol[j]
    img = findImage(container)
    if(img != ""){
    app.select(img)
    app.cut()
    app.select(container)
    app.pasteInto()
    }
    }
    }

    There are many assumptions made but you get the idea. Let me know if this is what you are after or not

    11 replies

    dublove
    dubloveAuthor
    Legend
    October 10, 2026

    Hi ​@rob day 

    Also, I get different results depending on which container is selected

    Yes.

    ​@Manan Joshi 

    Also,Also, when using the selection tool, it seems that the outermost frame has been replaced?

    is there a way to automatically convert selected target to the outermost framework?

    Community Expert
    October 10, 2026

    Try it on a test document first.

    Select the outermost frame first 
          


    #target "InDesign"

    (function () {
    if (app.documents.length === 0 ||
    app.selection.length !== 1) {
    alert("Select the outermost frame first.");
    return;
    }

    var outer = app.selection[0];
    var graphics;

    try {
    graphics = outer.allGraphics;

    if (graphics.length !== 1) {
    alert("Expected exactly one graphic.");
    return;
    }

    if (graphics[0].parent === outer) {
    alert("The graphic is already in the outer frame.");
    return;
    }
    } catch (e) {
    alert("Select a valid graphic container.");
    return;
    }

    try {
    app.doScript(function () {
    // Select the actual graphic, not its frame.
    app.select(outer.allGraphics[0]);
    app.cut();

    // Keep the original anchored container.
    app.select(outer);

    // Replace nested content with the graphic.
    app.pasteInto();

    }, ScriptLanguage.JAVASCRIPT,
    undefined,
    UndoModes.ENTIRE_SCRIPT,
    "Remove nested frames");

    } catch (e) {
    alert("Operation failed: " + e.message);
    }
    })();


    I’d be dubious about a global change in case it does something unpredictable. 
     

    You can run this across the entire InDesign document or a single text thread, without selecting each anchored image individually.

    1. Selected frame: Process one anchored object.
    2. Current text thread: Process all eligible anchored objects throughout the connected story, including overset text.
    3. Entire document: Process every eligible anchored object across all stories.

    This was trickier so forgive me if it doesn’t work the best but it works for me.

     

    #target "InDesign"

    (function () {
    if (app.documents.length === 0) {
    alert("Open an InDesign document first.");
    return;
    }

    var doc = app.activeDocument;
    var chosen = (app.selection.length === 1) ? app.selection[0] : null;
    var selectedStory = findStory(chosen);
    var scope = selectedStory ? "Selected text story" : "Entire document";
    var candidates = [], failures = [], skippedReasons = [];
    var seen = {}, nestedCount = 0, skipped = 0;
    var cleaned = 0, deletedFrames = 0, alreadyDirect = 0;

    function type(o) {
    try { return o.constructor.name; } catch (e) { return ""; }
    }
    function valid(o) {
    try { return !!(o && o.isValid); } catch (e) { return false; }
    }
    function frame(o) {
    var t = type(o);
    return t === "Rectangle" || t === "Oval" || t === "Polygon";
    }
    function locked(o) {
    try { return !!(o.locked || o.itemLayer.locked); }
    catch (e) { return true; }
    }
    function findStory(o) {
    for (var n = 0; n < 50 && o; n++) {
    try {
    if (type(o) === "Story") return o;
    if (o.parentStory && valid(o.parentStory)) return o.parentStory;
    if (o === doc) break;
    o = o.parent;
    } catch (e) { break; }
    }
    return null;
    }
    function sameObject(a, b) {
    try { return a.id === b.id; } catch (e) { return false; }
    }

    // Check both nested-item collections; never mistake a distant descendant
    // for a direct child. Duplicate items are deduplicated by their IDs.
    function childrenOf(parent) {
    var result = [], seenChildren = {};
    var lists = [];
    try { lists.push(parent.allPageItems); } catch (e) {}
    try { lists.push(parent.pageItems.everyItem().getElements()); }
    catch (e) {}
    for (var j = 0; j < lists.length; j++) {
    var arr = lists[j];
    for (var k = 0; arr && k < arr.length; k++) {
    var child = arr[k];
    try {
    if (valid(child) && sameObject(child.parent, parent) &&
    !seenChildren[child.id]) {
    result.push(child);
    seenChildren[child.id] = true;
    }
    } catch (e) {}
    }
    }
    return result;
    }

    function inspect(outer, report) {
    function reject(why) {
    if (report) report.reason = why;
    return null;
    }
    try {
    if (!frame(outer)) return reject("outer object is not a graphic frame");
    if (locked(outer)) return reject("outer frame or layer is locked");
    var all = outer.allGraphics;
    if (all.length !== 1) return reject("graphic count is " + all.length);
    var originalGraphic = all[0];
    var inner = originalGraphic.parent;
    if (!frame(inner)) return reject("graphic is inside " + type(inner));
    if (sameObject(inner, outer)) return reject("already direct");
    if (locked(inner)) return reject("image frame or layer is locked");

    // Graphic itself is allowed as the ONLY child of the image frame.
    var inside = childrenOf(inner);
    for (var i = 0; i < inside.length; i++) {
    if (!sameObject(inside[i], originalGraphic))
    return reject("inner frame also contains " + type(inside[i]));
    }

    var current = inner;
    var numberOfFrames = 0;
    for (var depth = 0; depth < 80; depth++) {
    var parent = current.parent;
    if (!frame(parent))
    return reject("chain passes through " + type(parent));
    if (locked(parent))
    return reject("nested frame or layer is locked");

    var direct = childrenOf(parent);
    if (direct.length !== 1 || !sameObject(direct[0], current))
    return reject("frame " + parent.id + " has " + direct.length +
    " direct page items, expected one");

    numberOfFrames++;
    if (sameObject(parent, outer)) {
    // Includes the innermost image frame itself. Thus a
    // one-frame nest is no longer missed (v3 skipped it).
    return {
    outer: outer,
    graphic: originalGraphic,
    branch: current,
    frames: numberOfFrames
    };
    }
    current = parent;
    }
    return reject("nesting exceeds 80 levels");
    } catch (e) {
    return reject(e.message);
    }
    }

    function addIfAnchored(o) {
    try {
    if (!valid(o) || type(o.parent) !== "Character" || !frame(o)) return;
    if (seen[o.id]) return;
    if (selectedStory) {
    var owningStory = o.parent.parentStory;
    if (!valid(owningStory) || !sameObject(owningStory, selectedStory))
    return;
    }
    seen[o.id] = true;
    if (o.allGraphics.length !== 1) return;
    if (sameObject(o.allGraphics[0].parent, o)) {
    alreadyDirect++;
    return;
    }
    nestedCount++;
    var report = {reason: "unknown"};
    var entry = inspect(o, report);
    if (entry) candidates.push(o);
    else {
    skipped++;
    skippedReasons.push("ID " + o.id + ": " + report.reason);
    }
    } catch (e) {
    skipped++;
    skippedReasons.push("ID " + (o ? o.id : "?") + ": " + e.message);
    }
    }

    // First enumerate all story objects (also catches overset/inline ones).
    var stories = selectedStory ? [selectedStory] :
    doc.stories.everyItem().getElements();
    for (var s = 0; s < stories.length; s++) {
    try {
    var storyItems = stories[s].pageItems.everyItem().getElements();
    for (var p = 0; p < storyItems.length; p++)
    addIfAnchored(storyItems[p]);
    } catch (e) {}
    }

    // Second pass: the document's allPageItems can include anchors not
    // exposed by the story.pageItems enumeration in a particular version.
    try {
    var docItems = doc.allPageItems;
    for (var a = 0; a < docItems.length; a++)
    addIfAnchored(docItems[a]);
    } catch (e) {}

    if (!candidates.length) {
    alert("Clean Nested Anchored Frames v4" +
    "\rScope: " + scope +
    "\rNested images: " + nestedCount +
    "\rEligible: 0" +
    "\rAlready direct: " + alreadyDirect +
    "\rSkipped: " + skipped +
    (skippedReasons.length ? "\r\r" + skippedReasons.slice(0, 6).join("\r") : "") +
    "\r\rNo changes made.");
    return;
    }

    if (!confirm("Clean nested anchored images?" +
    "\rScope: " + scope +
    "\rEligible: " + candidates.length +
    "\rSkipped: " + skipped +
    "\r\rKeep the original anchored outer frame and placed graphic; remove all inner frames."))
    return;

    function spreadTopLeft(o) {
    try {
    var r = o.resolve(AnchorPoint.TOP_LEFT_ANCHOR,
    CoordinateSpaces.SPREAD_COORDINATES);
    if (r && r[0] && r[0].length === 2)
    return [Number(r[0][0]), Number(r[0][1])];
    } catch (e) {}
    return null;
    }
    function boundsTopLeft(o) {
    try {
    var b = o.geometricBounds;
    return [Number(b[1]), Number(b[0])];
    } catch (e) { return null; }
    }

    function findDirectGraphic(outer, oldID) {
    var all = outer.allGraphics;
    for (var i = 0; i < all.length; i++) {
    try {
    if (all[i].id !== oldID && sameObject(all[i].parent, outer))
    return all[i];
    } catch (e) {}
    }
    return null;
    }

    function cleanOne(outer) {
    var entry = inspect(outer);
    if (!entry) return false;
    var graphic = entry.graphic;
    var oldID = graphic.id;
    var branch = entry.branch;
    var outerID = outer.id;
    var oldPos = spreadTopLeft(graphic);
    var oldBounds = boundsTopLeft(graphic);
    var pasted = null;

    app.select(graphic);
    app.copy();
    app.select(outer);

    // Try Paste Into while original image is still intact.
    try {
    app.pasteInto();
    pasted = findDirectGraphic(outer, oldID);
    } catch (e) {}

    if (!valid(pasted)) {
    // Only retry after verifying Paste Into did not add objects.
    if (!valid(branch) || outer.allGraphics.length !== 1 ||
    childrenOf(outer).length !== 1)
    throw Error("Paste Into altered the frame unexpectedly; undo the script.");
    branch.remove();
    app.select(outer);
    app.pasteInto();
    pasted = findDirectGraphic(outer, oldID);
    } else {
    if (valid(branch)) branch.remove();
    }

    // Prevent counting an operation as a success unless InDesign
    // really has just the image directly in the original outer frame.
    if (!valid(pasted) || !sameObject(pasted.parent, outer) ||
    outer.id !== outerID || outer.allGraphics.length !== 1)
    throw Error("Could not verify graphic in the original outer frame; undo the script.");

    // Paste Into may recenter graphics. Restore the graphic's location
    // without changing its size or refitting the image.
    var newPos = spreadTopLeft(pasted);
    var initial = oldPos && newPos ? oldPos : oldBounds;
    var current = oldPos && newPos ? newPos : boundsTopLeft(pasted);
    if (initial && current) {
    var dx = initial[0] - current[0];
    var dy = initial[1] - current[1];
    if (isFinite(dx) && isFinite(dy) &&
    (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01))
    pasted.move(undefined, [dx + " pt", dy + " pt"]);
    }
    deletedFrames += entry.frames;
    return true;
    }

    function doCleanup() {
    for (var i = 0; i < candidates.length; i++) {
    var outer = candidates[i];
    try {
    if (valid(outer) && cleanOne(outer)) cleaned++;
    } catch (e) {
    failures.push("Object " + outer.id + ": " + e.message);
    break;
    }
    }
    try { app.select(null); } catch (e) {}
    }

    try {
    app.doScript(doCleanup, ScriptLanguage.JAVASCRIPT, undefined,
    UndoModes.ENTIRE_SCRIPT, "Clean nested anchored frames v4");
    } catch (e) { failures.push(e.message); }

    var message = "Clean Nested Anchored Frames v4" +
    "\rScope: " + scope +
    "\rNested images found: " + nestedCount +
    "\rImages cleaned: " + cleaned +
    "\rFrames removed: " + deletedFrames +
    "\rAlready direct: " + alreadyDirect +
    "\rSkipped: " + skipped;
    if (skippedReasons.length)
    message += "\r\rSkipped details:\r" + skippedReasons.slice(0, 6).join("\r");
    if (failures.length)
    message += "\r\rStopped: " + failures.join("\r") +
    "\rUse one Undo to revert the operation.";
    alert(message);
    })();

     

    dublove
    dubloveAuthor
    Legend
    October 10, 2026

    Hi Eugene Tyson.

    Thanks.
    But I didn't test this successfully.
    Manan Joshi seems to be what he wants.

    Community Expert
    October 10, 2026

    i don’t understand. I used your document and both worked fine. 

    I tried ​@Manan Joshi script too, and if you run it twice it deletes the image completely. 

    So I have no idea what you expect here. But you’re welcome to the scripts. They worked for me on your sample file.

    Manan JoshiCommunity ExpertCorrect answer
    Community Expert
    October 10, 2026

    Try the following code

    function findImage(container) {
    while(container.pageItems.length)
    container = container.pageItems[0]
    if(container)
    return container
    else
    return ""
    }

    var container, img
    for(var i = 0; i < app.documents[0].stories.length; i++) {
    var rectCol = app.documents[0].stories[i].rectangles
    for(var j = 0; j < rectCol.length; j++) {
    container = rectCol[j]
    img = findImage(container)
    if(img != ""){
    app.select(img)
    app.cut()
    app.select(container)
    app.pasteInto()
    }
    }
    }

    There are many assumptions made but you get the idea. Let me know if this is what you are after or not

    -Manan
    dublove
    dubloveAuthor
    Legend
    October 10, 2026

    Hi ​Manan Joshi.

    This is okay, that's roughly what it means.
    Only applicable to the currently selected object.

    var d = app.activeDocument;
    var item = d.selection[0];
    var items = d.selection;
    function findImage(container) {
    while (container.pageItems.length)
    container = container.pageItems[0]
    if (container)
    return container
    else
    return ""
    }

    for (var j = 0; j < items.length; j++) {
    container = items[j]
    img = findImage(container)
    if (img != "") {
    app.select(img)
    app.cut()
    app.select(container)
    app.pasteInto()
    }
    }
    rob day
    Community Expert
    Community Expert
    October 10, 2026

    I think you will also need to check the selection, I get an error if the image and not one of the containers is selected.