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

Photoshop Script that checks each layer's dimensions are divisible by 'n' during PNG export

Community Beginner ,
Jul 15, 2023 Jul 15, 2023

Copy link to clipboard

Copied

Could it be possible to create a Photoshop script that on exporting layers as seperate PNGs does the following:

1. Checks if the layer is divisible by 16.

2. If it's not divisible by 16, pad on pixels equally to the Length & Width so it is now divisible by 16 & export PNG.

3. Else if the layer is divisible by 16, exports the png as normal.

 

This would be awesome in helping with exporting png assets for a game i'm creating.

Many thanks,

Steve

TOPICS
Actions and scripting , macOS , Windows

Views

469

Translate

Translate

Report

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 2 Correct answers

Community Expert , Jul 16, 2023 Jul 16, 2023
quote

Many thanks for that update, I got thte same error however but that's when no layer(s) are selected. The script worked after making sure at least one layer was selected 👍🏻

Would it be possible to tweak this with the following:

- all the layers in the PS doc are exported but as individual pngs, not merged into one?

- could each png be trimmed to remove the alpha, but still be divisible by 16? I guess the layer would be trimmed first then checked for it's divisibility. Th

...

Votes

Translate

Translate
Community Expert , Jul 24, 2023 Jul 24, 2023
quote

Hey @Stephen Marsh , apologies for taking a while to respond. Thank you so much for helping me with this conundrum, I tried your code and it works a treat! All layers in the PSD are being exported as required, layers not divisible 16 being adjusted and layers already divisible by 16 not effected and exported.

 

You're welcome, thank you for the feedback. Full credit to @jazz-y for the code to check if the document canvas is divisible by 16.

 

quote

Just one thing extra, is it possible to input a

...

Votes

Translate

Translate
Adobe
Community Expert ,
Jul 15, 2023 Jul 15, 2023

Copy link to clipboard

Copied

I'm pretty sure that this has come up before, did you search the forum?

 

EDIT:

 

https://community.adobe.com/t5/photoshop-ecosystem-discussions/save-an-image-so-it-s-width-amp-heigh...

 

EDIT 2:

 

I see that you posted in that topic a week ago without any reply.

Votes

Translate

Translate

Report

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
Community Expert ,
Jul 15, 2023 Jul 15, 2023

Copy link to clipboard

Copied

@SteviePee2 – Try this (now updated to a 1.3 version).

 

Notes:

 

* The original doc is duplicated to a temp doc and closed without saving after exporting at the new canvas size

 

* Images that are evenly divisible by 16 will not have the canvas size changed

 

* Images that are not evenly divisible by 16 will have the canvas size changed

 

* Transparent backgrounds are retained

 

* For flattened images, white will be used for the canvas extension colour, however, this could be changed to another RGB value or transparency if required

 

* Unsaved images will be prompted for a save location, while previously saved images will use the original document path for the PNG save location

 

* The original document name will be used as the base name for the PNG

 

* For safety, existing files will be prompted to be overwritten

 

* Export > Save for Web (Legacy) used rather than Save As

 

/*
Export Active Document to PNG Canvas Force Divisible by 16px.jsx
https://community.adobe.com/t5/photoshop-ecosystem-discussions/photoshop-script-that-checks-each-layer-s-dimensions-are-divisible-by-n-during-png-export/td-p/13938692
Based on code from jazz-y - https://community.adobe.com/t5/photoshop-ecosystem-discussions/save-an-image-so-it-s-width-amp-height-are-divisible-by-a-required-number/m-p/12819944
v1.3 - 17th July 2023, Stephen Marsh
*/

