Skip to main content
December 9, 2025
Question

Script to Paste Images with slight offset

  • December 9, 2025
  • 1 reply
  • 191 views

Is there a script or a way to paste the same image on every page but each page a slight different position? 


I'm doing a fore-edge print and need the same image on every other page but each time it has to move 0.246mm left

 

Thank You!

1 reply

m1b
Community Expert
Community Expert
December 9, 2025

Hi @Joaquin35467744k2vs here's a script that should be useful in your situation. There are instructions in the script documentation below.

- Mark

 

Notes:

  • You must name the start item "start_" then something to identify it (because you may have multiple interpolations in the same document). For example: "start_fore_edge_marker".
  • the "end_" item cannot be on the same page as the "start_" item.

 

Screenshot 2025-12-09 at 22.54.54.png

 

/**
 * @file Interpolate Item Across Pages.js
 *
 * Places a duplicate item on each intervening page
 * between start and end items. Interpolates the
 * positions between start and end items.
 *
 * Instructions:
 *   1. create a page item (can be a Group)
 *   2. position the item on the starting page,
 *      in the starting position
 *   3. in the layers panel, name the item "start_"
 *      + an identifier of your choice, eg. "start_line"
 *   4. copy the item and paste it on the ending page,
 *      and move to the ending position
 *   5. name the pasted item "end_" with the same
 *      identifier, eg. "end_line"
 *   6. select the start or end item and run script;
 *      will create duplicates called eg. "middle_line"
 *
 * Tips:
 *   - you can adjust the start and/or end items and
 *     run script again and it will update the interpolated
 *     duplicates (actually it will first remove them all
 *     and re-do the duplicating).
 *   - the interpolationed items will be placed on the same
 *     layer as the start item.
 *
 * @author m1b
 * @version 2025-12-13
 * @discussion https://community.adobe.com/t5/indesign-discussions/script-to-paste-images-with-slight-offset/m-p/15624584
 */
function main() {

    const PREFIX_START = 'start_';
    const PREFIX_MIDDLE = 'middle_';
    const PREFIX_END = 'end_';

    var doc = app.activeDocument;
    var item = doc.selection[0];

    if (
        !item
        || 'function' !== typeof item.duplicate
        || 'string' !== typeof item.name
    )
        return alert('Please select an item and try again.')


    var matchAnyPrefix = new RegExp('^(' + [PREFIX_START, PREFIX_MIDDLE, PREFIX_END].join('|') + ')');

    if (!matchAnyPrefix.test(item.name))
        return alert('The name of the selected item does not have an expected prefix, eg. "' + PREFIX_START + '". Please set up correctly and try again.');

    var id = item.name.replace(matchAnyPrefix, '');

    var startItemName = PREFIX_START + id;
    var middleItemName = PREFIX_MIDDLE + id;
    var endItemName = PREFIX_END + id;

    var middleItems = getThings(doc.allPageItems, 'name', middleItemName);

    // remove all middle items
    for (var i = middleItems.length - 1; i >= 0; i--)
        middleItems[i].remove();

    var startItem = getThing(doc.allPageItems, 'name', startItemName);
    var endItem = getThing(doc.allPageItems, 'name', endItemName);

    if (!startItem)
        return alert('Could not find the start item named "' + startItemName + '".');

    if (!endItem)
        return alert('Could not find the end item named "' + endItemName + '".');

    if (endItem.itemLayer !== startItem.itemLayer)
        endItem.move(startItem.itemLayer);

    var startPage = getNearestPage(startItem);
    var endPage = getNearestPage(endItem);

    var startSpread = startPage.parent;
    var endSpread = endPage.parent;

    if (startSpread === endSpread)
        return alert('Your start and end items must be on different spreads.');

    // collect the spreads we want
    var spreads = [];
    var count = Math.abs(endSpread.index - startSpread.index) - 1;
    var indices = interpolateRange(startSpread.index, endSpread.index, count);
    for (var i = 1; i <= count; i++)
        spreads.push(doc.spreads.item(indices[i]));

    // collect the pages we want
    var pages = [startPage];

    for (var i = 0, page, item; i < count; i++)
        pages.push(getPage(spreads[i]));

    pages.push(endPage);

    var startX = startItem.geometricBounds[1] - startPage.bounds[1];
    var startY = startItem.geometricBounds[0] - startPage.bounds[0];
    var endX = endItem.geometricBounds[1] - endPage.bounds[1];
    var endY = endItem.geometricBounds[0] - endPage.bounds[0];

    var xPositions = interpolateRange(startX, endX, count);
    var yPositions = interpolateRange(startY, endY, count);

    // duplicate the item to each page
    for (var i = 1, page, item; i < pages.length - 1; i++) {

        page = pages[i];
        item = startItem.duplicate(page);
        item.name = 'middle_' + id;

        positionItemRelativeToPage(item, xPositions[i], yPositions[i], page);

    }

    /**
     * Given a Spread, returns the page we want to interpolate onto.
     * @param {Spread} spread
     * @returns {Page}
     */
    function getPage(spread) {

        for (var i = 0, pg; i < spread.pages.length; i++) {

            pg = spread.pages[i];

            if (doc.documentPreferences.facingPages) {

                if (startPage.side === pg.side)
                    return pg;
                else
                    continue;

            }

            else if (startPage.index === pg.index)
                return pg;

        }

    };

};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Interpolate Item Across Pages');

