Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
10

AI generating bleed area beyond trim line

Community Beginner ,
Apr 10, 2024 Apr 10, 2024

Every so often I run into a situation where I've created a full bleed design but when I go to set it up to print I realize that I forgot to include the bleed area past the trim line. Where this is especially annoying is when it is a photograph or piece of art that I don't really want to enlarge to account for the bleed.

A work around is copying and then reflecting the image on each edge and butting it up against the trim line. Also, you can generative fill the bleed area. Both of those tactics work, but they are annoying.

It would be awesome if there was a simple button to press where you said "create bleed" and you could set the size of the bleed you wanted and AI would just generate the bleed for you without any fuss. This would have to be in Photoshop.

Maybe this is just a me problem, lol, but we have the technology!!

Idea No status
TOPICS
Actions and scripting , macOS , Windows
4.7K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Expert , May 09, 2025 May 09, 2025

Introducing my "Add Canvas Bleed & Trim Marks" script:

 

Add-Canvas-Bleed-&-Trim-Marks-v1-0.png

 

Key features include:

* Define bleed in millimetres or inches

* Choose your canvas resize anchor point (upper left, middle centre, etc.)

* Leverage Content Aware Fill (v2020+) or Generative Fill (v2024+) to create bleed

* Add trim marks and/or centre marks in all colour channels, including spot colour channels!


Notes:

* Although the RGB/CMYK trim marks are vector, they may not be recognised as such in other applications

* The canvas siz

...
Translate
9 Comments
Enthusiast ,
Apr 27, 2024 Apr 27, 2024

Couldn't agree more. It's a shame this kind of stuff doesn't seem to be on people's AI radar - useful, meat and potatoes stuff that would take the drudgery out of daily work.

 

e.g.
If you ever do signage/billboards/outdoor where characters and/or objects are cut-out, you have to clear-cut the character, add bleed around the clearcut portion (hair, clothes, skin etc), then supply a vector knife path. Day-in, day-out.

 

AI could easily make this kind of work a thing of the past, including the high-quality, vector knife - right?

 

If Adobe isn't training AI on drawing bezier curves, they absolutely should be.

 

And Adobe, forget the design by comittee approach to ideas and votes etc. Take more of a leadership role and get this sorted because despite only two upvotes, surely you can see how everyone would benefit hugely?

Translate
Report
Community Expert ,
Apr 28, 2024 Apr 28, 2024

Content Aware Fill has made this much simpler, but not always perfect. Filter > Other > Minimum can also be of use for tight bleed tolerances. Yes, more work could be done in this area (even if nobody cares about print production anymore), I have added my vote!

 

https://community.adobe.com/t5/photoshop-ecosystem-discussions/adding-bleeds-to-an-outlined-photo/m-...

 

https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-bleed-photos-with-diecut-shape...

 

https://community.adobe.com/t5/photoshop-ecosystem-discussions/adding-bleed-to-complex-stickers/m-p/...

Translate
Report
Community Expert ,
May 09, 2025 May 09, 2025

Introducing my "Add Canvas Bleed & Trim Marks" script:

 

Add-Canvas-Bleed-&-Trim-Marks-v1-0.png

 

Key features include:

* Define bleed in millimetres or inches

* Choose your canvas resize anchor point (upper left, middle centre, etc.)

* Leverage Content Aware Fill (v2020+) or Generative Fill (v2024+) to create bleed

* Add trim marks and/or centre marks in all colour channels, including spot colour channels!


Notes:

* Although the RGB/CMYK trim marks are vector, they may not be recognised as such in other applications

* The canvas size trim mark extension is centred on the enlarged canvas (based on sizing from InDesign)

* Photoshop can't write PDF page box information (e.g. trim box, bleed box, etc.) to the PDF file

* A new merged document is created with additional bleed and optional trim and centre mark layers

* Existing ruler guides are cleared with new guides added at the original canvas edges

* Trim and centre marks are added to all printable channels, including spot channels

* Artboards are not supported, the script will abort if artboards are detected

* Intended for adding simple rectangular bleed, not complex contours

 

/*

Add Canvas Bleed & Trim Marks v1-0.jsx
Stephen Marsh
v1.0 - 16th April 2025

https://community.adobe.com/t5/photoshop-ecosystem-ideas/ai-generating-bleed-area-beyond-trim-line/idi-p/14547217#U15312767

Notes:
* Photoshop isn't the best tool for this, however, some users wish to quickly add bleed and trim marks directly in Photoshop (without printing)
* Although the RGB/CMYK trim marks are vector, they may not be recognised as such in other applications
* The canvas size trim mark extension is centred on the enlarged canvas (based on sizing from InDesign)
* Photoshop can't write PDF page box information (e.g. trim box, bleed box, etc.) to the PDF file
* A new merged document is created with additional bleed and optional trim and centre mark layers
* Existing ruler guides are cleared with new guides added at the original canvas edges
* Trim and centre marks are added to all printable channels, including spot channels
* Artboards are not supported, the script will abort if artboards are detected
* Intended for adding simple rectangular bleed, not complex contours

*/

#target photoshop;

// Check if there are any open documents
if (app.documents.length === 0) {
    alert("A document must be open to run this script!");
    exit();
}

// Ensure that v2020 or later is being used for Content-Aware Fill
var versionNumber = app.version.split(".");
var versionCheck = parseInt(versionNumber[0]);
if (versionCheck < 21) { // Photoshop 2020 is version 21.x
    alert("You must use Photoshop 2020 or later to run this script!");
    exit();
}

// Artboard check
var artboards = findArtboards();
if (artboards.length > 0) {
    alert("This script doesn't support artboards!");
    exit();
}

// Create the main dialog window
var dlg = new Window("dialog", "Add Canvas Bleed & Trim Marks (v1.0)");
dlg.preferredSize.width = 350;
dlg.orientation = "column";
dlg.alignChildren = "fill";