if (app.documents.length > 0) {

    #target photoshop;

    ////////////////////
    try {
        outputPath = app.activeDocument.path.fsName;
    } catch (e) {
        var outputPath = Folder.selectDialog("Unsaved base file, select the output folder:");
    }
    var filename = app.activeDocument.name.replace(/\.[^\.]+$/, '');
    activeDocument.duplicate(filename, false);
    var savedBackgroundColor = app.backgroundColor;
    ////////////////////

    /* jazz-y */
    var s2t = stringIDToTypeID,
        res = executeActionGet(getPropertyRef(p = s2t('resolution'))).getUnitDoubleValue(p),
        w = executeActionGet(getPropertyRef(p = s2t('width'))).getUnitDoubleValue(p) * res / 72,
        h = executeActionGet(getPropertyRef(p = s2t('height'))).getUnitDoubleValue(p) * res / 72;

    (d = new ActionDescriptor()).putUnitDouble(s2t('width'), s2t('pixelsUnit'), (Math.ceil(w / 16)) * 16);
    d.putUnitDouble(s2t('height'), s2t('pixelsUnit'), (Math.ceil(h / 16)) * 16);
    d.putEnumerated(s2t('horizontal'), s2t('horizontalLocation'), s2t('center'));
    d.putEnumerated(s2t('vertical'), s2t('verticalLocation'), s2t('center'));
    /* jazz-y */

    ////////////////////
    var tempBGcolor = new SolidColor();
    tempBGcolor.rgb.red = 255;
    tempBGcolor.rgb.green = 255;
    tempBGcolor.rgb.blue = 255;
    app.backgroundColor = tempBGcolor;
    ////////////////////

    /* jazz-y */
    executeAction(s2t('canvasSize'), d, DialogModes.NO);
    /* jazz-y */

    ////////////////////
    app.backgroundColor = savedBackgroundColor;

    var pngOptions = new ExportOptionsSaveForWeb();
    pngOptions.PNG8 = false;
    pngOptions.transparency = true;
    pngOptions.interlaced = false;
    pngOptions.quality = 100;
    pngOptions.includeProfile = true;
    pngOptions.format = SaveDocumentType.PNG;
    var pngFile = new File(outputPath + "/" + filename + ".png");

    if (pngFile.exists) {
        // true = 'No' as default active button
        if (!confirm("File exists, overwrite: Yes or No?", true))
            // throw alert("Script cancelled!");
            throw null;
    }
    app.activeDocument.exportDocument(pngFile, ExportType.SAVEFORWEB, pngOptions);
    activeDocument.close(SaveOptions.DONOTSAVECHANGES);
    ////////////////////

    /* jazz-y */
    function getPropertyRef(property) {
        (r = new ActionReference()).putProperty(s2t('property'), property);
        r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
        return r;
    }
    /* jazz-y */

} else {
    alert('You must have a document open!');
}

 

 

Votes

Translate

Translate

Report

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
Community Expert ,
Jul 15, 2023 Jul 15, 2023

Copy link to clipboard

Copied

I have just realised that you wish to do this for the active layer – not the entire document!

 

 

EDIT: Here is updated code for the active layer!

 

Notes:

 

* Intended for use on a single active layer

 

* Multiple selected layers will be duplicated to a temp doc to create a combined image when saved as PNG

 

* The temp doc is closed without saving after exporting at the new canvas size

 

* Images that are evenly divisible by 16 will not have the canvas size changed

 

* Images that are not evenly divisible by 16 will have the canvas size changed

 

* Transparent backgrounds are retained

 

* For flattened images, white will be used for the canvas extension colour, however, this could be changed to another RGB value or transparency if required

 

* Unsaved images will be prompted for a save location, while previously saved images will use the original document path for the PNG save location

 

* The active layer name or the top layer name for multiple layers will be used as the PNG file name

 

* For safety, existing files will be prompted to be overwritten

 

* Export > Save for Web (Legacy) used rather than Save As

 

/*
Export Active Layer to PNG Canvas Force Divisible by 16px.jsx
https://community.adobe.com/t5/photoshop-ecosystem-discussions/photoshop-script-that-checks-each-layer-s-dimensions-are-divisible-by-n-during-png-export/td-p/13938692
Based on code from jazz-y - https://community.adobe.com/t5/photoshop-ecosystem-discussions/save-an-image-so-it-s-width-amp-height-are-divisible-by-a-required-number/m-p/12819944
v1.2 - 17th July 2023, Stephen Marsh
*/

#target photoshop;