/**
 * Returns an array including `count` interpolated
 * values between `start` and `end`, inclusive.
 * @author m1b
 * @version 2024-08-13
 * @param {Number} start - the first value.
 * @param {Number} end - the last value.
 * @param {Number} count - the number of interpolations.
 * @returns {Array<Number>}
 */
function interpolateRange(start, end, count) {

    var range = [start],
        step = (end - start) / (count + 1);

    for (var i = 1; i <= count; i++)
        range.push(start + i * step);

    range.push(end);

    return range;

};

/**
 * Moves `item` to the top left of the nearest page, offset by [dx, dy].
 * @author m1b
 * @version 2025-11-08
 * @param {PageItem} item - an Indesign PageItem.
 * @param {Number} dx - the X offset.
 * @param {Number} dy - the Y offset.
 * @param {Page} [nearestPage] - an Indesign Page (default: the nearest page).
 */
function positionItemRelativeToPage(item, dx, dy, nearestPage) {

    if ('function' !== typeof item.move)
        throw new Error('positionItemRelativeToPage: cannot move item.');

    // get the nearest page, in case the item isn't on a page
    var page = nearestPage || getNearestPage(item);

    // get the top left of the page
    var pos = [page.bounds[0], page.bounds[1]];

    // move the item
    item.move([pos[1] + dx, pos[0] + dy]);

};

/**
 * Returns the nearest page to `item`.
 * @author m1b
 * @version 2025-08-02
 * @param {PageItem} item - the target item.
 * @returns {Page} - the nearest page to `item`.
 */
function getNearestPage(item) {

    var page = item.parentPage;
    var spread;

    while (!page) {

        spread = item.parent;

        if ('Page' === spread.constructor.name)
            page = spread;


        if ('Spread' === spread.constructor.name) {

            var index = findClosestBounds(
                item.visibleBounds,
                spread.pages.everyItem().bounds
            );

            page = spread.pages.item(index);

        }

    }

    return page;

};

/**
 * Returns the index of the bounds in `boundsArray` that is
 * closest to the target bounds. All bounds are in [T, L, B, R] format.
 * @author m1b
 * @version 2025-08-02
 * @param {Array} targetBounds - The target bounds [T, L, B, R].
 * @param {Array<bounds>} boundsArray - An array of bounds arrays.
 * @returns {Number} Index of the closest bounds in the array.
 */
function findClosestBounds(targetBounds, boundsArray) {

    var targetCenter = centerOfBounds(targetBounds);
    var minDist = Infinity;
    var closestIndex = -1;

    for (var i = 0, center, dx, dy, distSq; i < boundsArray.length; i++) {

        center = centerOfBounds(boundsArray[i]);
        dx = targetCenter[0] - center[0];
        dy = targetCenter[1] - center[1];
        distSq = dx * dx + dy * dy;

        if (distSq < minDist) {
            minDist = distSq;
            closestIndex = i;
        }

    }

    return closestIndex;

};

/**
 * Returns the center of `bounds`;
 * @param {Array} bounds - The bounds [T, L, B, R].
 * @returns {Array<Number>} - [cx, cy].
 */
function centerOfBounds(bounds) {
    return [(bounds[1] + bounds[3]) / 2, (bounds[0] + bounds[2]) / 2];
};

/**
 * Returns a thing with matching property.
 * If `key` is undefined, evaluate the object itself.
 * @author m1b
 * @version 2024-04-21
 * @param {Array|Collection} things - the things to look through.
 * @param {String} [key] - the property name (default: undefined).
 * @param {*} value - the value to match.
 * @returns {*?} - the thing, if found.
 */
function getThing(things, key, value) {

    for (var i = 0; i < things.length; i++)
        if ((undefined == key ? things[i] : things[i][key]) == value)
            return things[i];

};

/**
 * Returns a property of each object.
 * @author m1b
 * @version 2023-09-08
 * @param {Array|Collection} things - the things to look through.
 * @param {String} key - the property name.
 * @param {*} value - the value to match.
 * @returns {Array<*>}
 */
function getThings(things, key, value) {

    var found = [];

    for (var i = 0; i < things.length; i++)
        if (things[i][key] == value)
            found.push(things[i]);

    return found;

};

Edit 2025-12-13: added better handling of spreads and facing pages.

m1b
Community Expert
Community Expert
December 13, 2025

@Joaquin35467744k2vs sorry I just realized that the script didn't consider spreads and facing pages. I have updated the code above. You mention "every other page"—I am assuming that is due to using two-page spreads? If so, I hope that revised script will suit your case.

- Mark