// Create main panel for controls
var mainPanel = dlg.add("panel");
mainPanel.orientation = "column";
mainPanel.alignChildren = "left";
mainPanel.margins = 16;

// Unit selection group
var unitGroup = mainPanel.add("group");
unitGroup.orientation = "row";
unitGroup.alignChildren = "left";
unitGroup.add("statictext", undefined, "Units:");
var unitDropdown = unitGroup.add("dropdownlist", undefined, ["Millimeters", "Inches"]);
unitDropdown.selection = 0;

// Size increment group
var sizeGroup = mainPanel.add("group");
sizeGroup.orientation = "row";
sizeGroup.alignChildren = "left";
sizeGroup.add("statictext", undefined, "Bleed:");
var sizeInput = sizeGroup.add("edittext", undefined, "3");
sizeInput.characters = 6;
var unitLabel = sizeGroup.add("statictext", undefined, "mm");

// Anchor point selection
var anchorGroup = mainPanel.add("group");
anchorGroup.orientation = "row";
anchorGroup.alignChildren = "left";
anchorGroup.add("statictext", undefined, "Canvas Resize Anchor Point:");
var anchorDropdown = anchorGroup.add("dropdownlist", undefined, [
    "Top Left", "Top Center", "Top Right",
    "Middle Left", "Middle Center", "Middle Right",
    "Bottom Left", "Bottom Center", "Bottom Right"
]);
anchorDropdown.selection = 4; // Default to Middle Center
anchorDropdown.helpTip = "Select the anchor point for the canvas resize. The bleed will be added to the opposite side(s).";

// Add selection for Fill Method
var fillMethodGroup = mainPanel.add("group");
fillMethodGroup.orientation = "row";
fillMethodGroup.alignChildren = "left";
fillMethodGroup.add("statictext", undefined, "Bleed Extension Method:");

// Check Photoshop version for Generative Fill, Photoshop 2024 is version 25.x
var psVersion = parseInt(app.version.split('.')[0]);
var fillMethods = ["Content-Aware Fill"];
if (psVersion >= 25) {
    fillMethods.push("Generative Fill");
}
var fillMethodDropdown = fillMethodGroup.add("dropdownlist", undefined, fillMethods);
// Default to Content-Aware Fill
fillMethodDropdown.selection = 0;
fillMethodDropdown.helpTip = "Generative Fill is only available in Photoshop 2024 and later.";

// Add trim marks checkbox
var trimMarksCheckbox = mainPanel.add("checkbox", undefined, "Add Trim Marks to All Printable Channels");
trimMarksCheckbox.value = true; // Default checked

// Add a checkbox for centre marks
var centerPathCheckbox = mainPanel.add("checkbox", undefined, "Add Center Marks to All Printable Channels");
centerPathCheckbox.value = false; // Default unchecked

// Event listener for anchor point selection
anchorDropdown.onChange = function () {
    // Check if the selected anchor point is "Middle Center"
    if (anchorDropdown.selection.text === "Middle Center") {
        trimMarksCheckbox.enabled = true; // Enable the checkbox
        trimMarksCheckbox.value = true;  // Set the checkbox to true
    } else {
        trimMarksCheckbox.enabled = false; // Disable the checkbox
        trimMarksCheckbox.value = false;  // Uncheck the checkbox
    }
};

// Buttons group
var buttonGroup = dlg.add("group");
buttonGroup.orientation = "row";
buttonGroup.alignChildren = "right";
buttonGroup.alignment = "right";

var cancelButton = buttonGroup.add("button", undefined, "Cancel");
var okButton = buttonGroup.add("button", undefined, "OK");

// Event handlers
unitDropdown.onChange = function () {
    var isMetric = unitDropdown.selection.index === 0;
    unitLabel.text = isMetric ? "mm" : "in";
    sizeInput.text = isMetric ? "3" : "0.125"; // 1/8 inch
};

cancelButton.onClick = function () {
    dlg.close();
};

