Label Graphic Links

Script for Adobe InDesign

Add a label to placed graphics that lists the file name.

  • Label all graphics or a selected graphic
  • Choose layer, paragraph style, and object style
  • Adapt open source to customize or create other scripts
Download
Label Graphic Links
Help me keep making new scripts by supporting my work. Click the PayPal button to contribute any amount you choose. Thank you. William Campbell

Script adds label with file name to placed graphics as shown.

How-to Video

How to use the script

Set desired options and click the OK button to proceed. Labels are added atop each placed graphic, on the selected layer and styled as described below.

Layer — the layer to which graphic labels are added. A choice of layer is required. The list defaults to a layer named ‘Graphic Labels.’ if it exists. If the layer does not exist, the choice [Create 'Graphic Labels'] appears in the drop-down list. This choice creates the layer. It is recommended to use this layer, or another separate layer without content, so the labels can be hidden or set to non-printing when time to output the document.

Paragraph style — the paragraph style assigned to the text of each label. Choose an existing style or create a new one for label text before launching the script. Or choose the default, [No Paragraph Style], which does not assign any paragraph style. In that case, the text defaults to Arial, 9 points, swatch Black. For other defaults, edit the script code.

Object style — the object style assigned to the frame of each label. Choose an existing style or create a new one for label frames before launching the script. Or choose the default, [None], which does not assign any object style. In that case, the frame fill color is set to 100% Yellow, the swatch for which is created if it does not exist. Whether choosing a style or none, label frames are set to auto-sizing height and width. For other defaults, edit the script code.

PositionCorner puts labels in the top-left corner of each graphic. Center puts labels in the center of each graphic.

Source code

(download button below)