if (app.documents.length > 0) {

    /* jazz-y */
    s2t = stringIDToTypeID;
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayers'));
    r.putEnumerated(s2t("document"), s2t("ordinal"), s2t("targetEnum"));
    if (!executeActionGet(r).getList(p).count) {
        alert('A layer must be selected to export to PNG!');

    } else {
        ////////////////////
        try {
            outputPath = app.activeDocument.path.fsName;
        } catch (e) {
            var outputPath = Folder.selectDialog("Unsaved base file, select the output folder:");
        }
        var filename = app.activeDocument.activeLayer.name;
        dupeSelectedLayers(filename);
        //activeDocument.activeLayer.merge();
        var savedBackgroundColor = app.backgroundColor;
        ////////////////////

        /* jazz-y */
        var s2t = stringIDToTypeID,
            res = executeActionGet(getPropertyRef(p = s2t('resolution'))).getUnitDoubleValue(p),
            w = executeActionGet(getPropertyRef(p = s2t('width'))).getUnitDoubleValue(p) * res / 72,
            h = executeActionGet(getPropertyRef(p = s2t('height'))).getUnitDoubleValue(p) * res / 72;

        (d = new ActionDescriptor()).putUnitDouble(s2t('width'), s2t('pixelsUnit'), (Math.ceil(w / 16)) * 16);
        d.putUnitDouble(s2t('height'), s2t('pixelsUnit'), (Math.ceil(h / 16)) * 16);
        d.putEnumerated(s2t('horizontal'), s2t('horizontalLocation'), s2t('center'));
        d.putEnumerated(s2t('vertical'), s2t('verticalLocation'), s2t('center'));
        /* jazz-y */

        ////////////////////
        var tempBGcolor = new SolidColor();
        tempBGcolor.rgb.red = 255;
        tempBGcolor.rgb.green = 255;
        tempBGcolor.rgb.blue = 255;
        app.backgroundColor = tempBGcolor;
        ////////////////////

        /* jazz-y */
        executeAction(s2t('canvasSize'), d, DialogModes.NO);
        /* jazz-y */

        ////////////////////
        app.backgroundColor = savedBackgroundColor;

        var pngOptions = new ExportOptionsSaveForWeb();
        pngOptions.PNG8 = false;
        pngOptions.transparency = true;
        pngOptions.interlaced = false;
        pngOptions.quality = 100;
        pngOptions.includeProfile = true;
        pngOptions.format = SaveDocumentType.PNG;
        var pngFile = new File(outputPath + "/" + filename + ".png");

        if (pngFile.exists) {
            // true = 'No' as default active button
            if (!confirm("File exists, overwrite: Yes or No?", true))
                // throw alert("Script cancelled!");
                throw null;
        }
        app.activeDocument.exportDocument(pngFile, ExportType.SAVEFORWEB, pngOptions);
        activeDocument.close(SaveOptions.DONOTSAVECHANGES);

        function dupeSelectedLayers(theDocName) {
            function s2t(s) {
                return app.stringIDToTypeID(s);
            }
            var descriptor = new ActionDescriptor();
            var reference = new ActionReference();
            var reference2 = new ActionReference();
            reference.putClass(s2t("document"));
            descriptor.putReference(s2t("null"), reference);
            descriptor.putString(s2t("name"), theDocName);
            reference2.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
            descriptor.putReference(s2t("using"), reference2);
            executeAction(s2t("make"), descriptor, DialogModes.NO);
        }
        ////////////////////

        /* jazz-y */
        function getPropertyRef(property) {
            (r = new ActionReference()).putProperty(s2t('property'), property);
            r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
            return r;
        }
        /* jazz-y */
    }

} else {
    alert('You must have a document open!');
}

 

 

Votes

Translate

Translate

Report

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
Community Beginner ,
Jul 16, 2023 Jul 16, 2023

Copy link to clipboard

Copied

Hey @Stephen Marsh , many thanks for your replies. Yes I added a comment on my previous post but got not reply, so thought i'd try a new post.

I tried your code above by coping into a text doc and saving as .jsx. Is this correct?

When testing the script on an open PS doc I got the following error message:

Screenshot 2023-07-16 at 13.34.04.png

Apologies again for my lack of coding knowledge, your help is greatly appreciated!

Cheers, Steve

Votes

Translate

Translate

Report

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
Community Expert ,
Jul 16, 2023 Jul 16, 2023

Copy link to clipboard

Copied

@SteviePee2 

 

Looks like a version issue, what are you running?

 

EDIT: I have updated the second script to a 1.1 version with raw AM code from v2022 (rather than cleanSL AM code from v2021)... Let me know if that works. I tested the old code in v2021, 2022 and 2023 on a Mac without errors.

