Skip to main content
Participant
July 18, 2024
Question

About how to automatically cut PSD files

  • July 18, 2024
  • 0 replies
  • 90 views

Creating a script to automatically cut PSD files. The conditions are as follows.

1. Select multiple PSD files and a storage location.
2. Open the corresponding psd file.
3. In all psd files, create a guide line with an interval of 5110px or less in a place that is not above text, shape, and smart object.
4. Take the location of the guide line, crop it, and save it.

5. When cropping, remove all off-screen layers.

 

The parts that are not currently in progress are No. 3 and No. 5. Especially for No. 3, a guide line is created and cropped on the text, shape, and smart object. In case of No. 5, the separately produced one works well, but when I try to match the two codes, the layers outside the screen are not removed. How can I fix the code?

 

 

/*
    crop_psd_auto.jsx
*/

var selectFiles = File.openDialog("select file", "*.psd;*.psb;", true); // multiselect
var saveFolder = Folder.selectDialog("select folder"); 

for (var index = 0; index < selectFiles.length; index++) {
    var openFile = selectFiles[index];

    // psd open
    var psdFile = File(openFile);
    open(psdFile);
    var oldUnits = preferences.rulerUnits;
    preferences.rulerUnits = Units.PIXELS;
    var doc = app.activeDocument;
    var guides = doc.guides;

    // psd size
    var psdHeight = doc.height.value; 
    var psdWidth = doc.width.value;  

    // crop save
    var filename = psdFile.name;    // psdFile.displayName;
    var onlyname = filename.split('.').slice(0, -1).join('.');
    var foldername = psdFile.parent.name;  
    var mkFolder = new Folder(saveFolder + "/PSD_" + foldername + "_" + onlyname);  
    mkFolder.create();  

    // Function to create horizontal guides at intervals
    function createGuides() {
        var interval = 5110;
        var guides = [];
        var y = interval;

        // Loop through document height to add guides
        while (y < psdHeight) {
            if (!isGuideOverLayer(y, doc)) {
                guides.push(y);
                y += interval;
            } else {
                y += interval / 10; // Increment by a smaller value to avoid overlapping with text or shape layers
            }
        }

        return guides;
    }
    
    // Function to check if a guide is over a restricted layer
    function isGuideOverLayer(y, doc) {
        for (var j = 0; j < doc.layers.length; j++) {
            var layer = doc.layers[j];
            
            if (layer.kind == LayerKind.TEXT || layer.kind == LayerKind.SOLIDFILL || layer.kind == LayerKind.SMARTOBJECT) {
                var bounds = layer.bounds;
                var top = bounds[1].as('px');
                var bottom = bounds[3].as('px');
                
                if (y > top && y < bottom) {
                    return true;
                }
            }
        }
        return false;
    }

    // Function to check if the crop area intersects with any restricted layers
    function isCropAreaValid(left_Y, right_Y, doc) {
        for (var j = 0; j < doc.layers.length; j++) {
            var layer = doc.layers[j];
            
            if (layer.kind == LayerKind.TEXT || layer.kind == LayerKind.SOLIDFILL || layer.kind == LayerKind.SMARTOBJECT) {
                var bounds = layer.bounds;
                var top = bounds[1].as('px');
                var bottom = bounds[3].as('px');
                
                if ((left_Y < bottom && right_Y > top)) {
                    return false; // The crop area intersects with a restricted layer
                }
            }
        }
        return true;
    }
    
    // Run the script
    var guides = createGuides();

    var psdOptions = new PhotoshopSaveOptions();
    var numGuides = guides.length;
    var left_Y = 0;
    for (var i = 0; i <= numGuides; i++) {
        var right_Y = (i == numGuides) ? psdHeight : guides[i];

        // Ensure crop area is valid
        if (!isCropAreaValid(left_Y, right_Y, doc)) {
            var tempRight_Y = left_Y + 5110;
            while (!isCropAreaValid(left_Y, tempRight_Y, doc) && tempRight_Y < psdHeight) {
                tempRight_Y += 10; // Increment by a small value to find a valid crop area
            }
            right_Y = tempRight_Y;
        }

        var bounds = [0, left_Y, psdWidth, right_Y];
        var newDoc = doc.duplicate();
        newDoc.crop(bounds);
        var newFilename = mkFolder + "/" + onlyname + "_" + (i + 1) + ".psd";
        newDoc.saveAs(File(newFilename), psdOptions, true);
        newDoc.close(SaveOptions.DONOTSAVECHANGES);
        left_Y = right_Y;
    }

    // Function to remove layers
    function removeLayers() {
        var doc = new AM('document'),
            lr = new AM('layer'),
            layers = getLayersCollection(doc, lr),
            canvas = [],
            locked = [];

        parseLayers(layers, layers, canvas, locked, lr);

        if (layers.length) {
            if (!canvas.length) {
                var res = doc.getProperty('resolution');
                canvas = [{
                    top: 0,
                    left: 0,
                    right: doc.getProperty('width') * res / 72,
                    bottom: doc.getProperty('height') * res / 72
                }];
            }

            var layersToDelete = [];
            do {
                var intersected = false,
                    cur = layers.shift();
                for (var i = 0; i < canvas.length; i++) {
                    if (intersect(cur.bounds, canvas[i]) && !(cur.bounds.top === 0 && cur.bounds.right === 0 && cur.bounds.left === 0 && cur.bounds.right === 0)) {
                        intersected = true;
                        break;
                    }
                }
                if (!intersected) layersToDelete.push(cur.id);
            } while (layers.length);

            if (layersToDelete.length) {
                doc.deleteLayers(layersToDelete);
                layers = getLayersCollection(doc, lr);
                var groupsToDelete = [];
                cleanupEmptyGroups(layers, groupsToDelete);
                if (groupsToDelete.length) doc.deleteLayers(groupsToDelete);
            }

            if (locked.length) {
                do {
                    var cur = locked.shift();
                    try {
                        lr.applyLocking(cur.id, cur.layerLocking);
                    } catch (e) {}
                } while (locked.length);
            }
        }
    }

    function intersect(layerA, layerB) {
        return !(layerB.right < layerA.left || layerB.left > layerA.right || layerB.top > layerA.bottom || layerB.bottom < layerA.top);
    }

    function parseLayers(layersCollection, layers, canvas, locked, lr) {
        for (var i = 0; i < layersCollection.length; i++) {
            if (isLocked(layersCollection[i].layerLocking)) {
                locked.push({ layerLocking: layersCollection[i].layerLocking, id: layersCollection[i].id });
                lr.applyLocking(layersCollection[i].id, {
                    "protectAll": false,
                    "protectArtboardAutonest": false,
                    "protectComposite": false,
                    "protectPosition": false,
                    "protectTransparency": false
                });
            }

            if (layersCollection[i].artboardRect) {
                canvas.push(layersCollection[i].artboardRect);
            }

            if (layersCollection[i].layerKind == 7) {
                parseLayers(layersCollection[i], layers, canvas, locked, lr);
            } else {
                layers.push(layersCollection[i]);
            }
        }
    }

    function cleanupEmptyGroups(layersCollection, toDelete) {
        var isEmpty = true;
        for (var i = 0; i < layersCollection.length; i++) {
            if (layersCollection[i].layerKind == 7 && !layersCollection[i].artboardRect) {
                if (layersCollection[i].length) {
                    if (cleanupEmptyGroups(layersCollection[i], toDelete) && layersCollection[i].id) {
                        toDelete.push(layersCollection[i].id);
                    } else {
                        isEmpty = false;
                    }
                } else {
                    toDelete.push(layersCollection[i].id);
                }
            } else {
                isEmpty = false;
            }
        }
        return isEmpty;
    }

    function getLayersCollection(document, layer) {
        var indexFrom = document.getProperty('hasBackgroundLayer') ? 0 : 1,
            indexTo = document.getProperty('numberOfLayers');

        return layersCollection(indexFrom, indexTo);

        function layersCollection(from, to, parentItem, group) {
            parentItem = parentItem || [];

            for (var i = from; i <= to; i++) {
                if (layer.getProperty('layerSection', i, true).value == 'layerSectionEnd') {
                    i = layersCollection(i + 1, to, [], parentItem);
                    continue;
                }

                var p = {};
                p.id = layer.getProperty('layerID', i, true);
                p.layerKind = layer.getProperty('layerKind', i, true);
                p.layerLocking = layer.descToObject(layer.getProperty('layerLocking', i, true).value);
                p.bounds = [layer.descToObject(layer.getProperty('bounds', i, true).value)];

                if (layer.hasProperty('boundsNoMask')) p.bounds.push(layer.descToObject(layer.getProperty('boundsNoMask', i, true).value));
                if (layer.hasProperty('boundsNoEffects')) p.bounds.push(layer.descToObject(layer.getProperty('boundsNoEffects', i, true).value));

                p.bounds = p.bounds.sort(function (a, b) {
                    return (a.right - a.left) + (a.bottom - a.top) > (b.right - b.left) + (b.bottom - b.top) ? 1 : 0;
                }).shift();

                if (p.layerKind == 7 && layer.getProperty('artboardEnabled', i, true)) {
                    p.artboardRect = layer.descToObject(layer.getProperty('artboard', i, true).value.getObjectValue(stringIDToTypeID('artboardRect')));
                }

                if (layer.getProperty('layerSection', i, true).value == 'layerSectionStart') {
                    for (var o in p) {
                        parentItem[o] = p[o];
                    }
                    group.push(parentItem);
                    return i;
                } else {
                    parentItem.push(p);
                }
            }
            return parentItem;
        }
    }

    function isLocked(locking) {
        for (var a in locking) {
            if (locking[a]) return true;
        }
        return false;
    }

    function AM(target, order) {
        var s2t = stringIDToTypeID,
            t2s = typeIDToStringID;

        target = target ? s2t(target) : null;

        this.getProperty = function (property, id, idxMode) {
            property = s2t(property);
            (r = new ActionReference()).putProperty(s2t('property'), property);
            id !== undefined ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id)) :
                r.putEnumerated(target, s2t('ordinal'), order ? s2t(order) : s2t('targetEnum'));
            return getDescValue(executeActionGet(r), property);
        };

        this.hasProperty = function (property, id, idxMode) {
            property = s2t(property);
            (r = new ActionReference()).putProperty(s2t('property'), property);
            id ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id)) :
                r.putEnumerated(target, s2t('ordinal'), order ? s2t(order) : s2t('targetEnum'));
            return executeActionGet(r).hasKey(property);
        };

        this.descToObject = function (d) {
            var o = {};
            for (var i = 0; i < d.count; i++) {
                var k = d.getKey(i);
                o[t2s(k)] = getDescValue(d, k);
            }
            return o;
        };

        this.objectToDesc = function (o) {
            var d = new ActionDescriptor();
            for (var k in o) {
                var v = o[k];
                switch (typeof (v)) {
                    case 'boolean':
                        d.putBoolean(s2t(k), v);
                        break;
                    case 'string':
                        d.putString(s2t(k), v);
                        break;
                    case 'number':
                        d.putInteger(s2t(k), v);
                        break;
                }
            }
            return d;
        };

        this.applyLocking = function (id, locking) {
            (r = new ActionReference()).putIdentifier(s2t('layer'), id);
            (d = new ActionDescriptor()).putReference(s2t('null'), r);
            d.putObject(s2t('layerLocking'), s2t('layerLocking'), this.objectToDesc(locking));
            executeAction(s2t('applyLocking'), d, DialogModes.NO);
        };

        this.selectLayers = function (list) {
            var r = new ActionReference();
            do { r.putIdentifier(s2t('layer'), list.shift()); } while (list.length);
            (d = new ActionDescriptor()).putReference(s2t('null'), r);
            executeAction(s2t('select'), d, DialogModes.NO);
        };

        this.deleteLayers = function (list) {
            var r = new ActionReference();
            do { r.putIdentifier(s2t('layer'), list.shift()); } while (list.length);
            (d = new ActionDescriptor()).putReference(s2t('null'), r);
            executeAction(s2t('delete'), d, DialogModes.NO);
        };

        function getDescValue(d, p) {
            switch (d.getType(p)) {
                case DescValueType.OBJECTTYPE:
                    return { type: t2s(d.getObjectType(p)), value: d.getObjectValue(p) };
                case DescValueType.LISTTYPE:
                    return d.getList(p);
                case DescValueType.REFERENCETYPE:
                    return d.getReference(p);
                case DescValueType.BOOLEANTYPE:
                    return d.getBoolean(p);
                case DescValueType.STRINGTYPE:
                    return d.getString(p);
                case DescValueType.INTEGERTYPE:
                    return d.getInteger(p);
                case DescValueType.LARGEINTEGERTYPE:
                    return d.getLargeInteger(p);
                case DescValueType.DOUBLETYPE:
                    return d.getDouble(p);
                case DescValueType.ALIASTYPE:
                    return d.getPath(p);
                case DescValueType.CLASSTYPE:
                    return d.getClass(p);
                case DescValueType.UNITDOUBLE:
                    return (d.getUnitDoubleValue(p));
                case DescValueType.ENUMERATEDTYPE:
                    return { type: t2s(d.getEnumerationType(p)), value: t2s(d.getEnumerationValue(p)) };
                default:
                    break;
            }
        }
    }

    doc.close(SaveOptions.DONOTSAVECHANGES);   
}

 

 

This topic has been closed for replies.