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

How to make a smart object that would retain the crop of the original image

Community Beginner ,
Feb 28, 2023 Feb 28, 2023

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?

TOPICS
macOS , Windows
2.6K
Translate
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 , Mar 01, 2023 Mar 01, 2023

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

...
Translate
Community Beginner , Mar 09, 2023 Mar 09, 2023

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

...
Translate
Adobe
Community Expert ,
Mar 01, 2023 Mar 01, 2023

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? 

Translate
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 ,
Mar 01, 2023 Mar 01, 2023

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.

Translate
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 ,
Mar 01, 2023 Mar 01, 2023

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.

Translate
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 ,
Mar 08, 2023 Mar 08, 2023

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
	};
Translate
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 ,
Mar 09, 2023 Mar 09, 2023

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 );

Translate
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 ,
Mar 08, 2023 Mar 08, 2023

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. 

Translate
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 ,
Mar 09, 2023 Mar 09, 2023

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.

 

 

 

 

Translate
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 ,
Mar 09, 2023 Mar 09, 2023

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);

 

Translate
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 ,
Apr 02, 2025 Apr 02, 2025

@levsavitskiy 

 

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'?

Translate
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 ,
Apr 02, 2025 Apr 02, 2025
LATEST
quote

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«. 

Translate
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