About how to automatically cut PSD files
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);
}