okButton.onClick = function () {
    try {

        // Duplicate the source doc to a merged copy using the selected fill method
        // Added for flexibility in future versions
        if (fillMethodDropdown.selection.index === 0) {
            dupeMergedDoc();
        } else {
            dupeMergedDoc();
        }

        // Get the values from UI
        var isMetric = unitDropdown.selection.index === 0;
        var increment = parseFloat(sizeInput.text);
        var anchorPosition = anchorDropdown.selection.index;
        var addTrimMarks = trimMarksCheckbox.value;

        // Clear existing guides
        var idClearAllGuides = stringIDToTypeID("clearAllGuides");
        executeAction(idClearAllGuides, undefined, DialogModes.NO);

        // Set ruler units to pixels to ensure consistency
        var originalRulerUnits = app.preferences.rulerUnits;
        app.preferences.rulerUnits = Units.PIXELS;

        // Add new guides at canvas edges
        var doc = app.activeDocument;
        // Convert canvas dimensions to pixels
        var canvasWidth = doc.width.as("px");
        var canvasHeight = doc.height.as("px");
        // Add vertical guides
        doc.guides.add(Direction.VERTICAL, 0); // Left edge
        doc.guides.add(Direction.VERTICAL, canvasWidth); // Right edge
        // Add horizontal guides
        doc.guides.add(Direction.HORIZONTAL, 0); // Top edge
        doc.guides.add(Direction.HORIZONTAL, canvasHeight); // Bottom edge

        // Restore original ruler units
        app.preferences.rulerUnits = originalRulerUnits;

        // Get the current document resolution in PPI
        var resolution = app.activeDocument.resolution;

        // Convert increment to pixels based on the unit and resolution
        var conversionFactor = isMetric ? (resolution / 25.45) : resolution; // metric conversion (rounded up .05)
        var pixelIncrement = increment * conversionFactor;

        // Get the increments based on anchor position
        var widthIncrement = pixelIncrement;
        var heightIncrement = pixelIncrement;

        // Double increments for center/middle positions (both bleed edges)
        if (anchorPosition === 1 || anchorPosition === 4 || anchorPosition === 7) { // Center horizontal
            widthIncrement *= 2;
        }
        if (anchorPosition === 3 || anchorPosition === 4 || anchorPosition === 5) { // Middle vertical
            heightIncrement *= 2;
        }

        // Action manager code for bleed canvas resize
        var idcanvasSize = stringIDToTypeID("canvasSize");
        var desc = new ActionDescriptor();
        // Set relative mode
        var idrelative = stringIDToTypeID("relative");
        desc.putBoolean(idrelative, true);
        // Set width and height
        var idwidth = stringIDToTypeID("width");
        var idheight = stringIDToTypeID("height");
        var idpixelsUnit = charIDToTypeID("#Pxl");
        desc.putUnitDouble(idwidth, idpixelsUnit, widthIncrement);
        desc.putUnitDouble(idheight, idpixelsUnit, heightIncrement);
        // Set horizontal position
        var idhorizontal = stringIDToTypeID("horizontal");
        var idhorizontalLocation = stringIDToTypeID("horizontalLocation");
        var horizontalValue;
        switch (anchorPosition) {
            case 2: case 5: case 8: // Right
                horizontalValue = "right";
                break;
            case 0: case 3: case 6: // Left
                horizontalValue = "left";
                break;
            default: // Center
                horizontalValue = "center";
        }
        desc.putEnumerated(idhorizontal, idhorizontalLocation, stringIDToTypeID(horizontalValue));
        // Set vertical position
        var idvertical = stringIDToTypeID("vertical");
        var idverticalLocation = stringIDToTypeID("verticalLocation");
        var verticalValue;
        switch (anchorPosition) {
            case 6: case 7: case 8: // Bottom
                verticalValue = "bottom";
                break;
            case 0: case 1: case 2: // Top
                verticalValue = "top";
                break;
            default: // Middle
                verticalValue = "center";
        }
        desc.putEnumerated(idvertical, idverticalLocation, stringIDToTypeID(verticalValue));
        executeAction(idcanvasSize, desc, DialogModes.NO);

        // Load the transparency selection from the base merged layer
        var idset = stringIDToTypeID("set");
        var desc627 = new ActionDescriptor();
        var idnull = stringIDToTypeID("null");
        var ref142 = new ActionReference();
        var idchannel = stringIDToTypeID("channel");
        var idselection = stringIDToTypeID("selection");
        ref142.putProperty(idchannel, idselection);
        desc627.putReference(idnull, ref142);
        var idto = stringIDToTypeID("to");
        var ref143 = new ActionReference();
        var idchannel = stringIDToTypeID("channel");
        var idchannel = stringIDToTypeID("channel");
        var idtransparencyEnum = stringIDToTypeID("transparencyEnum");
        ref143.putEnumerated(idchannel, idchannel, idtransparencyEnum);
        desc627.putReference(idto, ref143);
        executeAction(idset, desc627, DialogModes.NO);

        // Contract the selection by 1 pixel to remove any transparency anti-aliasing artifacts
        app.activeDocument.selection.contract(1);

        // Invert the selection
        executeAction(stringIDToTypeID("inverse"), undefined, DialogModes.NO);

        // Add the bleed using the selected fill method
        if (fillMethodDropdown.selection.index === 0) {
            contentAwareFill();
        } else {
            generativeFill();
        }

        // Deselect the selection
        app.activeDocument.selection.deselect();

        // If trim marks checkbox is checked, add additional canvas space
        if (addTrimMarks) {
            // Create action manager code for trim marks canvas resize
            var idcanvasSize = stringIDToTypeID("canvasSize");
            var descTrim = new ActionDescriptor();

            // Set relative mode
            var idrelative = stringIDToTypeID("relative");
            descTrim.putBoolean(idrelative, true);

            // Conditionally set width and height for trim marks space based on 3 mm or 0.125 inch
            if (isMetric) {
                // Metric trim mark space by scaling the bleed size, approximating InDesign
                var trimWidthIncrement = widthIncrement * 1.78;
                var trimHeightIncrement = heightIncrement * 1.78;
            } else {
                // Inches trim mark space by scaling the bleed size, approximating InDesign
                var trimWidthIncrement = widthIncrement * 1.75;
                var trimHeightIncrement = heightIncrement * 1.75;
            }

            // Set width and height for trim marks space
            var idwidth = stringIDToTypeID("width");
            var idheight = stringIDToTypeID("height");
            var idpixelsUnit = charIDToTypeID("#Pxl");
            descTrim.putUnitDouble(idwidth, idpixelsUnit, trimWidthIncrement);
            descTrim.putUnitDouble(idheight, idpixelsUnit, trimHeightIncrement);

            // Use the same anchor point as the bleed extension
            descTrim.putEnumerated(idhorizontal, idhorizontalLocation, stringIDToTypeID(horizontalValue));
            descTrim.putEnumerated(idvertical, idverticalLocation, stringIDToTypeID(verticalValue));

            // Resize the canvas for trim marks
            executeAction(idcanvasSize, descTrim, DialogModes.NO);

            // Call the function to add trim marks
            vectorTrimMarks();
        }

        // End of script execution
        app.beep();
        dlg.close();

    } catch (e) {
        alert("Error: " + e.message + '\n' + "Line: " + e.line);
    }
};

// Render the dialog
dlg.center();
dlg.show();

/*
// Merge the trim marks layer down into the bleed layer
var idmergeLayersNew = stringIDToTypeID("mergeLayersNew");
var desc367 = new ActionDescriptor();
var idapply = stringIDToTypeID("apply");
desc367.putBoolean(idapply, true);
executeAction(idmergeLayersNew, desc367, DialogModes.NO);
// Set the name of the merged layer
app.activeDocument.activeLayer.name = "Trim Marks & Bleed";
*/


///// FUNCTIONS /////

