Copy link to clipboard
Copied
Hey everyone!
I've been trying to either write a script or come up with a quicker way to do what I've written in the Subject.
The idea is, you have a composition with tons of layers, some are going beyond the frame, some are within, but you have a certain crop. If you try to convert everything into a smart object, it will first run "Reveal All" command and then convert everything into a smart object, which means when you go inside that smart object, you will see every single pixel that was there, the original crop would be gone, it will just adapt a new crop to show whatever pixels you had in there.
My colleague found a way, that if you save the file that you want to convert to a smart object first and then drop this exact file into itself in photoshop as a smart object, it will actually retain the exact same crop when you try to go inside that smart object.
My question, it there an easier way through scripting or some hidden command that I'm not aware of, that you can just select all layers, convert them to a smart object and retain the exact same crop of the original file?
The idea is, you have a composition with tons of layers, some are going beyond the frame, some are within, but you have a certain crop. If you try to convert everything into a smart object, it will first run "Reveal All" command and then convert everything into a smart object, which means when you go inside that smart object, you will see every single pixel that was there, the original crop would be gone, it will just adapt a new crop to show whatever pixels you had in there.
The conversion simp
...I actually figured it out! Read about the fullName attribute of the file and it seemed to work.
Here's the working script exactly for what I needed:
// Get a reference to the active document
var doc = app.activeDocument;
// Get the file name of the active document
var fileName = doc.name;
// Get the file path of the active document
var filePath = doc.path;
var docName = doc.fullName;
var convertToSmartObject = function () {
var idnewPlacedLayer = stringIDToTypeID('newPlacedLayer');
executeAction(idnewPl
Copy link to clipboard
Copied
The idea is, you have a composition with tons of layers, some are going beyond the frame, some are within, but you have a certain crop. If you try to convert everything into a smart object, it will first run "Reveal All" command and then convert everything into a smart object, which means when you go inside that smart object, you will see every single pixel that was there, the original crop would be gone, it will just adapt a new crop to show whatever pixels you had in there.
The conversion simply uses the available content; if the Layer is smaller than the Canvas Size would you want the resulting SO to have the Canvas Size or just the pixel content’s?
Why exactly do you want the SOs to have the containing document’s Canvas Size?
• For Pixel Layers you could destroy the off-canvas content before converting with
Select > All
Image > Crop
• Alternatively you could apply a Select > All-Layer Mask to each Layer before converting. (Naturally that would be a problem if Layers already have Layer Masks.)
• A Script could group the Layers, apply a Layer Mask to the Group, convert to SO, open SO, crop to the Group’s Mask, save and close, center the SO in the containing document; do you have experience with JavaScript and Photoshop Scripting?
Copy link to clipboard
Copied
The idea is to retain all the data that was originally there, but keep the same crop. I don't want to crop out elements that can be outside of the border, in case someone needs to move elements. Besides, some elements/models can be as their own smartobjects and also go beyond the border. If you just create a SO as is, it will reveal everything there is. If you drop the file into itself, it will create a smart object of it, but keep the exact same crop, because essentially when opening the SO, you'll be opening the original file.
The script that you've suggested should do exactly that. I'm not very good with those, but I can try figuring it out. In fact, I think you can pull something similar with just actions. Hopefully, it will be quicker than dropping the document into itself, so you can safe a bit of time doing that.
Copy link to clipboard
Copied
In fact, I just tried it and it worked! You can do it with actions, but there are two minor things that I would like to correct, but at least it works.
Basically, it's exactly what you suggested:
1) Select all
2) Apply mask
3) Create a group
4) Convert to SO
5) Open the SO
6) Select current mask
7) Crop, Apply crop
8) Move everything outside the group and delete the group with mask (just to make it cleaner and so it looks like the original)
9) Save and close
10) Select all
11) Center horizontally and vertically
12) Deselect
The only thing I would like to add here is that the final smart object gets the name of the file, but that will most likely require using scripts. I am not aware of a way to do that with actions.
Copy link to clipboard
Copied
This should name the active Layer with the document’s name minus the file extension.
// 2015; use it at your own risk;
// thanks to xbytor;
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
var theName = myDocument.name;
if (theName.indexOf(".") != -1) {
var myDocName = myDocument.name.match(/(.*)\.[^\.]+$/)[1]
}
else {
var myDocName = theName
};
var theLayer = myDocument.activeLayer;
if (theLayer.isBackgroundLayer == true) {
theLayer.isBackgroundLayer = false;
}
theLayer.name = myDocName
};
Copy link to clipboard
Copied
In case someone is looking for the actual script for that, I've managed to write one. It's a combo between listened and what I found online, so it' s a messy, but i's workable.
Here it is:
selectAllLayers();
function selectAllLayers() {
// Select all layers (doesn't include Background)
try {
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
desc.putReference( charIDToTypeID('null'), ref );
executeAction( stringIDToTypeID('selectAllLayers'), desc, DialogModes.NO );
} catch(e) {}
// Add Background Layer to the selection (if possible)
try {
activeDocument.backgroundLayer;
var bgID = activeDocument.backgroundLayer.id;
var ref = new ActionReference();
var desc = new ActionDescriptor();
ref.putIdentifier(charIDToTypeID('Lyr '), bgID);
desc.putReference(charIDToTypeID('null'), ref);
desc.putEnumerated( stringIDToTypeID('selectionModifier'), stringIDToTypeID('selectionModifierType'), stringIDToTypeID('addToSelection') );
desc.putBoolean(charIDToTypeID('MkVs'), false);
executeAction(charIDToTypeID('slct'), desc, DialogModes.NO);
} catch(e) {}
}
// Group all layers
var idMk = charIDToTypeID( "Mk " );
var desc15 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref6 = new ActionReference();
var idlayerSection = stringIDToTypeID( "layerSection" );
ref6.putClass( idlayerSection );
desc15.putReference( idnull, ref6 );
var idFrom = charIDToTypeID( "From" );
var ref7 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref7.putEnumerated( idLyr, idOrdn, idTrgt );
desc15.putReference( idFrom, ref7 );
var idlayerSectionStart = stringIDToTypeID( "layerSectionStart" );
desc15.putInteger( idlayerSectionStart, 738 );
var idlayerSectionEnd = stringIDToTypeID( "layerSectionEnd" );
desc15.putInteger( idlayerSectionEnd, 739 );
var idNm = charIDToTypeID( "Nm " );
desc15.putString( idNm, """Group 5""" );
executeAction( idMk, desc15, DialogModes.NO );
// Get the currently opened file
var file = app.activeDocument;
// Get the name of the file
var fileName = file.name;
// Remove the file extension from the name
fileName = fileName.replace(/\.[^\.]+$/, '');
// Get the current layer
var layer = app.activeDocument.activeLayer;
// Rename the layer
layer.name = fileName;
// Duplicate folder
layer.duplicate();
// Select group above
var idslct = charIDToTypeID( "slct" );
var desc19 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref11 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
var idOrdn = charIDToTypeID( "Ordn" );
var idFrwr = charIDToTypeID( "Frwr" );
ref11.putEnumerated( idLyr, idOrdn, idFrwr );
desc19.putReference( idnull, ref11 );
var idMkVs = charIDToTypeID( "MkVs" );
desc19.putBoolean( idMkVs, false );
var idLyrI = charIDToTypeID( "LyrI" );
var list2 = new ActionList();
list2.putInteger( 1197 );
desc19.putList( idLyrI, list2 );
executeAction( idslct, desc19, DialogModes.NO );
app.activeDocument.selection.selectAll(); // select the entire canvas
//Create mask of the selection
addMasks();
function addMasks(){
try{
loadLayerSelection();
addLayerMask();
} catch (e) {}
};
// =======================================================
function loadLayerSelection() {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference.putProperty( s2t( "channel" ), s2t( "selection" ));
descriptor.putReference( c2t( "null" ), reference );
reference2.putEnumerated( s2t( "channel" ), s2t( "channel" ), s2t( "transparencyEnum" ));
descriptor.putReference( s2t( "to" ), reference2 );
executeAction( s2t( "set" ), descriptor, DialogModes.NO );
}
// =======================================================
function addLayerMask() {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
descriptor.putClass( s2t( "new" ), s2t( "channel" ));
reference.putEnumerated( s2t( "channel" ), s2t( "channel" ), s2t( "mask" ));
descriptor.putReference( s2t( "at" ), reference );
descriptor.putEnumerated( s2t( "using" ), c2t( "UsrM" ), s2t( "revealSelection" ));
executeAction( s2t( "make" ), descriptor, DialogModes.NO );
}
// =======================================================
function deleteLayerMask(apply) {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
reference.putEnumerated( s2t( "channel" ), s2t( "channel" ), s2t( "mask" ));
descriptor.putReference( c2t( "null" ), reference );
descriptor.putBoolean( s2t( "apply" ), apply );
executeAction( s2t( "delete" ), descriptor, DialogModes.NO );
}
// Convert to smart object
createSmartObject();
function createSmartObject() {
var idnewPlacedLayer = stringIDToTypeID( 'newPlacedLayer' );
executeAction(idnewPlacedLayer, undefined, DialogModes.NO);
}
// Open Smart Object
var idplacedLayerEditContents = stringIDToTypeID( "placedLayerEditContents" );
var desc3 = new ActionDescriptor();
var idDocI = charIDToTypeID( "DocI" );
desc3.putInteger( idDocI, 5031 );
var idLyrI = charIDToTypeID( "LyrI" );
desc3.putInteger( idLyrI, 466 );
executeAction( idplacedLayerEditContents, desc3, DialogModes.NO );
// Trim to visible
var idtrim = stringIDToTypeID( "trim" );
var desc382 = new ActionDescriptor();
var idtrimBasedOn = stringIDToTypeID( "trimBasedOn" );
var idtrimBasedOn = stringIDToTypeID( "trimBasedOn" );
var idTrns = charIDToTypeID( "Trns" );
desc382.putEnumerated( idtrimBasedOn, idtrimBasedOn, idTrns );
var idTop = charIDToTypeID( "Top " );
desc382.putBoolean( idTop, true );
var idBtom = charIDToTypeID( "Btom" );
desc382.putBoolean( idBtom, true );
var idLeft = charIDToTypeID( "Left" );
desc382.putBoolean( idLeft, true );
var idRght = charIDToTypeID( "Rght" );
desc382.putBoolean( idRght, true );
executeAction( idtrim, desc382, DialogModes.NO );
// Delete current mask
function deleteLayerMask() {
try{
var idDlt = charIDToTypeID( "Dlt " );
var desc6 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref5 = new ActionReference();
var idChnl = charIDToTypeID( "Chnl" );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref5.putEnumerated( idChnl, idOrdn, idTrgt );
desc6.putReference( idnull, ref5 );
executeAction( idDlt, desc6, DialogModes.NO );
}catch(e) {
; // do nothing
}
}
deleteLayerMask();
// Get the active document
var doc = app.activeDocument;
// Save the document
doc.save();
// Close current Document
var idCls = charIDToTypeID( "Cls " );
var desc391 = new ActionDescriptor();
var idDocI = charIDToTypeID( "DocI" );
desc391.putInteger( idDocI, 6840 );
var idforceNotify = stringIDToTypeID( "forceNotify" );
desc391.putBoolean( idforceNotify, true );
executeAction( idCls, desc391, DialogModes.NO );
// Rename the layer to the currently opened fileName
// Get the currently opened file
var file = app.activeDocument;
// Get the name of the file
var fileName = file.name;
// Remove the file extension from the name
fileName = fileName.replace(/\.[^\.]+$/, '');
// Get the current layer
var layer = app.activeDocument.activeLayer;
// Rename the layer
layer.name = fileName;
// Select the whole canvas
app.activeDocument.selection.selectAll();
// Align horizontally
var idAlgn = charIDToTypeID( "Algn" );
var desc395 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref9 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref9.putEnumerated( idLyr, idOrdn, idTrgt );
desc395.putReference( idnull, ref9 );
var idUsng = charIDToTypeID( "Usng" );
var idADSt = charIDToTypeID( "ADSt" );
var idAdCH = charIDToTypeID( "AdCH" );
desc395.putEnumerated( idUsng, idADSt, idAdCH );
var idalignToCanvas = stringIDToTypeID( "alignToCanvas" );
desc395.putBoolean( idalignToCanvas, false );
executeAction( idAlgn, desc395, DialogModes.NO );
// Align vertically
var idAlgn = charIDToTypeID( "Algn" );
var desc403 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref10 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref10.putEnumerated( idLyr, idOrdn, idTrgt );
desc403.putReference( idnull, ref10 );
var idUsng = charIDToTypeID( "Usng" );
var idADSt = charIDToTypeID( "ADSt" );
var idAdCV = charIDToTypeID( "AdCV" );
desc403.putEnumerated( idUsng, idADSt, idAdCV );
var idalignToCanvas = stringIDToTypeID( "alignToCanvas" );
desc403.putBoolean( idalignToCanvas, false );
executeAction( idAlgn, desc403, DialogModes.NO );
// Deselect all
var idsetd = charIDToTypeID( "setd" );
var desc406 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref11 = new ActionReference();
var idChnl = charIDToTypeID( "Chnl" );
var idfsel = charIDToTypeID( "fsel" );
ref11.putProperty( idChnl, idfsel );
desc406.putReference( idnull, ref11 );
var idT = charIDToTypeID( "T " );
var idOrdn = charIDToTypeID( "Ordn" );
var idNone = charIDToTypeID( "None" );
desc406.putEnumerated( idT, idOrdn, idNone );
executeAction( idsetd, desc406, DialogModes.NO );
Copy link to clipboard
Copied
One issue with the Script could be that centering the cropped SO would need the SO to contain pixel data across the whole canvas to be able to center it reliably.
Copy link to clipboard
Copied
I actually managed to write a whole script that does exactly what you suggested and those steps I wrote for the action. The layer renaming part looks like this in my version:
// Rename the layer to the currently opened fileName
// Get the currently opened file
var file = app.activeDocument;
// Get the name of the file
var fileName = file.name;
// Remove the file extension from the name
fileName = fileName.replace(/\.[^\.]+$/, '');
// Get the current layer
var layer = app.activeDocument.activeLayer;
// Rename the layer
layer.name = fileName;
It works just as intended, but I just found out that the approach you've initially suggested turned out to be way slower, than just dropping already saved document into itself. The bigger the file, the bigger the difference.
So now I've been trying to write a script to do exactly that, but I'm afraid my knowledge is not enough to figure it out.
I ended up with the following one:
// Get a reference to the active document
var doc = app.activeDocument;
// Get the file name of the active document
var fileName = doc.name;
// Get the file path of the active document
var filePath = doc.path;
var convertToSmartObject = function () {
var idnewPlacedLayer = stringIDToTypeID('newPlacedLayer');
executeAction(idnewPlacedLayer, undefined, DialogModes.NO);
};
var replaceSmartObjectContents = function (fileName, smartObj) {
app.activeDocument.activeLayer = smartObj;
var idplacedLayerReplaceContents = stringIDToTypeID("placedLayerReplaceContents");
var desc3 = new ActionDescriptor();
var idnull = charIDToTypeID("null");
desc3.putPath(idnull, new File(fileName));
var idPgNm = charIDToTypeID("PgNm");
desc3.putInteger(idPgNm, 1);
executeAction(idplacedLayerReplaceContents, desc3, DialogModes.NO);
};
// Add a layer, this will now be the active layer
doc.artLayers.add();
// Convert active layer to Smart Object
convertToSmartObject();
// replace a provided layer with a provided file path
replaceSmartObjectContents(fileName, doc.activeLayer);
It creates an empty layer, turns it into a smart object and then should replace the contents of it with the same document that is opened, but in the case of this script it opens the Open file dialog box where you need to manually navigate to the file. It works, but it's semi-automatic. I cannot figure out a way to force it to grab the file without the Open dialog screen.
Copy link to clipboard
Copied
I actually figured it out! Read about the fullName attribute of the file and it seemed to work.
Here's the working script exactly for what I needed:
// Get a reference to the active document
var doc = app.activeDocument;
// Get the file name of the active document
var fileName = doc.name;
// Get the file path of the active document
var filePath = doc.path;
var docName = doc.fullName;
var convertToSmartObject = function () {
var idnewPlacedLayer = stringIDToTypeID('newPlacedLayer');
executeAction(idnewPlacedLayer, undefined, DialogModes.NO);
};
var replaceSmartObjectContents = function (docName, filePath) {
app.activeDocument.activeLayer = filePath;
var idplacedLayerReplaceContents = stringIDToTypeID("placedLayerReplaceContents");
var desc3 = new ActionDescriptor();
var idnull = charIDToTypeID("null");
desc3.putPath(idnull, new File(docName));
var idPgNm = charIDToTypeID("PgNm");
desc3.putInteger(idPgNm, 1);
executeAction(idplacedLayerReplaceContents, desc3, DialogModes.NO);
};
// Add a layer, this will now be the active layer
doc.artLayers.add();
// Convert active layer to Smart Object
convertToSmartObject();
// replace a provided layer with a provided file path
replaceSmartObjectContents(docName, doc.activeLayer);
Copy link to clipboard
Copied
What a great script.... thanks!
It does what Phoroshop should do (or have the option to do).
I would guess there is no way of opening an previously created Smart Object with the same crop as the 'parent'?
Copy link to clipboard
Copied
I would guess there is no way of opening an previously created Smart Object with the same crop as the 'parent'?
Please explain what you actually mean, ideally with the help of meaningful screenshots to clarify the layer structure and what exactly you mean by the »crop« you want to »transfer«.
Find more inspiration, events, and resources on the new Adobe Community
Explore Now