Skip to main content
N4A
Participant
April 20, 2026
Question

I have an image that is A3 size. Whenever I scale down the image, so I can reveal a white border, I notice they are not equal on all sides (as in the longest and shortest sides are not the same width) What am I doing wrong?

  • April 20, 2026
  • 5 replies
  • 96 views

I have an image that is A3 size. Whenever I scale down the image, so I can reveal a white border, I notice they are not equal on all sides (as in the longest and shortest sides are not the same width). 

Wether I do this by transforming holding ALT, or alone and centring after, I never have an equal border on all sides. 

If I change the canvas size (with relative ticked) this makes the entire image larger than A3, which I do not want. 

How can I scale down my image and add a border, so that the total image size is A3 and the border is an equal width on all sides?

 

Thanks - N4A

    5 replies

    Stephen Marsh
    Community Expert
    Community Expert
    April 30, 2026

    Here is the script:

     

     

    The “Scale Layer & Add Even Border Retaining Canvas Size 1-0.jsx” script does the following:

     

    Scales the main image layer inward and adds a uniform/even border around it, while keeping the overall canvas size exactly the same as the original.

     

    Key Features:

    • Works only on single-layer documents, flattened or floating (requires flattening/merging layers first).
    • Allows the user to specify border width in px, cm, mm, or inches, with live unit conversion and equivalent values displayed.
    • Allows the user to choose the border color via Photoshop’s color picker (defaults to paper white).
    • Offers two operating modes:
    1. Distort mode (default): Non-proportionally scales the image to exactly fit inside the border (different X and Y scaling percentages). The image may become slightly stretched/squashed.
    2. Scale & Mask mode (Proportional): Scales the image proportionally (using the larger of the two required scale factors so it fully fits), then adds a layer mask to “crop” and hide the overhanging parts, ensuring the visible content remains centered with a perfectly even border.


    Step-by-step :

    1. Centers the original layer in the canvas.
    2. Creates a new solid color layer named "Border" behind the image (this becomes the even border).
    3. Scales the image layer down so that the specified border width appears evenly on all four sides.
    4. In "Scale & Mask" mode: converts the layer to a Smart Object and masks it to prevent any overhang.
    5. Preserves the original document/canvas dimensions and resolution.

    Main Benefit:
    It automates and solves the problem where manually scaling an image and adding a border results in uneven borders, by mathematically calculating the exact scaling needed for perfectly even borders on all sides.

     

    /*
    Scale Layer & Add Even Border Retaining Canvas Size 1-0.jsx
    Stephen Marsh
    v1.0 - 30th April 2026: Inital release
    https://community.adobe.com/questions-712/i-have-an-image-that-is-a3-size-whenever-i-scale-down-the-image-so-i-can-reveal-a-white-border-i-notice-they-are-not-equal-on-all-sides-as-in-the-longest-and-shortest-sides-are-not-the-same-width-what-am-i-doing-wrong-1558366
    */

    #target photoshop

    (function () {
    if (!app.documents.length) {
    alert("A document must be open to run this script!");
    return;
    }

    var doc = app.activeDocument;

    if (doc.layers.length > 1) {
    alert("This script is intended for a single-layer document." + "\n" + "Please flatten or merge layers before running.");
    return;
    }

    var docW = doc.width.as("px");
    var docH = doc.height.as("px");
    var ppi = doc.resolution;

    ///// STATE /////
    var lastUnit = "px";

    // MAIN UI WINDOW
    var dlg = new Window("dialog", "Scale Layer & Add Even Border Retaining Canvas Size (v1.0)");
    dlg.orientation = "column";
    dlg.alignChildren = "fill";
    dlg.preferredSize.width = 320;

    // INPUT ROW
    var mainGroup = dlg.add("group");
    mainGroup.orientation = "row";
    mainGroup.alignChildren = "center";
    mainGroup.add("statictext", undefined, "Border:");

    var borderInput = mainGroup.add("editnumber", undefined, "118");
    borderInput.characters = 7;

    var unitDropdown = mainGroup.add("dropdownlist", undefined, ["px", "cm", "mm", "in"]);
    unitDropdown.selection = 0;

    var unitLabel = mainGroup.add("statictext", undefined, "");
    unitLabel.characters = 28;

    // COLOUR
    var colorGroup = dlg.add("group");
    colorGroup.add("statictext", undefined, "Border Color:");
    var colorButton = colorGroup.add("button", undefined, "Choose Color");
    var selectedColor = { r: 255, g: 255, b: 255 };

    // RGB LABELS
    var rgbGroup = dlg.add("group");
    var rLabel = rgbGroup.add("statictext", undefined, "R: 255");
    rgbGroup.add("statictext", undefined, " ");
    var gLabel = rgbGroup.add("statictext", undefined, "G: 255");
    rgbGroup.add("statictext", undefined, " ");
    var bLabel = rgbGroup.add("statictext", undefined, "B: 255");

    colorButton.onClick = function () {
    // Store original foreground color
    var origFG = app.foregroundColor;

    // Set foreground to current selected color so picker opens with it
    var tempColor = new SolidColor();
    tempColor.rgb.red = selectedColor.r;
    tempColor.rgb.green = selectedColor.g;
    tempColor.rgb.blue = selectedColor.b;
    app.foregroundColor = tempColor;

    // Open the native foreground color picker
    var d = new ActionDescriptor();
    d.putEnumerated(
    app.stringIDToTypeID("target"),
    app.stringIDToTypeID("colorPicker"),
    app.stringIDToTypeID("foregroundColor")
    );
    executeAction(app.stringIDToTypeID("showColorPicker"), d, DialogModes.ALL);

    // Read back whatever the user picked
    var picked = app.foregroundColor;
    selectedColor.r = picked.rgb.red;
    selectedColor.g = picked.rgb.green;
    selectedColor.b = picked.rgb.blue;

    updateRGBLabels();

    // Restore original foreground color
    app.foregroundColor = origFG;
    };

    // MODE PANEL
    var modePanel = dlg.add("panel", undefined, "Mode");
    modePanel.orientation = "column";
    modePanel.alignChildren = ["left", "top"];
    modePanel.margins = 10;
    var distortRadio = modePanel.add("radiobutton", undefined, "Distort");
    var maskRadio = modePanel.add("radiobutton", undefined, "Scale && Mask (Proportional)");
    distortRadio.value = true;

    // EVENTS
    borderInput.onChanging = function () {
    updateUnitLabel();
    updateDistortLabel();
    updateMaskLabel();
    };

    unitDropdown.onChange = function () {

    var newUnit = unitDropdown.selection.text;

    var currentValue = parseFloat(borderInput.text);
    if (isNaN(currentValue)) currentValue = 0;

    var oldUnit = lastUnit;

    var pxValue = toPx(currentValue, oldUnit);
    var newValue = fromPx(pxValue, newUnit);

    borderInput.text = newValue.toFixed(2);

    lastUnit = newUnit;

    updateUnitLabel();
    updateDistortLabel();
    updateMaskLabel();
    };

    updateUnitLabel();
    updateDistortLabel();
    updateMaskLabel();

    // BUTTONS
    var btns = dlg.add("group");
    btns.alignment = "right";
    btns.add("button", undefined, "Cancel");
    btns.add("button", undefined, "OK");

    if (dlg.show() != 1) return;

    var borderPx = getBorderPx();
    var mode = distortRadio.value ? "distort" : "mask";

    var originalUnits = app.preferences.rulerUnits;

    try {
    app.preferences.rulerUnits = Units.PIXELS;

    var innerW = docW - (borderPx * 2);
    var innerH = docH - (borderPx * 2);

    if (innerW <= 0 || innerH <= 0) {
    throw new Error("Border too large for the image.");
    }

    doc.suspendHistory("Scale Layer & Add Even Border Retaining Canvas Size", "main()");

    } catch (e) {
    alert(e.message);
    } finally {
    app.preferences.rulerUnits = originalUnits;
    }

    // MAIN LOGIC
    function main() {
    var layer = doc.activeLayer;

    if (layer.isBackgroundLayer) {
    layer.isBackgroundLayer = false;
    }

    var bounds = layer.bounds;
    var layerCenterX = (bounds[0].as("px") + bounds[2].as("px")) / 2;
    var layerCenterY = (bounds[1].as("px") + bounds[3].as("px")) / 2;

    var docCenterX = docW / 2;
    var docCenterY = docH / 2;

    layer.translate(docCenterX - layerCenterX, docCenterY - layerCenterY);

    var bg = doc.artLayers.add();
    bg.name = "Border";
    bg.move(layer, ElementPlacement.PLACEAFTER);

    doc.selection.selectAll();

    var borderColor = new SolidColor();
    borderColor.rgb.red = selectedColor.r;
    borderColor.rgb.green = selectedColor.g;
    borderColor.rgb.blue = selectedColor.b;
    doc.selection.fill(borderColor);
    doc.selection.deselect();
    doc.activeLayer = layer;

    var innerW = docW - (borderPx * 2);
    var innerH = docH - (borderPx * 2);
    var scaleX = (innerW / docW) * 100;
    var scaleY = (innerH / docH) * 100;

    if (mode === "distort") {
    executeAction(s2t("newPlacedLayer"), new ActionDescriptor(), DialogModes.NO);
    layer = doc.activeLayer;
    layer.resize(scaleX, scaleY, AnchorPosition.MIDDLECENTER);
    layer.name = "Smart Object";

    // TO DO: Select the layer and add a layer mask from selection, or is this just busy-work ???

    } else {
    var uniform = Math.max(scaleX, scaleY);
    layer.resize(uniform, uniform, AnchorPosition.MIDDLECENTER);
    executeAction(s2t("newPlacedLayer"), new ActionDescriptor(), DialogModes.NO);
    app.activeDocument.activeLayer.name = "Smart Object";
    addLayerMask();
    selectLayerMask();

    doc.selection.select([
    [borderPx, borderPx],
    [docW - borderPx, borderPx],
    [docW - borderPx, docH - borderPx],
    [borderPx, docH - borderPx]
    ]);

    doc.selection.invert();

    var black = new SolidColor();
    black.rgb.red = black.rgb.green = black.rgb.blue = 0;

    doc.selection.fill(black);
    doc.selection.deselect();

    selectRGBchannel();
    }
    }

    ///// CONVERSION CORE /////
    function toPx(value, unit) {
    switch (unit) {
    case "cm": return (value / 2.54) * ppi;
    case "mm": return (value / 25.4) * ppi;
    case "in": return value * ppi;
    default: return value;
    }
    }

    function fromPx(px, unit) {
    switch (unit) {
    case "cm": return (px / ppi) * 2.54;
    case "mm": return (px / ppi) * 25.4;
    case "in": return px / ppi;
    default: return px;
    }
    }

    function getBorderPx() {
    var value = parseFloat(borderInput.text);
    if (isNaN(value)) return 0;
    return toPx(value, unitDropdown.selection.text);
    }

    // UI UPDATES
    function updateUnitLabel() {
    var px = getBorderPx();
    var inches = px / ppi;
    var cm = inches * 2.54;
    var mm = inches * 25.4;

    unitLabel.text =
    "= " +
    cm.toFixed(2) + " cm / " +
    mm.toFixed(1) + " mm / " +
    inches.toFixed(2) + " in";
    }

    function updateDistortLabel() {
    var borderPx = getBorderPx();

    var innerW = docW - (borderPx * 2);
    var innerH = docH - (borderPx * 2);

    if (innerW > 0 && innerH > 0) {
    var scaleX = ((innerW / docW) * 100).toFixed(1);
    var scaleY = ((innerH / docH) * 100).toFixed(1);
    distortRadio.text = "Distort (X: " + scaleX + "% Y: " + scaleY + "%)";
    } else {
    distortRadio.text = "Distort && Mask (Border too large for canvas!)";
    }
    }

    // NEW FUNCTION
    function updateMaskLabel() {
    var borderPx = getBorderPx();

    var innerW = docW - (borderPx * 2);
    var innerH = docH - (borderPx * 2);

    if (innerW > 0 && innerH > 0) {
    var scaleX = (innerW / docW) * 100;
    var scaleY = (innerH / docH) * 100;

    var uniform = Math.max(scaleX, scaleY).toFixed(1);
    maskRadio.text = "Scale && Mask (Proportional: " + uniform + "%)";
    } else {
    maskRadio.text = "Scale && Mask (Border too large for canvas!)";
    }
    }

    function updateRGBLabels() {
    rLabel.text = "R: " + Math.round(selectedColor.r);
    gLabel.text = "G: " + Math.round(selectedColor.g);
    bLabel.text = "B: " + Math.round(selectedColor.b);
    }

    function s2t(s) {
    return app.stringIDToTypeID(s);
    }

    function addLayerMask() {
    var d = new ActionDescriptor();
    var r = new ActionReference();

    d.putClass(s2t("new"), s2t("channel"));
    r.putEnumerated(s2t("channel"), s2t("channel"), s2t("mask"));
    d.putReference(s2t("at"), r);
    d.putEnumerated(s2t("using"), s2t("userMaskEnabled"), s2t("revealAll"));

    executeAction(s2t("make"), d, DialogModes.NO);
    }

    function selectLayerMask() {
    var d = new ActionDescriptor();
    var r = new ActionReference();

    r.putEnumerated(s2t("channel"), s2t("channel"), s2t("mask"));
    d.putReference(s2t("null"), r);

    executeAction(s2t("select"), d, DialogModes.NO);
    }

    function selectRGBchannel() {
    var idslct = charIDToTypeID("slct");
    var desc2235 = new ActionDescriptor();
    var idnull = charIDToTypeID("null");
    var ref628 = new ActionReference();
    var idChnl = charIDToTypeID("Chnl");
    var idChnl = charIDToTypeID("Chnl");
    var idRGB = charIDToTypeID("RGB ");
    ref628.putEnumerated(idChnl, idChnl, idRGB);
    desc2235.putReference(idnull, ref628);
    var idMkVs = charIDToTypeID("MkVs");
    desc2235.putBoolean(idMkVs, false);
    executeAction(idslct, desc2235, DialogModes.NO);
    }

    })();

     

    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 for more info)

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

    N4A
    N4AAuthor
    Participant
    April 28, 2026

    Thank you all for your responses. I have tried the suggestions and in one way or another they change the size of the canvas. ​@ExUSA your answer makes sense and let’s me know I’m not crazy! 
     

    I’ll have to crop the image in some way. Thanks again all 🙏

    Claire H.
    Community Manager
    Community Manager
    April 28, 2026

    Hi ​@N4A, Glad to hear you found your answer! Can you let the community know what you ended up doing to solve your question? Thanks! ^CH

    Nancy OShea
    Community Expert
    Community Expert
    April 21, 2026

    Borders on images without rescaling. 

    Open Layer Styles with double-click on Layer.

    Set Stroke to desired size and color. See screenshot.

     

    Go to Image => Canvas Size

    Add Stroke Width Value (13 pixels) to Canvas width & height.

     

    Equal Borders on all sides. 

     

     

     

    Nancy O'Shea— Product User & Community Expert
    Legend
    April 21, 2026

    This will no longer be A3 size or aspect ratio, I think that’s the problem.

    War Unicorn
    Community Expert
    Community Expert
    April 21, 2026

    You could try creating a new layer, fill it with something like white, then add a stroke layer style (set as inner) with the color white. Then set that layer’s blend mode to Multiply, which will knock out the white. See screenshot.

    I think there’s more than one way of doing this, like using the Rectangle Tool with an inside stroke and no fill (via the Properties panel), but that’s the beauty of Photoshop.  :)

    Legend
    April 20, 2026

    You can’t without cropping. Math explains why.

    A3 paper has an aspect ratio of 1:1.414 which means if for every cm of the shorter dimension, you get 1.414 cm of the longer. So let’s say you add 1 cm all around to a 10cm x 14.14 cm image, you’d have 11 cm x 15.14 cm which is 1:1.376.

    Trevor.Dennis
    Community Expert
    Community Expert
    April 21, 2026

    Are you saying that you need the white outline to be a percentage of the width and height respectively?  Personally, I would add canvas (Ctrl Alt C)