/*

Label Graphic Links
Copyright 2024 William Campbell
All Rights Reserved
https://www.marspremedia.com/contact

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/

(function () {

    var title = "Label Graphic Links";

    if (!/indesign/i.test(app.name)) {
        alert("Script for InDesign", title, false);
        return;
    }

    // Script variables.
    var count;
    var doc;
    var doneMessage;
    var error;
    var font;
    var layer;
    var layerNames;
    var objectStyle;
    var objectStyleNames;
    var paragraphStyle;
    var paragraphStyleNames;
    var progress;
    var swatchFrameFill;
    var swatchFrameStroke;
    var swatchText;

    // Reusable UI variables.
    var g; // group
    var gc1; // group (column)
    var gc2; // group (column)
    var p; // panel
    var w; // window

    // Permanent UI variables.
    var btnCancel;
    var btnOk;
    var listLayer;
    var listObjectStyle;
    var listParagraphStyle;
    var rbAll;
    var rbCenter;
    var rbCorner;
    var rbSelected;

    // SETUP

    // Script requires open document.
    if (!app.documents.length) {
        alert("Open a document", title, false);
        return;
    }
    doc = app.activeDocument;
    // Get layer names.
    layerNames = doc.layers.everyItem().name;
    // Add 'Create Graphic Labels' if no layer 'Graphic Labels'.
    (function () {
        var i;
        for (i = layerNames.length - 1; i > -1; i--) {
            if (layerNames[i] == "Graphic Labels") {
                break;
            }
        }
        if (i < 0) {
            layerNames.unshift("[Create 'Graphic Labels']");
        }
    })();
    // Get style names.
    paragraphStyleNames = getStyleNames(doc.allParagraphStyles);
    objectStyleNames = getStyleNames(doc.allObjectStyles);

    // CREATE PROGRESS WINDOW

    progress = new Window("palette", "Progress", undefined, {
        "closeButton": false
    });
    progress.t = progress.add("statictext");
    progress.t.preferredSize.width = 450;
    progress.b = progress.add("progressbar");
    progress.b.preferredSize.width = 450;
    progress.display = function (message) {
        message && (this.t.text = message);
        this.show();
        this.update();
    };
    progress.increment = function () {
        this.b.value++;
    };
    progress.set = function (steps) {
        this.b.value = 0;
        this.b.minvalue = 0;
        this.b.maxvalue = steps;
    };

    // CREATE USER INTERFACE

    w = new Window("dialog", title);
    w.alignChildren = "fill";
    // Panel.
    p = w.add("panel");
    // Group of 2 columns.
    g = p.add("group");
    // Groups, columns 1 and 2.
    gc1 = g.add("group");
    gc1.orientation = "column";
    gc1.alignChildren = "left";
    gc2 = g.add("group");
    gc2.orientation = "column";
    gc2.alignChildren = "left";
    // Rows.
    gc1.add("statictext", undefined, "Graphics:").preferredSize.height = 23;
    g = gc2.add("group");
    g.margins = [0, 3, 0, -3];
    rbAll = g.add("radiobutton", undefined, "All");
    rbAll.preferredSize.height = 23;
    rbSelected = g.add("radiobutton", undefined, "Selected");
    rbSelected.preferredSize.height = 23;
    gc1.add("statictext", undefined, "Layer:").preferredSize.height = 23;
    listLayer = gc2.add("dropdownlist", undefined, layerNames);
    listLayer.preferredSize = [170, 23];
    gc1.add("statictext", undefined, "Paragraph style:").preferredSize.height = 23;
    listParagraphStyle = gc2.add("dropdownlist", undefined, paragraphStyleNames);
    listParagraphStyle.preferredSize = [170, 23];
    gc1.add("statictext", undefined, "Object style:").preferredSize.height = 23;
    listObjectStyle = gc2.add("dropdownlist", undefined, objectStyleNames);
    listObjectStyle.preferredSize = [170, 23];
    gc1.add("statictext", undefined, "Position:").preferredSize.height = 23;
    g = gc2.add("group");
    g.margins = [0, 3, 0, -3];
    rbCorner = g.add("radiobutton", undefined, "Corner");
    rbCorner.preferredSize.height = 23;
    rbCenter = g.add("radiobutton", undefined, "Center");
    rbCenter.preferredSize.height = 23;
    // Action Buttons.
    g = w.add("group");
    g.alignment = "center";
    btnOk = g.add("button", undefined, "OK");
    btnCancel = g.add("button", undefined, "Cancel");
    // Panel Copyright.
    p = w.add("panel");
    p.add("statictext", undefined, "Copyright 2024 William Campbell");

    // SET UI VALUES

    if (doc.selection.length) {
        // Something is selected.
        rbSelected.enabled = true;
        rbAll.value = false;
        rbSelected.value = true;
    } else {
        rbSelected.enabled = false;
        rbAll.value = true;
        rbSelected.value = false;
    }
    listLayer.selection = listLayer.find("Graphic Labels") || 0;
    listParagraphStyle.selection = 0;
    listObjectStyle.selection = 0;
    rbCenter.value = true;

    // UI ELEMENT EVENT HANDLERS

    btnOk.onClick = function () {
        w.close(1);
    };

    btnCancel.onClick = function () {
        w.close(0);
    };

    // DISPLAY THE DIALOG

    if (w.show() == 1) {
        doneMessage = "";
        try {
            app.doScript(process, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, title);
            doneMessage = count + " graphic links labeled";
        } catch (e) {
            error = error || e;
            doneMessage = "An error has occurred.\nLine " + error.line + ": " + error.message;
        }
        progress.close();
        doneMessage && alert(doneMessage, title, error);
    }

    //====================================================================
    //               END PROGRAM EXECUTION, BEGIN FUNCTIONS
    //====================================================================

    function itemByName(o, name) {
        var i;
        for (i = 0; i < o.length; i++) {
            if (name == o[i].name) {
                return o[i];
            }
        }
        return null;
    }

    function getStyleNames(o) {
        var a1 = [];
        var a2 = [];
        var i;
        var name;
        for (i = 0; i < o.length; i++) {
            name = o[i].name;
            if (/^\[/.test(name)) {
                // Name begins with bracket.
                a1.push(name);
            } else {
                a2.push(name);
            }
        }
        // Sort names that do not begin with bracket.
        a2.sort(function (a, b) {
            // Case-insensitive.
            var aLow = a.toLowerCase();
            var bLow = b.toLowerCase();
            if (aLow < bLow) return -1;
            if (aLow > bLow) return 1;
            return 0;
        });
        // Combine names with brackets + names without sorted.
        return a1.concat(a2);
    }

    function process() {
        var graphic;
        var graphics;
        var i;
        var link;
        var selection;
        // Preserve preferences.
        var preserve = {
            rulerOrigin: doc.viewPreferences.rulerOrigin
        };
        app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
        app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;
        doc.viewPreferences.rulerOrigin = RulerOrigin.SPREAD_ORIGIN;
        try {
            // Get (or make) layer.
            if (/\[Create /.test(listLayer.selection.text)) {
                doc.activeLayer = doc.layers[0];
                layer = doc.layers.add();
                layer.name = "Graphic Labels";
            } else {
                layer = doc.layers.item(listLayer.selection.text);
            }
            doc.activeLayer = layer;
            // Unlock layer and make visible.
            layer.locked = false;
            layer.visible = true;
            // Get swatch text.
            swatchText = doc.swatches.itemByName("Black");
            if (!swatchText.isValid) {
                // Couldn't get swatch by name. Try index 3, usual position for 'Black'.
                swatchText = doc.swatches[3];
            }
            // Get paragraph style.
            if (listParagraphStyle.selection > 0) {
                paragraphStyle = itemByName(doc.allParagraphStyles, listParagraphStyle.selection.text);
            } else {
                font = app.fonts.itemByName("Arial\tRegular");
                try {
                    font.fullName;
                } catch (_) {
                    // Couldn't get font by name. Use font from [Basic Paragraph]
                    font = doc.paragraphStyles[1].appliedFont;
                }
            }
            // Get object style.
            if (listObjectStyle.selection > 0) {
                objectStyle = itemByName(doc.allObjectStyles, listObjectStyle.selection.text);
            } else {
                swatchFrameFill = doc.swatches.itemByName("C=0 M=0 Y=100 K=0");
                if (!swatchFrameFill.isValid) {
                    // Couldn't get swatch by name. Create it.
                    swatchFrameFill = doc.colors.add();
                    swatchFrameFill.name = "C=0 M=0 Y=100 K=0";
                    swatchFrameFill.space = ColorSpace.CMYK;
                    swatchFrameFill.colorValue = [0, 0, 100, 0];
                    swatchFrameFill.model = ColorModel.PROCESS;
                }
                swatchFrameStroke = doc.swatches.itemByName("Black");
            }
            count = 0;
            if (rbAll.value) {
                // Label all graphics.
                graphics = doc.allGraphics;
                progress.set(graphics.length);
                for (i = graphics.length - 1; i > -1; i--) {
                    progress.increment();
                    graphic = graphics[i];
                    link = graphic.itemLink;
                    if (link) {
                        progress.display(link.name);
                        if (processGraphic(graphic, link.name)) {
                            count++;
                        }
                    }
                }
            } else {
                // Label selected graphic(s).
                for (i = 0; i < doc.selection.length; i++) {
                    selection = doc.selection[i];
                    if (!(selection instanceof Rectangle || selection.parent instanceof Rectangle)) {
                        // Selection is not a graphic or its frame.
                        continue;
                    }
                    if (selection instanceof Rectangle) {
                        graphic = selection.allGraphics[0];
                    } else {
                        graphic = selection;
                    }
                    link = graphic.itemLink;
                    if (link) {
                        processGraphic(graphic, link.name);
                        count++;
                    }
                }
            }
        } catch (e) {
            error = e;
            throw e;
        } finally {
            // Reset script preferences.
            app.scriptPreferences.userInteractionLevel = UserInteractionLevels.INTERACT_WITH_ALL;
            // Restore preferences.
            doc.viewPreferences.rulerOrigin = preserve.rulerOrigin;
        }
    }

    function processGraphic(graphic, name) {
        var gb1; // geometric bounds graphic
        var gb2; // geometric bounds text frame
        var parent;
        var textFrame;
        var x;
        var y;
        // Test if graphic is overset.
        try {
            gb1 = graphic.parent.geometricBounds;
        } catch (_) {
            // Probably overset.
            // Ignore.
            return false; // Failed
        }
        // Get parent page or spread.
        parent = graphic.parentPage || graphic.parent.parent;
        // Create and configure text frame.
        textFrame = parent.textFrames.add();
        textFrame.itemLayer = layer;
        // Character style 'None' must precede adding contents.
        textFrame.insertionPoints[0].appliedCharacterStyle = doc.characterStyles[0];
        textFrame.contents = name;
        // Set styles.
        if (paragraphStyle) {
            textFrame.paragraphs[0].appliedParagraphStyle = paragraphStyle;
        } else {
            textFrame.paragraphs[0].appliedParagraphStyle = doc.paragraphStyles[0];
            // Style paragraph manually.
            textFrame.paragraphs[0].appliedFont = font;
            textFrame.paragraphs[0].fillColor = swatchText;
            textFrame.paragraphs[0].fillTint = 100;
            textFrame.paragraphs[0].pointSize = 9; // points
        }
        // These paragraph properties always set manually.
        textFrame.paragraphs[0].justification = Justification.LEFT_ALIGN;
        textFrame.paragraphs[0].alignToBaseline = false;
        if (objectStyle) {
            textFrame.appliedObjectStyle = objectStyle;
        } else {
            textFrame.appliedObjectStyle = doc.objectStyles[0];
            // Style frame manually.
            textFrame.fillColor = swatchFrameFill;
            textFrame.fillTint = 100;
            textFrame.strokeColor = swatchFrameStroke;
            textFrame.strokeTint = 100;
            textFrame.strokeWeight = 1; // points
            textFrame.textFramePreferences.verticalJustification = VerticalJustification.CENTER_ALIGN;
            textFrame.textFramePreferences.insetSpacing = 6;
        }
        // These frame properties always set manually.
        textFrame.textFramePreferences.autoSizingReferencePoint = AutoSizingReferenceEnum.TOP_LEFT_POINT;
        textFrame.textFramePreferences.autoSizingType = AutoSizingTypeEnum.HEIGHT_AND_WIDTH;
        textFrame.textFramePreferences.useNoLineBreaksForAutoSizing = true;
        textFrame.textFramePreferences.ignoreWrap = true;
        textFrame.textWrapPreferences.textWrapMode = TextWrapModes.NONE;
        if (rbCorner.value) {
            // Move text frame to top-left corner of graphic.
            // But not beyond page edge.
            x = Math.max(0, gb1[1]);
            y = Math.max(0, gb1[0]);
        } else {
            // Center text frame within graphic.
            gb2 = textFrame.geometricBounds;
            x = (gb1[1] + ((gb1[3] - gb1[1]) / 2)) - ((gb2[3] - gb2[1]) / 2);
            y = (gb1[0] + ((gb1[2] - gb1[0]) / 2)) - ((gb2[2] - gb2[0]) / 2);
        }
        textFrame.move([x, y]); // Absolute coordinates.
        return true; // Success
    }

})();
Help me keep making new scripts by supporting my work. Click the PayPal button to contribute any amount you choose. Thank you. William Campbell
Download
Label Graphic Links

For help installing scripts, see How to Install and Use Scripts in Adobe Creative Cloud Applications.

IMPORTANT: scripts are developed for the latest Adobe Creative Cloud applications. Many scripts work in CC 2018 and later, even some as far back as CS6, but may not perform as expected, or run at all, when used in versions prior to 2018. Photoshop features Select Subject and Preserve Details 2.0 definitely fail prior to CC 2018 (version 19) as the features do not exist in earlier versions. For best results use the latest versions of Adobe Creative Cloud applications.

IMPORTANT: by downloading any of the scripts on this page you agree that the software is provided without any warranty, express or implied. USE AT YOUR OWN RISK. Always make backups of important data.

IMPORTANT: fees paid for software products are the purchase of a non-exclusive license to use the software product and do not grant the purchaser any degree of ownership of the software code. Author of the intellectual property and copyright holder William Campbell retains 100% ownership of all code used in all software products regardless of the inspiration for the software product design or functionality.