Votes

Translate

Translate

Report

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
Community Beginner ,
Jul 16, 2023 Jul 16, 2023

Copy link to clipboard

Copied

Hey @Stephen Marsh 

Many thanks for that update, I got thte same error however but that's when no layer(s) are selected. The script worked after making sure at least one layer was selected 👍🏻

Would it be possible to tweak this with the following:

- all the layers in the PS doc are exported but as individual pngs, not merged into one?

- could each png be trimmed to remove the alpha, but still be divisible by 16? I guess the layer would be trimmed first then checked for it's divisibility. Then finally pixels would be added or not added.

 

Many many thanks for your expertise!

Steve

Votes

Translate

Translate

Report

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
Community Expert ,
Jul 16, 2023 Jul 16, 2023

Copy link to clipboard

Copied

I didn't expect you to run the script without a layer being selected, as the script was clearly stated to export the active/selected layer! So I'm glad that is now sorted.

 

I have updated the code to a 1.2 version to ensure that a doc is open and that a layer is selected.

Yes, your additional requirements are all possible and shouldn't be too hard to include. I'll post a new script when I have time.

Votes

Translate

Translate

Report

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
Community Expert ,
Jul 16, 2023 Jul 16, 2023

Copy link to clipboard

Copied

quote

Many thanks for that update, I got thte same error however but that's when no layer(s) are selected. The script worked after making sure at least one layer was selected 👍🏻

Would it be possible to tweak this with the following:

- all the layers in the PS doc are exported but as individual pngs, not merged into one?

- could each png be trimmed to remove the alpha, but still be divisible by 16? I guess the layer would be trimmed first then checked for it's divisibility. Then finally pixels would be added or not added.

 

Many many thanks for your expertise!

Steve


By @SteviePee2

 

Please try the following script. Note: There is no validation or correction for invalid filename characters in the exported layer name used as the file name! It is also presumed that all layer names are unique.

 

/*
Export All Layers to PNG Canvas Force Divisible by 16px.jsx
https://community.adobe.com/t5/photoshop-ecosystem-discussions/photoshop-script-that-checks-each-layer-s-dimensions-are-divisible-by-n-during-png-export/td-p/13938692
Based on code from jazz-y - https://community.adobe.com/t5/photoshop-ecosystem-discussions/save-an-image-so-it-s-width-amp-height-are-divisible-by-a-required-number/m-p/12819944
v1.0 - 17th July 2023, Stephen Marsh
Note: There is no validation or correction for invalid filename characters in the exported layer name!
*/

#target photoshop