function findArtboards() {
    var layers = app.activeDocument.layers;
    var artboards = [];
    for (var i = 0; i < layers.length; i++) {
        if (isArtboard(layers[i].id)) {
            artboards.push(layers[i]);
        }
    }
    return artboards;

    function isArtboard(layerID) {
        try {
            var r = new ActionReference();
            r.putIdentifier(stringIDToTypeID('layer'), layerID);
            var options = executeActionGet(r);
            // Check for the artboard key
            return options.hasKey(stringIDToTypeID('artboard'));
        } catch (e) {
            // Return false if an error occurs
            return false;
        }
    }

}

function dupeMergedDoc() {
    var origDoc = app.activeDocument; // original source document
    var origDocName = origDoc.name.replace(/\.[^\.]+$/, '');
    var bleedDoc = origDoc.duplicate(origDocName + "_Bleed", true); // new document, use true for merged copy
    bleedDoc.activeLayer.name = origDocName + " (Merged Copy)";
    return origDoc;
}

function contentAwareFill() {
    var s2t = function (s) {
        return app.stringIDToTypeID(s);
    };
    var descriptor = new ActionDescriptor();
    descriptor.putEnumerated(s2t("cafSamplingRegion"), s2t("cafSamplingRegion"), s2t("cafSamplingRegionAuto"));
    descriptor.putBoolean(s2t("cafSampleAllLayers"), true);
    descriptor.putEnumerated(s2t("cafColorAdaptationLevel"), s2t("cafColorAdaptationLevel"), s2t("cafColorAdaptationDefault"));
    descriptor.putEnumerated(s2t("cafRotationAmount"), s2t("cafRotationAmount"), s2t("cafRotationAmountNone"));
    descriptor.putBoolean(s2t("cafScale"), false);
    descriptor.putBoolean(s2t("cafMirror"), false);
    descriptor.putEnumerated(s2t("cafOutput"), s2t("cafOutput"), s2t("cafOutputToNewLayer"));
    executeAction(s2t("cafWorkspace"), descriptor, DialogModes.NO);
    // Rename the layer to "Bleed"
    app.activeDocument.activeLayer.name = "Bleed";
}

function generativeFill() {
    var idsyntheticFill = stringIDToTypeID("syntheticFill");
    var desc299 = new ActionDescriptor();
    var idnull = stringIDToTypeID("null");
    var ref20 = new ActionReference();
    var iddocument = stringIDToTypeID("document");
    var idordinal = stringIDToTypeID("ordinal");
    var idtargetEnum = stringIDToTypeID("targetEnum");
    ref20.putEnumerated(iddocument, idordinal, idtargetEnum);
    desc299.putReference(idnull, ref20);
    var iddocumentID = stringIDToTypeID("documentID");
    desc299.putInteger(iddocumentID, 64);
    var idlayerID = stringIDToTypeID("layerID");
    desc299.putInteger(idlayerID, 43);
    var idprompt = stringIDToTypeID("prompt");
    desc299.putString(idprompt, """""");
    var idserviceID = stringIDToTypeID("serviceID");
    desc299.putString(idserviceID, """clio""");
    var idworkflowType = stringIDToTypeID("workflowType");
    var idgenWorkflow = stringIDToTypeID("genWorkflow");
    var idin_painting = stringIDToTypeID("in_painting");
    desc299.putEnumerated(idworkflowType, idgenWorkflow, idin_painting);
    var idserviceOptionsList = stringIDToTypeID("serviceOptionsList");
    var desc300 = new ActionDescriptor();
    var idclio = stringIDToTypeID("clio");
    var desc301 = new ActionDescriptor();
    var idgi_PROMPT = stringIDToTypeID("gi_PROMPT");
    desc301.putString(idgi_PROMPT, """""");
    var idgi_MODE = stringIDToTypeID("gi_MODE");
    desc301.putString(idgi_MODE, """ginp""");
    var idgi_SEED = stringIDToTypeID("gi_SEED");
    desc301.putInteger(idgi_SEED, -1);
    var idgi_NUM_STEPS = stringIDToTypeID("gi_NUM_STEPS");
    desc301.putInteger(idgi_NUM_STEPS, -1);
    var idgi_GUIDANCE = stringIDToTypeID("gi_GUIDANCE");
    desc301.putInteger(idgi_GUIDANCE, 6);
    var idgi_SIMILARITY = stringIDToTypeID("gi_SIMILARITY");
    desc301.putInteger(idgi_SIMILARITY, 0);
    var idgi_CROP = stringIDToTypeID("gi_CROP");
    desc301.putBoolean(idgi_CROP, false);
    var idgi_DILATE = stringIDToTypeID("gi_DILATE");
    desc301.putBoolean(idgi_DILATE, false);
    var idgi_CONTENT_PRESERVE = stringIDToTypeID("gi_CONTENT_PRESERVE");
    desc301.putInteger(idgi_CONTENT_PRESERVE, 0);
    var idgi_ENABLE_PROMPT_FILTER = stringIDToTypeID("gi_ENABLE_PROMPT_FILTER");
    desc301.putBoolean(idgi_ENABLE_PROMPT_FILTER, true);
    var iddualCrop = stringIDToTypeID("dualCrop");
    desc301.putBoolean(iddualCrop, true);
    var idgi_ADVANCED = stringIDToTypeID("gi_ADVANCED");
    desc301.putString(idgi_ADVANCED, """{"enable_mts":true}""");
    var idclio = stringIDToTypeID("clio");
    desc300.putObject(idclio, idclio, desc301);
    var idnull = stringIDToTypeID("null");
    desc299.putObject(idserviceOptionsList, idnull, desc300);
    executeAction(idsyntheticFill, desc299, DialogModes.NO);

    /*
    // Rasterize the genertaive fill layer
    try {
        var s2t = function (s) {
            return app.stringIDToTypeID(s);
        };
        var descriptor = new ActionDescriptor();
        var reference = new ActionReference();
        reference.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
        descriptor.putReference(s2t("null"), reference);
        executeAction(s2t("rasterizeLayer"), descriptor, DialogModes.NO);
    } catch (e) { alert(e); }
    */

    // Rename the layer to "Bleed"
    app.activeDocument.activeLayer.name = "Bleed";

}

function selBoundsToShapeSubtraction() {
    var s2t = function (s) {
        return app.stringIDToTypeID(s);
    };
    // Get the active selection bounds and convert to pixels
    var doc = app.activeDocument;
    var selBounds = doc.selection.bounds;
    var left = selBounds[0].as("px");   // Convert left to pixels
    var top = selBounds[1].as("px");    // Convert top to pixels
    var right = selBounds[2].as("px");  // Convert right to pixels
    var bottom = selBounds[3].as("px"); // Convert bottom to pixels
    // Create descriptors and reference
    var descriptor = new ActionDescriptor();
    var descriptor2 = new ActionDescriptor();
    var reference = new ActionReference();
    reference.putEnumerated(s2t("path"), s2t("ordinal"), s2t("targetEnum"));
    descriptor.putReference(s2t("null"), reference);
    // Set the rectangle coordinates using pixel-based selection bounds
    descriptor2.putInteger(s2t("unitValueQuadVersion"), 1);
    descriptor2.putUnitDouble(s2t("top"), s2t("pixelsUnit"), top);
    descriptor2.putUnitDouble(s2t("left"), s2t("pixelsUnit"), left);
    descriptor2.putUnitDouble(s2t("bottom"), s2t("pixelsUnit"), bottom);
    descriptor2.putUnitDouble(s2t("right"), s2t("pixelsUnit"), right);
    descriptor2.putUnitDouble(s2t("topRight"), s2t("pixelsUnit"), 0.000000);
    descriptor2.putUnitDouble(s2t("topLeft"), s2t("pixelsUnit"), 0.000000);
    descriptor2.putUnitDouble(s2t("bottomLeft"), s2t("pixelsUnit"), 0.000000);
    descriptor2.putUnitDouble(s2t("bottomRight"), s2t("pixelsUnit"), 0.000000);
    // Assign the rectangle object and execute the action
    descriptor.putObject(s2t("to"), s2t("rectangle"), descriptor2);
    executeAction(s2t("subtractFrom"), descriptor, DialogModes.NO);
}