if (app.documents.length > 0) {

    if (!documents.length) {
        alert('There are no documents open!');
    } else {

        try {
            var outputPath = app.activeDocument.path.fsName;
        } catch (e) {
            var outputPath = Folder.selectDialog("Unsaved base file, select the output folder:");
        }
        var counter = 0;
        processAllLayersAndSets(app.activeDocument);
    }

    // Loop over top level layers
    function processAllLayersAndSets(obj) {
        // Process all layers and layer sets
        // Change the following 2 entries of "obj.layers" to "obj.artLayers" to exclude layer sets
        for (var al = obj.layers.length - 1; 0 <= al; al--) {
            app.activeDocument.activeLayer = obj.layers[al];
            div16toPNG();
        }
    }

    function div16toPNG() {

        // Skip the Background layer
        if (!activeDocument.activeLayer.isBackgroundLayer) {

            var filename = app.activeDocument.activeLayer.name;
            dupeSelectedLayers(filename);
            activeDocument.trim(TrimType.TRANSPARENT);

            var s2t = stringIDToTypeID,
                res = executeActionGet(getPropertyRef(p = s2t('resolution'))).getUnitDoubleValue(p),
                w = executeActionGet(getPropertyRef(p = s2t('width'))).getUnitDoubleValue(p) * res / 72,
                h = executeActionGet(getPropertyRef(p = s2t('height'))).getUnitDoubleValue(p) * res / 72;
            (d = new ActionDescriptor()).putUnitDouble(s2t('width'), s2t('pixelsUnit'), (Math.ceil(w / 16)) * 16);
            d.putUnitDouble(s2t('height'), s2t('pixelsUnit'), (Math.ceil(h / 16)) * 16);
            d.putEnumerated(s2t('horizontal'), s2t('horizontalLocation'), s2t('center'));
            d.putEnumerated(s2t('vertical'), s2t('verticalLocation'), s2t('center'));

            executeAction(s2t('canvasSize'), d, DialogModes.NO);

            var pngOptions = new ExportOptionsSaveForWeb();
            pngOptions.PNG8 = false;
            pngOptions.transparency = true;
            pngOptions.interlaced = false;
            pngOptions.quality = 100;
            pngOptions.includeProfile = true;
            pngOptions.format = SaveDocumentType.PNG;
            var pngFile = new File(outputPath + "/" + filename + ".png");

            if (pngFile.exists) {
                // true = 'No' as default active button
                if (!confirm("The file: '" + pngFile.name + "' - already exists!" + "\r" + "Overwrite: Yes or No?", true))
                    // throw alert("Script cancelled!");
                    throw null;
            }
            
            app.activeDocument.exportDocument(pngFile, ExportType.SAVEFORWEB, pngOptions);

            counter++;

            activeDocument.close(SaveOptions.DONOTSAVECHANGES);

            function dupeSelectedLayers(theDocName) {
                function s2t(s) {
                    return app.stringIDToTypeID(s);
                }
                var descriptor = new ActionDescriptor();
                var reference = new ActionReference();
                var reference2 = new ActionReference();
                reference.putClass(s2t("document"));
                descriptor.putReference(s2t("null"), reference);
                descriptor.putString(s2t("name"), theDocName);
                reference2.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
                descriptor.putReference(s2t("using"), reference2);
                executeAction(s2t("make"), descriptor, DialogModes.NO);
            }

            function getPropertyRef(property) {
                (r = new ActionReference()).putProperty(s2t('property'), property);
                r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
                return r;
            }
        }
    }

    // Note: The Background layer is excluded from the length when using .artLayers vs .layers, layer sets are included in the count
    alert(activeDocument.layers.length + " top-level layers exported to " + counter + " PNG files at:" + "\r" + outputPath);

} else {
    alert("You must have a document open!");
}

 

 

Votes

Translate

Translate

Report

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
Community Beginner ,
Jul 24, 2023 Jul 24, 2023

Copy link to clipboard

Copied

Hey @Stephen Marsh , apologies for taking a while to respond. Thank you so much for helping me with this conundrum, I tried your code and it works a treat! All layers in the PSD are being exported as required, layers not divisible 16 being adjusted and layers already divisible by 16 not effected and exported.

Just one thing extra, is it possible to input a location for the export? A line in the code I could update maybe as needed?

Otherwise the code is perfect! Thanks again.

Votes

Translate

Translate

Report

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
Community Expert ,
Jul 24, 2023 Jul 24, 2023

Copy link to clipboard

Copied

quote

Hey @Stephen Marsh , apologies for taking a while to respond. Thank you so much for helping me with this conundrum, I tried your code and it works a treat! All layers in the PSD are being exported as required, layers not divisible 16 being adjusted and layers already divisible by 16 not effected and exported.

 

You're welcome, thank you for the feedback. Full credit to @jazz-y for the code to check if the document canvas is divisible by 16.

 

quote

Just one thing extra, is it possible to input a location for the export? A line in the code I could update maybe as needed?

Otherwise the code is perfect! Thanks again.


By @SteviePee2

 

Certainly, that is easy enough.

 

Change this code block from:

 

try {
            var outputPath = app.activeDocument.path.fsName;
        } catch (e) {
            var outputPath = Folder.selectDialog("Unsaved base file, select the output folder:");
        }

 

To this:

 

var outputPath = Folder.selectDialog("Select the output folder:");

 

EDIT: You could hard code in the save path, however, the folder selection is more flexible. It's also possible to set a default location for the folder selection window, which is the best of both worlds.

Votes

Translate

Translate

Report

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
Community Beginner ,
Jul 25, 2023 Jul 25, 2023

Copy link to clipboard

Copied

LATEST

Perfect! Thanks again for all your help and guidance. I'm trying to learn more about creating scripts for PS but it's a steep learning curve for my animator brain 😋

Many thanks to @jazz-y too for your help too!

Votes

Translate

Translate

Report

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