function vectorTrimMarks() {
    /*
    https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-export-guides-is-it-possible/m-p/13635116
    Based on code by c.pfaffenbichler
    */
    if (app.documents.length > 0 && app.activeDocument.guides.length > 0) {
        var originalRulerUnits = app.preferences.rulerUnits;
        app.preferences.rulerUnits = Units.POINTS;
        var myDocument = app.activeDocument;
        var theGuides = collectGuides();
        var theArray = new Array;

        // Process horizontal guides
        for (var m = 0; m < theGuides[0].length; m++) {
            var theW = Number(myDocument.width);
            var thisOne = Number(theGuides[0][m]);
            theArray.push([[[0, thisOne], [0, thisOne], [0, thisOne], false], [[theW, thisOne], [theW, thisOne], [theW, thisOne], false], true, 1737]);
        };

        // Conditionally add the center horizontal path
        if (centerPathCheckbox.value) {
            var centerY = Number(myDocument.height) / 2;
            theArray.push([[[0, centerY], [0, centerY], [0, centerY], false], [[theW, centerY], [theW, centerY], [theW, centerY], false], true, 1737]);
        }

        // Process vertical guides
        for (var n = 0; n < theGuides[1].length; n++) {
            var theH = Number(myDocument.height);
            var thisOne = Number(theGuides[1][n]);
            theArray.push([[[thisOne, 0], [thisOne, 0], [thisOne, 0], false], [[thisOne, theH], [thisOne, theH], [thisOne, theH], false], true, 1737]);
        };

        // Conditionally add the center vertical path
        if (centerPathCheckbox.value) {
            var centerX = Number(myDocument.width) / 2;
            theArray.push([[[centerX, 0], [centerX, 0], [centerX, 0], false], [[centerX, theH], [centerX, theH], [centerX, theH], false], true, 1737]);
            // Set a layer name variable for "Trim & Center Marks"
            var centerMarksLayerName = "Trim & Center Marks";
        }

        var thePath = createPath2022(theArray, "tempPaths");
        myDocument.pathItems[thePath - 1].select();
        shapeLayerWithStroke(2, 100, 100, 100, 100);
        app.preferences.rulerUnits = originalRulerUnits;

        // Rename the marks layer
        if (centerMarksLayerName != null) {
            activeDocument.activeLayer.name = centerMarksLayerName;
        } else {
            activeDocument.activeLayer.name = "Trim Marks";
        }

        /*
        // Rasterize the vector shape layer
        var idrasterizeLayer = stringIDToTypeID("rasterizeLayer");
        var desc1991 = new ActionDescriptor();
        var idnull = stringIDToTypeID("null");
        var ref686 = new ActionReference();
        var idlayer = stringIDToTypeID("layer");
        var idordinal = stringIDToTypeID("ordinal");
        var idtargetEnum = stringIDToTypeID("targetEnum");
        ref686.putEnumerated(idlayer, idordinal, idtargetEnum);
        desc1991.putReference(idnull, ref686);
        executeAction(idrasterizeLayer, desc1991, DialogModes.NO);
                
        if (centerMarksLayerName != null) {
            activeDocument.activeLayer.name = centerMarksLayerName;
        } else {
            activeDocument.activeLayer.name = "Trim Marks";
        }
        */

        // Select the bottom layer
        activeDocument.activeLayer = activeDocument.layers[activeDocument.layers.length - 1];

        // Load the transparency selection from the bottom layer
        var idset = stringIDToTypeID("set");
        var desc2119 = new ActionDescriptor();
        var idnull = stringIDToTypeID("null");
        var ref754 = new ActionReference();
        var idchannel = stringIDToTypeID("channel");
        var idselection = stringIDToTypeID("selection");
        ref754.putProperty(idchannel, idselection);
        desc2119.putReference(idnull, ref754);
        var idto = stringIDToTypeID("to");
        var ref755 = new ActionReference();
        var idchannel = stringIDToTypeID("channel");
        var idchannel = stringIDToTypeID("channel");
        var idtransparencyEnum = stringIDToTypeID("transparencyEnum");
        ref755.putEnumerated(idchannel, idchannel, idtransparencyEnum);
        desc2119.putReference(idto, ref755);
        executeAction(idset, desc2119, DialogModes.NO);

        // Select the bleed layer
        app.activeDocument.activeLayer = app.activeDocument.layers["Bleed"];

        // Add the transparency selection from the bleed layer
        var idadd = stringIDToTypeID("add");
        var desc2122 = new ActionDescriptor();
        var idnull = stringIDToTypeID("null");
        var ref757 = new ActionReference();
        var idchannel = stringIDToTypeID("channel");
        var idchannel = stringIDToTypeID("channel");
        var idtransparencyEnum = stringIDToTypeID("transparencyEnum");
        ref757.putEnumerated(idchannel, idchannel, idtransparencyEnum);
        desc2122.putReference(idnull, ref757);
        var idto = stringIDToTypeID("to");
        var ref758 = new ActionReference();
        var idchannel = stringIDToTypeID("channel");
        var idselection = stringIDToTypeID("selection");
        ref758.putProperty(idchannel, idselection);
        desc2122.putReference(idto, ref758);
        executeAction(idadd, desc2122, DialogModes.NO);

        // Expand the selection by 2px
        app.activeDocument.selection.expand(2);

        // Select the top trim mark vector shape layer
        activeDocument.activeLayer = activeDocument.layers[0];

        // Subtract the bleed area from the vector trim marks
        selBoundsToShapeSubtraction();

        // Delete the temp paths
        app.activeDocument.pathItems.getByName("tempPaths").remove();

        // Deselect the selection
        app.activeDocument.selection.deselect();

        // First check if any spot channels exist before creating the alpha channel
        var doc = app.activeDocument;
        var spotChannelsExist = false;
        for (var i = 0; i < doc.channels.length; i++) {
            if (doc.channels[i].kind == ChannelType.SPOTCOLOR) {
                spotChannelsExist = true;
                break;
            }
        }

        // Only create the trim marks alpha channel if spot channels exist
        if (spotChannelsExist) {

            // Store the original active channels to restore later
            var originalActiveChannels = [];
            for (var i = 0; i < doc.activeChannels.length; i++) {
                originalActiveChannels.push(doc.activeChannels[i]);
            }

            // Store the original visible channels
            var originalVisibleChannels = [];
            for (var i = 0; i < doc.channels.length; i++) {
                if (doc.channels[i].visible) {
                    originalVisibleChannels.push(doc.channels[i]);
                }
            }

            // Duplicate the vector trim marks layer to a temp raster layer
            var s2t = function (s) {
                return app.stringIDToTypeID(s);
            };
            var descriptor = new ActionDescriptor();
            var descriptor2 = new ActionDescriptor();
            var list = new ActionList();
            var reference = new ActionReference();
            var reference2 = new ActionReference();
            reference.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
            descriptor.putReference(s2t("null"), reference);
            descriptor.putString(s2t("name"), "Raster Trim Marks Temp");
            descriptor.putList(s2t("ID"), list);
            executeAction(s2t("duplicate"), descriptor, DialogModes.NO);
            reference2.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
            descriptor2.putReference(s2t("null"), reference2);
            executeAction(s2t("rasterizeLayer"), descriptor2, DialogModes.NO);

            // Load the transparency selection from the trim marks layer
            var idset = stringIDToTypeID("set");
            var desc2358 = new ActionDescriptor();
            var idnull = stringIDToTypeID("null");
            var ref860 = new ActionReference();
            var idchannel = stringIDToTypeID("channel");
            var idselection = stringIDToTypeID("selection");
            ref860.putProperty(idchannel, idselection);
            desc2358.putReference(idnull, ref860);
            var idto = stringIDToTypeID("to");
            var ref861 = new ActionReference();
            var idchannel = stringIDToTypeID("channel");
            var idchannel = stringIDToTypeID("channel");
            var idtransparencyEnum = stringIDToTypeID("transparencyEnum");
            ref861.putEnumerated(idchannel, idchannel, idtransparencyEnum);
            desc2358.putReference(idto, ref861);
            executeAction(idset, desc2358, DialogModes.NO);

            // Delete the temporary raster trim marks layer
            app.activeDocument.activeLayer.remove();

            // Create a new alpha channel for the trim marks from the selection
            var idmake = stringIDToTypeID("make");
            var desc2869 = new ActionDescriptor();
            var idnew = stringIDToTypeID("new");
            var desc2870 = new ActionDescriptor();
            var idname = stringIDToTypeID("name");
            desc2870.putString(idname, """Temp Trim Marks Alpha""");
            var idcolorIndicates = stringIDToTypeID("colorIndicates");
            var idmaskIndicator = stringIDToTypeID("maskIndicator");
            var idselectedAreas = stringIDToTypeID("selectedAreas");
            desc2870.putEnumerated(idcolorIndicates, idmaskIndicator, idselectedAreas);
            var idcolor = stringIDToTypeID("color");
            var desc2871 = new ActionDescriptor();
            var idcyan = stringIDToTypeID("cyan");
            desc2871.putDouble(idcyan, 100.000000);
            var idmagenta = stringIDToTypeID("magenta");
            desc2871.putDouble(idmagenta, 100.000000);
            var idyellowColor = stringIDToTypeID("yellowColor");
            desc2871.putDouble(idyellowColor, 100.000000);
            var idblack = stringIDToTypeID("black");
            desc2871.putDouble(idblack, 100.000000);
            var idCMYKColorClass = stringIDToTypeID("CMYKColorClass");
            desc2870.putObject(idcolor, idCMYKColorClass, desc2871);
            var idopacity = stringIDToTypeID("opacity");
            desc2870.putInteger(idopacity, 50);
            var idchannel = stringIDToTypeID("channel");
            desc2869.putObject(idnew, idchannel, desc2870);
            var idusing = stringIDToTypeID("using");
            var ref1080 = new ActionReference();
            var idchannel = stringIDToTypeID("channel");
            var idselection = stringIDToTypeID("selection");
            ref1080.putProperty(idchannel, idselection);
            desc2869.putReference(idusing, ref1080);
            executeAction(idmake, desc2869, DialogModes.NO);

            // Loop through all channels in the document and apply the trim marks to spot channels
            for (var i = 0; i < doc.channels.length; i++) {
                var currentChannel = doc.channels[i];

                // Check if the channel is a spot color channel
                if (currentChannel.kind == ChannelType.SPOTCOLOR) {
                    // Make the spot channel active/selected
                    doc.activeChannels = [currentChannel];
                    // Apply the trim marks to the active spot channel
                    var idapplyImageEvent = stringIDToTypeID("applyImageEvent");
                    var desc760 = new ActionDescriptor();
                    var idwith = stringIDToTypeID("with");
                    var desc761 = new ActionDescriptor();
                    var idto = stringIDToTypeID("to");
                    var ref232 = new ActionReference();
                    var idchannel = stringIDToTypeID("channel");
                    ref232.putName(idchannel, "Temp Trim Marks Alpha");
                    desc761.putReference(idto, ref232);
                    var idcalculation = stringIDToTypeID("calculation");
                    var idcalculationType = stringIDToTypeID("calculationType");
                    var idmultiply = stringIDToTypeID("multiply");
                    desc761.putEnumerated(idcalculation, idcalculationType, idmultiply);
                    var idpreserveTransparency = stringIDToTypeID("preserveTransparency");
                    desc761.putBoolean(idpreserveTransparency, true);
                    var idcalculation = stringIDToTypeID("calculation");
                    desc760.putObject(idwith, idcalculation, desc761);
                    executeAction(idapplyImageEvent, desc760, DialogModes.NO);
                }
            }

            // Deselect the selection
            app.activeDocument.selection.deselect();

            // Restore the original active channels
            doc.activeChannels = originalActiveChannels;

            // Restore the original visible channels
            for (var i = 0; i < doc.channels.length; i++) {
                var currentChannel = doc.channels[i];
                // Set visibility based on whether it was in our saved array
                var shouldBeVisible = false;
                for (var j = 0; j < originalVisibleChannels.length; j++) {
                    if (currentChannel == originalVisibleChannels[j]) {
                        shouldBeVisible = true;
                        break;
                    }
                }
                currentChannel.visible = shouldBeVisible;
            }

            // Delete the temp Trim Marks alpha channel
            var iddelete = stringIDToTypeID("delete");
            var desc794 = new ActionDescriptor();
            var idnull = stringIDToTypeID("null");
            var ref285 = new ActionReference();
            var idchannel = stringIDToTypeID("channel");
            ref285.putName(idchannel, "Temp Trim Marks Alpha");
            desc794.putReference(idnull, ref285);
            executeAction(iddelete, desc794, DialogModes.NO);
        }

        function collectGuides() {
            // set to pixels;
            var myDocument = app.activeDocument;
            var originalRulerUnits = app.preferences.rulerUnits;
            app.preferences.rulerUnits = Units.POINTS;
            // collect guides;
            var theArray1 = new Array;
            var theArray2 = new Array;
            for (var m = 0; m < myDocument.guides.length; m++) {
                //theArray.push([myDocument.guides[m].coordinate, myDocument.guides[m].direction]);
                if (myDocument.guides[m].direction == Direction.HORIZONTAL) { theArray1.push(myDocument.guides[m].coordinate) }
                else { theArray2.push(myDocument.guides[m].coordinate) }
            };
            app.preferences.rulerUnits = originalRulerUnits;
            return ([theArray1, theArray2]);
        };

        function createPath2022(theArray, thePathsName) {
            var originalRulerUnits = app.preferences.rulerUnits;
            app.preferences.rulerUnits = Units.PIXELS;
            // thanks to xbytor;
            cTID = function (s) { return app.charIDToTypeID(s); };
            sTID = function (s) { return app.stringIDToTypeID(s); };

            var desc1 = new ActionDescriptor();
            var ref1 = new ActionReference();
            ref1.putProperty(cTID('Path'), cTID('WrPt'));
            desc1.putReference(sTID('null'), ref1);
            var list1 = new ActionList();

            for (var m = 0; m < theArray.length; m++) {
                var thisSubPath = theArray[m];

                var desc2 = new ActionDescriptor();
                desc2.putEnumerated(sTID('shapeOperation'), sTID('shapeOperation'), thisSubPath[thisSubPath.length - 1]);
                var list2 = new ActionList();
                var desc3 = new ActionDescriptor();
                desc3.putBoolean(cTID('Clsp'), thisSubPath[thisSubPath.length - 2]);
                var list3 = new ActionList();

                for (var n = 0; n < thisSubPath.length - 2; n++) {
                    var thisPoint = thisSubPath[n];

                    var desc4 = new ActionDescriptor();
                    var desc5 = new ActionDescriptor();
                    desc5.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[0][0]);
                    desc5.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[0][1]);
                    desc4.putObject(cTID('Anch'), cTID('Pnt '), desc5);
                    var desc6 = new ActionDescriptor();
                    desc6.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[1][0]);
                    desc6.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[1][1]);
                    desc4.putObject(cTID('Fwd '), cTID('Pnt '), desc6);
                    var desc7 = new ActionDescriptor();
                    desc7.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[2][0]);
                    desc7.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[2][1]);
                    desc4.putObject(cTID('Bwd '), cTID('Pnt '), desc7);
                    desc4.putBoolean(cTID('Smoo'), thisPoint[3]);
                    list3.putObject(cTID('Pthp'), desc4);

                };

                desc3.putList(cTID('Pts '), list3);
                list2.putObject(cTID('Sbpl'), desc3);
                desc2.putList(cTID('SbpL'), list2);
                list1.putObject(cTID('PaCm'), desc2);
            };

            desc1.putList(cTID('T   '), list1);
            executeAction(cTID('setd'), desc1, DialogModes.NO);

            // name work path;
            var desc30 = new ActionDescriptor();
            var ref6 = new ActionReference();
            var idPath = charIDToTypeID("Path");
            ref6.putClass(idPath);
            desc30.putReference(charIDToTypeID("null"), ref6);
            var ref7 = new ActionReference();
            ref7.putProperty(idPath, charIDToTypeID("WrPt"));
            desc30.putReference(charIDToTypeID("From"), ref7);
            desc30.putString(charIDToTypeID("Nm  "), thePathsName);
            executeAction(charIDToTypeID("Mk  "), desc30, DialogModes.NO);
            // get index;
            var ref = new ActionReference();
            ref.putProperty(stringIDToTypeID("property"), stringIDToTypeID("itemIndex"));
            ref.putEnumerated(charIDToTypeID("Path"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
            var pathDesc = executeActionGet(ref);
            app.preferences.rulerUnits = originalRulerUnits;
            return pathDesc.getInteger(stringIDToTypeID("itemIndex"))
        };

        function shapeLayerWithStroke(theWidth, cyan, magenta, yellow, black) {
            try {
                var idMk = charIDToTypeID("Mk  ");
                var desc23 = new ActionDescriptor();
                var idnull = charIDToTypeID("null");
                var ref8 = new ActionReference();
                var idcontentLayer = stringIDToTypeID("contentLayer");
                ref8.putClass(idcontentLayer);
                desc23.putReference(idnull, ref8);
                var idUsng = charIDToTypeID("Usng");
                var desc24 = new ActionDescriptor();
                var idType = charIDToTypeID("Type");
                var desc25 = new ActionDescriptor();
                var idClr = charIDToTypeID("Clr ");
                // Replace RGB with CMYK
                var idcyan = stringIDToTypeID("cyan");
                var idmagenta = stringIDToTypeID("magenta");
                var idyellowColor = stringIDToTypeID("yellowColor");
                var idblack = stringIDToTypeID("black");
                var idCMYKC = charIDToTypeID("CMYC");
                var idsolidColorLayer = stringIDToTypeID("solidColorLayer");
                desc24.putObject(idType, idsolidColorLayer, desc25);
                var idstrokeStyle = stringIDToTypeID("strokeStyle");
                var desc28 = new ActionDescriptor();
                var idstrokeStyleVersion = stringIDToTypeID("strokeStyleVersion");
                desc28.putInteger(idstrokeStyleVersion, 2);
                var idstrokeEnabled = stringIDToTypeID("strokeEnabled");
                desc28.putBoolean(idstrokeEnabled, true);
                var idfillEnabled = stringIDToTypeID("fillEnabled");
                desc28.putBoolean(idfillEnabled, false);
                /* stroke width */
                var idstrokeStyleLineWidth = stringIDToTypeID("strokeStyleLineWidth");
                var idPxl = charIDToTypeID("#Pxl");
                desc28.putUnitDouble(idstrokeStyleLineWidth, idPxl, theWidth);
                var idstrokeStyleLineDashOffset = stringIDToTypeID("strokeStyleLineDashOffset");
                desc28.putUnitDouble(idstrokeStyleLineDashOffset, idPxl, 0.000000);
                var idstrokeStyleMiterLimit = stringIDToTypeID("strokeStyleMiterLimit");
                desc28.putDouble(idstrokeStyleMiterLimit, 100.000000);
                /* stroke alignment */
                var idstrokeStyleLineAlignment = stringIDToTypeID("strokeStyleLineAlignment");
                var idstrokeStyleAlignCenter = stringIDToTypeID("strokeStyleAlignCenter");
                desc28.putEnumerated(idstrokeStyleLineAlignment, idstrokeStyleLineAlignment, idstrokeStyleAlignCenter);
                /* stroke color */
                var idstrokeStyleContent = stringIDToTypeID("strokeStyleContent");
                var desc29 = new ActionDescriptor();
                var desc30 = new ActionDescriptor();
                desc30.putDouble(idcyan, cyan);
                desc30.putDouble(idmagenta, magenta);
                desc30.putDouble(idyellowColor, yellow);
                desc30.putDouble(idblack, black);
                desc29.putObject(idClr, idCMYKC, desc30);
                desc28.putObject(idstrokeStyleContent, idsolidColorLayer, desc29);
                desc24.putObject(idstrokeStyle, idstrokeStyle, desc28);
                desc23.putObject(idUsng, idcontentLayer, desc24);
                var idLyrI = charIDToTypeID("LyrI");
                desc23.putInteger(idLyrI, 15);
                executeAction(idMk, desc23, DialogModes.NO);
            } catch (e) { }
        }
    }
}

 

  1. Copy the code text to the clipboard
  2. Open a new blank file in a plain-text editor (not in a word processor)
  3. Paste the code in
  4. Save as a plain text format file – .txt
  5. Rename the saved file extension from .txt to .jsx
  6. Install or browse to the .jsx file to run (see below)

https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html

Translate
Report
New Here ,
Jul 09, 2025 Jul 09, 2025

@Stephen Marsh Your script is really great, exactly what I needed!

Translate
Report
Enthusiast ,
Jul 09, 2025 Jul 09, 2025

Amazing - thank you!

Translate
Report
Community Beginner ,
Aug 05, 2025 Aug 05, 2025

@Stephen Marsh You are my hero!

Translate
Report
Community Expert ,
Aug 05, 2025 Aug 05, 2025

@SCA LLC - Thank you!

Translate
Report
New Here ,
Aug 05, 2025 Aug 05, 2025

Amazing script! So useful for my studio team! Thanks a bunch.

Translate
Report
Community Expert ,
Aug 05, 2025 Aug 05, 2025
LATEST

@markallibone - You're welcome, I'm glad that you and your team are finding it useful.

Translate
Report